首页 JavaScript JavaScript 对象属性描述符

JavaScript 对象属性描述符

0 1.3K

当我们创建 JavaScript 对象时,不论是使用对象字面量语法还是其它别的语法,我们都可以给这些对象添加属性。每一个属性默认会有一个属性描述符。属性描述符就是一个简单的 JavaScript 对象,与目标对象的属性关联起来,包含了该属性的各种信息,比如值value和其它元数据。

var myObj = {
    myPropOne: 1,
    myPropTwo: 2
};

console.dir( myObj ); // { myPropOne: 1, myPropTwo: 2 }

在上面的代码中,我们使用字面量语法创建了一个 JavaScript 对象myObj;该对象添加了两个属性myPropOnemyPropTwo,这两个属性分别给了初始值12

现在,如果按照下面的方式给myPropOne属性赋值,是可以成功添加的,并且值会有变化。

var myObj = {
    myPropOne: 1,
    myPropTwo: 2
};

// override `myPropOne` property
myObj.myPropOne = 10;

console.log( 'myObj.myPropOne =>', myObj.myPropOne ); // myObj.myPropOne => 10
console.log( 'myObj =>', myObj ); // { myPropOne: 10, myPropTwo: 2 }

为了访问属性的属性描述符,我们需要使用Object的静态方法。Object.getOwnPropertyDescriptor返回obj对象的属性名为prop的属性描述符。

Object.getOwnPropertyDescriptor(obj, prop);

函数名中的Own意味着这个属性prop属于obj对象本身,而不是在其原型链上。如果obj没有属性prop,则返回undefined

如果你想了解有关原型和原型链的内容,请阅读这篇文章

var myObj = {
    myPropOne: 1,
    myPropTwo: 2
};

// get property descriptor of `myPropOne`
let descriptor = Object.getOwnPropertyDescriptor(
    myObj, 'myPropOne'
);

console.log( descriptor ); // { value: 1, writable: true, enumerable: true, configurable: true }

Object.getOwnPropertyDescriptor函数返回一个对象,这个对象包含描述该属性的配置和当前值的信息。属性描述符的value属性是属性的当前值;writable是用户是否可以给这个属性赋新值;enumerable是该属性是否会出现在枚举语句中,比如for...in循环或者for...of循环或者Object.keys等等;configurable用于设置用户是否有权限修改属性描述符的属性,例如设置writableenumerable的值。

属性描述符还有setget关键字,代表设置值和返回值的中间函数,不过这些是可选的。

为了给对象添加新的属性,或者使用自定义描述符更新已有属性,可以使用Object.defineProperty。下面,我们修改已有属性myPropOne的值,将writable设置为false,这将禁止给myObj.myPropOne赋值。

'use strict';

var myObj = {
    myPropOne: 1,
    myPropTwo: 2
};

// modify property descriptor
Object.defineProperty( myObj, 'myPropOne', {
    writable: false
} );

// print property descriptor
let descriptor = Object.getOwnPropertyDescriptor(
    myObj, 'myPropOne'
);

console.log( descriptor );

// set new value
myObj.myPropOne = 2;

上面的代码,我们将myPropOne设置为不可写的,因此,当尝试给myPropOne赋值时就会报错。

使用Object.defineProperty更新已有属性的属性描述符时,原始的属性描述符会与新的描述符合并Object.defineProperty返回的是变更之后的原始对象myObj

下面我们看看将enumerable设置为false会发生什么。

var myObj = {
    myPropOne: 1,
    myPropTwo: 2
};

// modify property descriptor
Object.defineProperty( myObj, 'myPropOne', {
    enumerable: false
} );

// print property descriptor
let descriptor = Object.getOwnPropertyDescriptor(
    myObj, 'myPropOne'
);

console.log( descriptor ); // { value: 1, writable: true, enumerable: false, configurable: true }

// print keys
console.log( Object.keys( myObj ) ); // [ 'myPropTwo' ]

运行结果是,Object.keys的返回值里面没有myPropOne这个属性了。

使用Object.defineProperty给对象定义新的属性时,如果参数是{},那么,默认的属性描述符类似下面这样:

{
    value: undefined,
    writable: true,
    enumerable: false,
    configurable: false
}

现在,我们使用自定义描述符定义一个新的属性,注意,confiurable值为false。我们将writable设置为falseenumerable同样为true,而value值设置为3

var myObj = {
    myPropOne: 1,
    myPropTwo: 2
};

// modify property descriptor
Object.defineProperty( myObj, 'myPropThree', {
    value: 3,
    writable: false,
    configurable: false,
    enumerable: true
} );

// print property descriptor
let descriptor = Object.getOwnPropertyDescriptor(
    myObj, 'myPropThree'
);

console.log( descriptor );

// change property descriptor
Object.defineProperty( myObj, 'myPropThree', {
    writable: true
} );

通过设置configurablefalse,我们就不能修改myPropThree的描述符了。如果你不希望用户改变对象的推荐行为,这一点是非常有效的。

属性的getset也可以在属性描述符中设置,其名字就是getset。但在定义时,有一定的限制。你不能添加初始值或描述符的value字段,因为只能由 getter 返回这个属性的值。你不能使用描述符的writable字段,因为写操作是通过 setter 完成的,你可以通过 setter 完成写操作。详细介绍请阅读 MDN 的gettersetter

使用Object.defineProperties可以创建或更新多个属性。这个函数接受两个参数,第一个参数是目标对象,也就是属性被添加或修改到的对象;第二个参数是一个对象,其中,key作为属性名,value作为属性描述符。该函数返回目标对象。

Object.create也可以用于创建对象。这是创建没有原型或使用自定义原型的对象的最简单方式。同时,这也是直接通过自定义属性描述符创建对象的简单方式。

Object.create函数签名如下:

var obj = Object.create( prototype, { property: descriptor, ... } )

这里,prototype是一个对象,作为obj的原型。如果prototypenull,那么,obj就没有任何原型。如果是只用var obj = {};来创建对象,默认情况下,obj.__proto__指向Object.prototype,也就是说,obj的原型是Object类。

这与使用Object.prototype作为第一个参数来调用Object.create是等价的:

'use strict';

// create an object using `Object.create`
var o = Object.create( Object.prototype, {
    a: { value: 1, writable: false },
    b: { value: 2, writable: true }
} );

// see prototype of `o`
console.log( 'o.__proto__ =>', o.__proto__ );

// see the property descriptor of `a`
console.log( 'o.hasOwnProperty( "a" ) =>', o.hasOwnProperty( "a" ) );

但是,当我们将protoype设置为null时,就会有下面的错误:

'use strict';

// create an object with `null` prototype
var o = Object.create( null, {
    a: { value: 1, writable: false },
    b: { value: 2, writable: true }
} );

// log prototype
console.log( o.__proto__ );

// get property descriptor of `a`
console.log( 'o.hasOwnProperty( "a" ) =>', o.hasOwnProperty( "a" ) );

发表评论

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2