JavaScript之四种继承方式讲解

时间:2021-04-09 00:16:18

  在Javascript中,所有开发者定义的类都可以作为基类,但出于安全性考虑,本地类和宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码可以被用于恶意攻击。

选定基类后,就可以创建它的子类了。是否使用基类完全由你决定。有时,你可能想创建一个不能直接使用的基类,它只是用于给子类提供通用的函数。在这种情况下,基类被看作抽象类。

创建的子类将继承超类的所有属性和方法,包括构造函数及方法的实现。在Javascript中,所有方法和属性都是公用的,因此子类可直接访问这些方法。子类还可添加超类中没有的新属性和方法,也可以覆盖超类中的属性和方法。

这里列出了四种方法来实现继承机制

1.   对象冒充

对象冒失是在函数环境中使用this关键字后发展出来的一种继承方式。其原理如下:构造函数使用this关键字给所有属性和方法赋值(即采用类声明的构造 函数方式)。因为构造函数只是一个函数,所以可使ClassA的构造函数成为ClassB的方法,然后调用它。ClassB就会收到ClassA的构造函 数中定义的属性和方法。例如,用下面的方式定义ClassA和ClassB:

function ClassA (sColor) {

this.color = sColor;

this.sayColor = funciton() {

alert(this.sColor);

};

}

function ClassB (sColor) {

}

还记得吗,关键字this引用的是构造函数当前创建的对象,不过在这个方法中,this指向的是所属的对象。这个原理是把ClassA作为常规函数来建立继承机制,而不是作为构造函数。如下使用构造函数ClassB可以实现继承机制:

function ClassB (sColor) {

this.newMethod = ClassA;

this.newMethod(sColor);

delete this.newMethod;

}

在这段代码中,位ClassA赋予了方法newMethod。然后调用该方法,传递给它的是ClassB的构造参数sColor。最后 一行代码删除了对ClassA的引用,这样以后就不能在调用它。所有的新属性和新方法都必须在删除了新方法的代码行后定义,否则,可能会覆盖超类的相关属 性和方法。

有意思的是,对象冒充可以支持多重继承,也就是说,一个类可以继承多个超类。如,如果存在两个类ClassX和ClassY,ClassZ想继承这两个类,可以使用下面的代码:

function ClassZ {

this.newMethod = ClassX;

this.newMethod();

delete this.newMethod;

this.newMethod = ClassY;

this.newMethod();

delete this.newMethod;

}

不过,这里存在一个弊端,如果ClassX和ClassY具有同名的属性或方法,ClassY具有高优先级,因为它从后面的类继承。

2.    call()方法

call()方法是与经典的对象冒充方法最相似的方法。它的第一个参数用作this的对象,其他参数都直接传递给函数自身。例如:

function sayColor(sPrefix, sSuffix) {

alert(sPrefix + this.color + sSuffix);

}

var obj = new Object();

obj.color = "red";

sayColor.call(obj, "Color is", ", a nice color");       // output: Color is red, a nice color

在这个例子中,函数sayColor()在对象外定义,即使它不属于任何对象,也可以应用关键字this。对象obj的color属性等于"red"。调 用call()方法时,第一个参数是obj,说明应该赋予sayColor()函数中的this关键字值是obj。第二个和第三个参数是字符串。它们与 sayColor()函数中的参数sPrefix和sSuffix匹配。

要与继承机制的对象冒充方法一起使用该方法,只需将前三行的赋值、调用和删除代码替换即可:

function ClassB(sColor, sName) {

//this.newMethod = ClassA;

//this.newMethod();

//delete this.newMethod;

ClassA.call(this, sColor);

this.name = sName;

this.sayName = function() {

alert(this.name);

};

}

这里,想让ClassA中的关键字this等于新创建的ClassB对象,因此this是第一个参数。第二个参数sColor对两个类来说都是唯一的参数。

3.    apply()方法

apply()方法有两个参数,用作this的对象和要传递给函数的参数的数组。如:

function sayColor(sPrefix, sSuffix) {

alert(sPrefix + this.color + sSuffix);

}

var obj = new Object();

obj.color = "red";

sayColor.apply(obj, new Array("the color is ", ", a nice color"));

这个例子与前面的例子相同,只是现在调用的是apply()方法。调用apply()方法时,第一个参数仍是obj,说明应赋予sayColor()中的 this关键字值obj。第二个参数是由两个字符串构成的数组,与sayColor()的参数sPrefix和sSuffix匹配。

该方法也用于替换前三行的赋值、调用和删除新方法的代码:

function ClassB(sColor, sName) {

//this.newMethod = ClassA;

//this.newMethod(sColor);

//delete this.newMethod;

ClassA.apply(this, new Array(sColor));

this.name = sName;

this.sayName = function() {

alert(this.name);

};

}

同样的,第一个参数仍是this。第二个参数是只有一个值color的数组。可以把ClassB的整个arguments对象作为第二个参数传递给apply()方法:

function ClassB(sColor, sName) {

//this.newMethod = ClassA;

//this.newMethod(sColor);

//delete this.newMethod;

ClassA.apply(this, arguments);

this.name = sName;

this.sayName = function() {

alert(this.name);

};

}

当然,只有超类中的参数顺序与子类中的参数顺序完全一致时才可以传递参数对象。如果不一致,就必须创建一个单独的数组,按照正确的顺序放置参数。

4.    原型链

我们知道,prototype对象其实是个模板,要实例化的对象都以这个模板为基础,prototype对象的任何属性和方法都被传递给那个类的所有实例。原型链利用这种功能来实现继承机制。

如果用原型方式重定义前面的例子中的类,它们将变为下列形式:

function ClassA() {

}

ClassA.prototype.color = "red";

ClassA.prototype.sayColor = function() {

alert(this.color);

};

function ClassB() {

}

ClassB.prototype = new ClassA();

这里,把ClassB的prototype属性设置成ClassA的实例,这样就可以得到ClassA的所有属性和方法。

注意:调用ClassA的构造函数时,没有给它传递参数。这在原型链中时标准做法。要确保构造函数没有任何参数。

与对象冒充相似,子类的所有属性和方法都必须出现在prototype属性被赋值后,因为在它之前赋值的所有方法都会被删除,因为prototype属性 被替换成了新对象,添加了新方法的原始对象将被销毁。所以,为ClassB类添加name属性和sayName()方法的代码如下:

function ClassB() {

}

ClassB.prototype = new ClassA();

ClassB.prototype.name = "";

ClassB.prototype.sayName = function() {

alert(this.name);

};

此外,在原型链中,instanceof运算符的运行方式也很独特。对ClassB的所有实例,instanceof为ClassA和ClassB都返回true。

现在我们有了四种继承的方法,其中,用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法。用这两种方式重写前面的例子,代码如下:

function ClassA(sColor) {

this.color = sColor;

}

ClassA.prototype.sayColor = function() {

alert(this.color);

};

function ClassB(sColor, sName) {

ClassA.call(this, sColor);

this.name = sName;

}

ClassB.prototype = new ClassA();

ClassB.prototype.sayName = function() {

alert(this.name);

};

JavaScript之四种继承方式讲解的更多相关文章

  1. JavaScript几种继承方式

    我们先构建一个Person的构造函数 function Person(name) { this.name=name; } Person.prototype.sayHi=function () { co ...

  2. JavaScript几种继承方式的总结

    1.原型链继承 直接将子类型的原型指向父类型的实例,即"子类型.prototype = new 父类型();",实现方法如下: //父类构造函数 function father(n ...

  3. JavaScript五种继承方式详解

    本文抄袭仅供学习http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html 一. 构造函数绑定 ...

  4. Javascript的四种继承方式

    在Javascript中,所有开发者定义的类都可以作为基类,但出于安全性考虑,本地类和宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码可以被用于恶意攻击. 选定基类后,就可 ...

  5. JavaScript的3种继承方式

    JavaScript的继承方式有多种,这里列举3种,分别是原型继承.类继承以及混合继承. 1.原型继承 优点:既继承了父类的模板,又继承了父类的原型对象: 缺点:不是子类实例传参,而是需要通过父类实例 ...

  6. JavaScript 的对象继承方式,有几种写法?

    JavaScript 的对象继承方式,有几种写法? 一.对象冒充 其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数,所以可使 Pa ...

  7. 架构师JavaScript 的对象继承方式,有几种程序写法?

    架构师JavaScript 的对象继承方式,有几种程序写法?   一.对象冒充 其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数, ...

  8. 都0202年了,你还不知道javascript有几种继承方式?

    前言     当面试官问你:你了解js哪些继承方式?es6的class继承是如何实现的?你心中有很清晰的答案吗?如果没有的话,可以通过阅读本文,帮助你更深刻地理解js的所有继承方式.       js ...

  9. 【设计模式+原型理解】第三章:javascript五种继承父类方式

    [前言] 我们都知道,面向对象(类)的三大特征:封装.继承.多态 继承:子类继承父类的私有属性和公有方法 封装:把相同的代码写在一个函数中 多态: ->重载:JS严格意义上是没有重载,但可以通过 ...

随机推荐

  1. [ASP.NET MVC 小牛之路]15 - Model Binding

    Model Binding(模型绑定)是 MVC 框架根据 HTTP 请求数据创建 .NET 对象的一个过程.我们之前所有示例中传递给 Action 方法参数的对象都是在 Model Binding ...

  2. asp.net中时间差的问题

    asp.net中时间差的问题 在asp中我们可以用datediff来处理,时间的差,相当的不错,可是在asp.net中C#语言中却没有.可是ASP.net给我们提供了一个TimeSpan,我们可以用它 ...

  3. 2.大约QT数据库操作,简单的数据库连接操作,增删改查数据库,QSqlTableModel和QTableView,事务性操作,大约QItemDelegate 代理

     Linux下的qt安装,命令时:sudoapt-get install qt-sdk 安装mysql数据库,安装方法參考博客:http://blog.csdn.net/tototuzuoquan ...

  4. nmap 使用注意事项

    1.可行的网络主机的高速发现 nmap -sP 192.168.1.*  要么 nmap -sP 192.168.1.-254 2.扫描UDPport DP扫描方式用于推断UDPport的情况. 向目 ...

  5. QCanvasItem介绍-QT3

    QCanvasItem类提供一个在QCanvas上的抽象图形对象. 各种QCanvasItem子类提供立即可用的行为.这个类是一个纯粹的抽象超类,它提供了在所有具体的canvas项目类*享的行为.Q ...

  6. 关于windows下的文件结束符

    在<c++ primer>中有说,在windows中文件结束符为:ctrl+z,在Linux中为:ctrl+D. 但是在while(cin>>s)的语句运行中,需要两次的^Z, ...

  7. &lbrack;kubernetes&rsqb;helm安装

    下载 HELM_VERSION=${K8S_VERSION:-"2.11.0"} HELM="helm-v${HELM_VERSION}-linux-amd64&quot ...

  8. ubuntu上u-boot的编译

    1,下载u-boot 2,将Windows中的u-boot复制到ubuntu虚拟机中自定义目录并解压 3,进入该目录cd 4,安装dtc:sudo apt-get install device-tre ...

  9. 【UML】-NO&period;42&period;EBook&period;5&period;UML&period;1&period;002-【UML 大战需求分析】- 活动图 (Activity Diagram)

    1.0.0 Summary Tittle:[UML]-NO.42.EBook.1.UML.1.002-[UML 大战需求分析]- 活动图 Style:DesignPattern Series:Desi ...

  10. Angular 201703

    $http vm.auth = function() { return $http({ method: 'POST', url: 'sys/auth.json' }) } service.auth() ...