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

【前言】html

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

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

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

  多态:spa

    ->重载:JS严格意义上是没有重载,但能够经过传递不一样参数实现不一样功能prototype

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

 

【一、原型继承】htm

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

<!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__嵌套了一层又一层,以下,这是一条很是长的原型链blog

// #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方法,须要一级级向上查找来使用。

 

【二、call继承】

  -> 把父类私有属性和方法,克隆一份如出一辙的,做为子类私有的属性和方法

function A() { // 一个函数,它有三种角色:一、普通函数(私有做用域);二、类(new);三、普通对象(__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继承,把父类的私有属性和私有方法所有都拿过来了,可是却拿不了父类的公有方法,这是它的缺点,也是优势。

 

【三、冒充对象继承】

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

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继承只是把父类的私有拿过来变成本身私有的,可是“冒充对象继承”则是把父类的私有+公有的属性和方法拿过来变成本身私有的。

 

【四、混合模式继承】

  ->原型继承 + 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把父类私有的,复制给了子类私有的;第二遍就是使用原型继承,把父类私有+公有的属性,给了子类公有的

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

 

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

  ->目的:子类继承父类,父类私有的子类就继承私有的,父类公有的子类就继承公有的(注意,是在__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--

相关文章
相关标签/搜索