简单理解JavaScript中的原型
1.什么是原型?
原型是一个对象,其他对象可以通过它实现属性的继承,所有的对象在默认的情况下都有一个原型,而这个原型本身又是一个对象,所以每个原型本身又有一个原型,只有一个例外就是,默认的对象原型在原型链的顶端。
2.原型的产生
所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过JavaScript代码Object.prototype获得对原型对象的引用。
通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。
例如:
和使用{}创建对象一样,通过new Object()创建的对象继承自Object.prototype
通过new Array()创建的对象的原型就是Array.prototype
通过new Date()创建的对象的原型就是Date.prototype
但是,Object.prototype没有原型,所以也不继承任何属性。
3. 原型属性:prototype
每个函数都有一个属性叫prototype,这个prototype的属性值是一个对象,默认的只有一个constructor属性,指向这个函数本身。
检测一个对象是否是另一个对象的原型或处于原型链中使用的方法:isPrototypeOf()方法。
例如:
var p = { x:1 }; //定义一个原型对象
var o = Object.create(p); //使用这个原型创建一个对象
alert(p.isPrototypeOf(o)); //结果:true
alert(Object.prototype.isPrototypeOf(o)); //结果:true
以上代码在Google,FireF,IE浏览器测试结果都一样。
当给JavaScript类的prototype属性添加函数或属性时,可以视为对原有类的扩展,即增加了prototype属性的类继承了原有的类,这就是JavaScript所提供的的伪继承机制。
也就是说,JavaScript并没有提供真正你的继承,当通过某个类的prototype属性动态的增加属性或方法时,实质是对原有类的修改,并没有真正产生新的子类。
例如:
//添加并使用原型属性和方法
function Person(name,age){
this.name = name;
this.age = age;
this.info = function(){
alert("hello "+ this.name + " your age is "+ this.age);
};
}
var p = new Person("Cynthia",22);
p.info(); //结果: hello Cynthia your age is 22
Person.prototype.walk = function(){
alert(this.name + " is coming....");
}
var p1 = new Person("Morning",25);
p1.info(); //结果: hello Morning your age is 25
p1.walk(); //结果:Moring is coming...
p.walk(); //结果:Cynthia is coming...
在这个程序中,采用prototype给Person这个类增加了一个walk方法,这会让所有的Person砬都共享有walk方法,而原有的那个没有walk方法的Person类就不存在了。
调用对象属性执行过程:
(1)在引用对象的一个属性时,首相检查该对象本身是否拥有该属性,如果有就直接返回,没有…
(2)再查看对象的原型(Prototype),检查该原型上是否有要找的属性,如果有就返回,没有该值就是undefined。
例如:为JavaScript内建类Array增加indexof()方法,该方法由于判断数组中是否包含了某元素。
Array.prototype.indexOf = function(obj){
var result = -1;
for ( var i = 0; i<this.lenght; i++){
if(this[i] == obj){
result = i;
break;
}
}
return result;
}
var arr = [4,5,7,-2,8];
alert(arr.indexOf(-2)); //测试为arr新增的indexof方法
//结果:-1 (得到的是resultde 值)
4.理解原型对象
function Person(){} //声明一个构造函数
Person.prototype.name = "Cynthia"; //添加原型的属性
Person.prototype.age = "21";
Person.prototype.job = "student"
Person.prototype.sayName = function(){ //添加原型的方法
alert(this.name);
};
var person1 = new Person(); //创建一个实例对象
person1.sayName();
var person2 = new Person();
person2.sayName();
alert(person1.sayName() == person2.sayName());
在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在的函数指针。
Person构造函数、person的原型属性、Person实例的关系为:
5.原型链(prototype chain)
所有的内置构造函数以及大部分的自定义的构造函数,都具有继承自Object.prototype的原型。
例如: Date.prototype的属性继承自Object.prototype ,因此通过new Date()创建的Date对象同时继承自Date.prototype和Object.prototype。
这一系列链接的原型对象就构成了原型链。
创建一个原型链最好的方式就是,使用一个对象实例作为另一个对象的原型。
6.重写原型
重写原型,不会保留之前原型的任何信息,即重写原型对象切断了现有原型与之前任何已经存在的对象之间的实例。
例如:
function Person(){} //声明一个构造函数
var person = new Person(); //创建一个实例对象
Person.prototype = {
constructor: Person,
name:"Cynthia",
age:21,
job:"student",
sayName:function(){
alert(this.name);
}
};
person.sayName(); //结果:报错
7.原型属性共享
原型中,所有的属性时被很多实例共享的。
例如:
function Person(){} //声明一个构造函数
Person.prototype = {
constructor: Person,
name:"Cynthia",
age:21,
job:"student",
friends:["Morning"],
sayName:function(){
alert(this.name);
}
};
var person1 = new Person(); //创建实例对象
var person2 = new Person();
person1.friends.push("Katie");
alert(person1.friends); //结果:Morning,Katie
alert(person2.friends); //结果:Morning,Katie
alert(person1.friends == person2.friends); //结果:true