ECMAScript6(8):對象的擴展

基本擴展

  • 容許使用已有對象賦值定義對象字面量,而且只寫變量名便可
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"
  • 能夠像定義存取器那樣定義方法
var o = {
  _age: 10,
  _score: 60,
  age(num){
    if(num > 0) {
      this._age = num;
      return this;
    }
    return this._age;
  },
  get score(){
    return this._score;
  }
};

console.log(o.age());    //10
o.age(15);
console.log(o.age());    //15
console.log(o.score);    //60
o.score = 100;           //TypeError

注意,如下代碼是等同的:數組

var obj = {
  class () {}       //並不會由於 class 是關鍵字而解析錯誤
};
//等價於
var obj = {
  'class': function() {}
};

若是一個方法是 Generator 函數,須要在前面加 *:babel

var obj = {
  time: 1,
  *gen(){
    yield "hello " + time;
    time++;
  }
}
  • 屬性名錶達式

js 原本能夠這樣 obj['k'+'ey'] 訪問一個對象屬性,如今也能夠這樣定義屬性了:函數

var key1 = "name";
var key2 = "age";

var o = {
  [key1]: "Bob",
  [key2]: 18,
  ['first' + key1]: "Ellen"
};
o.name;    //"Bob"
o.age;     //18
o.firstname;   //"Ellen"

注意:該方法不能和上一小節使用已有標識符定義對象字面量的方法混合使用,不然會報錯;this

//錯誤用法
var foo = 'bar';
var bar = 'abc';
var baz = {[foo]};  //報錯
  • 方法的 name 屬性

函數有 name 屬性,方法也就有 name 屬性。通常方法 name 返回函數名(不包括對象名),對於存取器方法,沒有 name 屬性:prototype

var o = {
  _age: 10,
  _score: 60,
  _name: "Bob",
  _firstname: "Ellen",
  set age(num){
    if(num > 0) {
      this._age = num;
      return this;
    }
  },
  get age(){
    return this._age;
  },
  get score(){
    return this._score;
  },
  name(n){
    if(!n) return this._name + ' ' + this._firstname;
    this._name = n;
    return this;
  },
  set firstname(n){
    if(n) this._firstname = n;
    return this;
  }
};
console.log(o.name.name);      //"name"
console.log(o.age.name);       //undefined
console.log(o.score.name);     //undefined
console.log(o.firstname);      //undefined,因此 set 函數更不會有 name 屬性

若是對象的方法是個 symbol,name 屬性爲空字符串 ""rest

var sym1 = new Symbol("description of sym1");
var sym2 = new Symbol();
var o = {
  [sym1](){},
  [sym2](){},
};
o[sym1].name;    //""
o[sym2].name;    //""
  • 靜態方法
  1. Object.is(a,b): 比較a,b兩個值是否嚴格相等,至關於 ===, 但有一點不同:
-0 === +0;     //true
NaN === NaN;   //false

Object.is(-0, +0);     //false
Object.is(NaN, NaN);   //true
  1. Object.assign(target, source1,source2,...): 將每一個 source 對象自身的可枚舉屬性複製到 target 對象上,不包括原型鏈上的屬性和不可枚舉屬性。只有有一個參數不是對象,就會拋出 TypeError 錯誤。遇到同名屬性,排在後面的會覆蓋前面的:
var target = {a:1,b:2};
var source1 = {a:3,c:3};
var source2 = {a:2,d:0};
Object.assign(target, source1, source2);
console.log(target);      //{a: 2, b: 2, c: 3, d: 0}

對於屬性名是 symbol 的可枚舉屬性也會被複制:code

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

對於同名屬性存在嵌套對象,外層會被直接替換:對象

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

能夠用 Object.assign處理數組,但會視其爲對象:排序

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

技巧:爲對象添加屬性方法繼承

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

技巧:克隆對象

Object.assign({},origin);

技巧:爲對象添加屬性方法

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

技巧:爲對象添加屬性方法

const DEFAULT_OPTION = {   //默認值
  a: 1,
  b: 2
};
function processContent(newOption){
  return Object.assign({}, DEFAULT_OPTION, newOption);
}
//設置屬性應該是基本類型,不然會由於深拷貝出問題

對象屬性的可枚舉性與遍歷

如下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 的原型方法都是不可枚舉的:

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 上的一個存取器函數:

function isObject(a){
  return Object(a) === a;
}
Object.defineProperty(Object.prototype, '__proto__', {
  get(){
    let _thisObj = Object(this);
    return Object.getPrototypeOf(_thisObj);
  },
  set(proto){
    if(this == null) throw new TypeError();
    if(!isObject(this) || !isObject(proto)) return undefined;
    let status = Object.setPrototypeOf(this, proto);
    if(! status) throw new TypeError();
  }
});

可是,仍是不建議使用這個東西,畢竟看它這名字就是個內部屬性,由於它有了不加,但不保證因此終端都能用,因此ES6推薦用下面這兩個屬性:

Object.setPrototypeOf(obj, newProto);   //寫
Object.getPrototypeOf(obj);   //讀

簡單舉一個例子:

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

固然若是你把一個對象的原型設置成了 null, 也是能夠的,只是,它不具有任何你據說過的方法了:

var o = Object.setPrototypeOf({}, null);
//等價於 var o = {'__proto__': null};
Object.getPrototypeOf(o);                //null
o.toString();                    //TypeError: o.toString is not a function

對象的擴展運算符

這是 ES7 的一個提案, babel可使用器部分功能使用。

  • rest參數

用法和數組中很相似,這裏再也不過多贅述,直接看幾個例子吧:

var {x,y,...z}  = {x: 1, y: 2, a: 3, d: 4}; //x=1, y=2, z={a: 3, d: 4}

值得強調的是, 對象的rest參數形式執行的是淺拷貝,賦值獲得的是原對象的引用:

let obj = {a: {b: 1}};
let {...x} = obj;
obj.a.b = 2;
console.log(x.a.b);    //2

此外 rest 不會複製不可枚舉屬性和繼承自原型的屬性:

var p = {a: 0};
var o = {b: 2};
var o = Object.defineProperty(o, "foo", {
  value: 2,
  configurable: true,
  enumerable: false,
  writable: true
});
Object.setPrototypeOf(o, p);
var u = { ...p };
console.log(u);    //{b:2}
  • 擴展運算符

複製參數對象全部可遍歷屬性到當前對象中:

var o1 = {a: 1, b: 2};
var n = { ...o1 };    //n={a: 1, b: 2};
//至關於
var n = Object.assign({}, o1);

能夠用擴展運算符合並多個對象, 排後的屬性會覆蓋以前的屬性:

var source0 = {a:1,b:2};
var source1 = {a:3,c:3};
var source2 = {a:2,d:0};
Object.assign(target, source1, source2);
var target = {...source0, ...source1, ...source2};
console.log(target);      //{a: 2, b: 2, c: 3, d: 0}

注意一點:若是擴展運算符的參數對象有 get 方法,該方法會被執行:

var a = {o:1,p:2,m:4};
var withoutError = {
  ...a,
  get x(){
    throw new Error();
  }
};           //不報錯
var withError = {
  ...a,
  ...{get x(){
    throw new Error();
  }}
};           //報錯

若是擴展對象是 null 或 undefined,會被忽略,不報錯

var o = { ...null,  ...undefined};    //不報錯
console.log(o);    //{}
相關文章
相關標籤/搜索