04 ES6标准入门(对象的扩展)

==对原型、super关键字的理解还是模模糊糊,super指向当前对象的原型对象,怎么使用呢?为什么在constructor里面要用super呢?看看后面再回头看能不能理解吧。(20180126)==

对象的简写

对象中可以直接写变量,此时属性名为变量名,属性值为变量值

let a = 'z';
let b = {a};

除了属性,方法也可以简写:

let obj = {
  test() {
    alert(1)
  }
};
// 等同于
let obj = {
  test: function () {
    alert(1)
  }
};

简写可以用来便捷的定义函数返回值:

function test(x, y) {
  return {x, y};
  // 等同于
  // return {x: x, y: y}
}

属性名表达式

在使用字面量定义对象时,ES5中,属性名只能是字符串,不能是变量,ES6里可以使用变量:

let a = 'test';
let obj = {
  [a]: 1, // test : 1
  ['a' + 'bc']: 123 // abc: 123
};

Object.is()

===基本相同,除了对NaN的判定以及+0-0的判定

NaN === NaN // false
Object.is(NaN, NaN) // true

+0 === -0 // true
Object.is(0, -0)// false

Object.assign()

Object.assign()合并的是自身的(不拷贝继承属性)可枚举的(enumerable: true)属性

let a = {};
let b = {};
Object.defineProperty(a, 'test', {
  value: 123,
  enumerable: false})
a; // {test: 123}
b = Object.assign(b, a);//{}

性名为Symbol值的属性,也会被Object.assign拷贝。

Object.assign()是一层深拷贝,嵌套拷贝(即源对象的某个属性是对象或数组)则是浅拷贝

对同名属性进行替换, 而不是添加:

const target = { a: { b: 'c', d: 'e' } }
const source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }

Object.assign可以用来处理数组,但是会把数组视为对象。

Object.assign([1, 2, 3], [4, 5])
// 等同于
// Object.assign({0:1, 1:2, 2:3}, {0:4, 1:5})
// 结果
// [4, 5, 3]

Object.assign()的用途

1、为对象添加属性:

class Point {
  constructor(x, y) {
    Object.assign(this, {x, y})
  }
}
let a = new Point(1, 2);
// Point {x: 1, y: 2}

上面方法通过Object.assign方法,将x属性和y属性添加到Point类的对象实例。

2、为对象添加方法:

function Test(name) {
  this.name = name
}
Object.assign(Test.prototype, {
  sayHello: function() {
    alert('Hello ' + this.name)
  },
  method2: function(){},
  // More Methods
});
// 等同于
//  Test.prototype.sayHello = function () {
//    alert('Hello ' + this.name)
//  };
let a = new Test('Tom')

3、克隆对象

function clone(origin) {
  return Object.assign({}, origin);
}

这种方式克隆的对象,只能克隆自身可枚举属性,不能克隆原型链的值

如果要克隆原型链的值,需要结合使用getPrototypeOf方法

function clone(origin) {
  let originProto = Object.getPrototypeOf(origin);
  return Object.assign(Object.create(originProto), origin);
}

4、指定对象属性默认值

let defaultVal = {
  name: 'tom',
  age: 18
};
let option = {};
let obj = Object.assign({}, defaultVal, option);

如果 option 有对应值,则会将默认值覆盖

但是注意,使用这种方式属性值最好都是简单类型,不能是另外一个对象,否则会将其余没有默认值的属性删除:

const DEFAULTS = {
  url: {
    host: 'example.com',
    port: 7070
  },
};

processContent({ url: {port: 8000} })
// {
//   url: {port: 8000}
// }

属性的可枚举性

对象的每一个属性都有一个描述对象(Descriptor),用来控制该属性的行为
Object.getOwnPropertyDescriptor() 可以获取指定属性的描述对象

let a = {name:'123'};
Object.getOwnPropertyDescriptor(a, 'name');
// {
//  configurable: true
//  enumerable: true
//  value: "123"
//  writable: true
//}

enumerable 就是可枚举性,如果为 false,某些遍历操作会忽略该属性

  • for...in
  • Object.keys()
  • Object.assign()
  • JSON.stringify()

Object.getOwnPropertyNames会遍历自身可枚举和不可枚举的属性

另外,ES6规定,所有Class的原型的方法都是不可枚举的

class Test {
  sayHello() {
    alert(1)
  }
}
console.log(Object.getOwnPropertyDescriptor(Test.prototype, 'sayHello').enumerable); // false

属性的遍历

除了已经了解的for...inObject.keysObject.getOwnPropertyNames之外:

还有:

  • Object.getOwnPropertySymbols:返回一个数组,包含对象自身所有的Symbol属性的键名
  • Reflect.ownKeys(obj):返回一个数组,包含对象自身所有键名,不管键名是Symbol或字符串,也不管是否可枚举

遍历次序:
1. 首先遍历数值键,按数值升序排列
2. 然后遍历字符串键,按照加入时间升序排列
3. 最后遍历Symbol键,按照加入时间升序排列

let obj = {
  [Symbol('z')]: 'ff',
  'x': 'ccc',
  1: 'aaa',
  0: 'bbb',
  'y': 'ccc',
  [Symbol('m')]: 'ff'
};
console.log(Reflect.ownKeys(obj));
// [0,1,'x','y', Symbol('z'), Symbol('m')]

Object.getOwnPropertyDescriptors

  • Object.getOwnPropertyDescriptor不加s,返回对象中某个具体属性的描述对象
  • Object.getOwnPropertyDescriptors加上s,返回对象中所有属性的描述对象组成的数组

Object.setPrototypeOf()Object.getPrototypeOf()

__proto__ 指向当前对象的 prototype

function test(){}
let a = new test()
a.__proto__ === test.prototype; // true

无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的 Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create() (生成操作)代替。

Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的prototype对象,返回参数对象本身。

Object.getPrototypeOf()用于读取一个对象的原型对象。

function test() {}
let a = new test()
Object.getPrototypeOf(a).constructor.name;//"test"

super()关键字

关键字super,指向当前对象的原型对象

注意,super关键字表示原型对象时,只能用在对象的方法之中(并且是对象方法的简写法中),用在其他地方都会报错。

Object.keys()Object.values()Object.entries()

  • Object.keys()返回一个数组,成员是对象自身(不含继承的)可枚举的的属性的键名
  • Object.values()返回一个数组,成员是对象自身(不含继承的)可枚举的的属性的键值(ES2017引入)
  • Object.entries()返回一个数组,成员是对象自身(不含继承的)可枚举的的属性的键名和键值,利用解构赋值取值 (ES2017引入)
let obj = {
  a: 'test',
  b: 'ffff'
};
Object.entries(obj).forEach((value, index) = > {
  let[key, val] = value;
  console.log(key); // a, b
  console.log(val); // 'test', 'ffff'
});
for (let[key, val] of Object.entries(obj)) {
  console.log(key); // a, b
  console.log(val); // 'test', 'ffff'
}

上述方法都不含Symbol作为键名的属性

Object.create()

Object.create(proto[, propertiesObject])
  • proto 指定新创建对象的原型对象
  • propertiesObject 指定新创建对象的自身的可枚举的属性

创建一个对象, 默认创建对象的可枚举行性是false

let obj = Object.create({}, {
  p: {
    value: 123
  }
});
console.log(obj); //{p: 123}
console.log(Object.getOwnPropertyDescriptor(obj, 'p').enumerable); //false

对象的解构赋值

ES2018将...扩展运算符引入了对象

let {x, ...y} = {x:1, a:3, b:4}
y;//{a: 3, b: 4}

解构赋值必须是最后一个参数,否则会报错。

注意,解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。

let obj = { a: { b: 1 } };
let { ...x } = obj;
obj.a.b = 2;
x.a.b // 2

另外,扩展运算符的解构赋值,不能复制继承自原型对象的属性。

对象的扩展运算符

对象的扩展运算符...用于取出参数对象的可遍历属性,拷贝到当前对象中

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

等同于使用Object.assign方法。

let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);

完整拷贝

要实现对象本身属性和继承属性的拷贝,可以采用下面的写法:

// 写法一
const clone1 = {
  __proto__: Object.getPrototypeOf(obj), 
  ...obj
};

// 写法二
const clone2 = Obejct.assign(Object.create(Object.getPrototypeOf(obj)), obj)

// 写法三
const clone3 = Obect.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))

上面代码中,写法一的__proto__属性在非浏览器的环境不一定部署,因此推荐使用写法二和写法三。

let a = {
  age: 18
};
let obj = {
  name: 'jay'
};
obj.__proto__ = a;
let c = {
  __proto__: Object.getPrototypeOf(obj),
  ...obj
};
let d = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
let e = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))

扩展运算符还可以用于合并对象:

let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);

扩展元素符在对象内出现时,永远是后面的覆盖前面的属性

let a = {
  x: 100,
  z: 99
};
let b = {...a, x: 1,y: 2,}; // {x:1, y:2, z:99}

let c = {
  x: 100,
  z: 99
};
let d = {x: 1,y: 2,...a}; //{x:100, y:2, z:99}
  • 当扩展运算符在前面而自定义属性在后面时,可以用来修改对象部分属性
  • 当扩展运算符在后面而自定义属性在前面时,可以用来设置新对象的默认属性

如果扩展运算符的参数是nullundefined,这两个值会被忽略,不会报错。

猜你喜欢

转载自blog.csdn.net/duola8789/article/details/79993823