原型是JavaScript中比较难懂的一块知识。这篇笔记将详细原型以及原型的使用方法。
一、理解原型
我们创建的每个函数都有一个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
1.理解原型对象:
如上面的例子中,创建了person1之后,person1的prototype会指向Person函数 的原型对象,在默认情况下,所有原型对象都会获得一个constructor属性,这个属性指向prototype属性所在的函数的指针,即Person。由此便形成了一条原型链。
在读取某个对象中的属性时,先搜索对象本身,如果搜索不到,再沿着原型链搜索。
如果在对象中添加一个属性,该属性与原型链中的属性重名,那么原型链中的属性就会被屏蔽。使用delete操作符删除实例属性后,可以重新访问到原型属性。
2.hasOwnProperty()方法
通过hasOwnProperty()方法,可以知道什么时候访问的是实例属性,什么时候访问的是原型属性。
alert(person1.hasOwnProperty("name"));
3.in 操作符
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();
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
结合in 和hasOwnProperty() 就可以知道属性是存在于实例中还是存在于原型中。
4.hasPrototypeProperty()
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();
alert(hasPrototypeProperty(person, "name")); //true
person1.name = "Greg";
alert(hasPrototypeProperty(person, "name")); //false
5.Object.keys()方法与Object.getOwnPropertyNames()方法
前者返回对象上的所有可枚举的实例属性
后者返回所有实例属性,不论是否可枚举
二、优化原型语法
用对象字面量来重写整个原型对象,可以从视觉上更好的封装原型的功能。
function Person(){
}
Person.prototype = {
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName: function(){
alert(this.name);
}
};
此时,constructor不再指向Person。可以在原型中像下面这样设置:
constructor: Person
三、组合使用构造函数模式和原型模式
由于原型模式的共享特性会带来一些问题,因此,可以使用构造函数与原型模式结合。构造函数模式用户定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Jane", "Maria"];
}
Person.prototype = {
constructor: Person,
sayName: function(){
alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 21, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //Jane, Maria, Van
alert(person2.friends); //Jane, Maria
alert(person1.friends===person2.friends); //false
alert(person1.sayName ===person2.sayName); //true
四、动态原型模式
通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。
function Person(){
this.name = name;
this.age = age;
this.job = job;
if(typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
var friends = new Person("Nicholas", 29, "Software Engineer");
friends.sayName();