内容简介:JavaScript 中的对象是动态的,可在代码执行的任意时刻发生改变。基于类的语言会根据类的定义锁定对象。当一个属性第一次被添加到对象时,JavaScript 会在对象上调用一个名为
JavaScript 中的对象是动态的,可在代码执行的任意时刻发生改变。基于类的语言会根据类的定义锁定对象。
3.1 定义属性
当一个属性第一次被添加到对象时,JavaScript 会在对象上调用一个名为 [[Put]]
的内部方法。
[[Put]]
方法会在对象上创建一个新节点来保存属性。
当一个已有的属性被赋予一个新值时,调用的是一个名为 [[Set]]
的方法。
3.2 属性探测
检查对象是否已有一个属性。JavaScript 开发新手错误地使用以下模式检测属性是否存在。
if(person.age){ // do something with ag } 复制代码
上面的问题在于 JavaScript 的类型强制会影响该模式的输出结果。
当 if 判断中的值如下时,会判断为 真 :
- 对象
- 非空字符串
- 非零
- true
当 if 判断中的值如下时,会判断为 假 :
- null
- undefined
- false
- NaN
- 空字符串
因此判断属性是否存在的方法是使用 in
操作符。
in
操作符会检查 自有属性和原型属性
。
所有的对象都拥有的 hasOwnProperty()
方法(其实是 Object.prototype
原型对象的),该方法在给定的属性存在且为 自有属性
时返回 true
。
var person = { name: "Nicholas" } console.log("name" in person); // true console.log(person.hasOwnpropert("name")); // true console.log("toString" in person); // true console.log(person.hasOwnproperty("toString")); // false 复制代码
3.3 删除属性
设置一个属性的值为 null
并不能从对象中彻底移除那个属性,这只是调用 [[Set]]
将 null
值替换了该属性原来的值而已。
delete
操作符针对单个对象属性调用名为 [[Delete]]
的内部方法。删除成功时,返回 true
。
var person = { name: "Nicholas" } person.name = null; console.log("name" in person); // true delete person.name; console.log(person.name); // undefined 访问一个不存在的属性将返回 undefined console.log("name" in person); // false 复制代码
3.4 属性枚举
所有人为添加的属性默认都是可枚举的。可枚举的内部特征 [[Enumerable]]
都被设置为 true
。
for-in
循环会枚举一个对象所有的可枚举属性。
ECMAScript 5 的 Object.keys() 方法可以获取可枚举属性的名字的数组。
var person = { name: "Ljc", age: 18 } Object.keys(person); // ["name", "age"]; 复制代码
for-in
与 Object.keys()
的一个区别是:前者也会遍历原型属性,而后者返回自有(实例)属性。
实际上,对象的大部分原生方法的 [[Enumerable]]
特征都被设置为 false
。可用 propertyIsEnumerable()
方法检查一个属性是否为可枚举的。
var arr = ["abc", 2]; console.log(arr.propertyIsEnumerable("length")); // false 复制代码
3.5 属性类型
属性有两种类型: 数据属性 和 访问器属性 。
数据属性
包含一个值。 [[Put]]
方法的默认行为是创建 数据属性
。
访问器属性
不包含值而是定义了一个当属性被读取时调用的函数(称为 getter
)和一个当属性被写入时调用的函数(称为 setter
)。访问器属性仅需要 getter
或 setter
两者中的任意一个,当然也可以两者。
// 对象字面形式中定义访问器属性有特殊的语法: var person = { _name: "Nicholas", get name(){ console.log("Reading name"); return this._name; }, set name(value){ console.log("Setting name to %s", value); this._name = value; } }; console.log(person.name); // "Reading name" 然后输出 "Nicholas" person.name = "Greg"; console.log(person.name); // "Setting name to Greg" 然后输出 "Greg" 复制代码
前置下划线 _ 是一个约定俗成的命名规范,表示该属性是私有的,实际上它还是公开的。
访问器就是定义了我们在对象读取或设置属性时,触发的动作(函数), _name
相当于一个内部变量。
当你希望赋值(读取)操作会触发一些行为,访问器就会非常有用。
当只定义 getter 或 setter 其一时,该属性就会变成只读或只写。
3.6 属性特征
在 ECMAScript 5 之前没有办法指定一个属性是否可枚举。实际上根本没有方法访问属性的任何内部特征。
为了改变这点,ECMAScript 5 引入了多种方法来和属性特征值直接互动。
3.6.1 通用特征
数据属性和访问器属性均由以下两个属性特制:
[[Enumerable]] [[Configurable]]
所有人为定义的属性默认都是可枚举、可配置的。
可以用 Object.defineProperty()
方法改变属性特征。
其参数有三:拥有该属性的对象、属性名和包含需要设置的特性的属性描述对象。
var person = { name: "Nicholas" } Object.defineProperty(person, "name", { enumerable: false }) console.log("name" in person); // true console.log(person.propertyIsEnumerable("name")); // false var properties = Object.keys(person); console.log(properties.length); // 0 Object.defineProperty(person, "name",{ configurable: false }) delete person.name; // false console.log("name" in person); // true Object.defineProperty(person, "name",{ // error! // 在 chrome:Uncaught TypeError: Cannot redefine property: name configurable: true }) 复制代码
无法将一个不可配置的属性变为可配置,相反则可以。
3.6.2 数据属性特征
数据属性额外拥有两个访问器属性不具备的特征。
[[Value]] [[Writable]]
var person = {}; Object.defineProperty(person, "name", { value: "Nicholas", enumerable: true, configurable: true, writable: true }) 复制代码
在 Object.defineProperty()
被调用时,如果属性本来就有,则会按照新定义属性特征值去覆盖默认属性特征( enumberable
、 configurable
和 writable
均为 true
)。
但如果用该方法定义新的属性时,没有为所有的特征值指定一个值,则所有布尔值的特征值会被默认设置为 false
。即不可枚举、不可配置、不可写的。
当你用 Object.defineProperty()
改变一个已有的属性时,只有你指定的特征会被改变。
3.6.3 访问器属性特征
访问器属性额外拥有两个特征。 [[Get]]
和 [[Set]]
,内含 getter
和 setter
函数。
使用访问器属性特征比使用对象字面形式定义访问器属性的优势在于: 可以为已有的对象定义这些属性。而后者只能在创建时定义访问器属性 。
var person = { _name: "Nicholas" }; Object.defineProperty(person, "name", { get: function(){ return this._name; }, set: function(value){ this._name = value; }, enumerable: true, configurable: true }) for(var x in person){ console.log(x); // _name \n(换行) name(访问器属性) } 复制代码
设置一个不可配置、不可枚举、不可以写的属性:
Object.defineProperty(person, "name",{ get: function(){ return this._name; } }) 复制代码
对于一个新的访问器属性,没有显示设置值为布尔值的属性,默认为 false
。
3.6.4 定义多重属性
Object.defineProperties()
方法可以定义任意数量的属性,甚至可以同时改变已有的属性并创建新属性。
var person = {}; Object.defineProperties(person, { // data property to store data _name: { value: "Nicholas", enumerable: true, configurable: true, writable: true }, // accessor property name: { get: function(){ return this._name; }, set: function(value){ this._name = value; } } }) 复制代码
3.6.5 获取属性特征
Object.getOwnPropertyDescriptor()
方法。该方法接受两个参数:对象和属性名。如果属性存在,它会返回一个属性描述对象,内涵 4
个属性: configurable
和 enumerable
,另外两个属性则根据属性类型决定。
var person = { name: "Nicholas" } var descriptor = Object.getOwnPropertyDescriptor(person, "name"); console.log(descriptor.enumerable); // true console.log(descriptor.configuable); // true console.log(descriptor.value); // "Nicholas" console.log(descriptor.wirtable); // true 复制代码
3.7 禁止修改对象
对象和属性一样具有指导其行为的内部特性。
其中, [[Extensible]]
是布尔值,指明该对象本身是否可以被修改。默认是 true
。当值为 false
时,就能禁止新属性的添加。
建议在 "use strict"; 严格模式下进行。
3.7.1 禁止扩展
Object.preventExtensions()
创建一个不可扩展的对象(即 不能添加新属性
)。 Object.isExtensible()
检查 [[Extensible]]
的值。
var person = { name: "Nocholas" } Object.preventExtensions(person); person.sayName = function(){ console.log(this.name) } console.log("sayName" in person); // false 复制代码
3.7.2 对象封印
一个被封印的对象是不可扩展的且其所有属性都是不可配置的(即 不能添加、删除属性或修改其属性类型(从数据属性变成访问器属性或相反) )。 只能读写它的属性 。
Object.seal()
调用此方法后,该对象的 [[Extensible]]
特征被设置为 false
,其所有属性的 [[configurable]]
特征被设置为 false
。
Object.isSealed()
判断一个对象是否被封印。
3.7.3 对象冻结
被冻结的对象不能添加或删除属性,不能修改属性类型,也不能写入任何数据属性。简言而之,被冻结对象是一个 数据属性都为只读 的被封印对象。
Object.freeze() Object.isFrozen()
Object.freeze()
比 Object.seal()
更严格,它阻止更改任何现有属性
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 《JavaScript面向对象精要》之六:对象模式
- 《JavaScript面向对象精要》之四:构造函数和原型对象
- 《JavaScript面向对象精要》之五:继承
- 《JavaScript面向对象精要》之二:函数
- 《JavaScript面向对象精要》之一:基本类型和引用类型
- 《JavaScript面向对象精要》--构造函数和原型链读后总结
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。