一,理解对象
var person = { name:'Nicholas', age:29, job:'Software Engineer', sayName:function(){ alert(this.name); } }; person.sayName(); //访问对象里的方法
1.1,属性类型
数据属性包含一个数据值的位置,在这个位置可以读取和写入值,数据属性有4个描述其行为的特性。
1,Configurable:表示能否通过delete删除属性,从而重新定义属性。
2,Enumerable :表示能否通过for-in循环返回属性
3,Writable:表示能否修改属性的值。
4,包含这个属性的数据值。
例如:设置name属性为只读
var person = {}; Object.defineProperty(person,"name",{ writable:false, value:'Nicholas' }); alert(person.name); person.name = "Greg"; alert(person.name);
2,把configurable设置为false,表示不可配置的属性。
var person = {}; Object.defineProperty(person,"name",{ configurable:false, value:'Nicholas' }); alert(person.name); //Nicholas delete person.name alert(person.name); //Nicholas
二,创建对象
2.1 工厂模式
这种模式抽象了创建具体对象的过程,考虑到ECMAScript中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节,如下面例子所示 function createPerson(name,age,job){
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");
person1.sayName();
var person2 = createPerson("Greg",27,"Doctor");
alert(person2.name);
弊端:工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)
2.2 构造函数模式
ECMAScript中的构造函数可用来创建特定类型的对象,像object和Array这样的原生构造函数,在运行时会自动出现在执行环境中,此外也可以创建自定义的构造函数
从而定义自定义对象类型的属性和方法,例如:
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"); // 调用 person1.sayName(); var person2 = new Person("Greg",27,"Doctor");
在这个例子中,Person()函数取代了createPerson()函数。Person()中的代码与createPerson()存在以下不同之处:
1,没有显式地创建对象;
2,直接将属性和方法赋给了this对象;
3,没有return语句。
2.3 原型模式
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。按照字面意思来理解,那么prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法,换句话说,不必再构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中 function Person(){}
Person.prototype.name = 'Nicholas'; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person();
var person2 = new Person();
person1.sayName(); // Nicholas
person2.sayName(); // Nicholas
在此,我们将sayName()方法和所有属性直接添加到了Person的prototype属性中,构造函数变成了空函数,即使如此,也仍然可以通过调用构造函数来创建新对象,而且新对象还会具有相同的属性和方法。但与构造函数模式不同的是,新对象的这些属性和方法是由所有实例共享的。换句话说,person1和person2访问的都是同一组属性和同一个sayName()函数。
但是如果方法比较多的话,大多数人会采用一种更简洁的方法,直接使用一个对象字面形式替换原型对象,如下:
Person.prototype = { sayName:function(){ console.log(this.name); }, toString :function(){ return "[Person"+this.name+"]" } }
这种方式非常流行,因为你不用多次键入Person.prototype,但是有一个副作用:
使用字面量形式改写了原型对象改变了构造函数的属性,因此他指向Object而不是Person。这是因为原型对象具有一个constructor属性,这是其他对象实例所没有的。当一个函数被创建时,它的prototype属性也被创建,且该原型对象的constructor属性指向该函数。当使用对象字面量形式改写原型对象时,其constructor属性将被置为泛用对象Object.为了避免这一点,需要在改写原型对象的时候手动重置constructor,如下:
没有改变前:
function Person(name){ this.name=name; } var p1 = new Person('John');
alert(p1 instanceof Person); //true instanceof 用于判断一个变量是否某个对象的实例
alert(p1.constructor === Person); //true
alert(p1.constructor === Object); //false
改变后:
function Person(name){ this.name=name; } Person.prototype = { sayName:function(){ console.log(this.name); }, toString:function(){ return "[Person"+this.name+"]" } }; var p1 = new Person('John'); p1.sayName(); //说明p1的实例化必须放在Person.prototype的后面,否则找不到sayName()方法报错,
,
//alert(p1 instanceof Person); //true
//alert(p1.constructor === Person); //false
alert(p1.constructor === Object); //true
当使用对象字面量形式改写原型对象时,其constructor属性将被置为泛用对象Object,为了避免这一点,需要在改写原型对象的时候手动重置constructor如下:
function Person(name){ this.name=name; } Person.prototype = { constructor:Person, sayName:function(){ console.log(this.name); }, toString:function(){ return "[Person"+this.name+"]" } }; var p1 = new Person('John'); p1.sayName(); //alert(p1 instanceof Person); //true alert(p1.constructor === Person); //true //alert(p1.constructor === Object); //false