JavaScript高级程序设计第6章读书笔记(2)
定义多个属性
由于为对象定义多个属性的可能性很大,ECMAScript 5又定义了一个Object.defineProperties()
方法。利用这个方法可以通过描述符一次定义多个属性。该方法接收2个对象参数: 第一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
var book = {}; Object.defineProperties(book, { _year: { value: 2004 }, edition: { value: 1 }, year: { get: function(){ return this._year; }, set: function(newValue){ if(newValue > 2004){ this._year = newValue; this.edition += newValue - 2004; } } } }) |
以上代码在book对外上定义了两个数据属性(_year和edition)和一个访问器属性(year)。
支持Object.defineProperties()方法的浏览器有IE9+、Firefox 4+、Safari 5+、Opera 12+和Chrome。
读取属性的特性
使用ECMAScript 5的Object.getOwnPropertyDescription()
方法,可以取得给定属性的描述符。这个方法接收2个参数: 属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果是访问器属性,这个对象的属性有configurable、enumerable、get和set;如果是数据属性,这个对象的属性有configurable、enumerable、writable和value。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
var book = {}; Object.defineProperties(book, { _year: { value: 2004 }, edition: { value: 1 }, year: { get: function(){ return this._year; }, set: function(newValue){ if(newValue > 2004){ this._year = newValue; this.edition += newValue - 2004; } } } }); var descriptor = Object.getOwnPropertyDescriptor(book, "_year"); alert(descriptor.value); // 2004 alert(descriptor.configurable); // false alert(typeof descriptor.get); // "undefined" var descriptor = Object.getOwnPropertyDescriptor(book, "year"); alert(descriptor.value); // undefined alert(descriptor.enumerable); // false alert(typeof descriptor.get); // "function" |
在JavaScript中,可以针对任何对象(包括DOM和BOM对象)使用Object.getOwnPropertyDescriptor()方法。支持这个方法的浏览器有IE9+、Firefox 4+、Safari 5+、Opera 12+和Chrome。
创建对象
工厂模式
由于ECMAScript中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var person1 = createPerson("Nicholas", 29, "Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor"); |
函数createPerson() 能够根据接收的参数来构建一个包含所有必要信息的Person对象。可以无数次调用这个函数,而每次都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但是没有解决对象识别的问题(即怎样知道一个对象的类型)。随着JavaScript的发展,一个新的模式又出现了。
构造函数模式
ECMAScript中的构造函数可以用来创建特定类型的对象。像Object、Array这样的原生构造函数,在运行时会自动出现在执行环境中。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); |
Person()函数中的代码,没有显式地创建对象,它直接将属性和方法赋给了this对象,并且没有return语句。按照惯例,构造函数始终应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。
要创建Person的新实例,必须使用new操作符。这种方式调用构造函数实际上会经历以下4个步骤:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
前面的例子中,person1和person2分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person,如下所示:
1 2 3 |
alert(person1.constructor == Person); // true alert(person2.constructor == Person); // true |
对象的constructor属性最初是用来标识对象类型的。但是,提到检测对象类型,还是用instanceof操作符会更可靠一些。我们在这个例子中创建的所有对象既是Object的实例,也是Person的实例。
1 2 3 4 5 |
alert(person1 instanceof Object); // true alert(person1 instanceof Person); // true alert(person2 instanceof Object); // true alert(person2 instanceof Person); // true |
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。
以这种方式定义的构造函数是定义在Global对象(在浏览器中是window对象)中的。