关于继承可以朴素的理解为,通过继承,子类可以复用父类的方法,以达到代码重用。JavaScript 可用多种方式模拟继承,本文先举例介绍各种方法,后对比分析优缺点。
在 JavaScript 中,构造函数也和普通的函数一样,可以被赋值和调用,对象冒充通过此原理来模拟继承。
Function ClassA(name)
{
this.name = name;
this.sayHello = function(){
alert("Hello, " + this.name);
}
}
Function ClassB(name,time)
{
this.newMethod = ClassA;
this.newMethod(name);
delete this.newMethod;
this.time = time;
this.sayGoodbye = function(){
alert("Goodbye " + this.name + ",it's " + this.time + " now !");
}
}
var objA = new ClassA("Tom");
var objB = new ClassB("Jerry","11:30am");
objA.sayHello(); // output is : "Hello,Tom"
objB.sayHello(); // output is : "Hello,Jerry"
objB.sayGoodbye();// output is : "Goodbye Jerry, it ’ s 11:30am now!"
|
如清单 1 所示,将 ClassA 的构造函数赋值为 ClassB 的一个普通方法,然后调用它,由于此时 this 指向的是 ClassB 的实例,那么 ClassB 的实例就会收到 ClassA 构造函数中定义的属性和方法,从而达到了继承的效果。
需要注意的是,应及时删除临时引用(this.newMethod),以防止 ClassB 更改 ClassA 类对象的引用。因为对临时引用(this.newMethod)的更改,也会导致 ClassA 的结构变化。并且 ClassB 的所有新属性和新方法,应该在删除临时引用后定义,否则,可能会覆盖父类的相关属性和方法。
认识到对象冒充的本质后,可以采用 JavaScript 中的 call 或者 apply 函数达到同样的效果,如清单 2 代码所示。其原理也是在调用 ClassA 的构造函数时,将 this 指向 ClassB 的实例。
Function ClassB(name,time)
{
ClassA.call(this,name); // 或者 ClassA.apply(this,[name]);
this.time = time;
this.sayGoodbye = function(){
alert("Goodbye " + this.name + ",it's " + this.time + " now !");
}
}
|
JavaScript 中的每个对象都包含一个原型对象(prototype),指向对某个对象的引用,而由于原型对象本身也是对象,则也会包含对它的原型的引用,由此构成一条原型链。原型链终止于内建 Object 类的原型。当要读取某个对象的属性或方法时,JavaScript 首先在该对象中查找,若没有找到,便在该对象的原型中继续查找,若仍未找到,便顺着原型链继续在原型的原型中查找,直到查找到或到达原型链的尽头。这样的系统被称为原型继承。而基于原型的继承,则是指利用了 prototype 或者说以某种方式覆盖了
prototype,从而达到属性及方法复用的目的。如下所示:
Function ClassA()
{
this.name = "";
this.sayHello = function(){
alert("Hello, " + this.name);
}
}
Function ClassB(){};
ClassB.prototype = new ClassA();
var objB = new ClassB();
objB.name = "Jerry";
objB.sayHello(); //output: "Hello,Jerry";
|
示例代码中将 ClassB 的 prototype 指向 ClassA 的实例,其原型链如图 1 所示,这样 ClassB 便拥有了 ClassA 所有的属性和方法,可以自由的赋值和调用。
对象冒充和基于原型的方式都可以让子类复用父类的代码以模拟继承,但这两种方法均各有利弊。
利用对象冒充,可以方便的实现多继承,只需要对所有需要继承的父类重复进行赋值流程便可。但这样却有着有明显的性能缺陷,因为在利用对象冒充模拟继承时,每个实例都会拥有一份父类成员变量和方法的副本,而成员方法只是一段对变量操作的可执行文本区域而已,这段区域不用为每个实例复制一份,所有的实例均可共享,这就造成了对内存资源的极度浪费。并且对象冒充也无法继承 prototype 域的变量和方法。
而基于原型的继承则可以使子类拥有一条完整的原型链,并且所有子类实例的原型都指向同一引用,相对于对象冒充,可极大的节省内存开销。但基于原型继承的缺陷也相当明显,就是父类的构造函数不能有参数,因为对子类 prototype 域的修改需在声明子类对象之后才能进行,而用子类构造函数的参数去初始化父类构造函数的属性是无法实现的。
因此可以结合二者的优点,采用混合的方式模拟继承,即用对象冒充的方式给属性赋值,用原型链的方式继承方法,示例代码如下:
Function ClassA(name){
this.name = name;
}
ClassA.prototype.sayHello = function(){
alert("Hello, " + this.name);
}
Function ClassB(name,time){
ClassA.call(this, name);
this.time = time;
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayGoodbye = function(){
alert("Goodbye " + this.name + ",it's " + this.time + " now !");
}
var objA = new ClassA("Tom");
var objB = new ClassB("Jerry","11:30am");
objA. sayHello();// output is: "Hello, Tom"
objB.sayHello(); // output is: "Hello, Jerry"
objB.sayGoodbye();//output is: "Goodbye Jerry,it ’ s 11:30am now !"
|
分享到:
相关推荐
组合继承使用原型链继承,并且利用借调父类的构造函数这两个方法的组合,可以实现一种组合式继承,方法通过父类的原型传给子类,然后子类调用父类的构造函数来获得父类的一
JavaScript继承方式实例,需要的朋友可以参考下。
主要介绍了6种JavaScript继承方式及优缺点(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
在JavaScript中继承是一个非常复杂的话题,比其他任何面向对象的语言中的继承都复杂得多。在大多数其他面向对象语言中,继承一个类只需使用一个关键字即可。与它们不同,在JavaScript中要想达到传承公用成员的目的,...
JavaScript继承第1种方式:对象冒充;第2种方式:call方法;第3种方式:apply方法;第4种方式:原型链方式;第5种方式:混合方式(推荐)
主要介绍了2种JavaScript主要继承方式,一种是通过原型的方式,一种是通过借用call&apply的构造函数方式,感兴趣的小伙伴们可以参考一下
Javascript继承机制原理 可以参考下
我们还知道,面向对象编程有三个重要的概念 - 封装、继承和多态。 但是在JavaScript的世界中,所有的这一切特性似乎都不存在。 因为JavaScript本身不是面向对象的语言,而是基于对象的语言。
浅析Javascript原型继承,浅析Javascript原型继承
NULL 博文链接:https://raychase.iteye.com/blog/1337415
javascript控件开发之继承关系的源码
JavaScript继承与多继承实例分析.docx
javascript继承之为什么要继承.docx
javascript 继承派生等问题的资料,都是网上收集的,学会apply call 就懂js面向对象的原理了。
JavaScript继承机制研究.pdf
JavaScript继承的三种方法实例 继承 继承: 首先继承是一种关系,类(class)与类之间的关系,JS中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承。 继承也是为了数据共享,js中的继承也是为了实现...