标准库二属性描述对象

3次阅读

共计 6840 个字符,预计需要花费 18 分钟才能阅读完成。

属性描述对象

概述
Object.getOwnPropertyDescriptor()
Object.getOwnPropertyNames()
Object.defineProperty(),Object.defineProperties()
Object.prototype.propertyIsEnumerable()
元属性
value
writable
enumerable
configurable
存取器
对象的拷贝
控制对象状态
Object.preventExtensions()
Object.isExtensible()
Object.seal()
Object.isSealed()
Object.freeze()
Object.isFrozen()
局限性

1. 概述
每个属性都有自己对应的属性描述对象,保存该属性的一些元信息
{
value: 123,
writable: false,
enumerable: true,
configurable: false,
get: undefined,
set: undefined
}
属性描述对象提供 6 个元属性。

(1)value

value 是该属性的属性值,默认为 undefined。

(2)writable

writable 是一个布尔值,表示属性值(value)是否可改变(即是否可写),默认为 true。

(3)enumerable

enumerable 是一个布尔值,表示该属性是否可遍历,默认为 true。如果设为 false,会使得某些操作(比如 for…in 循环、Object.keys())跳过该属性。

(4)configurable

configurable 是一个布尔值,表示可配置性,默认为 true。如果设为 false,将阻止某些操作改写该属性,比如无法删除该属性,也不得改变该属性的属性描述对象(value 属性除外)。也就是说,configurable 属性控制了属性描述对象的可写性。

(5)get

get 是一个函数,表示该属性的取值函数(getter),默认为 undefined。

(6)set

set 是一个函数,表示该属性的存值函数(setter),默认为 undefined。

2.Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor()方法可以获取属性描述对象。它的第一个参数是目标对象,第二个参数是一个字符串,对应目标对象的某个属性名。

Object.getOwnPropertyDescriptor()方法只能用于对象自身的属性,不能用于继承的属性。

var obj = {p: ‘a’};

Object.getOwnPropertyDescriptor(obj, ‘toString’)
// undefined
上面代码中,toString 是 obj 对象继承的属性,Object.getOwnPropertyDescriptor()无法获取。

3.Object.getOwnPropertyNames()
Object.keys([]) // []
Object.getOwnPropertyNames([]) // [‘length’]

Object.keys(Object.prototype) // []
Object.getOwnPropertyNames(Object.prototype)
// [‘hasOwnProperty’,
// ‘valueOf’,
// ‘constructor’,
// ‘toLocaleString’,
// ‘isPrototypeOf’,
// ‘propertyIsEnumerable’,
// ‘toString’]
上面代码中,数组自身的 length 属性是不可遍历的,Object.keys 不会返回该属性。第二个例子的 Object.prototype 也是一个对象,所有实例对象都会继承它,它自身的属性都是不可遍历的。

4.Object.defineProperty(),
Object.defineProperty 方法接受三个参数,依次如下。

object:属性所在的对象
propertyName:字符串,表示属性名
attributesObject:属性描述对象

4.1Object.defineProperties()
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
4.1.2writable、configurable、enumerable 这三个属性的默认值都为 false
4.1.1 注意,一旦定义了取值函数 get(或存值函数 set),就不能将 writable 属性设为 true,或者同时定义 value 属性,否则会报错。

5.Object.prototype.propertyIsEnumerable()
这个方法只能用于判断对象自身的属性,对于继承的属性一律返回 false
var obj = {};
obj.p = 123;

obj.propertyIsEnumerable(‘p’) // true
obj.propertyIsEnumerable(‘toString’) // false
上面代码中,obj.p 是可遍历的,而 obj.toString 是继承的属性

6. 元属性
6.1value
value 属性是目标属性的值。

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

Object.getOwnPropertyDescriptor(obj, ‘p’).value
// 123

Object.defineProperty(obj, ‘p’, { value: 246});
obj.p // 246
上面代码是通过 value 属性,读取或改写 obj.p 的例子

6.2
writable 属性是一个布尔值,决定了目标属性的值(value)是否可以被改变
6.2.1 如果原型对象的某个属性的 writable 为 false,那么子对象将无法自定义这个属性。

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

var obj = Object.create(proto);

obj.foo = ‘b’;
obj.foo // ‘a’
上面代码中,proto 是原型对象,它的 foo 属性不可写。obj 对象继承 proto,也不可以再自定义这个属性了。如果是严格模式,这样做还会抛出一个错误。

6.3enumerable
6.3.1enumerable(可遍历性)返回一个布尔值,表示目标属性是否可遍历

6.3.2in 运算符不管某个属性是对象自身的还是继承的,都会返回 true。

后来就引入了“可遍历性”这个概念。只有可遍历的属性,才会被 for…in 循环遍历,同时还规定 toString 这一类实例对象继承的原生属性,都是不可遍历的,这样就保证了 for…in 循环的可用性。

6.3.3 具体来说,如果一个属性的 enumerable 为 false,下面三个操作不会取到该属性。

for..in 循环
Object.keys 方法
JSON.stringify 方法
object.getpropertynames 都可以遍历。

6.3.4 用于保密属性
JSON.stringify 方法会排除 enumerable 为 false 的属性,有时可以利用这一点。如果对象的 JSON 格式输出要排除某些属性,就可以把这些属性的 enumerable 设为 false。

6.4configurable
onfigurable 为 false 时,value、writable、enumerable 和 configurable 都不能被修改了

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.p 的 configurable 为 false。然后,改动 value、writable、enumerable、configurable,结果都报错。

6.4.1 注意,writable 只有在 false 改为 true 会报错,true 改为 false 是允许的

6.4.2 至于 value,只要 writable 和 configurable 有一个为 true,就允许改动

6.4.3 可配置性决定了目标属性是否可以被删除(delete)

6.5 存取器
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 定义了 get 和 set 属性。obj.p 取值时,就会调用 get;赋值时,就会调用 set。

JavaScript 还提供了存取器的另一种写法。

var obj = {
get p() {

return 'getter';

},
set p(value) {

console.log('setter:' + value);

}
};
上面的写法与定义属性描述对象是等价的,而且使用更广泛。

6.5.1 应用
存取器往往用于,属性的值依赖对象内部数据的场合。

var obj ={
$n : 5,
get next() { return this.$n++},
set next(n) {

if (n >= this.$n) this.$n = n;
else throw new Error('新的值必须大于当前值');

}
};

obj.next // 5

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

obj.next = 5;
// Uncaught Error: 新的值必须大于当前值
上面代码中,next 属性的存值函数和取值函数,都依赖于内部属性 $n。

7. 对象的拷贝
var extend = function (to, from) {
for (var property in from) {

to[property] = from[property];

}

return to;
}

extend({}, {
a: 1
})
// {a: 1}
7.1 上面这个方法的问题在于,如果遇到存取器定义的属性,会只拷贝值。
extend({}, {
get a() { return 1}
})
// {a: 1}
为了解决这个问题,我们可以通过 Object.defineProperty 方法来拷贝属性。

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 那一行用来过滤掉继承的属性,否则可能会报错,因为 Object.getOwnPropertyDescriptor 读不到继承属性的属性描述对象。
8. 控制对象状态
有时需要冻结对象的读写状态,防止对象被改变。JavaScript 提供了三种冻结方法,最弱的一种是 Object.preventExtensions,其次是 Object.seal,最强的是 Object.freeze

8.1Object.preventExtensions()
无法增加属性
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

Object.isExtensible()
8.2Object.seal()
无法增删属性
8.2.1Object.seal 实质是把属性描述对象的 configurable 属性设为 false,因此属性描述对象不再能改变了
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 方法之后,属性描述对象的 configurable 属性就变成了 false,然后改变 enumerable 属性就会报错

8.2.2Object.seal 只是禁止新增或删除属性,并不影响修改某个属性的值。

var obj = {p: ‘a’};
Object.seal(obj);
obj.p = ‘b’;
obj.p // ‘b’
上面代码中,Object.seal 方法对 p 属性的 value 无效,是因为此时 p 属性的可写性由 writable 决定。

Object.isSealed()
8.3Object.freeze()
使得这个对象实际上变成了常量
无法增删改属性
使用 Object.freeze 方法以后,Object.isSealed 将会返回 true,Object.isExtensible 返回 false

Object.isFrozen()
Object.isFrozen 的一个用途是,确认某个对象没有被冻结后,再对它的属性赋值。

var obj = {
p: ‘hello’
};

Object.freeze(obj);

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

9. 局限性
9.1 可以通过改变原型对象,来为对象增加属性

9.1.1 一种解决方案是,把 obj 的原型也冻结住。

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

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

proto.t = ‘hello’;
obj.t // undefined

9.2 如果属性值是对象,上面这些方法只能冻结属性指向的对象,而不能冻结对象本身的内容
var obj = {
foo: 1,
bar: [‘a’, ‘b’]
};
Object.freeze(obj);

obj.bar.push(‘c’);
obj.bar // [“a”, “b”, “c”]
上面代码中,obj.bar 属性指向一个数组,obj 对象被冻结以后,这个指向无法改变,即无法指向其他值,但是所指向的数组是可以改变的

正文完
 0

标准库二属性描述对象

3次阅读

共计 6840 个字符,预计需要花费 18 分钟才能阅读完成。

属性描述对象

概述
Object.getOwnPropertyDescriptor()
Object.getOwnPropertyNames()
Object.defineProperty(),Object.defineProperties()
Object.prototype.propertyIsEnumerable()
元属性
value
writable
enumerable
configurable
存取器
对象的拷贝
控制对象状态
Object.preventExtensions()
Object.isExtensible()
Object.seal()
Object.isSealed()
Object.freeze()
Object.isFrozen()
局限性

1. 概述
每个属性都有自己对应的属性描述对象,保存该属性的一些元信息
{
value: 123,
writable: false,
enumerable: true,
configurable: false,
get: undefined,
set: undefined
}
属性描述对象提供 6 个元属性。

(1)value

value 是该属性的属性值,默认为 undefined。

(2)writable

writable 是一个布尔值,表示属性值(value)是否可改变(即是否可写),默认为 true。

(3)enumerable

enumerable 是一个布尔值,表示该属性是否可遍历,默认为 true。如果设为 false,会使得某些操作(比如 for…in 循环、Object.keys())跳过该属性。

(4)configurable

configurable 是一个布尔值,表示可配置性,默认为 true。如果设为 false,将阻止某些操作改写该属性,比如无法删除该属性,也不得改变该属性的属性描述对象(value 属性除外)。也就是说,configurable 属性控制了属性描述对象的可写性。

(5)get

get 是一个函数,表示该属性的取值函数(getter),默认为 undefined。

(6)set

set 是一个函数,表示该属性的存值函数(setter),默认为 undefined。

2.Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor()方法可以获取属性描述对象。它的第一个参数是目标对象,第二个参数是一个字符串,对应目标对象的某个属性名。

Object.getOwnPropertyDescriptor()方法只能用于对象自身的属性,不能用于继承的属性。

var obj = {p: ‘a’};

Object.getOwnPropertyDescriptor(obj, ‘toString’)
// undefined
上面代码中,toString 是 obj 对象继承的属性,Object.getOwnPropertyDescriptor()无法获取。

3.Object.getOwnPropertyNames()
Object.keys([]) // []
Object.getOwnPropertyNames([]) // [‘length’]

Object.keys(Object.prototype) // []
Object.getOwnPropertyNames(Object.prototype)
// [‘hasOwnProperty’,
// ‘valueOf’,
// ‘constructor’,
// ‘toLocaleString’,
// ‘isPrototypeOf’,
// ‘propertyIsEnumerable’,
// ‘toString’]
上面代码中,数组自身的 length 属性是不可遍历的,Object.keys 不会返回该属性。第二个例子的 Object.prototype 也是一个对象,所有实例对象都会继承它,它自身的属性都是不可遍历的。

4.Object.defineProperty(),
Object.defineProperty 方法接受三个参数,依次如下。

object:属性所在的对象
propertyName:字符串,表示属性名
attributesObject:属性描述对象

4.1Object.defineProperties()
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
4.1.2writable、configurable、enumerable 这三个属性的默认值都为 false
4.1.1 注意,一旦定义了取值函数 get(或存值函数 set),就不能将 writable 属性设为 true,或者同时定义 value 属性,否则会报错。

5.Object.prototype.propertyIsEnumerable()
这个方法只能用于判断对象自身的属性,对于继承的属性一律返回 false
var obj = {};
obj.p = 123;

obj.propertyIsEnumerable(‘p’) // true
obj.propertyIsEnumerable(‘toString’) // false
上面代码中,obj.p 是可遍历的,而 obj.toString 是继承的属性

6. 元属性
6.1value
value 属性是目标属性的值。

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

Object.getOwnPropertyDescriptor(obj, ‘p’).value
// 123

Object.defineProperty(obj, ‘p’, { value: 246});
obj.p // 246
上面代码是通过 value 属性,读取或改写 obj.p 的例子

6.2
writable 属性是一个布尔值,决定了目标属性的值(value)是否可以被改变
6.2.1 如果原型对象的某个属性的 writable 为 false,那么子对象将无法自定义这个属性。

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

var obj = Object.create(proto);

obj.foo = ‘b’;
obj.foo // ‘a’
上面代码中,proto 是原型对象,它的 foo 属性不可写。obj 对象继承 proto,也不可以再自定义这个属性了。如果是严格模式,这样做还会抛出一个错误。

6.3enumerable
6.3.1enumerable(可遍历性)返回一个布尔值,表示目标属性是否可遍历

6.3.2in 运算符不管某个属性是对象自身的还是继承的,都会返回 true。

后来就引入了“可遍历性”这个概念。只有可遍历的属性,才会被 for…in 循环遍历,同时还规定 toString 这一类实例对象继承的原生属性,都是不可遍历的,这样就保证了 for…in 循环的可用性。

6.3.3 具体来说,如果一个属性的 enumerable 为 false,下面三个操作不会取到该属性。

for..in 循环
Object.keys 方法
JSON.stringify 方法
object.getpropertynames 都可以遍历。

6.3.4 用于保密属性
JSON.stringify 方法会排除 enumerable 为 false 的属性,有时可以利用这一点。如果对象的 JSON 格式输出要排除某些属性,就可以把这些属性的 enumerable 设为 false。

6.4configurable
onfigurable 为 false 时,value、writable、enumerable 和 configurable 都不能被修改了

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.p 的 configurable 为 false。然后,改动 value、writable、enumerable、configurable,结果都报错。

6.4.1 注意,writable 只有在 false 改为 true 会报错,true 改为 false 是允许的

6.4.2 至于 value,只要 writable 和 configurable 有一个为 true,就允许改动

6.4.3 可配置性决定了目标属性是否可以被删除(delete)

6.5 存取器
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 定义了 get 和 set 属性。obj.p 取值时,就会调用 get;赋值时,就会调用 set。

JavaScript 还提供了存取器的另一种写法。

var obj = {
get p() {

return 'getter';

},
set p(value) {

console.log('setter:' + value);

}
};
上面的写法与定义属性描述对象是等价的,而且使用更广泛。

6.5.1 应用
存取器往往用于,属性的值依赖对象内部数据的场合。

var obj ={
$n : 5,
get next() { return this.$n++},
set next(n) {

if (n >= this.$n) this.$n = n;
else throw new Error('新的值必须大于当前值');

}
};

obj.next // 5

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

obj.next = 5;
// Uncaught Error: 新的值必须大于当前值
上面代码中,next 属性的存值函数和取值函数,都依赖于内部属性 $n。

7. 对象的拷贝
var extend = function (to, from) {
for (var property in from) {

to[property] = from[property];

}

return to;
}

extend({}, {
a: 1
})
// {a: 1}
7.1 上面这个方法的问题在于,如果遇到存取器定义的属性,会只拷贝值。
extend({}, {
get a() { return 1}
})
// {a: 1}
为了解决这个问题,我们可以通过 Object.defineProperty 方法来拷贝属性。

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 那一行用来过滤掉继承的属性,否则可能会报错,因为 Object.getOwnPropertyDescriptor 读不到继承属性的属性描述对象。
8. 控制对象状态
有时需要冻结对象的读写状态,防止对象被改变。JavaScript 提供了三种冻结方法,最弱的一种是 Object.preventExtensions,其次是 Object.seal,最强的是 Object.freeze

8.1Object.preventExtensions()
无法增加属性
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

Object.isExtensible()
8.2Object.seal()
无法增删属性
8.2.1Object.seal 实质是把属性描述对象的 configurable 属性设为 false,因此属性描述对象不再能改变了
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 方法之后,属性描述对象的 configurable 属性就变成了 false,然后改变 enumerable 属性就会报错

8.2.2Object.seal 只是禁止新增或删除属性,并不影响修改某个属性的值。

var obj = {p: ‘a’};
Object.seal(obj);
obj.p = ‘b’;
obj.p // ‘b’
上面代码中,Object.seal 方法对 p 属性的 value 无效,是因为此时 p 属性的可写性由 writable 决定。

Object.isSealed()
8.3Object.freeze()
使得这个对象实际上变成了常量
无法增删改属性
使用 Object.freeze 方法以后,Object.isSealed 将会返回 true,Object.isExtensible 返回 false

Object.isFrozen()
Object.isFrozen 的一个用途是,确认某个对象没有被冻结后,再对它的属性赋值。

var obj = {
p: ‘hello’
};

Object.freeze(obj);

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

9. 局限性
9.1 可以通过改变原型对象,来为对象增加属性

9.1.1 一种解决方案是,把 obj 的原型也冻结住。

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

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

proto.t = ‘hello’;
obj.t // undefined

9.2 如果属性值是对象,上面这些方法只能冻结属性指向的对象,而不能冻结对象本身的内容
var obj = {
foo: 1,
bar: [‘a’, ‘b’]
};
Object.freeze(obj);

obj.bar.push(‘c’);
obj.bar // [“a”, “b”, “c”]
上面代码中,obj.bar 属性指向一个数组,obj 对象被冻结以后,这个指向无法改变,即无法指向其他值,但是所指向的数组是可以改变的

正文完
 0