资讯专栏INFORMATION COLUMN

标准库(二)属性描述对象

MartinDai / 1546人阅读

摘要:也就是说,属性控制了属性描述对象的可写性。可遍历性返回一个布尔值,表示目标属性是否可遍历运算符不管某个属性是对象自身的还是继承的,都会返回。上面的写法与定义属性描述对象是等价的,而且使用更广泛。

属性描述对象

概述
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对象被冻结以后,这个指向无法改变,即无法指向其他值,但是所指向的数组是可以改变的

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105896.html

相关文章

  • 标准)属性描述对象

    摘要:也就是说,属性控制了属性描述对象的可写性。可遍历性返回一个布尔值,表示目标属性是否可遍历运算符不管某个属性是对象自身的还是继承的,都会返回。上面的写法与定义属性描述对象是等价的,而且使用更广泛。 属性描述对象 概述Object.getOwnPropertyDescriptor()Object.getOwnPropertyNames()Object.defineProperty(),Ob...

    468122151 评论0 收藏0
  • 译:Swift ABI ()

    摘要:为了达成二进制层次上的交互这里应该指的是不同版本编译器生成的结果在二进制上兼容,它们必须共享相同的布局协议。例如,对于一个包含个的传统风格来说,它的值用两个就可以表示了数字,,分别对应三个,它们对应二进制的,和。 泊学翻译自Swift在Github上发布的Swift ABI Manifesto Swift ABI的构成 在实践中,ABI关注的内容是紧密耦合在一起的。但是,作为一个概念模...

    Clect 评论0 收藏0
  • JavaWEB开发11——JSP

    摘要:出现的目的同一样也是要提到页面中的脚本代码。标准标准标签库有个子库,但随着发展,目前常使用的是他的核心库标签库标签库的前缀下载与导入下载从的网站下载的包。 一、JSP技术1.jsp脚本和注释jsp脚本:1) ----- 内部的java代码翻译到service方法的内部2) ----- 会被翻译成service方法内部out.print()3) ---- 会被翻译成servlet的成员的...

    beita 评论0 收藏0
  • Python标准---16、内置类型:上下文管理器类型、其他、特殊属性

    摘要:退出运行时上下文并返回一个布尔值旗标来表明所发生的任何异常是否应当被屏蔽。除了实现上下文管理协议以外,不同类型不会被特殊处理。其中一些并不会被内置函数所列出。 上一篇文章:Python标准库---15、内置类型:集合类型、映射类型下一篇文章:Python标准库---17、内置异常 上下文管理器类型 Python 的 with 语句支持通过上下文管理器所定义的运行时上下文这一概念。 此...

    zhisheng 评论0 收藏0
  • 0x07 数据民工,一致性洗车

    摘要:千万不要以为数据分析或者机器学习只是单纯的统计和数学模型,不懂业务的机器学习工程师不是一个好的产品经理。但人不一样,这才是数据工程师和机器学习工程师最核心的价值。 摘要:数据科学家是二十一世纪最性感的的职业。面临充斥着脏乱差的数据,处理数据一致性问题是其中一项重要任务。人的强项在于智能,却不善于处理重复且枯燥无味的事情。机器不具有智能,却善于处理重复且简单的事情。 01 性感还是闷骚《...

    timger 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<