JavaScript 中的对象是动态的,可在代码执行的任意时刻发生改变。基于类的语言会根据类的定义锁定对象。chrome
当一个属性第一次被添加到对象时,JavaScript 会在对象上调用一个名为 [[Put]]
的内部方法。数组
[[Put]]
方法会在对象上建立一个新节点来保存属性。函数
当一个已有的属性被赋予一个新值时,调用的是一个名为 [[Set]]
的方法。ui
检查对象是否已有一个属性。JavaScript 开发新手错误地使用如下模式检测属性是否存在。this
if(person.age){
// do something with ag
}
复制代码
上面的问题在于 JavaScript 的类型强制会影响该模式的输出结果。
当 if 判断中的值以下时,会判断为真:spa
当 if 判断中的值以下时,会判断为假:prototype
所以判断属性是否存在的方法是使用 in
操做符。code
in
操做符会检查自有属性和原型属性。对象
全部的对象都拥有的 hasOwnProperty()
方法(实际上是 Object.prototype
原型对象的),该方法在给定的属性存在且为自有属性时返回 true
。ip
var person = {
name: "Nicholas"
}
console.log("name" in person); // true
console.log(person.hasOwnpropert("name")); // true
console.log("toString" in person); // true
console.log(person.hasOwnproperty("toString")); // false
复制代码
设置一个属性的值为 null
并不能从对象中完全移除那个属性,这只是调用 [[Set]]
将 null
值替换了该属性原来的值而已。
delete
操做符针对单个对象属性调用名为 [[Delete]]
的内部方法。删除成功时,返回 true
。
var person = {
name: "Nicholas"
}
person.name = null;
console.log("name" in person); // true
delete person.name;
console.log(person.name); // undefined 访问一个不存在的属性将返回 undefined
console.log("name" in person); // false
复制代码
全部人为添加的属性默认都是可枚举的。可枚举的内部特征 [[Enumerable]]
都被设置为 true
。
for-in
循环会枚举一个对象全部的可枚举属性。
ECMAScript 5 的 Object.keys() 方法能够获取可枚举属性的名字的数组。
var person = {
name: "Nicholas",
age: 18
}
Object.keys(person); // ["name", "age"];
复制代码
for-in
与 Object.keys()
的一个区别是:前者也会遍历原型属性,然后者返回自有(实例)属性。
实际上,对象的大部分原生方法的 [[Enumerable]]
特征都被设置为 false
。可用 propertyIsEnumerable()
方法检查一个属性是否为可枚举的。
var arr = ["abc", 2];
console.log(arr.propertyIsEnumerable("length")); // false
复制代码
属性有两种类型:数据属性和访问器属性。
数据属性
包含一个值。[[Put]]
方法的默认行为是建立数据属性。
访问器属性
不包含值而是定义了一个当属性被读取时调用的函数(称为 getter
)和一个当属性被写入时调用的函数(称为 setter
)。访问器属性仅须要 getter
或 setter
二者中的任意一个,固然也能够二者。
// 对象字面形式中定义访问器属性有特殊的语法:
var person = {
_name: "Nicholas",
get name(){
console.log("Reading name");
return this._name;
},
set name(value){
console.log("Setting name to %s", value);
this._name = value;
}
};
console.log(person.name); // "Reading name" 而后输出 "Nicholas"
person.name = "Greg";
console.log(person.name); // "Setting name to Greg" 而后输出 "Greg"
复制代码
前置下划线 _ 是一个约定俗成的命名规范,表示该属性是私有的,实际上它仍是公开的。
访问器就是定义了咱们在对象读取或设置属性时,触发的动做(函数),_name
至关于一个内部变量。
当你但愿赋值(读取)操做会触发一些行为,访问器就会很是有用。
当只定义 getter 或 setter 其一时,该属性就会变成只读或只写。
在 ECMAScript 5 以前没有办法指定一个属性是否可枚举。实际上根本没有方法访问属性的任何内部特征。
为了改变这点,ECMAScript 5 引入了多种方法来和属性特征值直接互动。
数据属性和访问器属性均由如下两个属性特制:
[[Enumerable]]
决定了是否能够遍历该属性;[[Configurable]]
决定了该属性是否可配置。全部人为定义的属性默认都是可枚举、可配置的。
能够用 Object.defineProperty()
方法改变属性特征。
其参数有三:拥有该属性的对象、属性名和包含须要设置的特性的属性描述对象。
var person = {
name: "Nicholas"
}
Object.defineProperty(person, "name", {
enumerable: false
})
console.log("name" in person); // true
console.log(person.propertyIsEnumerable("name")); // false
var properties = Object.keys(person);
console.log(properties.length); // 0
Object.defineProperty(person, "name",{
configurable: false
})
delete person.name; // false
console.log("name" in person); // true
Object.defineProperty(person, "name",{ // error!
// 在 chrome:Uncaught TypeError: Cannot redefine property: name
configurable: true
})
复制代码
没法将一个不可配置的属性变为可配置,相反则能够。
数据属性额外拥有两个访问器属性不具有的特征。
[[Value]]
包含属性的值(哪怕是函数)。[[Writable]]
布尔值,指示该属性是否可写入。全部属性默认都是可写的。var person = {};
Object.defineProperty(person, "name", {
value: "Nicholas",
enumerable: true,
configurable: true,
writable: true
})
复制代码
在 Object.defineProperty()
被调用时,若是属性原本就有,则会按照新定义属性特征值去覆盖默认属性特征(enumberable
、configurable
和 writable
均为 true
)。
但若是用该方法定义新的属性时,没有为全部的特征值指定一个值,则全部布尔值的特征值会被默认设置为 false
。即不可枚举、不可配置、不可写的。
当你用 Object.defineProperty()
改变一个已有的属性时,只有你指定的特征会被改变。
访问器属性额外拥有两个特征。[[Get]]
和 [[Set]]
,内含 getter
和 setter
函数。
使用访问器属性特征比使用对象字面形式定义访问器属性的优点在于:能够为已有的对象定义这些属性。然后者只能在建立时定义访问器属性。
var person = {
_name: "Nicholas"
};
Object.defineProperty(person, "name", {
get: function(){
return this._name;
},
set: function(value){
this._name = value;
},
enumerable: true,
configurable: true
})
for(var x in person){
console.log(x); // _name \n(换行) name(访问器属性)
}
复制代码
设置一个不可配置、不可枚举、不能够写的属性:
Object.defineProperty(person, "name",{
get: function(){
return this._name;
}
})
复制代码
对于一个新的访问器属性,没有显示设置值为布尔值的属性,默认为 false
。
Object.defineProperties()
方法能够定义任意数量的属性,甚至能够同时改变已有的属性并建立新属性。
var person = {};
Object.defineProperties(person, {
// data property to store data
_name: {
value: "Nicholas",
enumerable: true,
configurable: true,
writable: true
},
// accessor property
name: {
get: function(){
return this._name;
},
set: function(value){
this._name = value;
}
}
})
复制代码
Object.getOwnPropertyDescriptor()
方法。该方法接受两个参数:对象和属性名。若是属性存在,它会返回一个属性描述对象,内涵 4
个属性:configurable
和 enumerable
,另外两个属性则根据属性类型决定。
var person = {
name: "Nicholas"
}
var descriptor = Object.getOwnPropertyDescriptor(person, "name");
console.log(descriptor.enumerable); // true
console.log(descriptor.configuable); // true
console.log(descriptor.value); // "Nicholas"
console.log(descriptor.wirtable); // true
复制代码
对象和属性同样具备指导其行为的内部特性。
其中, [[Extensible]]
是布尔值,指明该对象自己是否能够被修改。默认是 true
。当值为 false
时,就能禁止新属性的添加。
建议在 "use strict"; 严格模式下进行。
Object.preventExtensions()
建立一个不可扩展的对象(即不能添加新属性)。 Object.isExtensible()
检查 [[Extensible]]
的值。
var person = {
name: "Nocholas"
}
Object.preventExtensions(person);
person.sayName = function(){
console.log(this.name)
}
console.log("sayName" in person); // false
复制代码
一个被封印的对象是不可扩展的且其全部属性都是不可配置的(即不能添加、删除属性或修改其属性类型(从数据属性变成访问器属性或相反))。只能读写它的属性。
Object.seal()
调用此方法后,该对象的 [[Extensible]]
特征被设置为 false
,其全部属性的 [[configurable]]
特征被设置为 false
。
Object.isSealed()
判断一个对象是否被封印。
被冻结的对象不能添加或删除属性,不能修改属性类型,也不能写入任何数据属性。简言而之,被冻结对象是一个数据属性都为只读的被封印对象。
Object.freeze()
冻结对象。Object.isFrozen()
判断对象是否被冻结。
Object.freeze()
比Object.seal()
更严格,它阻止更改任何现有属性
in
操做符检测自有属性和原型属性,而 hasOwnProperty()
只检查自有属性。delete
操做符删除对象属性。[[Enumerable]]
和 [[Configurable]]
的两种属性都有的,而数据属性还有 [[Value]]
和 [[Writable]]
,访问器属性还有 [[Get]]
和 [[Set]]
。可经过 Object.defineProperty()
和 Object.defineProperties()
改变这些特征。用 Object.getOwnPropertyDescriptor()
获取它们。3
种能够锁定对象属性的方式。