先给大家介绍下JavaScript实现继承的几种方式,然后再详细讨论讨论JS的prototype和constructor。
一 JavaScript实现继承的方式
1. 继承第一种方式:对象冒充
先定义一个父类:
function parent(name){
this.name = name;
this.hello = function(){
alert(this.name);
}
}
再定义一个子类:
通过以下3行实现将parent的属性和方法追加到Child中,从而实现继承
第一步:this.method是作为一个临时的属性,并且指向Parent所指向的对象,
第二步:执行this.method方法,即执行parent所指向的对象函数 。这就相当与在对象自己身上调用parent的hello函数,parent的this指向了child。相当与 parent.call(this);
第三步:销毁this.method属性,即此时Child就已经拥有了Parent的所有属性和方法 。
注意:this.method已经执行,删不删都一样,不影响属性的添加,区别只在构造出的对象中是否存在该方法,如果不删除,还可以在实例中调用该方法,修改那两个成员的值
function child(name, pwd){
this.method = Parent;
this.method(name);
delete this.method;
this.pwd = pwd;
this.world = function(){
alert(this.pwd);
};
}
测试:
var parent = new parent("zhangsan");
var child = new child("lisi","123456");
parent.hello();
child.hello();
child.world();
parent和child的hello方法是相互独立的,修改任何一方都不会影响另外一个。
2. 继承第二种方式:call()方法方式
call方法是Function类中的方法
call方法的第一个参数的值赋值给类(即方法)中出现的this
call方法的第二个参数开始依次赋值给类(即方法)所接受的参数
function test(str){
alert(this.name + " " + str);
}
var object = new Object();
object.name = "zhangsan";
test.call(object,"langsin");//此时,第一个参数值object传递给了test类(即方法)中出现的this,而第二个参数"langsin"则赋值给了test类(即方法)的str
先定义一个父类:
function parent(name){
this.name = name;
this.hello = function(){
alert(this.name);
}
}
再定义一个子类:
function child(name, pwd){parent.call(this, name);
this.pwd = pwd;
this.world = function(){
alert(this.pwd);
}
}
parent和child的hello方法是相互独立的,修改任何一方都不会影响另外一个。
var parent = new parent("zhangsan");
var child = new child("lisi","123456");
parent.hello();
child.hello();
child.world();
3. 继承的第三种方式:apply()方法方式
apply方法和call 方法类似
apply方法接受2个参数,
A、第一个参数与call方法的第一个参数一样,即赋值给类(即方法)中出现的this
B、第二个参数为数组类型,这个数组中的每个元素依次赋值给类(即方法)所接受的参数
function parent(name){
this.name = name;
this.hello = function(){
alert(this.name);
}
}
function child(name,password){
parent.apply(this,new Array(name));
this.pwd = pwd;
this.world = function(){
alert(this.pwd);
}
}
var parent = new parent("zhangsan");
var child = new child("lisi","123456");
parent.hello();
child.hello();
child.world();
4. 继承的第四种方式:原型链方式
即子类通过prototype将所有在父类中通过prototype追加的属性和方法都追加到child,从而实现了继承
function parent(){
}
parent.prototype.hello = "hello";
parent.prototype.sayHello = function(){
alert(this.hello);
}
function child(){
}
child.prototype = new parent();//这行的作用是:将parent中将所有通过prototype追加的属性和方法都追加到child,从而实现了继承
child.prototype.constructor = child; //纠正child的prototype的constructor
child.prototype.world = "world";
child.prototype.sayWorld = function(){
alert(this.world);
}
var c = new Child();
c.sayHello();
c.sayWorld();
child.prototype = new parent();它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。
任何一个prototype对象都有一个constructor属性,指向它的构造函数。如果没有"child.prototype = new parent();"这一行,child.prototype.constructor是指向child的;加了这一行以后,child.prototype.constructor指向parent。
alert(child.prototype.constructor == parent); //true
更重要的是,每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。
alert(c.constructor == child.prototype.constructor); // true
因此,在运行"child.prototype = new parent();"这一行之后,c.constructor也指向parent!
这显然会导致继承链的紊乱(c明明是用构造函数child生成的),因此我们必须手动纠正,将child.prototype对象的constructor值改为child。child.prototype.constructor = child;就是做这样的事情的。
5. 继承的第五种方式:混合方式
混合了call方式、原型链方式function Parent(hello){
this.hello = hello;
}
Parent.prototype.sayHello = function(){
alert(this.hello);
}
function Child(hello,world){
Parent.call(this,hello);//将父类的属性继承过来
this.world = world;//新增一些属性
}
Child.prototype = new Parent();//将父类的方法继承过来
Child.prototype.constructor = Child;
Child.prototype.sayWorld = function(){//新增一些方法
alert(this.world);
}
var c = new Child("zhangsan","lisi");
c.sayHello();
c.sayWorld();
二 JS的prototype和constructor
prototype:原型prototype方式的属性查询可以理解为js独有的一个机制。
就是说,js读取一个对象属性的时候,对象本身若找不到,则会去读取构造函数的prototype对象的同名属性,然后若还是找不到,则去读取Object构造函数的prototype对象的同名属性——即所谓的“继承”...
如果要实现继承关系A->B,则想要A继承B的属性,则需要自己去维护这个prototype对象。
constructor:它是每个对象都有的一个属性,它引用初始化这个对象时候的构造函数。js这也是独有的一个机制。它有默认值。
你可以不去维护它,这个时候你不去用它,也不会有任何问题。但是,当你想让它正确工作的时候,则就需要我们去维护它了!