总的来说,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的替换。

它可以实现多重继承,但真正这样用的不多,因为它有着明显的性能缺陷。

借用构造函数方式模拟的继承里,所有的成员方法都是针对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){
Person.call(this, name);
};
Man.prototype = new Person();
Man.prototype.showName = function(){
alert("My name is " + this.name + ".");
};

用借用构造函数方式来继承父类属性,用原型方式来继承父类的方法。这样在对子类进行实例化的时候,就可以同时初始化从父类继承下来的属性。

同时,由于父类的方法都是定义在prototype域上,通过原型方式继承,这样就不会造成资源的浪费。

美中不足就是始终不支持多继承,但足可满足大多数情况的需要了。

除去上面的几种方式外,这里再补充一种名为寄生组合式的继承方式:

function inheritPrototype(subClass, superClass){
var prototype = Object(superClass.prototype);
prototype.constructor = subClass;
subClass.prototype = prototype;
} function Person(name){
this.name = name;
};
Person.prototype.move = function(){
alert("move");
}; function Man(name){
Person.call(this, name);
};
inheritPrototype(Man, Person);
Man.prototype.showName = function(){
alert("My name is " + this.name + ".");
}; var person = new Person();
console.info(person instanceof Man); // 结果是true,这并非是我们想要的,是此方法的缺点

与上面混合方式相比,都使用了借用构造函数的方式来继承父类属性。

但这里没有把子类的原型指向父类的实例,而是直接把子类的原型指向改写了constructor属性的父类原型

以此来实现了原型链,达到继承的目的。

这种方式的优点是,实现继承只调用了一次父类的构造函数。缺点是,父类的实例使用instanceof操作符判断它是子类的实例时也会返回true。另外,父类的cunstructior属性也跟着指向了子类的构造函数了。显然这并非是我们想要的。

这种方法,实际上也相当于所有类共用同一个原型,试想一下,当出现复杂的继承关系,原型就变得很难维护了(容易出现方法重写覆盖的情况)。

所以,个人还是更推荐用前一种方法,混合式继承。  

实现JS继承的几种方法的更多相关文章

  1. JS继承的6种方法

    1.原型链 基本思想:利用原型让一个引用类型继承另外一个引用类型的属性和方法. 构造函数,原型,实例之间的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原 ...

  2. js继承的几种方法理解和代码演示

    1.属性继承 :call .apply:不建议使用浪费内存. function Person(name,age,sex){ this.name = name; this.age = age; this ...

  3. JS学习笔记——JavaScript继承的6种方法(原型链、借用构造函数、组合、原型式、寄生式、寄生组合式)

    JavaScript继承的6种方法 1,原型链继承 2,借用构造函数继承 3,组合继承(原型+借用构造) 4,原型式继承 5,寄生式继承 6,寄生组合式继承 1.原型链继承. <script t ...

  4. js对象之间的"继承"的五种方法

    今天要介绍的是,对象之间的"继承"的五种方法. 比如,现在有一个"动物"对象的构造函数. function Animal(){ this.species = & ...

  5. js去除空格12种方法

    注:本文非本人原著:原文作者: 黄卉  <js去除空格12种方法> //JS去除空格的方法目前共有12种: //实现1 String.prototype.trim = function() ...

  6. 判断数组的方法/判断JS数据类型的四种方法

    参考文: 以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣Object.prototype.toString.call() . instanceof 以及 Array.isArray() h ...

  7. js继承的几种实现方法

    一.用function实现: function Person(name) { this.name = name; } Person.prototype.getName = function() { r ...

  8. js继承的几种方法和es6继承方法

        一.原型链继     1.基本思想     利用原型链来实现继承,超类的一个实例作为子类的原型     2.具体实现     function F() {}     //原型属性,原型方法: ...

  9. js继承的三种实现

    概念:在有些面向对象语言中,可以使用一个类(子类)继承另一个类(父类),子类可以拥有父类的属性和方法,这个功能可以在js中进行模拟. 三种方法: 第一种:扩展Object方法 Object.proto ...

随机推荐

  1. [LeetCode 题解]:Swap Nodes in Pairs

    前言   [LeetCode 题解]系列传送门:  http://www.cnblogs.com/double-win/category/573499.html   1.题目描述 Given a li ...

  2. RobotFramework的Setup和Teardown

    测试套件级别的Setup会在本套件测试用例集合执行前先执行,同理Teardown会在本组所有用例执行完成后运行 测试用例级别的Setup会在本条测试用例执行前先执行,同理Teardown会在本条用例执 ...

  3. C#图片缩放平移 —— 从功能分析到编码实现

    序 一直都是在看别人的博客,查到想要的,看完后把页面一关就万事大吉了,没啥感觉:直到后来遇到了同样的问题,总想不起来咋弄,关键是还查不到以前看过的,郁闷!现在想想,还是“好记性不如烂笔头”啊,自己弄过 ...

  4. .Net Core配置与自动更新

    .Net Core 将之前Web.Config中的配置迁移到了appsettings.json文件中,并使用ConfigurationBuilder来读取这个配置文件.并可设置在配置文件变化以后,自动 ...

  5. ML.NET Cookbook --- 1.如何从文本文件中加载数据?

    使用ML.NET中的TextLoader扩展方法从文本文件中加载数据.你需要知道在文本文件中数据列在那里,它们的类型是什么,在文本文件中什么位置可以找到它们. 请注意:对于ML.NET只读取文件的某些 ...

  6. Chat Order (map映射)

     Chat Order Time Limit:3000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit ...

  7. CSS3水平翻转样式和background-size兼容问题

    一.水平翻转和垂直翻转:第一种:随着现代浏览器对CSS3的支持愈发完善,对于实现各个浏览器兼容的元素的水平翻转或是垂直翻转效果也就成为了可能.相关的CSS代码如下: /*水平翻转*/ .flipx { ...

  8. redis 3.0 集群__安装

    参考文档 http://redis.io/topics/cluster-tutorial http://redis.io/topics/cluster-spec http://redis.readth ...

  9. 阿里VS华为-开源镜像站体验及评测

    最近对阿里和华为的开源镜像站做了深度体验,并将评测结果分享给大家: 一.评测产品: 华为开源镜像站(https://mirrors.huaweicloud.com/)以下简称 华为 阿里开源镜像站(h ...

  10. win10打开移动热点让手机连接上网教程

    概述: 为什么要这么做呢? 我笔记本插网线可以上网,但是没有买猫盒,所以只能pc开热点,让手机上网. 过程如下: 1开启移动热点,设置密码 1.1开启移动热点,截图如下: 1.2设置热点名称,密码 2 ...