【前言】

  我们都知道,面向对象(类)的三大特征:封装、继承、多态

  继承:子类继承父类的私有属性和公有方法

  封装:把相同的代码写在一个函数中

  多态:

    ->重载:JS严格意义上是没有重载,但可以通过传递不同参数实现不同功能

    ->重写:子类重写父类的方法(这里只要把父类的原型一改,父类的其他实例会受到影响,又因为子类的原型链继承父类的实例,这就会导致同样会影响到子类的实例,本质是因为在JS原型继承中,由于它的核心原理,继承并不是从父类中拿过一份一模一样的东西拷贝过来,而是让子类和父类之间增加了一个原型链这样一个桥梁)

【1、原型继承】

  什么是原型继承,下面是一个非常常见的例子:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="#div1"></div>
</body>
<script>
console.dir(document.getElementById('div1'))
</script>
</html>

  当你展开id="div1"这个dom节点的时候,你会看到,它的__proto__嵌套了一层又一层,如下,这是一条非常长的原型链

// #div1.__proto__ -> HTMLDivElement.prototype -> HTMLElement.prototype -> Element.prototype
// -> Node.prototype -> EventTarget.prototype -> Object.prototype

  上面的原型链非常长,但是是如何将它们一级一级关联起来的呢?实现原理如下:

// 第四层 Object

// 第三层 myObject
function myObject() { }
myObject.prototype = {
constructor: myObject,
hasOwnProperty: function () {}
}; // 第二层 myEventTraget
function myEventTraget() { }
myEventTraget.prototype = new myObject(); // 子类的原型等于父类的实例
myEventTraget.prototype.constructor = myEventTraget;
myEventTraget.prototype.addEventListener = function () {}; // 第一层 myNode
function myNode() { }
myNode.prototype = new myEventTraget(); // 子类的原型等于父类的实例
myNode.prototype.constructor = myNode;
myNode.prototype.createElment = function () {}; // 实例化
var n = new myNode; // 打印出来,看看n的结果,已经有四层__proto__了。

  实例n 打印出来的样子如下:

  

  上面的多层继承,是不是看起来特别想dom的原型继承,一层套一层,下面来个简化版的。

// 原型继承简化
function A() {
this.x = 100;
}
A.prototype.getX = function () {
console.log(this.x)
};
function B() {
this.y = 200;
}
// 现在,B想继承A的私有+公有的属性和方法
B.prototype = new A;
B.prototype.constructor = B;
var b = new B;

  上面简化版的原型继承原理,可以参考下图:

  原型继承总结:

    “原型继承”是JS最常见的一种继承方式

    子类B想要继承父类A中的所有属性和方法(私有+公有),只需要B.prototype = new A即可

    特点:它把父类中私有+公有的都继承到子类原型上(即子类公有的)

    核心:原型继承并不是把父类中的属性和方法克隆一份一模一样的给B,而是让B和A之间增加原型链的连接,以后实例b想要A中的getX方法,需要一级级向上查找来使用。

【2、call继承】

  -> 把父类私有属性和方法,克隆一份一模一样的,作为子类私有的属性和方法

function A() { // 一个函数,它有三种角色:1、普通函数(私有作用域);2、类(new);3、普通对象(__proto__)
this.x = 100;
this.a = function () {
console.log(this.x);
}
}
A.prototype.getX = function () {
console.log(this.x);
};
function B() {
this.y = 100;
// this->b
A.call(this);// ->A.call(b) 把A执行,让A中的this变为了n!!!
//此时的A.prototype对B类来说是没用的,因为B没有继承A
}
var b = new B;
console.log(b.x); // ->100
b.a(); // ->100
b.getX();// ->b.getX is not a function

  值得注意的是,b.getX() // -> b.getX is not a function。为什么会这样呢,因为b实际上并没有继承A类,所以A.prototype对B是没有任何作用的,此时的A,实际上只是作为函数,而不是作为一个类!!!

  总结call继承

    ->call继承,把父类的私有属性和私有方法全部都拿过来了,但是却拿不了父类的公有方法,这是它的缺点,也是优点。

【3、冒充对象继承】

  ->把父类私有的+公有的属性和方法克隆一份一模一样的,给子类私有的

function A() {
this.x = 100;
}
A.prototype.getX = function () {
console.log(this.x);
};
function B() {
// -> this->b
var temp = new A; // 将A的实例当做普通对象来做遍历,这就是冒充对象
for (var key in temp) {// 把父类A私有的和公有的,都复制过来给子类B私有的
this[key] = temp[key];
}
temp = null;
}
var b = new B;
b.getX(); // ->x、getX

  注意:for in的写法,可以把对象公用和私有的属性和方法,全部都打印出来;

     obj.propertyIsEnumerable(key),此方法可以判断对象的私有属性

     obj .hasOwnProperty(key),此方法同样也可以判断对象的私有属性

  总结“冒充对象继承”:

    ->这个继承方法比call跟完善了一步,call继承只是把父类的私有拿过来变成自己私有的,但是“冒充对象继承”则是把父类的私有+公有的属性和方法拿过来变成自己私有的。

【4、混合模式继承】

  ->原型继承 + call继承

function A() {
tihs.x = 100;
}
A.prototype.getX = function () {
console.log(this.x)
};
function B() {
A.call(this); // ->这一步,即等于: x=100
}
B.prototype = new A; // ->这一步,即等于:B.prototype: x=100 getX=....
B.prototype.constructor = B;
var b = new B;
b.getX();

  总结:

    ->这种方法,缺点是将A这个类,执行了两次

    ->首先父类私有的复制了两遍,第一遍是用call把父类私有的,复制给了子类私有的;第二遍就是使用原型继承,把父类私有+公有的属性,给了子类公有的

    ->也就是说重复了父类私有的存在于子类私有上, 也存在于子类公有上。也就是说,重复了一次父类私有的复制。

【5、寄生组合式继承】(强烈推荐)

  ->目的:子类继承父类,父类私有的子类就继承私有的,父类公有的子类就继承公有的(注意,是在__proto__又套了一层原型)

// 寄生组合式继承
function A() {
this.x = 100;
}
A.prototype.getX = function () {
console.log(this.x);
};
function B() {
// ->this->b
A.call(this);
}
// B.prototype = Object.create(A.prototype); // 意思是把父类的原型,给了子类的原型
// Object.create创建了一个对象,并且把这个新对象的原型指向了a的原型,然后B的原型指向了这个新对象
B.prototype = objectCreate(A.prototype);
B.prototype.constructor = B;
var b = new B;
console.log(b); // Object.create的原理如下
function objectCreate(o) {
function Fn() {}
Fn.prototype=o;
return new Fn;
}

    注意,这种方式跟原型继承是有区别的,

    ->原型继承:是把父类私有+公有的属性给了子类的公有上

    ->寄生组合式继承:

      继承私有:A.call(this);  

      继承公有:先把父类私有的清空(这里的清空可以新建一个新对象,然后新对象的原型指向父类的原型即可),然后子类的原型指向该新对象

    ->比较绕,可以看看下图,即寄生组合式继承原理图

  总结“寄生式继承”

    ->其实是比较完美地实现了继承,子类继承父类,父类私有的属性就放在子类私有上,父类公有的属性就放在子类公有上。

--END--

【设计模式+原型理解】第三章:javascript五种继承父类方式的更多相关文章

  1. 【设计模式+原型理解】第一章:使用Javascript来巧妙实现经典的设计模式

    刚开始学习设计模式之前,我是没想说要学习设计模式的,我只是因为想学习JS中的原型prototype知识,一开始我想JS中为什么要存在原型这个东西?于是慢慢通过原型而接触到设计模式,后来发现我这个过程是 ...

  2. JavaScript五种继承方式详解

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

  3. JavaScript 五种(非构造方式)继承

    参考链接:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance_continued.html

  4. 第三章 JavaScript操作BOM对象

    第三章   JavaScript操作BOM对象 一.window对象 浏览器对象模型(BOM)是javascript的组成之一,它提供了独立与浏览器窗口进行交换的对象,使用浏览器对象模型可以实现与HT ...

  5. 【软件构造】第三章第五节 ADT和OOP中的等价性

    第三章第五节 ADT和OOP中的等价性 在很多场景下,需要判定两个对象是否 “相等”,例如:判断某个Collection 中是否包含特定元素. ==和equals()有和区别?如何为自定义 ADT正确 ...

  6. JavaScript之四种继承方式讲解

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

  7. Android五种数据存储方式

    android 五种数据存储 :SharePreferences.SQLite.Contert Provider.File.网络存储 Android系统提供了四种存储数据方式.分别为:SharePre ...

  8. Javascript中实现继承的方式

    js中实现继承和传统的面向对象语言中有所不同:传统的面向对象语言的继承由类来实现,而在js中,是通过构造原型来实现的,原型与如下几个术语有关: ①构造函数:在构造函数内部拥有一个prototype属性 ...

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

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

随机推荐

  1. BZOJ_3514_Codechef MARCH14 GERALD07加强版_主席树+LCT

    BZOJ_3514_Codechef MARCH14 GERALD07加强版_主席树+LCT Description N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. I ...

  2. 使用limit查询的同时取得总的记录数:SQL_CALC_FOUND_ROWS和FOUND_ROWS()

    通常我们都用如下的sql来进行列表 SELECT COUNT(*) FROM users WHERE name LIKE 'a%';SELECT name, email FROM users WHER ...

  3. 一副美丽而庞大的SQL TUNING计划美图

    最近因为很忙,一直忙着开发公司的产品和客户的事情,杂七杂八,忙的不亦乐乎,所以,好久没有来这里写东西,这两天为客户优化了一个系统,优化前CPU持续冲顶爆满,导致系统卡死,应用系统不能正常使用,其中有个 ...

  4. 重磅!!!微软发布ASP.NET Core 2.2,先睹为快。

    我很高兴地宣布ASP.NET Core 2.2现在作为.NET Core 2.2的一部分提供! 如何获取? 您可以从.NET Core 2.2下载页面下载适用于您的开发机器和构建服务器的新.NET C ...

  5. 一文学会Scala

    整体介绍 Scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性. 联邦理工学院洛桑(EPFL)的Martin Odersky于2001 ...

  6. 企业私有源代码上传github致入侵之大疆案判决了

    事件简单回顾: 1.2017年8月28日,大疆宣布“大疆威胁识别奖励计划”,最高3万美元: 2.然而在此之前,大疆农业事业部某员工将企业私有源代码上传到了github: 3.就职于大疆竞对公司Depa ...

  7. Android开发:文本控件详解——EditText(一)基本属性

    一.简单实例: EditText输入的文字样式部分的属性,基本都是和TextView中的属性一样. 除此之外,EditText还有自己独有的属性. 二.基本属性: hint  输入框显示的提示文本  ...

  8. org.springframework.cache.interceptor.SimpleKey cannot be cast to java.lang.String

    springboot整合redis时,使用@Cacheable注解,如果方法的key参数为空,就会报org.springframework.cache.interceptor.SimpleKey ca ...

  9. 轻量级.Net Core服务注册工具CodeDi发布啦

    为什么做这么一个工具 因为我们的系统往往时面向接口编程的,所以在开发Asp .net core项目的时候,一定会有大量大接口及其对应的实现要在ConfigureService注册到ServiceCol ...

  10. .net mvc + layui做图片上传(一)

    图片上传和展示是互联网应用中比较常见的一个功能,最近做的一个门户网站项目就有多个需要上传图片的功能模块.关于这部分内容,本来功能不复杂,但后面做起来却还是出现了一些波折.因为缺乏经验,对几种图片上传的 ...