总的来说,JS的继承大体上分为两种:对象冒充和原型方式
首先,我们来看对象冒充的几种实现方式:
//方式一
function Person(name, sex){
this.name = name;
this.sex = sex;
this.move = function(){
alert("move");
};
};
function Man(name, sex, address, phone){
this.pe = Person;
this.pe(name, sex);
delete this.pe;
this.address = address;
this.phone = phone;
};
原理是这样的,将整个方法替换掉pe,当我们调用 this.(name,sex); 时,
相当于在Man里面执行上面的那一段代码,而此时的this已经代表的是Man对象,使Man也具有了name,sex等属性与及move方法。
//方式二
function Person(name, sex){
this.name = name;
this.sex = sex;
this.move = function(){
alert("move");
};
};
function Man(name, sex, address, phone){
Person.call(this, name, sex);
this.address = address;
this.phone = phone;
};
call方法实现继承,call方法会将this及Man中的参数传递到调用的方法(Person)中,此时Person里的this代表的是Man对象
当调用到Person的构造方法的时候,调用this.name的时候已经是Man.name了,这种方法也可以实现继承。
//方式三
function Person(name, sex){
this.name = name;
this.sex = sex;
this.move = function(){
alert("move");
};
};
function Man(name, sex, address, phone){
Person.apply(this, new Array(name, sex));
this.address = address;
this.phone = phone;
};
apply方法实现继承,其实apply方法和call方法是一样,只不过apply传递过去的参数要用一个数组包装起来而已。
上面就是对象冒充的三种实现方式,原理其实就是上下文环境变量this的替换。
它可以实现多重继承,但真正这样用的不多,因为它有着明显的性能缺陷。
Js利用对象冒充模拟的继承里,所有的成员方法都是针对this而创建的,也就是所有的实例都会拥有一份成员方法的副本,这是对内存资源的一种极度浪费。
还有就是对象冒充无法继承prototype域的变量和方法。
再来看看原型方式:
function Person(){};
Person.prototype.name = "";
Person.prototype.sex = "";
Person.prototype.move = function(){
alert("move");
};
function Man(){};
Man.prototype = new Person();
关键是对最后一句Man的原型指向Person类构造的对象(注意是对象,是实例)。
Js对象在读取某个对象属性的时候,总是先查看自身域的属性列表,如果有就返回,否则去读取prototype域(每个对象共享构造对象的类的prototype域所有属性和方法),如果找到就返回,由于prototype可以指向别的对象,所以Js解释器会递归的去查找prototype域指向对象的prototype域,直到prototype为本身,查找变成了一种循环,就停止,此时还没找到就成undefined了。
这样看来,最后一句发生的效果就是将父类所有属性和方法连接到子类的prototype域上,这样子类就继承了父类所有的属性和方法。
原型继承的缺陷也相当明显,就是继承时父类的构造函数时不能带参数,以及不支持多继承。
综合两种方式的利弊,可以总结出一种混合式继承方法:
function Person(name){
this.name = name;
};
Person.prototype.move = function(){
alert("move");
};
function Man(name){
this.pe = Person.call(this, name);
};
Man.prototype = new Person();
Man.prototype.showName = function(){
alert("My name is " + this.name + ".");
};
用对象冒充的方式来继承父类属性,用原型方式来继承父类的方法。这样在对子类进行实例化的时候,就可以同时初始化从父类继承下来的属性。
同时,由于父类的方法都是定义在prototype域上,通过原型方式继承,这样就不会造成资源的浪费。
唯一的美中不足就是始终不支持多继承,但足可满足大多数情况的需要了。