基本扩展

  • 允许使用已有对象赋值定义对象字面量,并且只写变量名即可 ```javascript var name = “Bob”; var getName = function(){console.log(this.name);};

var person = {name, getName}; //相当于 //var person = { //name: “Bob”, //getName: function(){console.log(this.name);} //} person.getName(); //“Bob”

  1. - 可以像定义存取器那样定义方法
  2. ```javascript
  3. var o = {
  4. _age: 10,
  5. _score: 60,
  6. age(num){
  7. if(num > 0) {
  8. this._age = num;
  9. return this;
  10. }
  11. return this._age;
  12. },
  13. get score(){
  14. return this._score;
  15. }
  16. };
  17. console.log(o.age()); //10
  18. o.age(15);
  19. console.log(o.age()); //15
  20. console.log(o.score); //60
  21. o.score = 100; //TypeError

注意,以下代码是等同的:

  1. var obj = {
  2. class () {} //并不会因为 class 是关键字而解析错误
  3. };
  4. //等价于
  5. var obj = {
  6. 'class': function() {}
  7. };

如果一个方法是 Generator 函数,需要在前面加 *:

  1. var obj = {
  2. time: 1,
  3. *gen(){
  4. yield "hello " + time;
  5. time++;
  6. }
  7. }
  • 属性名表达式
    js 本来可以这样 obj['k'+'ey'] 访问一个对象属性,现在也可以这样定义属性了: ```javascript var key1 = “name”; var key2 = “age”;

var o = {

}; o.name; //“Bob” o.age; //18 o.firstname; //“Ellen”

  1. 注意:该方法不能和上一小节使用已有标识符定义对象字面量的方法混合使用,否则会报错;
  2. ```javascript
  3. //错误用法
  4. var foo = 'bar';
  5. var bar = 'abc';
  6. var baz = {[foo]}; //报错
  • 方法的 name 属性
    函数有 name 属性,方法也就有 name 属性。一般方法 name 返回函数名(不包括对象名),对于存取器方法,没有 name 属性:
    1. var o = {
    2. _age: 10,
    3. _score: 60,
    4. _name: "Bob",
    5. _firstname: "Ellen",
    6. set age(num){
    7. if(num > 0) {
    8. this._age = num;
    9. return this;
    10. }
    11. },
    12. get age(){
    13. return this._age;
    14. },
    15. get score(){
    16. return this._score;
    17. },
    18. name(n){
    19. if(!n) return this._name + ' ' + this._firstname;
    20. this._name = n;
    21. return this;
    22. },
    23. set firstname(n){
    24. if(n) this._firstname = n;
    25. return this;
    26. }
    27. };
    28. console.log(o.name.name); //"name"
    29. console.log(o.age.name); //undefined
    30. console.log(o.score.name); //undefined
    31. console.log(o.firstname); //undefined,所以 set 函数更不会有 name 属性

如果对象的方法是个 symbol,name 属性为空字符串 ""

  1. var sym1 = new Symbol("description of sym1");
  2. var sym2 = new Symbol();
  3. var o = {
  4. [sym1](){},
  5. [sym2](){},
  6. };
  7. o[sym1].name; //""
  8. o[sym2].name; //""
  • 静态方法
  1. Object.is(a,b): 比较a,b两个值是否严格相等,相当于 ===, 但有一点不一样: ```javascript -0 === +0; //true NaN === NaN; //false

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

  1. 2. Object.assign(target, source1,source2,...): 将每个 source 对象自身的可枚举属性复制到 target 对象上,不包括原型链上的属性和不可枚举属性。只有有一个参数不是对象,就会抛出 TypeError 错误。遇到同名属性,排在后面的会覆盖前面的:
  2. ```javascript
  3. var target = {a:1,b:2};
  4. var source1 = {a:3,c:3};
  5. var source2 = {a:2,d:0};
  6. Object.assign(target, source1, source2);
  7. console.log(target); //{a: 2, b: 2, c: 3, d: 0}

对于属性名是 symbol 的可枚举属性也会被复制:

  1. Object.assign({a:'b'}, {[Symbol('c')]:'d'}); //{a: "b", Symbol(c): "d"}

对于同名属性存在嵌套对象,外层会被直接替换:

  1. Object.assign({a:{b:'c',d:'e'}}, {a:{b:'hello'}}); //{a:{b:'hello'}}

可以用 Object.assign处理数组,但会视其为对象:

  1. Object.assign([1,2,3], [4,5]); //[4, 5, 3]

技巧:为对象添加属性方法

  1. Object.assign(String.prototype, {
  2. newProperty: "value",
  3. newFunction: function(){}
  4. })

技巧:克隆对象

  1. Object.assign({},origin);

技巧:为对象添加属性方法

  1. Object.assign(target, ...source);

技巧:为对象添加属性方法

  1. const DEFAULT_OPTION = { //默认值
  2. a: 1,
  3. b: 2
  4. };
  5. function processContent(newOption){
  6. return Object.assign({}, DEFAULT_OPTION, newOption);
  7. }
  8. //设置属性应该是基本类型,否则会因为深拷贝出问题

对象属性的可枚举性与遍历

以下6个操作会忽略不可枚举的属性

  • for…in循环
  • Object.keys()
  • JSON.stringify()
  • Object.assign()
  • Reflect.enumerate()
  • 扩展运算符 ...

以下4个方法不忽略不可枚举属性

  • Object.getOwnPropertyNames()
  • Object.getOwnPropertySymbols()
  • Reflect.ownKeys()

以上9个方法中,只有2个会操作包含继承到的属性

  • for…in循环
  • Reflect.enumerate()

以上9个方法中,只有1个方法可以获得 Symbol 属性

  • Object.getOwnPropertySymbols()

除此之外需要强调的是 ES6 中,所有 class 的原型方法都是不可枚举的:

  1. Object.getOwnPropertyDescriptor(class{foo(){}}.prototype, foo).enumerable; //false

ES6 起,有了7中遍历属性的方法:

  • for…in: 循环遍历对象自身和继承到的可枚举属性,不包括 Symbol 属性
  • Object.keys(obj): 返回包含自身可枚举属性的属性名数组,不包含 Symbol 属性
  • Object.getOwnPropertyNames(obj): 同上,但包括不可枚举属性
  • Object.getOwnPropertySymbols(obj): 返回自身所有 Symbol 属性名的数组,包括不可枚举属性
  • Reflect.ownKey(obj): 返回自身所有属性名数组,包括不可枚举属性和 Symbol 属性名
  • Reflect.enumerate(): 返回一个 Iterator, 用来遍历对象自身及继承到的可枚举属性,不包括 Symbol 属性;和 for…in 一样
  • for…of: 只能遍历具有 Iterator 接口的对象,具体作用范围由 iterator 决定,遍历没有 iterator 的对象会报错

以上方法除了 for…of 以外,遍历顺序为:

  • 首先遍历所有属性名为数字的属性,按数字大小排序;
  • 其次遍历所有属性名为字符串的属性,按属性生成时间排序;
  • 最后遍历所有属性名为 Symbol 的属性,按属性生成时间排序;

对象的**proto**属性

这是个很老很老的属性,在大家想期待下,ES6终于把它写进去了,嗯?…是写进附录了。这个属性用来读写当前的对象的原型对象obj.constructor.prototype

从本质上来讲,__proto__ 是定义在Object.prototype 上的一个存取器函数:

  1. function isObject(a){
  2. return Object(a) === a;
  3. }
  4. Object.defineProperty(Object.prototype, '__proto__', {
  5. get(){
  6. let _thisObj = Object(this);
  7. return Object.getPrototypeOf(_thisObj);
  8. },
  9. set(proto){
  10. if(this == null) throw new TypeError();
  11. if(!isObject(this) || !isObject(proto)) return undefined;
  12. let status = Object.setPrototypeOf(this, proto);
  13. if(! status) throw new TypeError();
  14. }
  15. });

但是,还是不建议使用这个东西,毕竟看它这名字就是个内部属性,因为它有了不加,但不保证所以终端都能用,所以ES6推荐用下面这两个属性:

  1. Object.setPrototypeOf(obj, newProto); //写
  2. Object.getPrototypeOf(obj); //读

简单举一个例子:

  1. function Rectangle(){}
  2. var rec = new Rectangle();
  3. Object.getPrototypeOf(rec) === Rectangle.prototype; //true
  4. Object.setPrototypeOf(rec, Object.prototype);
  5. Object.getPrototypeOf(rec) === Rectangle.prototype; //false

当然如果你把一个对象的原型设置成了 null, 也是可以的,只是,它不具备任何你听说过的方法了:

  1. var o = Object.setPrototypeOf({}, null);
  2. //等价于 var o = {'__proto__': null};
  3. Object.getPrototypeOf(o); //null
  4. o.toString(); //TypeError: o.toString is not a function