创建对象的几种方式
工厂模式
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");
创建的对象都是object类型
构造函数模式
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");
与工厂模式相比:
- 没有显示的创建对象
- 直接将属性和方法赋给this对象
- 没有return语句
- 创建实例需要用new
如果不使用new创建实例,构造函数就是一个普通的函数,this指向全局
Person("Greg", 27, "Doctor"); // 添加到window
window.sayName(); //"Greg"
可以使用call或apply在另一个对象的作用域调用
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName(); //"Kristen"
构造函数的问题:
构造函数中的方法,在每个实例中都会重新创建一遍
做同一件事情的方法被创建多次,是没有必要的。
alert(person1.sayName == person2.sayName); //false
可以将方法定义到构造函数的外部
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
}
如果有多个方法还得创建多个全局的函数?
原型模式
每个函数都有一个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();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true
原型对象
创建一个新的函数,就会为该函数生成一个prototype属性,这个属性指向函数的原型对象。
原型对象会自动获取一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
从图中可以看到三种关系:
person1,person2:实例
Person: 构造函数
Person Prototype: 原型对象
实例与构造函数:
虽然实例是通过new 构造函数创建,但实例和构造函数之间没有关系。
实例与原型对象:
每个实例中包含一个[[Prototype]]的属性,指向原型对象,但却无法直接访问[[Prototype]]。
可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。
Object.getPrototypeOf(),这个方法返回可以[[Prototype]]的值。
alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"
构造函数与原型对象:
构造函数的prototype指向原型对象,原型对象的constructor 又指回了构造函数。
hasOwnProperty() 和 in
hasOwnProperty()
hasOwnProperty()可以检测一个属性是存在实例中,还是存在原型中,
在实例中返回true,在原型中返回false。
in
in 操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。
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();
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
person1.name = "Greg";
alert(person1.name); //"Greg" ——来自实例
alert(person1.hasOwnProperty("name")); //true
alert("name" in person1); //true
alert(person2.name); //"Nicholas" ——来自原型
alert(person2.hasOwnProperty("name")); //false
alert("name" in person2); //true
delete person1.name;
alert(person1.name); //"Nicholas" ——来自原型
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor : Person,
sayName : function(){
alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false