Object 的各類方法

分類

Object() 函數

Object 自己是一個函數,用來將任意值轉爲對象。javascript

若是參數爲空(或者爲 undefinednull),Object() 返回一個空對象。html

var obj = Object();
// 等同於
var obj = Object(undefined);
var obj = Object(null);

obj instanceof Object // true

若是參數是原始類型的值,Object 方法將其轉爲對應的包裝對象的實例。java

var obj = Object(1);
obj instanceof Object // true
obj instanceof Number // true

var obj = Object('foo');
obj instanceof Object // true
obj instanceof String // true

var obj = Object(true);
obj instanceof Object // true
obj instanceof Boolean // true

若是 Object 方法的參數是一個對象,它老是返回該對象,即不用轉換。segmentfault

var arr = [];
var obj = Object(arr);  // 返回原數組
obj === arr             // true

var value = {};
var obj = Object(value) // 返回原對象
obj === value           // true

var fn = function () {};
var obj = Object(fn);  // 返回原函數
obj === fn             // true

所以,能夠寫一個判斷變量是否爲對象的函數。這個方法經常使用於保證某個值必定是對象。數組

function isObject(value) {
  return value === Object(value);
}

isObject([]) // true
isObject(true) // false

構造函數 new Object()

Object 構造函數的首要用途,是直接經過它來生成新對象。瀏覽器

var obj = new Object();

// 等同於
var obj = {}

new Object() 構造函數與 Object() 的用法很類似,幾乎如出一轍。使用時,能夠接受一個參數,若是該參數是一個對象,則直接返回這個對象;若是是一個原始類型的值,則返回該值對應的包裝對象。函數

var o1 = {a: 1};
var o2 = new Object(o1);
o1 === o2 // true

var obj = new Object(123);
obj instanceof Number // true

二者區別是語義不一樣。Object(value) 表示將 value 轉成一個對象,new Object(value) 則表示新生成一個對象,它的值是 valuethis

Object 對象的原生方法

Object 對象的原生方法分紅兩類:Object 自己的方法與Object 的實例方法。prototype

(1) 自己的方法

自己的方法就是直接定義在 Object 對象的方法。code

1. 遍歷對象屬性
  • Object.keys():遍歷對象自身的(非繼承的)可枚舉屬性,返回屬性名。
  • Object.getOwnPropertyNames():遍歷對象自身的(非繼承的)所有(可枚舉+不可枚舉歷)屬性。
  • Object.values():遍歷對象自身的(非繼承的)可枚舉屬性,返回屬性值。
  • Object.entries():遍歷對象自身的(非繼承的)可枚舉屬性,返回鍵值對。
2. 對象的屬性描述對象相關方法
  • Object.getOwnPropertyDescriptor():獲取某個屬性的描述對象。
  • Object.getOwnPropertyDescriptors():獲取對象的全部屬性的描述對象。
  • Object.defineProperty():定義某個屬性的描述對象。
  • Object.defineProperties():定義多個屬性的描述對象。
3. 控制對象狀態的方法
  • Object.preventExtensions():防止對象擴展,沒法添加新屬性。
  • Object.isExtensible():判斷對象是否可擴展。
  • Object.seal():禁止對象配置,沒法添加新屬性,沒法刪除屬性。
  • Object.isSealed():判斷一個對象是否可配置。
  • Object.freeze():凍結一個對象,沒法添加新屬性,沒法刪除屬性,沒法改變屬性值。
  • Object.isFrozen():判斷一個對象是否被凍結。
4. 原型鏈相關方法
  • Object.create():以參數爲原型返回一個新的實例對象。
  • Object.getPrototypeOf():獲取對象的原型對象。
  • Object.setPrototypeOf():設置對象的原型對象。
5. 其它
  • Object.assign()
  • Object.is()

(2) 實例方法

實例方法就是定義在 Object 原型對象 Object.prototype 上的方法。它能夠被 Object 實例直接使用。

Object 實例對象的方法,主要有如下六個:

  • Object.prototype.valueOf():返回當前對象對應的值。
  • Object.prototype.toString():返回當前對象對應的字符串形式。
  • Object.prototype.toLocaleString():返回當前對象對應的本地字符串形式。
  • Object.prototype.hasOwnProperty():判斷某個屬性是否爲當前對象自身的屬性,仍是繼承自原型對象的屬性。
  • Object.prototype.isPrototypeOf():判斷當前對象是否爲另外一個對象的原型。
  • Object.prototype.propertyIsEnumerable():判斷對象自身的(非繼承的)屬性是否可枚舉。

方法介紹

獲取屬性相關

1. Object.keys() , Object.getOwnPropertyNames()

Object.keys 方法和 Object.getOwnPropertyNames 方法都用來遍歷對象的屬性。

Object.keys 方法的參數是一個對象,返回一個數組。該數組的成員都是該對象自身的(非繼承的)全部屬性名,且只返回可枚舉的屬性。

var obj = Object.defineProperties({}, {
  p1: { value: 1, enumerable: true },
  p2: { value: 2, enumerable: false }
});

Object.keys(obj)  // ["p1"]

Object.getOwnPropertyNames 方法與 Object.keys 相似,也是接受一個對象做爲參數,返回一個數組,該數組的成員是參數對象自身的(非繼承的)所有屬性的屬性名,無論該屬性是否可枚舉。

var a = ['Hello', 'World'];

Object.keys(a)                   // ["0", "1"]
Object.getOwnPropertyNames(a)    // ["0", "1", "length"]

上面代碼中,數組的 length 屬性是不可枚舉的屬性,因此只出如今 Object.getOwnPropertyNames 方法的返回結果中。

因爲 JavaScript 沒有提供計算對象屬性個數的方法,因此能夠用這兩個方法代替。

var obj = { p1: 123, p2: 456 };

Object.keys(obj).length                  // 2
Object.getOwnPropertyNames(obj).length   // 2

通常狀況下,幾乎老是使用 Object.keys 方法,遍歷對象的屬性。

2. Object.values()

Object.values() 方法返回一個數組,成員是參數對象自身的(非繼承的)全部可枚舉屬性的屬性值。

var obj = { p1: 123, p2: 456 };
Object.values(obj)   // [123, 456]

3. Object.entries()

Object.entries() 方法返回一個數組,成員是參數對象自身的(非繼承的)全部可枚舉屬性的鍵值對數組。

var obj = { p1: 123, p2: 456 };
Object.entries(obj)  // [["p1", "123"], ["p2", 456]]

4. Object.prototype.hasOwnProperty()

實例對象的 hasOwnProperty() 方法接受一個字符串做爲參數,返回一個布爾值,表示該實例對象自身是否具備該屬性。有返回 true,沒有或是繼承的屬性都返回 false

var obj = { p: 123 };

obj.hasOwnProperty('p')            // true
obj.hasOwnProperty('toString')     // false

原型鏈相關

1. Object.getPrototypeOf()

Object.getPrototypeOf() 方法返回參數對象的原型。這是獲取原型對象的標準方法。

var F = function () {};
var f = new F();
Object.getPrototypeOf(f) === F.prototype // true

Object.prototype 的原型是 null

Object.getPrototypeOf(Object.prototype) === null // true

2. Object.setPrototypeOf()

Object.setPrototypeOf() 方法爲參數對象設置原型,返回該參數對象。它接受兩個參數,第一個是現有對象,第二個是原型對象。

var a = {};
var b = {x: 1};
Object.setPrototypeOf(a, b);

Object.getPrototypeOf(a) === b // true
a.x // 1

new 命令可使用 Object.setPrototypeOf() 方法模擬。

var F = function () { this.foo = 'bar'; };

var f = new F();
// 等同於
var f = Object.setPrototypeOf({}, F.prototype);
F.call(f);

3. Object.prototype.__proto__

實例對象的 __proto__ 屬性,返回該對象的原型。該屬性可讀寫。

var obj = {};
var p = {};

obj.__proto__ = p;
Object.getPrototypeOf(obj) === p   // true

根據語言標準,__proto__ 屬性只有瀏覽器才須要部署,其餘環境能夠沒有這個屬性。它先後的兩根下劃線,代表它本質是一個內部屬性,不該該對使用者暴露。所以,應該儘可能少用這個屬性,而是用 Object.getPrototypeof()Object.setPrototypeOf(),進行原型對象的讀寫操做。

4. Object.prototype.isPrototypeOf()

實例對象的 isPrototypeOf() 方法,用來判斷該對象是否爲參數對象的原型。

var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);

o2.isPrototypeOf(o3)   // true
o1.isPrototypeOf(o3)   // true

只要實例對象處在參數對象的原型鏈上,isPrototypeOf() 方法都返回true。

Object.prototype.isPrototypeOf({})                   // true
Object.prototype.isPrototypeOf([])                   // true
Object.prototype.isPrototypeOf(/xyz/)                // true
Object.prototype.isPrototypeOf(Object.create(null))  // false

因爲 Object.prototype 處於原型鏈的最頂端,因此對各類實例都返回 true,只有直接繼承自 null 的對象除外。

5. Object.create()

Object.create() 方法接受一個對象做爲參數,目的是以參數對象爲原型,返回一個實例對象。該實例徹底繼承原型對象的屬性。

不少時候,須要從一個實例對象 A 生成另外一個實例對象 B,若是 A 是由構造函數建立的,那麼能夠很輕鬆的獲得 A 的構造函數從新生成實例 B,然而不少時候,A 只是一個普通的對象,並非由構造函數生成的,這時候就須要使用Object.create() 方法由 A 生成 B。

var A = {
  print: function () {
    console.log('hello');
  }
};

var B = Object.create(A);

Object.getPrototypeOf(B) === A    // true
B.print()                         // hello
B.print === A.print               // true

Object.create() 方法兼容性處理,即生成實例的本質:

if (typeof Object.create !== 'function') {
  Object.create = function (obj) {
    function F() {}       // 新建一個空的構造函數 F
    F.prototype = obj;    // 讓 F.prototype 屬性指向參數對象 obj
    return new F();       // 最後返回一個 F 的實例
  };
}

下面三種方式生成的新對象是等價的:

var obj1 = Object.create({});
var obj2 = Object.create(Object.prototype);
var obj3 = new Object();

若是想要生成一個不繼承任何屬性(好比沒有 toStringvalueOf 方法)的對象,能夠將 Object.create 的參數設爲 null。由於生成的實例對象原型是 null,因此它就不具有定義在 Object.prototype 原型上面的方法。

var obj = Object.create(null);

Object.create() 方法還能夠接受第二個參數。該參數是一個屬性描述對象,它所描述的對象屬性,會添加到實例對象,做爲該對象自身的屬性。

var obj = Object.create({}, {
  p1: {
    value: 123,
    enumerable: true,
    configurable: true,
    writable: true,
  },
  p2: {
    value: 'abc',
    enumerable: true,
    configurable: true,
    writable: true,
  }
});

// 等同於
var obj = Object.create({});
obj.p1 = 123;
obj.p2 = 'abc';

Object.create() 方法生成的對象,繼承了它的原型對象的構造函數。

function A() {}
var a = new A();
var b = Object.create(a);

b.constructor === A   // true
b instanceof A        // true

屬性描述對象相關

1. Object.getOwnPropertyDescriptor() , Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptor() 能夠獲取某個屬性的屬性描述對象。它的第一個參數是對象,第二個參數是對象的某個屬性名。返回的是該屬性的屬性描述對象。

var obj = { p1: 'a',  p2: 'b'};

Object.getOwnPropertyDescriptor(obj, 'p1')
// { value: "a",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

只能用於對象自身的(非繼承的)屬性。繼承的或不存在的屬性返回 undefined

Object.getOwnPropertyDescriptor(obj, 'toString')   // undefined

Object.getOwnPropertyDescriptors() 能夠獲取參數對象的全部屬性的屬性描述對象。ES2017 引入標準。

Object.getOwnPropertyDescriptors(obj)
// { p1: {value: "a", writable: true, enumerable: true, configurable: true}
//   p2: {value: "b", writable: true, enumerable: true, configurable: true}
// }

2. Object.defineProperty() ,Object.defineProperties()

Object.defineProperty() 方法容許經過屬性描述對象,定義或修改一個屬性,而後返回修改後的描述對象。

Object.defineProperty(object, propertyName, attributesObject)

Object.defineProperty() 方法接受三個參數,依次以下。

  • object:屬性所在的對象
  • propertyName:字符串,表示屬性名
  • attributesObject:屬性描述對象
var obj = Object.defineProperty({}, 'p', {
  value: 123,
  writable: false,
  enumerable: true,
  configurable: false
});
obj.p         // 123
obj.p = 246;
obj.p         // 123

注意,上例中第一個參數是{ }(一個新建的空對象),p屬性直接定義在這個空對象上面,而後返回這個對象,這是 Object.defineProperty() 的常見用法。

若是屬性已經存在,Object.defineProperty() 方法至關於更新該屬性的屬性描述對象。

Object.defineProperties() 方法能夠定義或修改多個屬性。接受兩個參數。

var obj = Object.defineProperties({}, {
  p1: { value: 123, enumerable: true },
  p2: { value: 'abc', enumerable: true },
  p3: { get: function () { return this.p1 + this.p2 },
    enumerable:true,
    configurable:true
  }
});

obj.p1 // 123
obj.p2 // "abc"
obj.p3 // "123abc"

注意,一旦定義了取值函數 get 或存值函數 set,就不能同時定義 writable 屬性或 value 屬性,不然會報錯。

元屬性默認值

Object.defineProperty()Object.defineProperties() 參數裏面的屬性描述對象,writableconfigurableenumerable 這三個屬性的默認值都爲 false

var obj = {};
Object.defineProperty(obj, 'foo', {});
Object.getOwnPropertyDescriptor(obj, 'foo')
// {
//   value: undefined,
//   writable: false,
//   enumerable: false,
//   configurable: false
// }

3. Object.prototype.propertyIsEnumerable()

實例對象的 propertyIsEnumerable() 方法返回一個布爾值,用來判斷某個屬性是否可枚舉。

var obj = {};
obj.p = 123;

obj.propertyIsEnumerable('p')           // true
obj.propertyIsEnumerable('toString')    // false

注意,這個方法只能用於判斷對象自身的屬性,對於繼承的屬性一概返回 false

控制對象狀態相關

有時須要凍結對象的讀寫狀態,防止對象被改變。JavaScript 提供了三種凍結方法,最弱的一種是 Object.preventExtensions(),其次是 Object.seal(),最強的是 Object.freeze()

1. Object.preventExtensions()

Object.preventExtensions() 方法可使得一個對象沒法再添加新的屬性。

var obj = new Object();
Object.preventExtensions(obj);

Object.defineProperty(obj, 'p', { value: 'hello' });
// TypeError: Cannot define property p, object is not extensible.

obj.p = 1;
obj.p      // undefined

2. Object.isExtensible()

Object.isExtensible() 方法用於檢查是否能夠爲一個對象添加屬性。能夠添加返回 true,不能夠添加返回 false

var obj = new Object();

Object.isExtensible(obj) // true
Object.preventExtensions(obj);
Object.isExtensible(obj) // false

3. Object.seal()

Object.seal() 方法使得一個對象既沒法添加新屬性,也沒法刪除舊屬性。

var obj = { p: 'hello' };
Object.seal(obj);

delete obj.p;
obj.p // "hello"

obj.x = 'world';
obj.x // undefined

Object.seal 實質是把屬性描述對象的 configurable 屬性設爲 false,所以屬性描述對象就不能再改變了。

var obj = { p: 'a' };

// seal方法以前
Object.getOwnPropertyDescriptor(obj, 'p')  // {... configurable: true }

Object.seal(obj);

// seal方法以後
Object.getOwnPropertyDescriptor(obj, 'p')  // {... configurable: false }

Object.defineProperty(obj, 'p', {
  enumerable: false
})
// TypeError: Cannot redefine property: p

Object.seal 只是禁止新增或刪除屬性,並不影響修改某個屬性的值。

var obj = { p: 'a' };
Object.seal(obj);
obj.p = 'b';
obj.p // 'b'

Object.seal 方法對 p 屬性的 value 無效,是由於此時 p 屬性的可寫性由writable 決定。

4. Object.isSealed()

Object.isSealed() 方法用於檢查一個對象是否使用了 Object.seal 方法。未使用返回false,使用了返回 true

var obj = { p: 'a' };

Object.seal(obj);
Object.isSealed(obj) // true

此時,Object.isExtensible() 方法也返回 false

Object.isExtensible(obj) // false

5. Object.freeze()

Object.freeze() 方法可使得一個對象沒法添加新屬性、沒法刪除舊屬性、也沒法改變屬性的值,使得這個對象實際上變成了常量。

var obj = { p: 'hello' };

Object.freeze(obj);

obj.p = 'world';
obj.p             // "hello"

obj.t = 'hello';
obj.t             // undefined

delete obj.p     // false
obj.p            // "hello"

6. Object.isFrozen()

Object.isFrozen() 方法用於檢查一個對象是否使用了Object.freeze方法。未使用返回false,使用了返回 true。此時 Object.isExtensible() 也返回 false

var obj = { p: 'hello' };

Object.freeze(obj);
Object.isFrozen(obj)        // true
Object.isExtensible(obj)    // false

侷限性

以上三個方法鎖定對象有侷限性,並非徹底凍結。

  1. 能夠經過改變原型對象,來爲對象增長新屬性。

    var obj = new Object();
    Object.preventExtensions(obj);
      
    var proto = Object.getPrototypeOf(obj);
    proto.t = 'hello';
    obj.t    // hello

    解決方案是,把 obj 的原型也凍結住。

    Object.preventExtensions(proto);
          
    proto.t = 'hello';
    obj.t   // undefined
  2. 若是屬性值是對象,以上三個方法只能凍結屬性指向的對象地址,而不能凍結對象自己。

    var obj = {
       foo: 1,
       bar: ['a', 'b']
     };
     Object.freeze(obj);
      
     obj.bar.push('c');
     obj.bar // ["a", "b", "c"]

    obj.bar 屬性指向一個數組,obj 對象被凍結之後,這個指向沒法改變,即沒法指向其餘值,可是所指向的數組是能夠改變的。

徹底凍結

var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach((key, i) => {
    if ( typeof obj[key] === 'object' ) {
      constantize(obj[key]);
    }
  });
};

var obj = {
  foo: 1,
  bar: ['a', 'b']
};
constantize(obj);

obj.bar.push('c'); 
// TypeError: Cannot add property 2, object is not extensible

對象的合併及拷貝

1. Object.assign()

Object.assign() 方法用於對象的合併,將全部自身的(非繼承的)可枚舉屬性的值從一個或多個源對象複製到目標對象。返回目標對象。目標對象自身也會改變。

Object.assign(target, ...sources)
  • target: 目標對象。
  • sources: 源對象。

若是目標對象中的屬性具備相同的鍵,則屬性將被源中的屬性覆蓋。後來的源的屬性將相似地覆蓋早先的屬性。

var o1 = { a: 1, b: 1, c: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { c: 3 };

var obj = Object.assign({}, o1, o2, o3);
obj    // { a: 1, b: 2, c: 3 }

Object.assign() 不會跳過那些值爲 nullundefined 的源對象。

var o1 = { a: null, b: 1};
var o2 = { c: undefined };
    
var obj = Object.assign({}, o1, o2);
obj   // {a: null, b: 1, c: undefined}

Object.assign() 拷貝的是屬性值。假如源對象的屬性值是一個指向對象的引用,它也只拷貝那個引用值。

var obj1 = { a: 0 , b: { c: 0 } };
var obj2 = Object.assign({}, obj1);
obj2   // { a: 0, b: { c: 0 } };

obj2.b.c = 3;
obj1   // { a: 0, b: { c: 3 } };
obj2   // { a: 0, b: { c: 3 } };

所以針對深拷貝,須要使用其餘方法。

var obj1 = { a: 0 , b: { c: 0}};
var obj2 = JSON.parse(JSON.stringify(obj1));
obj1.b.c = 4;
obj2    // { a: 0, b: { c: 0}}

Object.assign() 若是遇到存取器定義的屬性,會只拷貝值。

var obj = {
  foo: 1,
  get bar() { return 2; }
};

var copy = Object.assign({}, obj); 
copy  // { foo: 1, bar: 2 }

所以必須使用 Object.getOwnPropertyDescriptors() 方法配合 Object.defineProperties() 方法,就能夠實現正確拷貝。但僅限於可拷貝 gettersetter ,對於屬性的引用類型仍是屬於淺拷貝。

var obj = {
  foo: { a : 0 },
  get bar() { return 2; }
};
var target = Object.defineProperties({},
  Object.getOwnPropertyDescriptors(obj)
);
Object.getOwnPropertyDescriptor(target, 'bar')
// { get : ƒ bar(),
   set : undefined,
   enumerable : true, 
   configurable : true }
   
obj.foo.a = 6
target.foo.a   // 6

若是屬性不可寫,會引起報錯,若是在引起錯誤以前添加了任何屬性,則能夠更改target對象。

其它

1. Object.is()

Object.is() 用來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行爲基本一致。返回布爾值,相等返回 true,不相等返回 false。

不一樣之處只有兩個:一是+0不等於-0,二是NaN等於自身。

+0 === -0 //true
NaN === NaN // false

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

詳情見 JavaScript 的相等比較

ES5 能夠經過下面的代碼,部署 Object.is

Object.defineProperty(Object, 'is', {
  value: function(x, y) {
    if (x === y) {
      // 針對+0 不等於 -0的狀況
      return x !== 0 || 1 / x === 1 / y;
    }
    // 針對NaN的狀況
    return x !== x && y !== y;
  },
  configurable: true,
  enumerable: false,
  writable: true
});

2. Object.prototype.valueOf()

valueOf 方法的做用是返回一個對象的「值」,默認狀況下返回對象自己。

var obj = new Object();
obj.valueOf() === obj      // true

主要用途是,JavaScript 自動類型轉換時會默認調用這個方法。所以,若是給實例對象自定義 valueOf() 方法,覆蓋 Object.prototype.valueOf(),就能夠獲得想要的結果。

var obj = new Object();
obj.valueOf = function () {
  return 2;
};

1 + obj // 3

3. Object.prototype.toString()

toString 方法的做用是返回一個對象的字符串形式,默認狀況下返回類型字符串。

var obj = {};
obj.toString()   // "[object Object]"

JavaScript 自動類型轉換時也會調用這個方法。所以能夠經過自定義實例對象的 toString 方法,覆蓋掉 Object.prototype.toString(),獲得想要的字符串形式。

var obj = new Object();

obj.toString = function () {
  return 'hello';
};

obj + ' ' + 'world'     // "hello world"

數組、字符串、函數、Date 對象都分別部署了自定義的 toString 方法,覆蓋了 Object.prototype.toString() 方法。

[1, 2, 3].toString() // "1,2,3"

'123'.toString() // "123"

(function () {
  return 123;
}).toString()
// "function () {
//   return 123;
// }"

(new Date()).toString()
// "Tue May 10 2016 09:11:31 GMT+0800 (CST)"

Object.prototype.toString.call(value) 可用於判斷數據類型,詳情見 判斷數據類型的各類方法

4. Object.prototype.toLocaleString()

Object.prototype.toLocaleString 方法與 toString 的返回結果相同,也是返回一個值的字符串形式。

var obj = {};
obj.toString(obj)         // "[object Object]"
obj.toLocaleString(obj)   // "[object Object]"

這個方法的主要做用是留出一個接口,讓各類不一樣的對象實現本身版本的 toLocaleString,用來返回針對某些地域的特定的值。

目前,主要有三個對象自定義了 toLocaleString 方法。

  • Array.prototype.toLocaleString()
  • Number.prototype.toLocaleString()
  • Date.prototype.toLocaleString()

日期的實例對象的 toStringtoLocaleString 返回值就不同,並且 toLocaleString 的返回值跟用戶設定的所在地域相關。

var date = new Date();
date.toString()       // "Thu Nov 29 2018 16:50:00 GMT+0800 (中國標準時間)"
date.toLocaleString() // "2018/11/29 下午4:50:00"

參考連接:JavaScript 教程 Object 對象

相關文章
相關標籤/搜索