# 理解对象 面向对象的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象创建自定义对象的最简单方式就是创建一个 Object 的实例,然后再为它添加属性和方法; var person = new Object();//Object就是一个类;person是Object的一个实例; person.name = "zab"; person.age = 26; person.job = "WEB"; person.sayName = function(){ console.log(this.name); }; 上面的例子创建了一个名为 person 的对象,并为它添加了三个属性( name 、 age 和 job )和一个方法( sayName() )。其中, sayName() 方法用于显示 this.name (将被解析为 person.name )的值。 不推荐这么写,推荐字面量的创建方式。前面的例子用对象字面量语法可以写成这样; var person = { name: "zab", age: 26, job: "WEB", sayName: function(){ console.log(this.name); } }; 字面量的写法,可以更像一块;而且传参的时候,可以更直接的看到传的是哪些参数; ECMA-262 把对象定义为:**“无序属性的集合,其属性可以包含基本值、对象或者函数。”** 严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。正因为这样(以及其他将要讨论的原因),我们可以把 ECMAScript 的对象想象成散列表:无非就是一组名值对,其中值可以是数据或函数。每个对象都是基于一个引用类型创建的, - 知识点一、属性类型 defineProperty() - 知识点二、定义多个属性(仅作了解) defineProperties() - 知识点三、读取属性的特性 getOwnPropertyDescriptor() # 知识点一、属性类型 ECMAScript 中有两种属性:**数据属性**和**访问器属性**。 - 数据属性 - 数据属性 - [参考网站](https://msdn.microsoft.com/zh-cn/library/hh965578(v=vs.94).aspx) ECMA-262 第 5 版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征。ECMA-262 定义这些特性是为了实现 JavaScript 引擎用的,因此在 JavaScript 中不能直接访问它们。为了表示特性是内部值,该规范把它们放在了两对儿方括号中,例如 [[Enumerable]] 。 ##### 数据属性 defineProperty **数据属性指某个数据值是否可以读取和写入值**;数据属性有4个描述其行为的特性; - [[enumerable]] 是否可枚举 / 是否可读,该特性默认是true;(表示能否通过for-in循环读取到对象的属性; ) - [[writable]] 是否可写,该特性默认是true;(表示能够修改属性的值) - [[configurable]] 是否可删改,该特性默认是true;(表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。) - [[value]] 某个属性的属性值;默认是undefined;(读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。) 像前面的例子,他的 Enumerable / Writable / Configurable 默认都是true;name属性的Value被设置为"zab";对该值的任何修改都会在这个属性上反应出来; **修改数据属性:Object.defineProperty(object,name,{})** 要修改属性默认的特性,必须使用 ECMAScript 5 的 Object.defineProperty() 方法。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属性必须是: configurable 、 enumerable 、 writable 和 value 。设置其中的一或多个值,可以修改对应的特性值。 > 默认的时候 var person = { name: "zab", age: 26, job: "WEB", sayName: function(){ console.log(this.name); } }; console.log(person.name,person.age); person.name="zab MODI" delete person.age; console.log(person.name,person.age); for (var key in person) { console.log("枚举对象的属性:",key); } > 使用属性的时候; var person = { name: "zab", age: 26, job: "WEB", sayName: function(){ console.log(this.name); } }; console.log(person.name);//zab 26 Object.defineProperty(person,"name",{//注意:属性一定要放在字符串中; enumerable:false, writable:false, configurable:false, value:"zab1" }); console.log(person.name);//zab1 person.name="zab MODI" console.log(person.name);//zab1,person.name="zab MODI"无效; delete person.name; console.log(person.name);//zab1 命令delete person.name;无效; for (var variable in person) { console.log("枚举对象的属性,是否有name:",variable);//其它的所有属性都枚举出来了,就是name没有枚举出来; } **如果把某个特性设置为false,假如再次尝试操作它,在非严格模式下,操作将被忽略(在非严格模式下什么也不会发生);在严格模式下,操作将会导致抛出错误。** **而且,一旦把属性定义为不可配置的,就不能再把它变回可配置了。此时,再调用 Object.defineProperty() 方法修改除 writable 之外的特性,都会导致错误:** 例子: var person = { name: "zab", age: 26, job: "WEB", sayName: function(){ console.log(this.name); } }; console.log(person.name);//zab Object.defineProperty(person,"name",{//注意:属性一定要放在字符串中; enumerable:false, writable:false, configurable:false, value:"zab1" }); console.log(person.name);//zab1 Object.defineProperty(person,"name",{//再次设置name会报错 configurable:true, value:"zab2" }); console.log(person.name);//无法正常打印 > 在调用 Object.defineProperty() 方法时,如果不指定, configurable 、 enumerable 和writable 特性的默认值都是 false 。多数情况下,可能都没有必要利用 Object.defineProperty()方法提供的这些高级功能。不过,理解这些概念对理解 JavaScript 对象却非常有用。 ##### 访问器属性 defineProperty 应用场景:设置一个属性的值会导致其他属性发生变化时。 > 访问器属性不包含数据值;它们包含一对** getter **和** setter **函数(不过,这两个函数都不是必需的)。 > 在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值; > 在写入访问器属性时,会调用setter 函数并传入新值,这个函数负责决定如何处理数据。 - [[Get]] :在读取属性时调用的函数。默认值为 undefined 。 - [[Set]] :在写入属性时调用的函数。默认值为 undefined 。 **访问器属性不能直接定义,必须使用 Object.defineProperty() 来定义。** var book = { _year: 2004, age: 1 }; Object.defineProperty(book, "year", { get: function(){ return this._year; }, set: function(newValue){ if (newValue > 2004) { this._year = newValue; this.age = (newValue - 2004)+1; } } }); book.year = 2005; console.log(book.age); //2 book.year = 2006; console.log(book.age); //3 > 更改书的时间,会引起书的年限; 以上代码创建了一个 book 对象,并给它定义两个默认的属性: _year 和 age 。 _year 前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。而访问器属性 year 则包含一个getter 函数和一个 setter 函数。getter 函数返回 _year 的值,setter 函数通过计算来确定正确的书籍年龄。因此,把 year 属性修改为 2005 会导致 _year 变成 2005,而 edition 变为 2。这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。 不一定非要同时指定 getter 和 setter。只指定 getter 意味着属性是不能写,尝试写入属性会被忽略。在严格模式下,尝试写入只指定了 getter 函数的属性会抛出错误。 var book = { _year: 2004, age: 1 }; Object.defineProperty(book, "year", { get: function(){ return this._year; } }); book.year = 2005; console.log(book.age); //1 book.year = 2006; console.log(book.age); //1 > 如果只设置了setter但是不设置getter,在chrome浏览器下可以正常访问; > 高程三上说142页,说法有错误,原话是【类似地,只指定 setter 函数的属性也不能读,否则在非严格模式下会返回 undefined ,而在严格模式下会抛出错误。】,亲测可以正常使用,环境chorme 52.0.2743.116 m; > 支持 ECMAScript 5 的这个方法的浏览器有 IE9+(IE8 只是部分实现)、Firefox 4+、Safari 5+、Opera12+和 Chrome。在这个方法之前,要创建访问器属性,一般都使用两个非标准的方法:**__defineGetter__()** 和** __defineSetter__() **。 var book = { _year: 2004, age: 0 }; // 定义访问器的旧有方法 book.__defineGetter__("year", function(){ return this._year; }); book.__defineSetter__("year", function(newValue){ if (newValue > 2004) { this._year = newValue; this.age = newValue - 2004; } }); book.year = 2005; console.log(book.age); //1 # 知识点二、定义多个属性(仅作了解) defineProperties() 由于为对象定义多个属性的可能性很大,ECMAScript 5 又定义了一个 Object.definePro-perties() 方法。利用这个方法可以通过描述符一次定义多个属性。这个方法接收两个对象参数:第一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。 var book = {}; Object.defineProperties(book, { _year: { value: 2004 }, age: { value: 1 }, year: { get: function(){ return this._year; }, set: function(newValue){ if (newValue > 2004) { this._year = newValue; this.age = newValue - 2004; } } } }) > 以上代码在 book 对象上定义了两个数据属性( _year 和 age )和一个访问器属性( year )。最终的对象与上面中定义的对象相同。唯一的区别是这里的属性都是在同一时间创建的。 支持 Object.defineProperties() 方法的浏览器有 IE9+、Firefox 4+、Safari 5+、Opera 12+和Chrome。 # 知识点三、读取属性的特性 getOwnPropertyDescriptor() 使用 ECMAScript 5 的 Object.getOwnPropertyDescriptor() 方法,可以取得给定属性的描述符。这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果是访问器属性,这个对象的属性有 configurable 、 enumerable 、 get 和 set ;如果是数据属性,这个对象的属性有configurable 、 enumerable 、 writable 和 value var book = {}; Object.defineProperties(book, { _year: { value: 2004 }, age: { value: 0 }, year: { get: function(){ return this._year; }, set: function(newValue){ if (newValue > 2004) { this._year = newValue; this.age = newValue - 2004; } } } }) var descriptor = Object.getOwnPropertyDescriptor(book, "_year"); console.log(descriptor.value); //2004 console.log(descriptor.configurable); //false console.log(typeof descriptor.get); //"undefined" var descriptor = Object.getOwnPropertyDescriptor(book, "year"); console.log(descriptor.value); //undefined console.log(descriptor.enumerable); //false console.log(typeof descriptor.get); //"function" 对于数据属性 _year , value 等于最初的值, configurable 是 false ,而 get 等于 undefined 。对于访问器属性 year , value 等于 undefined,enumerable 是 false ,而 get 是一个指向 getter函数的指针。 在 JavaScript 中,可以针对任何对象——包括 DOM 和 BOM 对象,使用 Object.getOwnPropertyDescriptor() 方法。支持这个方法的浏览器有 IE9+、Firefox 4+、Safari 5+、Opera 12+和 Chrome。