es5--標準庫之Object對象 Ⅰ

Object 對象

JavaScript 原生提供Object對象(注意起首的O是大寫),JavaScript 的所有其他對象都繼承自Object對象,即那些對象都是Object的實例。Object對象的原生方法分成兩類:Object本身的方法與Object的實例方法。

所謂”本身的方法“就是直接定義在Object對象的方法。下面代碼中,print方法就是直接定義在Object對象上。

Object.print = function (o) { console.log(o) };

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

Object.prototype.print = function () {
  console.log(this);
};
var obj = new Object();
obj.print() // Object

上面代碼中,Object.prototype定義了一個print方法,然后生成一個Object的實例obj。obj直接繼承了Object.prototype的屬性和方法,可以直接使用obj.print調用print方法。也就是說,obj對象的print方法實質上就是調用Object.prototype.print方法。

以下先介紹Object作為函數(shù)的用法,然后再介紹Object對象的原生方法,分成對象自身的方法(又稱為”靜態(tài)方法“)和實例方法兩部分。

  • Object()

Object本身是一個函數(shù),可以當作工具方法使用,將任意值轉為對象。這個方法常用于保證某個值一定是對象。如果參數(shù)為空(或者為undefinednull),Object()返回一個空對象。如果參數(shù)是原始類型的值,Object方法將其轉為對應的包裝對象的實例。

var obj = Object();
// 等同于
var obj = Object(undefined);
var obj = Object(null);
obj instanceof Object // true;

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

instanceof運算符用來驗證,一個對象是否為指定的構造函數(shù)的實例。obj instanceof Object返回true,就表示obj對象是Object的實例。
如果Object方法的參數(shù)是一個對象,它總是返回該對象,即不用轉換。

var arr = [];
var obj = Object(arr); // 返回原數(shù)組
obj === arr // true
var value = {};
var obj = Object(value) // 返回原對象
obj === value // true
var fn = function () {};
var obj = Object(fn); // 返回原函數(shù)
obj === fn // true

//判斷變量是否為對象的函數(shù)
function isObject(value) {
  return value === Object(value);
}
isObject([]) // true
isObject(true) // false
  • Object 構造函數(shù)

Object不僅可以當作工具函數(shù)使用,還可以當作構造函數(shù)使用,即前面可以使用new命令。Object構造函數(shù)的首要用途,是直接通過它來生成新對象。

var obj = new Object();

注意,通過var obj = new Object()的寫法生成新對象,與字面量的寫法var obj = {}是等價的?;蛘哒f,后者只是前者的一種簡便寫法。

Object構造函數(shù)的用法與工具方法很相似,幾乎一模一樣。使用時,可以接受一個參數(shù),如果該參數(shù)是一個對象,則直接返回這個對象;如果是一個原始類型的值,則返回該值對應的包裝對象。雖然用法相似,但是Object(value)與new Object(value)兩者的語義是不同的,Object(value)表示將value轉成一個對象,new Object(value)則表示新生成一個對象,它的值是value。

var o1 = {a: 1};
var o2 = new Object(o1);
o1 === o2 // true
var obj = new Object(123);
obj instanceof Number // true
  • Object 的靜態(tài)方法

所謂“靜態(tài)方法”,是指部署在Object對象自身的方法。Object.keys方法和Object.getOwnPropertyNames方法都用來遍歷對象的屬性。Object.getOwnPropertyNames方法與Object.keys類似,都是接受一個對象作為參數(shù),返回一個數(shù)組,該數(shù)組的成員都是該對象自身的(而不是繼承的)所有屬性名。

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

對于一般的對象來說,Object.keys()Object.getOwnPropertyNames()返回的結果是一樣的。只有涉及不可枚舉屬性時,才會有不一樣的結果。Object.keys方法只返回可枚舉的屬性,Object.getOwnPropertyNames方法還返回不可枚舉的屬性名。一般情況下,幾乎總是使用Object.keys方法,遍歷數(shù)組的屬性。

var a = ['Hello', 'World'];
Object.keys(a) // ["0", "1"]
Object.getOwnPropertyNames(a) // ["0", "1", "length"]

除了上面提到的兩個方法,Object還有不少其他靜態(tài)方法,將在后文逐一詳細介紹。

  1. 對象屬性模型的相關方法
    • Object.getOwnPropertyDescriptor():獲取某個屬性的描述對象。
    • Object.defineProperty():通過描述對象,定義某個屬性。
    • Object.defineProperties():通過描述對象,定義多個屬性。
  2. 控制對象狀態(tài)的方法
    • Object.preventExtensions():防止對象擴展。
    • Object.isExtensible():判斷對象是否可擴展。
    • Object.seal():禁止對象配置。
    • Object.isSealed():判斷一個對象是否可配置。
    • Object.freeze():凍結一個對象。
    • Object.isFrozen():判斷一個對象是否被凍結。
      3.原型鏈相關方法
      -Object.create():該方法可以指定原型對象和屬性,返回一個新的對象。
    • Object.getPrototypeOf():獲取對象的Prototype對象。
  • Object 的實例方法

除了靜態(tài)方法,還有不少方法定義在Object.prototype對象。它們稱為實例方法,所有Object的實例對象都繼承了這些方法。Object實例對象的方法,主要有以下六個。

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

Object.valueOf()方法的作用是返回一個對象的“值”,默認情況下返回對象本身。valueOf方法的主要用途是,JavaScript 自動類型轉換時會默認調用這個方法。

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

var obj = new Object();
1 + obj // "1[object Object]"

上面代碼將對象obj與數(shù)字1相加,這時 JavaScript 就會默認調用valueOf()方法,求出obj的值再與1相加。所以,如果自定義valueOf方法,就可以得到想要的結果。

var obj = new Object();
obj.valueOf = function () {
  return 2;
};
1 + obj // 3

上面代碼自定義了obj對象的valueOf方法,于是1 + obj就得到了3。這種方法就相當于用自定義的obj.valueOf,覆蓋Object.prototype.valueOf

Object.toString()方法的作用是返回一個對象的字符串形式,默認情況下返回類型字符串。

var o1 = new Object();
o1.toString() // "[object Object]"
var o2 = {a:1};
o2.toString() // "[object Object]"

上面代碼表示,對于一個對象調用toString方法,會返回字符串[object Object],該字符串說明對象的類型。字符串[object Object]本身沒有太大的用處,但是通過自定義toString方法,可以讓對象在自動類型轉換時,得到想要的字符串形式。數(shù)組、字符串、函數(shù)、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方法返回對象的類型字符串,因此可以用來判斷一個值的類型。
下面代碼調用空對象的toString方法,結果返回一個字符串object Object,其中第二個Object表示該值的構造函數(shù)。這是一個十分有用的判斷數(shù)據(jù)類型的方法。

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

由于實例對象可能會自定義toString方法,覆蓋掉Object.prototype.toString方法,所以為了得到類型字符串,最好直接使用Object.prototype.toString方法。通過函數(shù)的call方法,可以在任意值上調用這個方法,幫助我們判斷這個值的類型。

Object.prototype.toString.call(value)

上面代碼表示對value這個值調用Object.prototype.toString方法。不同數(shù)據(jù)類型的Object.prototype.toString方法返回值如下。
數(shù)值:返回[object Number]。字符串:返回[object String]。布爾值:返回[object Boolean]undefined:返回[object Undefined]。null:返回[object Null]數(shù)組:返回[object Array]。arguments 對象:返回[object Arguments]函數(shù):返回[object Function]。Error 對象:返回[object Error]Date 對象:返回[object Date]。RegExp 對象:返回[object RegExp]其他對象:返回[object Object]。
這就是說,Object.prototype.toString可以看出一個值到底是什么類型。利用這個特性,可以寫出一個比typeof運算符更準確的類型判斷函數(shù)。

var type = function (o){
  var s = Object.prototype.toString.call(o);
  return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};
type({}); // "object"
type([]); // "array"
type(5); // "number"
type(null); // "null"
type(); // "undefined"
type(/abcd/); // "regex"
type(new Date()); // "date"

//在上面這個type函數(shù)的基礎上,還可以加上專門判斷某種類型數(shù)據(jù)的方法。
var type = function (o){
  var s = Object.prototype.toString.call(o);
  return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};
['Null',
 'Undefined',
 'Object',
 'Array',
 'String',
 'Number',
 'Boolean',
 'Function',
 'RegExp'
].forEach(function (t) {
  type['is' + t] = function (o) {
    return type(o) === t.toLowerCase();
  };
});
type.isObject({}) // true
type.isNumber(NaN) // true
type.isRegExp(/abc/) // true

Object.prototype.toLocaleString方法與toString的返回結果相同,也是返回一個值的字符串形式。這個方法的主要作用是留出一個接口,讓各種不同的對象實現(xiàn)自己版本的toLocaleString,用來返回針對某些地域的特定的值。目前,主要有三個對象自定義了toLocaleString方法。Array.prototype.toLocaleString()、Number.prototype.toLocaleString()Date.prototype.toLocaleString()。舉例來說,日期的實例對象的toString和toLocaleString返回值就不一樣,而且toLocaleString的返回值跟用戶設定的所在地域相關。

var date = new Date();
date.toString() // "Tue Jan 01 2018 12:01:33 GMT+0800 (CST)"
date.toLocaleString() // "1/01/2018, 12:01:33 PM"

Object.prototype.hasOwnProperty方法接受一個字符串作為參數(shù),返回一個布爾值,表示該實例對象自身是否具有該屬性。下面代碼中,對象obj自身具有p屬性,所以返回truetoString屬性是繼承的,所以返回false。

var obj = {
  p: 123
};
obj.hasOwnProperty('p') // true
obj.hasOwnProperty('toString') // false

屬性描述對象

JavaScript 提供了一個內部數(shù)據(jù)結構,用來描述對象的屬性,控制它的行為,比如該屬性是否可寫、可遍歷等等。這個內部數(shù)據(jù)結構稱為“屬性描述對象”(attributes object)。每個屬性都有自己對應的屬性描述對象,保存該屬性的一些元信息。
屬性描述對象提供6個元屬性。
1. value是該屬性的屬性值,默認為undefined。
2.writable是一個布爾值,表示屬性值(value)是否可改變(即是否可寫),默認為true。
3.enumerable是一個布爾值,表示該屬性是否可遍歷,默認為true。如果設為false,會使得某些操作(比如for...in循環(huán)、Object.keys())跳過該屬性。
4.configurable是一個布爾值,表示可配置性,默認為true。如果設為false,將阻止某些操作改寫該屬性,比如無法刪除該屬性,也不得改變該屬性的屬性描述對象(value屬性除外)。也就是說,configurable屬性控制了屬性描述對象的可寫性。
5.get是一個函數(shù),表示該屬性的取值函數(shù)(getter),默認為undefined。
6.set是一個函數(shù),表示該屬性的存值函數(shù)(setter),默認為undefined。

Object.getOwnPropertyDescriptor()方法可以獲取屬性描述對象。它的第一個參數(shù)是目標對象,第二個參數(shù)是一個字符串,對應目標對象的某個屬性名。下面代碼中,Object.getOwnPropertyDescriptor()方法獲取obj.p的屬性描述對象。注意,Object.getOwnPropertyDescriptor()方法只能用于對象自身的屬性,不能用于繼承的屬性。

var obj = { p: 'a' };
Object.getOwnPropertyDescriptor(obj, 'p')
// Object { value: "a",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

Object.getOwnPropertyNames方法返回一個數(shù)組,成員是參數(shù)對象自身的全部屬性的屬性名,不管該屬性是否可遍歷。下面代碼中,obj.p1是可遍歷的,obj.p2是不可遍歷的。Object.getOwnPropertyNames會將它們都返回。這跟Object.keys的行為不同,Object.keys只返回對象自身的可遍歷屬性的全部屬性名。

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

Object.keys([]) // []
Object.getOwnPropertyNames([]) // [ 'length' ]

Object.keys(Object.prototype) // []
Object.getOwnPropertyNames(Object.prototype)
// ['hasOwnProperty',
//  'valueOf',
//  'constructor',
//  'toLocaleString',
//  'isPrototypeOf',
//  'propertyIsEnumerable',
//  'toString']

上面代碼中,數(shù)組自身的length屬性是不可遍歷的,Object.keys不會返回該屬性。最后例子Object.prototype也是一個對象,所有實例對象都會繼承它,它自身的屬性都是不可遍歷的。

Object.defineProperty()方法允許通過屬性描述對象,定義或修改一個屬性,然后返回修改后的對象,它的用法如下。

Object.defineProperty(object, propertyName, attributesObject)

Object.defineProperty方法接受三個參數(shù),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

上面代碼中,Object.defineProperty()方法定義了obj.p屬性。由于屬性描述對象的writable屬性為false,所以obj.p屬性不可寫。注意,這里的Object.defineProperty方法的第一個參數(shù)是{}(一個新建的空對象),p屬性直接定義在這個空對象上面,然后返回這個對象,這是Object.defineProperty()的常見用法。如果屬性已經(jīng)存在,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"

上面代碼中,Object.defineProperties()同時定義了obj對象的三個屬性。其中,p3屬性定義了取值函數(shù)get,即每次讀取該屬性,都會調用這個取值函數(shù)。注意,一旦定義了取值函數(shù)get(或存值函數(shù)set),就不能將writable屬性設為true,或者同時定義value屬性,否則會報錯。Object.defineProperty()Object.defineProperties()參數(shù)里面的屬性描述對象,writableconfigurable、enumerable這三個屬性的默認值都為false

實例對象的propertyIsEnumerable()方法返回一個布爾值,用來判斷某個屬性是否可遍歷。注意,這個方法只能用于判斷對象自身的屬性,對于繼承的屬性一律返回false。

var obj = {};
obj.p = 123;
obj.propertyIsEnumerable('p') // true
obj.propertyIsEnumerable('toString') // false
//obj.p是可遍歷的,而obj.toString是繼承的屬性。
  • 元屬性

屬性描述對象的各個屬性稱為“元屬性”,因為它們可以看作是控制屬性的屬性。
value屬性是目標屬性的值。可讀取可改寫
writable屬性是一個布爾值,決定了目標屬性的值(value)是否可以被改變。

注意,正常模式下,對writablefalse的屬性賦值不會報錯,只會默默失敗。但是,嚴格模式下會報錯,即使對a屬性重新賦予一個同樣的值。如果原型對象的某個屬性的writablefalse,那么子對象將無法自定義這個屬性。

var proto = Object.defineProperty({}, 'foo', {
  value: 'a',
  writable: false
});
var obj = Object.create(proto);
obj.foo = 'b';
obj.foo // 'a'

上面代碼中,proto是原型對象,它的foo屬性不可寫。obj對象繼承proto,也不可以再自定義這個屬性了。如果是嚴格模式,這樣做還會拋出一個錯誤。但是,有一個規(guī)避方法,就是通過覆蓋屬性描述對象,繞過這個限制。原因是這種情況下,原型鏈會被完全忽視。

var proto = Object.defineProperty({}, 'foo', {
  value: 'a',
  writable: false
});
var obj = Object.create(proto);
Object.defineProperty(obj, 'foo', {
  value: 'b'
});
obj.foo // "b"

enumerable(可遍歷性)返回一個布爾值,表示目標屬性是否可遍歷。JavaScript 的早期版本,for...in循環(huán)是基于in運算符的。我們知道,in運算符不管某個屬性是對象自身的還是繼承的,都會返回true。

var obj = {};
'toString' in obj // true

上面代碼中,toString不是obj對象自身的屬性,但是in運算符也返回true,這導致了toString屬性也會被for...in循環(huán)遍歷。這顯然不太合理,后來就引入了“可遍歷性”這個概念。只有可遍歷的屬性,才會被for...in循環(huán)遍歷,同時還規(guī)定toString這一類實例對象繼承的原生屬性,都是不可遍歷的,這樣就保證了for...in循環(huán)的可用性。

具體來說,如果一個屬性的enumerablefalse,這三個操作不會取到該屬性:for..in循環(huán)、Object.keys方法、JSON.stringify方法。因此,enumerable可以用來設置“秘密”屬性。

var obj = {};
Object.defineProperty(obj, 'x', {
  value: 123,
  enumerable: false
});
obj.x // 123
for (var key in obj) {
  console.log(key);
}
// undefined
Object.keys(obj)  // []
JSON.stringify(obj) // "{}"

上面代碼中,obj.x屬性的enumerablefalse,所以一般的遍歷操作都無法獲取該屬性,使得它有點像“秘密”屬性,但不是真正的私有屬性,還是可以直接獲取它的值。

注意,for...in循環(huán)包括繼承的屬性,Object.keys方法不包括繼承的屬性。如果需要獲取對象自身的所有屬性,不管是否可遍歷,可以使用Object.getOwnPropertyNames方法。
另外,JSON.stringify方法會排除enumerablefalse的屬性,有時可以利用這一點。如果對象的JSON格式輸出要排除某些屬性,就可以把這些屬性的enumerable設為false。

configurable(可配置性)返回一個布爾值,決定了是否可以修改屬性描述對象。也就是說,configurablefalse時,value、writableenumerableconfigurable都不能被修改了。

var obj = Object.defineProperty({}, 'p', {
  value: 1,
  writable: false,
  enumerable: false,
  configurable: false
});

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

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

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

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

上面代碼中,obj.p的configurablefalse。然后,改動value、writableenumerable、configurable,結果都報錯。

注意,writable只有在false改為true會報錯,true改為false是允許的。至于value,只要writableconfigurable有一個為true,就允許改動。另外,configurablefalse時,直接目標屬性賦值,不報錯,但不會成功。可配置性決定了目標屬性是否可以被刪除(delete)。configurabletrue,可以被刪除,反之不能。

  • 存取器

除了直接定義以外,屬性還可以用存取器(accessor)定義。其中,存值函數(shù)稱為setter,使用屬性描述對象的set屬性;取值函數(shù)稱為getter,使用屬性描述對象的get屬性。一旦對目標屬性定義了存取器,那么存取的時候,都將執(zhí)行對應的函數(shù)。利用這個功能,可以實現(xiàn)許多高級特性,比如某個屬性禁止賦值。

var obj = Object.defineProperty({}, 'p', {
  get: function () {
    return 'getter';
  },
  set: function (value) {
    console.log('setter: ' + value);
  }
});
obj.p // "getter"
obj.p = 123 // "setter: 123"

上面代碼中,obj.p定義了get和set屬性。obj.p取值時,就會調用get;賦值時,就會調用set。JavaScript 還提供了存取器的另一種寫法。

var obj = {
  get p() {
    return 'getter';
  },
  set p(value) {
    console.log('setter: ' + value);
  }
};

var obj ={
  $n : 5,
  get next() { return this.$n++ },
  set next(n) {
    if (n >= this.$n) this.$n = n;
    else throw new Error('新的值必須大于當前值');
  }
};
obj.next // 5
obj.next = 10;
obj.next // 10
obj.next = 5;
// Uncaught Error: 新的值必須大于當前值

上面的寫法與定義屬性描述對象是等價的,而且使用更廣泛。注意,取值函數(shù)get不能接受參數(shù),存值函數(shù)set只能接受一個參數(shù)(即屬性的值)。存取器往往用于,屬性的值依賴對象內部數(shù)據(jù)的場合。

  • 對象的拷貝

有時,我們需要將一個對象的所有屬性,拷貝到另一個對象,可以用下面的方法實現(xiàn)。

var extend = function (to, from) {
  for (var property in from) {
    to[property] = from[property];
  }
  return to;
}
extend({}, {
  a: 1
})
// {a: 1}

extend({}, {
  get a() { return 1 }
})
// {a: 1}

上面這個方法的問題在于,如果遇到存取器定義的屬性,會只拷貝值。為了解決這個問題,我們可以通過Object.defineProperty方法來拷貝屬性。

var extend = function (to, from) {
  for (var property in from) {
    if (!from.hasOwnProperty(property)) continue;
    Object.defineProperty(
      to,
      property,
      Object.getOwnPropertyDescriptor(from, property)
    );
  }

  return to;
}
extend({}, { get a(){ return 1 } })
// { get a(){ return 1 } })

上面代碼中,hasOwnProperty那一行用來過濾掉繼承的屬性,否則會報錯,因為Object.getOwnPropertyDescriptor讀不到繼承屬性的屬性描述對象。

  • 控制對象狀態(tài)

有時需要凍結對象的讀寫狀態(tài),防止對象被改變。JavaScript 提供了三種凍結方法,最弱的一種是Object.preventExtensions,其次是Object.seal,最強的是Object.freeze。
Object.preventExtensions()方法可以使得一個對象無法再添加新的屬性。下面代碼,obj對象經(jīng)過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

Object.isExtensible()方法用于檢查一個對象是否使用了Object.preventExtensions方法。也就是說,檢查是否可以為一個對象添加屬性。下面代碼中,對obj對象使用Object.preventExtensions方法以后,再使用Object.isExtensible方法,返回false,表示已經(jīng)不能添加新屬性了。

var obj = new Object();
Object.isExtensible(obj) // true
Object.preventExtensions(obj);
Object.isExtensible(obj) // false

Object.seal()方法使得一個對象既無法添加新屬性,也無法刪除舊屬性。下面代碼中,obj對象執(zhí)行Object.seal方法以后,就無法添加新屬性和刪除舊屬性了。Object.seal實質是把屬性描述對象的configurable屬性設為false,因此屬性描述對象不再能改變了。Object.seal只是禁止新增或刪除屬性,并不影響修改某個屬性的值。

var obj = { p: 'hello' };
Object.seal(obj);
delete obj.p;
obj.p // "hello"
obj.x = 'world';
obj.x // undefined

Object.isSealed()方法用于檢查一個對象是否使用了Object.seal()方法。

var obj = { p: 'a' };
Object.seal(obj);
Object.isSealed(obj) // true
Object.isExtensible(obj) // false   這時,Object.isExtensible方法也返回false。

Object.freeze()方法可以使得一個對象無法添加新屬性、無法刪除舊屬性、也無法改變屬性的值,使得這個對象實際上變成了常量。下面代碼中,對obj對象進行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"

Object.isFrozen()方法用于檢查一個對象是否使用了Object.freeze()方法。使用Object.freeze方法以后,Object.isSealed將會返回true,Object.isExtensible返回falseObject.isFrozen的一個用途是,確認某個對象沒有被凍結后,再對它的屬性賦值。

var obj = {
  p: 'hello'
};
Object.freeze(obj);
Object.isFrozen(obj) // true
Object.isSealed(obj) // true
Object.isExtensible(obj) // false

上面的三個方法鎖定對象的可寫性有一個漏洞:可以通過改變原型對象,來為對象增加屬性。下面代碼中,對象obj本身不能新增屬性,但是可以在它的原型對象上新增屬性,就依然能夠在obj上讀到。

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

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

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

另外一個局限是,如果屬性值是對象,上面這些方法只能凍結屬性指向的對象,而不能凍結對象本身的內容。

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

上面代碼中,obj.bar屬性指向一個數(shù)組,obj對象被凍結以后,這個指向無法改變,即無法指向其他值,但是所指向的數(shù)組是可以改變的。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 把酒店換到了距離中山路很近的一家酒店:斯鉑瑞酒店。對面就是中山公園南門,酒店安全性感覺很好,上電梯要刷房卡,房門也...
    一大只羊閱讀 643評論 0 3
  • 今天作業(yè)好玩!
    凌薇薇閱讀 295評論 0 0
  • 如果覺得很難放棄的話 不如去見他一面 見完以后你會發(fā)現(xiàn) 那些你覺得的好其實都是幻想的 他還是那個破樣
    Sery耐閱讀 178評論 0 0
  • 昨天周末在家閑來無事,把以前的電子小說拿過來翻了翻,看到瓊瑤的小說《窗外》,就打開來閱讀,三個多小時看完的。這篇小...
    霞姝兒閱讀 630評論 0 4

友情鏈接更多精彩內容