在JavaScript中,所有的对象都是一组属性的集合,属性可以是数值,字符串等原始类型,也可以是函数,或者是其他对象。
属性的类型
JavaScript中的属性有两种类型:数据属性和访问器属性。
数据属性
数据属性可以看成是直接封装了一个内部变量,内部变量中存放了该属性的值。当对某个对象尚未存在的属性进行赋值时,该属性将会作为数据属性被自动创建。
代码语言:javascript复制var o = {};
o.prop1 = "value1";
上面的代码中,对象o的属性prop1即会在被赋值时自动创建。
数据属性也可通过Object.defineProperty方法和value定义来创建。
代码语言:javascript复制var o = {};
Object.defineProperty(o,"prop1",{
value : "value1",
writable : true
}
访问器属性
访问器属性类似于C#,Ruby,Delphi等语言中的属性,内部可以不用直接关联一个数据变量,而是为属性的读取和更新分别提供了一个相应的getter方法和setter方法。访问器属性必须通过Object.defineProperty或其他类似的方法事先进行定义。
代码语言:javascript复制var o = {};
Object.defineProperty(o,"prop1",{
get: function(){
if (this._prop1 === undefined) {
this._prop1 = 1;
}
return this._prop1;
},
set : function(s){
if (typeof s == "string"){
this._prop1 = parseFloat(s);
} else if ( typeof s == "number") {
this._prop1 = s;
} else {
throw new Error("invalie parameter!");
}
}
}
有了get方法,我们就可以在属性第一次被访问时才去进行初期化处理,而有了set方法,我们就可以追加对赋值进行类型转化等很多数据属性没法实现的程序逻辑。
属性的特性
ES5开始,JavaScript为属性提供了三个特性用于描述其各种特征。特性是内部值,不能直接访问。属性的特性会有一些默认值,要修改特性的默认值,必须使用Object.defineProperty方法。
下面依次对这些特性进行说明
configurable
configurable特性定义是否可以通过delete操作符来删除属性,默认值是true。
代码语言:javascript复制var o = {};
o.p1="value1";
console.log(o.p1); // value1
delete o.p1;
console.log(o.p1); // undefine
如以上代码所示,属性被通过delete操作符删除之后再去访问就已变成未定义了。然后,我们可以把属性的Configurable特性设置为false来防止属性删除。
代码语言:javascript复制var o = {};
o.p1="value1";
console.log(o.p1); // value1
Object.defineProperty(o, "p1", {
configurable: false
})
delete o.p1;
console.log(o.p1); // value1
enumerable
enumerable特性定义是否能够通过for…in语句来枚举出属性,默认是true
writable
writable特性定义表示属性值是否可以修改,默认为true。
属性的继承
属性可以通过对象的原型链进行继承。
代码语言:javascript复制var objA = new Object();
objA.prop1 = 10;
function Func1() {}
Func1.prototype = objA;
var objB = new Func1();
function Func2() {}
Func2.prototype = objB;
var objC = new Func2();
console.log(objC.prop1); // 10
上面的代码中,objC本身没有prop1属性,因此访问objC.prop1时,JavaScript将会按照objC—>objB—>objA的原型链进行顺序查找,最后从objA中取出该属性值。
代码语言:javascript复制objC.prop1 = 20;
这时如重新将objC.prop1进行赋值,并不会影响到objB和objA,而是objC自身会被自动创建一个同名的数据属性。
代码语言:javascript复制console.log(objC.prop1); //20
console.log(objB.prop1); //10
console.log(objA.prop1); //10
属性的键值
JavaScript里对象的属性是以键/值对的形式存在的,这里的「键」不限于字符串类型,也可以是数值或其他对象。
事实上,JavaScript中的数组(Array),本质上也是一个键/值对的集合,数值类型的自然索引也是作为属性名(键)存在的。请看以下代码:
代码语言:javascript复制//数值也可以是属性名(键)
var a = [0,9,2];
console.log(2 in a); // 输出:true
console.log(3 in a); // 输出:false
console.log(a.1); // 语法错
//任意对象也可以是属性名(键)
var d1 = new Date("2012/01/01"),
d2 = new Date("2013/03/01"),
o = {
};
o[d1] = "2012/01/01";
o[d2] = "2013/03/01";
console.log(o[d1]); // 2012/01/01
console.log(o[d2]); // 2013/03/01
//下面代码,d1,d2在JavaScript语法里被当作变量名的字符串处理,
console.log(o.d1); // undefined
console.log(o.d2); // undefined
字符串以外的值虽然也可以作为属性的键值,但因为JavasSript语法只允许字符串为变量名,所以不能以a.1或o.d1这样的方式,而只能以a[1]或o[d1]的方式访问对象的属性。
对象限制
ES5中提供了一系列限制对象被修改的方法,按限制程度由低到高,依次有禁止扩展,密封,冻结三种模式。当然,即使是冻结模式,访问器属性的set方法仍然可正常动作,表现出来就是该属性值仍可修改。
禁止扩展对象
通过Object.preventExtensions()方法可以禁止将对象进行扩展,禁止扩展后的对象无法:
- 添加新的属性
但可以:
- 删除已有的属性
- 改变已有属性的特性
- 修改已有数据属性的值(如果该属性可写)
- 修改已有访问器属性的值(如果有set方法)
密封对象
通过Object.seal方法可以将对象进行密封,密封后的对象无法:
- 添加新的属性
- 删除已有的属性
- 改变已有属性的特性
但可以
- 修改已有数据属性的值(如果该属性可写)
- 修改已有访问器属性的值(如果有set方法)
冻结对象
通过Object.freeze方法可以将对象进行冻结,冻结后的对象无法:
- 添加新的属性
- 删除已有的属性
- 改变已有属性的特性
- 修改已有数据属性的值(即使该属性可写)
但可以
- 修改已有访问器属性的值(如果有set方法)
相关方法汇总
属性的相关方法都是以Object的静态方法或原型方法的形式提供的,下面简单的做一下汇总:
- 属性定义相关
- Object.create() 创建对象的同时定义该对象的属性
- Object.defineProperty() 定义一个属性
- Object.defineProperties() 定义一组属性
- Object.getOwnPropertyDescriptor() 获取属性定义信息
- 属性访问相关
- Object.keys() 获取对象的所有属性名,仅限于可枚举的自身属性
- Object.getOwnPropertyNames() 获取对象的所有属性名,包括可枚举和不可枚举,仅限自身属性
- Object.prototype.hasOwnProperty() 判断对象自身是否拥有某个属性
- 对象限制相关
- Object.preventExtensions() 限制对象扩展
- Object.isExtensible() 判断对象是否可以扩展
- Object.seal() 密封对象
- Object.isSealed() 判断对象是否被密封
- Object.freeze() 冻结对象
- Object.isFrozen() 判断对象是否被冻结