JS屬性描述對(duì)象

概述

JavaScript 提供了一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu),用來(lái)描述對(duì)象的屬性,控制它的行為,比如該屬性是否可寫(xiě)竿屹、可遍歷等等。這個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu)稱為“屬性描述對(duì)象”(attributes object)灸姊。每個(gè)屬性都有自己對(duì)應(yīng)的屬性描述對(duì)象拱燃,保存該屬性的一些元信息。

下面是屬性描述對(duì)象的一個(gè)例子力惯。

{
  value: 123,
  writable: false,
  enumerable: true,
  configurable: false,
  get: undefined,
  set: undefined
}

屬性描述對(duì)象提供6個(gè)元屬性碗誉。

  • value
    value是該屬性的屬性值召嘶,默認(rèn)為undefined
  • writable
    writable是一個(gè)布爾值哮缺,表示屬性值(value)是否可改變(即是否可寫(xiě))弄跌,默認(rèn)為true
  • enumerable
    enumerable是一個(gè)布爾值尝苇,表示該屬性是否可遍歷铛只,默認(rèn)為true。如果設(shè)為false糠溜,會(huì)使得某些操作(比如for...in循環(huán)淳玩、Object.keys())跳過(guò)該屬性。
  • configurable
    configurable是一個(gè)布爾值非竿,表示可配置性蜕着,默認(rèn)為true。如果設(shè)為false红柱,將阻止某些操作改寫(xiě)該屬性承匣,比如無(wú)法刪除該屬性,也不得改變?cè)搶傩缘膶傩悦枋鰧?duì)象(value屬性除外)锤悄。也就是說(shuō)悄雅,configurable屬性控制了屬性描述對(duì)象的可寫(xiě)性。
  • get
    get是一個(gè)函數(shù)铁蹈,表示該屬性的取值函數(shù)(getter),默認(rèn)為undefined众眨。
  • set
    set是一個(gè)函數(shù)握牧,表示該屬性的存值函數(shù)(setter),默認(rèn)為undefined娩梨。

Object.getOwnPropertyDescriptor()

Object.getOwnPropertyDescriptor()方法可以獲取屬性描述對(duì)象沿腰。它的第一個(gè)參數(shù)是目標(biāo)對(duì)象,第二個(gè)參數(shù)是一個(gè)字符串狈定,對(duì)應(yīng)目標(biāo)對(duì)象的某個(gè)屬性名颂龙。

var obj = { p: 'a' };

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

注意,Object.getOwnPropertyDescriptor()方法只能用于對(duì)象自身的屬性纽什,不能用于繼承的屬性措嵌。

var obj = { p: 'a' };

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

上面代碼中,toStringobj對(duì)象繼承的屬性芦缰,Object.getOwnPropertyDescriptor()無(wú)法獲取企巢。

Object.getOwnPropertyNames()

Object.getOwnPropertyNames方法返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的全部屬性的屬性名让蕾,不管該屬性是否可遍歷浪规。

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

Object.getOwnPropertyNames(obj) // ["p1", "p2"]

這跟Object.keys的行為不同或听,Object.keys只返回對(duì)象自身的可遍歷屬性的全部屬性名。

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

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

上面代碼中笋婿,數(shù)組自身的length屬性是不可遍歷的誉裆,Object.keys不會(huì)返回該屬性。第二個(gè)例子的Object.prototype也是一個(gè)對(duì)象缸濒,所有實(shí)例對(duì)象都會(huì)繼承它足丢,它自身的屬性都是不可遍歷的。

Object.defineProperty()绍填,Object.defineProperties()

Object.defineProperty()方法允許通過(guò)屬性描述對(duì)象霎桅,定義或修改一個(gè)屬性,然后返回修改后的對(duì)象讨永,它的用法如下滔驶。

Object.defineProperty(object, propertyName, attributesObject)

Object.defineProperty方法接受三個(gè)參數(shù),依次如下卿闹。

  • object:屬性所在的對(duì)象
  • propertyName:字符串揭糕,表示屬性名
  • attributesObject:屬性描述對(duì)象

舉例來(lái)說(shuō),定義obj.p可以寫(xiě)成下面這樣锻霎。

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屬性。由于屬性描述對(duì)象的writable屬性為false旋恼,所以obj.p屬性不可寫(xiě)吏口。注意,這里的Object.defineProperty方法的第一個(gè)參數(shù)是{}(一個(gè)新建的空對(duì)象)冰更,p屬性直接定義在這個(gè)空對(duì)象上面产徊,然后返回這個(gè)對(duì)象,這是Object.defineProperty()的常見(jiàn)用法蜀细。

如果屬性已經(jīng)存在舟铜,Object.defineProperty()方法相當(dāng)于更新該屬性的屬性描述對(duì)象。

如果一次性定義或修改多個(gè)屬性奠衔,可以使用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()同時(shí)定義了obj對(duì)象的三個(gè)屬性归斤。其中痊夭,p3屬性定義了取值函數(shù)get,即每次讀取該屬性官册,都會(huì)調(diào)用這個(gè)取值函數(shù)生兆。

注意,一旦定義了取值函數(shù)get(或存值函數(shù)set),就不能將writable屬性設(shè)為true鸦难,或者同時(shí)定義value屬性根吁,否則會(huì)報(bào)錯(cuò)。

var obj = {};

Object.defineProperty(obj, 'p', {
  value: 123,
  get: function() { return 456; }
});
// TypeError: Invalid property.
// A property cannot both have accessors and be writable or have a value

Object.defineProperty(obj, 'p', {
  writable: true,
  get: function() { return 456; }
});
// TypeError: Invalid property descriptor.
// Cannot both specify accessors and a value or writable attribute

Object.defineProperty()Object.defineProperties()參數(shù)里面的屬性描述對(duì)象合蔽,writable击敌、configurableenumerable這三個(gè)屬性的默認(rèn)值都為false拴事。

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

上面代碼中沃斤,定義obj.foo時(shí)用了一個(gè)空的屬性描述對(duì)象,就可以看到各個(gè)元屬性的默認(rèn)值刃宵。

Object.prototype.propertyIsEnumerable()

實(shí)例對(duì)象的propertyIsEnumerable()方法返回一個(gè)布爾值衡瓶,用來(lái)判斷某個(gè)屬性是否可遍歷。注意牲证,這個(gè)方法只能用于判斷對(duì)象自身的屬性哮针,對(duì)于繼承的屬性一律返回false

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

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

上面代碼中坦袍,obj.p是可遍歷的十厢,而obj.toString是繼承的屬性。

元屬性

屬性描述對(duì)象的各個(gè)屬性稱為“元屬性”捂齐,因?yàn)樗鼈兛梢钥醋魇强刂茖傩缘膶傩浴?/p>

value

value屬性是目標(biāo)屬性的值蛮放。

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

Object.getOwnPropertyDescriptor(obj, 'p').value // 123

Object.defineProperty(obj, 'p', { value: 246 });
obj.p // 246

上面代碼是通過(guò)value屬性,讀取或改寫(xiě)obj.p的例子奠宜。

writable

writable屬性是一個(gè)布爾值包颁,決定了目標(biāo)屬性的值(value)是否可以被改變。

var obj = {};
Object.defineProperty(obj, 'a', {
  value: 37,
  writable: false
});

obj.a // 37
obj.a = 25;
obj.a // 37

上面代碼中压真,obj.awritable屬性是false徘六。然后,改變obj.a的值榴都,不會(huì)有任何效果。

注意漠其,正常模式下嘴高,對(duì)writablefalse的屬性賦值不會(huì)報(bào)錯(cuò),只會(huì)默默失敗和屎。但是拴驮,嚴(yán)格模式下會(huì)報(bào)錯(cuò),即使對(duì)a屬性重新賦予一個(gè)同樣的值柴信。

'use strict';
var obj = {};

Object.defineProperty(obj, 'a', {
  value: 37,
  writable: false
});

obj.a = 37;
// Uncaught TypeError: Cannot assign to read only property 'a' of object

上面代碼是嚴(yán)格模式套啤,對(duì)obj.a任何賦值行為都會(huì)報(bào)錯(cuò)。

如果原型對(duì)象的某個(gè)屬性的writablefalse随常,那么子對(duì)象將無(wú)法自定義這個(gè)屬性潜沦。

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

var obj = Object.create(proto);

obj.foo = 'b';
obj.foo // 'a'

上面代碼中萄涯,proto是原型對(duì)象,它的foo屬性不可寫(xiě)唆鸡。obj對(duì)象繼承proto涝影,也不可以再自定義這個(gè)屬性了。如果是嚴(yán)格模式争占,這樣做還會(huì)拋出一個(gè)錯(cuò)誤燃逻。

但是,有一個(gè)規(guī)避方法臂痕,就是通過(guò)覆蓋屬性描述對(duì)象伯襟,繞過(guò)這個(gè)限制。原因是這種情況下握童,原型鏈會(huì)被完全忽視姆怪。

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

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

obj.foo // "b"

enumerable

enumerable(可遍歷性)返回一個(gè)布爾值,表示目標(biāo)屬性是否可遍歷舆瘪。

JavaScript 的早期版本片效,for...in循環(huán)是基于in運(yùn)算符的。我們知道英古,in運(yùn)算符不管某個(gè)屬性是對(duì)象自身的還是繼承的淀衣,都會(huì)返回true

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

上面代碼中召调,toString不是obj對(duì)象自身的屬性膨桥,但是in運(yùn)算符也返回true,這導(dǎo)致了toString屬性也會(huì)被for...in循環(huán)遍歷唠叛。

這顯然不太合理只嚣,后來(lái)就引入了“可遍歷性”這個(gè)概念。只有可遍歷的屬性艺沼,才會(huì)被for...in循環(huán)遍歷册舞,同時(shí)還規(guī)定toString這一類實(shí)例對(duì)象繼承的原生屬性,都是不可遍歷的障般,這樣就保證了for...in循環(huán)的可用性调鲸。

具體來(lái)說(shuō),如果一個(gè)屬性的enumerablefalse挽荡,下面三個(gè)操作不會(huì)取到該屬性藐石。

  • for..in循環(huán)
  • Object.keys方法
  • JSON.stringify方法

因此,enumerable可以用來(lái)設(shè)置“秘密”屬性定拟。

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,所以一般的遍歷操作都無(wú)法獲取該屬性,使得它有點(diǎn)像“秘密”屬性株依,但不是真正的私有屬性驱证,還是可以直接獲取它的值。

注意勺三,for...in循環(huán)包括繼承的屬性雷滚,Object.keys方法不包括繼承的屬性。如果需要獲取對(duì)象自身的所有屬性吗坚,不管是否可遍歷祈远,可以使用Object.getOwnPropertyNames方法。

另外商源,JSON.stringify方法會(huì)排除enumerablefalse的屬性车份,有時(shí)可以利用這一點(diǎn)。如果對(duì)象的 JSON 格式輸出要排除某些屬性牡彻,就可以把這些屬性的enumerable設(shè)為false扫沼。

configurable

configurable(可配置性)返回一個(gè)布爾值,決定了是否可以修改屬性描述對(duì)象庄吼。也就是說(shuō)缎除,configurablefalse時(shí),value总寻、writable器罐、enumerableconfigurable都不能被修改了。

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.pconfigurablefalse轰坊。然后,改動(dòng)value祟印、writable肴沫、enumerableconfigurable蕴忆,結(jié)果都報(bào)錯(cuò)颤芬。

注意,writable只有在false改為true會(huì)報(bào)錯(cuò)套鹅,true改為false是允許的驻襟。

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

Object.defineProperty(obj, 'p', {writable: false})
// 修改成功

至于value,只要writableconfigurable有一個(gè)為true芋哭,就允許改動(dòng)。

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

Object.defineProperty(o1, 'p', {value: 2})
// 修改成功

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

Object.defineProperty(o2, 'p', {value: 2})
// 修改成功

另外郁副,writablefalse時(shí)减牺,直接目標(biāo)屬性賦值,不報(bào)錯(cuò),但不會(huì)成功拔疚。

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

obj.p = 2;
obj.p // 1

上面代碼中肥隆,obj.pwritablefalse,對(duì)obj.p直接賦值不會(huì)生效稚失。如果是嚴(yán)格模式栋艳,還會(huì)報(bào)錯(cuò)。

可配置性決定了目標(biāo)屬性是否可以被刪除句各。

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

delete obj.p1 // true
delete obj.p2 // false

obj.p1 // undefined
obj.p2 // 2

上面代碼中吸占,obj.p1configurabletrue,所以可以被刪除凿宾,obj.p2就無(wú)法刪除矾屯。

存取器

除了直接定義以外,屬性還可以用存取器(accessor)定義初厚。其中件蚕,存值函數(shù)稱為setter,使用屬性描述對(duì)象的set屬性产禾;取值函數(shù)稱為getter排作,使用屬性描述對(duì)象的get屬性。

一旦對(duì)目標(biāo)屬性定義了存取器亚情,那么存取的時(shí)候妄痪,都將執(zhí)行對(duì)應(yīng)的函數(shù)。利用這個(gè)功能势似,可以實(shí)現(xiàn)許多高級(jí)特性拌夏,比如某個(gè)屬性禁止賦值。

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定義了getset屬性障簿。obj.p取值時(shí),就會(huì)調(diào)用get栅迄;賦值時(shí)站故,就會(huì)調(diào)用set

JavaScript 還提供了存取器的另一種寫(xiě)法毅舆。

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

上面的寫(xiě)法與定義屬性描述對(duì)象是等價(jià)的西篓,而且使用更廣泛。

注意憋活,取值函數(shù)get不能接受參數(shù)岂津,存值函數(shù)set只能接受一個(gè)參數(shù)(即屬性的值)。

存取器往往用于悦即,屬性的值依賴對(duì)象內(nèi)部數(shù)據(jù)的場(chǎng)合吮成。

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

obj.next // 5

obj.next = 10;
obj.next // 10

obj.next = 5;
// Uncaught Error: 新的值必須大于當(dāng)前值

上面代碼中橱乱,next屬性的存值函數(shù)和取值函數(shù),都依賴于內(nèi)部屬性$n粱甫。

對(duì)象的拷貝

有時(shí)泳叠,我們需要將一個(gè)對(duì)象的所有屬性,拷貝到另一個(gè)對(duì)象茶宵,可以用下面的方法實(shí)現(xiàn)危纫。

var extend = function (to, from) {
  for (var property in from) {
    to[property] = from[property];
  }

  return to;
}

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

上面這個(gè)方法的問(wèn)題在于,如果遇到存取器定義的屬性乌庶,會(huì)只拷貝值种蝶。

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

為了解決這個(gè)問(wèn)題,我們可以通過(guò)Object.defineProperty方法來(lái)拷貝屬性安拟。

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那一行用來(lái)過(guò)濾掉繼承的屬性,否則可能會(huì)報(bào)錯(cuò)糠赦,因?yàn)?code>Object.getOwnPropertyDescriptor讀不到繼承屬性的屬性描述對(duì)象会傲。

控制對(duì)象狀態(tài)

有時(shí)需要凍結(jié)對(duì)象的讀寫(xiě)狀態(tài),防止對(duì)象被改變拙泽。JavaScript 提供了三種凍結(jié)方法淌山,最弱的一種是Object.preventExtensions,其次是Object.seal顾瞻,最強(qiáng)的是Object.freeze泼疑。

Object.preventExtensions()

Object.preventExtensions方法可以使得一個(gè)對(duì)象無(wú)法再添加新的屬性。

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

上面代碼中荷荤,obj對(duì)象經(jīng)過(guò)Object.preventExtensions以后退渗,就無(wú)法添加新屬性了。

Object.isExtensible()

Object.isExtensible方法用于檢查一個(gè)對(duì)象是否使用了Object.preventExtensions方法蕴纳。也就是說(shuō)会油,檢查是否可以為一個(gè)對(duì)象添加屬性。

var obj = new Object();

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

上面代碼中古毛,對(duì)obj對(duì)象使用Object.preventExtensions方法以后翻翩,再使用Object.isExtensible方法,返回false稻薇,表示已經(jīng)不能添加新屬性了嫂冻。

Object.seal()

Object.seal方法使得一個(gè)對(duì)象既無(wú)法添加新屬性,也無(wú)法刪除舊屬性塞椎。

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

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

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

上面代碼中桨仿,obj對(duì)象執(zhí)行Object.seal方法以后,就無(wú)法添加新屬性和刪除舊屬性了案狠。

Object.seal實(shí)質(zhì)是把屬性描述對(duì)象的configurable屬性設(shè)為false服傍,因此屬性描述對(duì)象不再能改變了暇昂。

var obj = {
  p: 'a'
};

// seal方法之前
Object.getOwnPropertyDescriptor(obj, 'p')
// Object {
//   value: "a",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

Object.seal(obj);

// seal方法之后
Object.getOwnPropertyDescriptor(obj, 'p')
// Object {
//   value: "a",
//   writable: true,
//   enumerable: true,
//   configurable: false
// }

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

上面代碼中,使用Object.seal方法之后伴嗡,屬性描述對(duì)象的configurable屬性就變成了false,然后改變enumerable屬性就會(huì)報(bào)錯(cuò)从铲。

Object.seal只是禁止新增或刪除屬性瘪校,并不影響修改某個(gè)屬性的值。

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

上面代碼中名段,Object.seal方法對(duì)p屬性的value無(wú)效阱扬,是因?yàn)榇藭r(shí)p屬性的可寫(xiě)性由writable決定。

Object.isSealed()

Object.isSealed方法用于檢查一個(gè)對(duì)象是否使用了Object.seal方法伸辟。

var obj = { p: 'a' };

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

這時(shí)麻惶,Object.isExtensible方法也返回false

var obj = { p: 'a' };

Object.seal(obj);
Object.isExtensible(obj) // false

Object.freeze()

Object.freeze方法可以使得一個(gè)對(duì)象無(wú)法添加新屬性信夫、無(wú)法刪除舊屬性窃蹋、也無(wú)法改變屬性的值,使得這個(gè)對(duì)象實(shí)際上變成了常量静稻。

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"

上面代碼中警没,對(duì)obj對(duì)象進(jìn)行Object.freeze()以后,修改屬性振湾、新增屬性杀迹、刪除屬性都無(wú)效了。這些操作并不報(bào)錯(cuò)押搪,只是默默地失敗树酪。如果在嚴(yán)格模式下,則會(huì)報(bào)錯(cuò)大州。

Object.isFrozen()

Object.isFrozen方法用于檢查一個(gè)對(duì)象是否使用了Object.freeze方法续语。

var obj = {
  p: 'hello'
};

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

使用Object.freeze方法以后,Object.isSealed將會(huì)返回true摧茴,Object.isExtensible返回false绵载。

var obj = {
  p: 'hello'
};

Object.freeze(obj);

Object.isSealed(obj) // true
Object.isExtensible(obj) // false

Object.isFrozen的一個(gè)用途是,確認(rèn)某個(gè)對(duì)象沒(méi)有被凍結(jié)后苛白,再對(duì)它的屬性賦值娃豹。

var obj = {
  p: 'hello'
};

Object.freeze(obj);

if (!Object.isFrozen(obj)) {
  obj.p = 'world';
}

上面代碼中,確認(rèn)obj沒(méi)有被凍結(jié)后购裙,再對(duì)它的屬性賦值懂版,就不會(huì)報(bào)錯(cuò)了。

局限性

上面的三個(gè)方法鎖定對(duì)象的可寫(xiě)性有一個(gè)漏洞:可以通過(guò)改變?cè)蛯?duì)象躏率,來(lái)為對(duì)象增加屬性躯畴。

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

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

上面代碼中民鼓,對(duì)象obj本身不能新增屬性,但是可以在它的原型對(duì)象上新增屬性蓬抄,就依然能夠在obj上讀到丰嘉。

一種解決方案是,把obj的原型也凍結(jié)住嚷缭。

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

var proto = Object.getPrototypeOf(obj);
Object.preventExtensions(proto);

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

另外一個(gè)局限是饮亏,如果屬性值是對(duì)象,上面這些方法只能凍結(jié)屬性指向的對(duì)象阅爽,而不能凍結(jié)對(duì)象本身的內(nèi)容路幸。

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

obj.bar.push('c');
obj.bar // ["a", "b", "c"]

上面代碼中,obj.bar屬性指向一個(gè)數(shù)組付翁,obj對(duì)象被凍結(jié)以后简肴,這個(gè)指向無(wú)法改變,即無(wú)法指向其他值百侧,但是所指向的數(shù)組是可以改變的砰识。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市移层,隨后出現(xiàn)的幾起案子仍翰,更是在濱河造成了極大的恐慌,老刑警劉巖观话,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件予借,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡频蛔,警方通過(guò)查閱死者的電腦和手機(jī)灵迫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)晦溪,“玉大人瀑粥,你說(shuō)我怎么就攤上這事∪玻” “怎么了狞换?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)舟肉。 經(jīng)常有香客問(wèn)我修噪,道長(zhǎng),這世上最難降的妖魔是什么路媚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任黄琼,我火速辦了婚禮,結(jié)果婚禮上整慎,老公的妹妹穿的比我還像新娘脏款。我一直安慰自己围苫,他們只是感情好险毁,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布改橘。 她就那樣靜靜地躺著,像睡著了一般慰照。 火紅的嫁衣襯著肌膚如雪剃盾。 梳的紋絲不亂的頭發(fā)上周循,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音万俗,去河邊找鬼。 笑死饮怯,一個(gè)胖子當(dāng)著我的面吹牛闰歪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蓖墅,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼库倘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了论矾?” 一聲冷哼從身側(cè)響起教翩,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贪壳,沒(méi)想到半個(gè)月后饱亿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闰靴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年彪笼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚂且。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡配猫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出杏死,到底是詐尸還是另有隱情泵肄,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布淑翼,位于F島的核電站腐巢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏窒舟。R本人自食惡果不足惜系忙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惠豺。 院中可真熱鬧银还,春花似錦风宁、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至捺弦,卻和暖如春饮寞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背列吼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工幽崩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寞钥。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓慌申,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親理郑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蹄溉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 參考文章:屬性描述對(duì)象 概述 JavaScript 提供了一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu),用來(lái)描述對(duì)象的屬性您炉,控制它的行為柒爵,比如...
    chrisghb閱讀 272評(píng)論 0 0
  • 概述 JavaScript提供了一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu),用來(lái)描述一個(gè)對(duì)象的屬性的行為赚爵,控制它的行為棉胀。這被稱為“屬性描述對(duì)...
    zjh111閱讀 724評(píng)論 0 0
  • 概述 JavaScript提供了一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu),用來(lái)描述一個(gè)對(duì)象的屬性的行為冀膝,控制它的行為膏蚓。這被稱為“屬性描述對(duì)...
    許先生__閱讀 484評(píng)論 0 1
  • (本文摘錄于阮一峰老師所寫(xiě)文章,https://wangdoc.com/javascript/basic/inde...
    柳叁叁閱讀 249評(píng)論 0 0
  • (注1:如果有問(wèn)題歡迎留言探討畸写,一起學(xué)習(xí)驮瞧!轉(zhuǎn)載請(qǐng)注明出處,喜歡可以點(diǎn)個(gè)贊哦?莘摇)(注2:更多內(nèi)容請(qǐng)查看我的目錄论笔。) ...
    love丁酥酥閱讀 1,854評(píng)論 4 7