和数据属性一样,存取器属性是可以继承的,因此可以将上述代码中的p对象当做另一个“点”的原型。可以给性对象定义它的x和y属性,但r和theta属性继承而来:
var q = inherit(p); q.x = 1, q.y = 1; console.log(q.r); cosole.log(q.theta);
3.2 属性特性
我们可以将存取器属性的getter和setter方法看成属性的特性。按照这个逻辑,我们也可把属性的值同样看着属性的特性。因此,可以认为一个属性包含一个名字和4个特性。
数字属性的4个特性分别是它的值(value)、可写性(writeable)、可枚举性(enumerable)和可配置型(configurable)。
存取器属性不具有值(value)特性和可写性,因此包含:读取(get)、写入(set)、可枚举性、可配置性。
ES5定义了一个名为“属性描述符”的对象,这个对象代表那4个特性。数据属性的描述符对象的属性有value、writable、enumerable和configurable。存取器属性的描述符对象则用get属性和set属性代替value和writable。其中writable、enumerable、configurable都是布尔值,get属性和set属性是函数值。
通过调用Object.getOwnPropertyDescriptor()可以获取某个对象特定属性的属性描述符:
//返回{value: 1, writable: true, enumerable: true, configurable: true} Object.getOwnProeprtyDescriptor({x: 1},"x"); //查询上文中定义的random对象的octet属性 //返回{get: /*func */, set: undefined, enumerable: true, configurable: true} Object.getOwnPropertyDesciptor(random, "octet"); //对于继承属性和不存在属性,返回undefined Object.getOwnPropertyDesciptor({}, "x"); Object.getOwnPropertyDesciptor({}, "toString");
从函数名就可以看出,Object.getOwnPropertyDesciptor()只能得到自有属性的描述符。要想获得继承属性的特性,需要遍历原型链(Object.getPrototypeOf())。
想要设置属性的特性,或者让新建属性具有某些特性,则需要调用Object.defineProperty(),包含三个参数:对象、属性名、属性描述符对象:
// 属性是存在的,但不可枚举 o.x; //=> 1 Object.keys(o) //=> [] //现在对属性x做修改,让它变成只读 Object.defineProperty(o, "x", {writable: true }); //视图更改这个属性的值 o.x = 2; //操作失败但不报错,而在严格模式中抛出类型错误异常 //属性依然是可配置的,因此可通过这种方式对它进行修改: Object.defineProperty(o, "x", {value: 2 }); o.x //=> 2 //现在将x从数据属性修改为存取器属性 Object.defineProperty(o, "x", { get: function() {return 0;} }); o.x // => 0
如果要同时修改或创建多个属性,则需要使用Object.defineProperties()。第一个参数是要修改的对象,第二个参数㐊一个映射表。例如:
var p = Object.defineProperties({}, { x: { value: 1, writable: true, enumerable: true, configurable: true}, y: { value: 2, writable: true, enumerable: true, configurable: true}, r: { get: function(){ return Math.sqrt(this.x * this.x + this.y * this.y); }, enumerable: true, configurable: true } });
getter和setter的老式API: 在ES5采纳之前,大多数Javascript的实现已经可以支持对象直接量语法中get和set写法。这些实现提供了非标准的老式API用来查询和设置getter和setter。这些API由四个方法组成,所有对象都拥有这些方法。
__lookupGetter__()和__lookupSetter__()用以返回一个命名属性的getter和setter方法。
__defineGetter__()和__defineSetter__()用以定义getter和setter,第一个参数是属性名字,第二个参数是getter和setter方法。
var o = {}; o.__defineGetter__("x", function(){return 0;}); o.__defineSetter__("y", function(value){console.log("set value:" + value);});
4.对象的三个属性
每一个对象都有与之相关的原型(prototype)、类(class)、可扩展性(extensible attribute)。接下来讲述这些属性有什么作用。
4.1 原型属性
对象的原型属性是用来继承属性的,我们经常把“o的原型属性”直接叫做“o的原型”。在之前“创建对象”介绍了三种方式创建对象。通过对象直接量创建的对象使用Object.prototype作为它们的原型。通过new创建的对象使用构造函数的prototype属性作为它们的原型。通过Object.create()创建的对象使用第一个参数作为它们的原型。
在ES5中,可通过Object.getPrototypeOf()查询对象原型。在ES3中,没有与之等价的函数,而是使用表达式o.constructor.prototype检查对象的原型。
要想检测一个对象是否是另一个对象的原型(或处于原型链中),使用isPrototypeOf()方法。例如,可以通过p.isPrototypeOf(o)来检测p是否是o的原型: