在js中经常看到prototype,看了一遍,理解了后又忘了,- - …可能是理解的不够深入,现在写个博客总结下:
每个函数都有一个prototype属性,这个属性是指向一个对象的引用,这个对象称为原型对象,原型对象包含函数实例共享的方法和属性,也就是说将函数被用作构造函数调用(使用new操作符调用)的时候,新创建的对象会从原型对象上继承属性和方法。
你不需要显式地声明一个prototype属性,因为在每一个构造函数中都有它的存在。
看下面例子:
function Test(){}
alert(Test.prototype); // 输出 "Object"
下面说明一下构造函数:
function Person(name)
{
this.name=name;
this.showMe=function()
{
alert(this.name);
}
};
var one=new Person("liming");
one.showMe();//liming
这个构造函数与一般函数调用的区别在于new操作符,关于构造函数调用时是否有new还有不同的区别,可以看我另一篇文章:构造函数是否有new的区别 。
按照javascript的说法,function定义的这个Person就是一个Object(对象),而且还是一个很特殊的对象,这个使用function定义的对象与使用new操作符生成的对象之间有一个重要的区别。这个区别就是function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性。
prototype属性又指向了一个prototype对象,注意prototype属性与prototype对象是两个不同的东西,要注意区别。在prototype对象中又有一个constructor属性,这个constructor属性同样指向一个constructor对象,而这个constructor对象恰恰就是这个function函数本身。
看个代码:
function Person(name)
{
this.name=name;
this.showMe=function()
{
alert(this.name);
}
};
var one=new Person('js');
alert(Person.prototype)//Object
alert(one.prototype)//undefined
alert(typeof Person.prototype);//object
alert(Person.prototype.constructor);//function Person(name) {...};
上面的代码证明了one这个对象没有prototype属性。
所有对象本身都可以有prototype属性,它本身也是对象。如此循环下去就形成一个prototype链,这个链当遇到链中的prototype为null时即中止。(Object的默认prototype是null)。
如上代码var one=new Person(‘js’),这个one对象的prototyp是null, 所以 one的prototype链只包含一个对象Object.prototype。这也说明了为什么function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性,因为已经实例化了。
//首先创建一个基本的Person类型
function Person(name)
{
this.name=name;
this.showMe=function()
{
alert(this.name);
}
};
//可以通过prototype属性来为该类型新增属性或方法
Person.prototype.from=function()
{
alert('I come from prototype.');
}
//实例化Person对象
var one=new Person('js');
one.showMe(); //js
one.from();//I come from prototype.
看看继承:
分析如下的代码结构,可以看出类型MyClass2本身并没有定义testNumber,但通过prototype属性将MyClass1的所有定义都继承下来了,而且还继承了Object中的toString方法。因此该prototype的链包含3个。
var MyClass1 =function(formalParameter) {
this.testNumber =formalParameter;
};
var MyClass2 = function(formalParameter){
this.testString = formalParameter;
};
MyClass2.prototype = new MyClass1(9);
var myObject2 = new MyClass2("Hello World!");
alert(myObject2.testString);//Hello World!
alert(myObject2.testNumber); //9
alert(myObject2.toString); //function toString() {}
在js中,每个对象都可以继承自另外一个其它的对象,我们可以称之为引用原型。当我们访问一个属性时,如果能通过对象本身直接找到,则返回,否则它会向引用原型中追溯,直到根部prototype(即object)。如下所示:
Object.prototype.inObj = 1;
var A = function() {
this.inA = 2;
}
A.prototype.inAProto = 3;
var B = function() {
this.inB = 4;
}
B.prototype = new A();
B.prototype.inBProto = 5;
var x = new B();
alert(x.inObj + “,” + x.inA + “,” + x.inAProto + “,” + x.inB + “,” +x.inBProto); //1, 2, 3, 4, 5
对于上述的代码,创建一个对象x,它所生成的prototype链结构如下:
我们知道,将ClassA的一个实例赋值给ClassB,则 ClassB就继承了ClassA的所有属性。
var ClassA = function() {
this.a = "a";
};
var ClassB = function() {
this.b= "b";
};
ClassB.prototype = new ClassA();
var objB = new ClassB();
alert(objB.a); // a
alert(objB.b); // b
从上述的变量输出结果,可以看出本身ClassB是没有属性a的,但在创建的对象中调用objB.a时却输出了a,说明在执行ClassB.prototype= new ClassA();后,ClassB继承了ClassA的所有属性。
js的原型继承是引用原型,而不是复制原型。当修改原型时会导致所有的实例变化。
var ClassA = function() {
this.a = "a";
};
var ClassB = function() {
this.b = "b";
};
ClassB.prototype = new ClassA();
var objB = new ClassB();
alert(objB.a); // a
ClassB.prototype.a ="changed";
alert(objB.a); //changed
每个子类对象都执行同一个原型的引用,所以子类对象中的原型成员实际是同一个
var ClassA = function() {
this.a = "a";
this.Method = function() {
alert("ClassA");
}
};
var ClassB = function() {
this.b = "b";
};
ClassB.prototype = new ClassA();
var objB1 = new ClassB();
var objB2 = new ClassB();
alert(objB1.a == objB2.a); //true
alert(objB1.b == objB2.b); //true
alert(objB1.Method ==objB2.Method);//true
子类对象的写操作只访问子类对象的成员,相互之间不产生影响。写一定是写子类,读则要看是否子类中有,若有则读子类,若无则读原型
var ClassA = function() {
this.a = "a";
};
var ClassB = function() {
this.b = "b";
};
ClassB.prototype = new ClassA();
var objB1 = new ClassB();
var objB2 = new ClassB();
objB1.a = "change";
alert(objB1.a); // change
alert(objB2.a); // a
由上述代码分析,其中由于进行了objB1.a进行了重新改变,所以其值在接下来进行了改变,但它不会影响到objB2对象。但若是原型发生了变化,则如2所示,它下面的所有实例均会发生变化。
构造子类时,原型的构造函数不会被执行
var ClassA = function() {
alert("ClassA");
this.a = "a";
};
var ClassB = function() {
alert("ClassB");
this.b = "b";
};
ClassB.prototype = new ClassA();// ClassA
var objB1 = new ClassB(); // ClassB
var objB2 = new ClassB(); // ClassB
由此可以看出,在构造子类时并没有象Java中那样需要调用父类的构造函数。
在子类对象中访问原型的成员对象,会影响到其它对象
var ClassA = function() {
this.a = [];
};
var ClassB = function() {
this.b = "b";
};
ClassB.prototype = new ClassA();
var objB1 = new ClassB();
var objB2 = new ClassB();
objB1.a.push(1,2,3);
alert(objB2.a); // 1,2,3
由此可以看出,在子类对象中访问原型的成员对象后,由于采用的是引用原型方式,其实它和ClassB.a.push实现的是同样的功能。所以其它的子类也会跟着变化。