JS 中的继承

推荐文章:一篇文章理解JS继承——原型链/构造函数/组合/原型式/寄生式/寄生组合/Class extends

继承方式的分类

原型链继承

  • 重点:子的原型对象为new一个父的实例 Child.prototype = new Parent();
  • 缺点:多个实例对引用类型属性的操作会被篡改;在创建Child实例时,不能向 Parent 传参
function Animal() {}
function Dog(){}  
Dog.prototype = new Animal();

引用类型的属性被所有实例共享的栗子

function Parent () {
    this.names = ['kevin', 'daisy'];
}
function Child () {
}
Child.prototype = new Parent();

var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]

var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy", "yayu"]

构造函数继承

  • 重点:在子构造函数内部调用父构造函数 Parent.call(this)
  • 缺点:方法都在构造函数中定义,每次创建实例都会创建一遍方法
function Cat(name) {            
    Animal.call(this, name);   // 核心,把父类的实例方法属性指向子类
}

组合继承(原型链继承和构造函数继承组合)

  • 重点:使用原型链继承共享的属性和方法,通过借用构造函数继承实例属性
  • 优点:
    1、创建子类实例,可以向父类构造函数传参数;
    2、父类的实例方法定义在父类的原型对象上,可以实现方法复用;
    3、不共享父类的构造方法及属性;
  • 缺点:无论在什么情况都会调用两次父构造函数,一次是创建子类型原型,另一次是在子构造函数内部
function Cat(name) {
    // 核心,把父类的实例方法属性指向子类。即,继承属性 。                 
    Animal.call(this, name);                          
}
// 核心, 父类的实例作为子类的原型对象,即继承方法
Cat.prototype = new Animal()
 // 修复子类Cat的构造器指向,防止原型链的混乱                                
Cat.prototype.constructor = Cat;                           

原型式继承

  • 重点:将源对象赋给目标对象的原型,原型式继承的object方法本质上是对参数对象的一个浅复制。
  • 优点:父类方法可以复用
  • 缺点:和原型链继承一样,父类的引用属性会被所有子类实例共享、子类构建实例时不能向父类传递参数。

实现方式 1:

    function object(o) {
        // 创建临时新的构造函数
        function F() { }
        // 将传入的这个对象作为这个构造函数的原型
        F.prototype = o;
        // 返回这个临时类型的一个新的实例
        return new F();
    }

    let person = {
        name: "可莉",
        friends: ["七七", "雷泽"]
    }

    let anotherPerson = object(person);
    anotherPerson.name = "七七";
    anotherPerson.friends.push("可莉");

    console.log(person);
    //{name: "可莉", friends: ["七七", "雷泽", "可莉"]}
    console.log(anotherPerson);
    //{name: "七七", __proto__: person}

实现方法 2:
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。规范了原型式继承。
方法接收两个参数,一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象,这第二个参数和 Object.defineProperties() 方法的第二个参数格式相同。

    let anotherPerson = Object.create(person, {
        name: {
            value: "琴团长"
        }
    });
    console.log(anotherPerson);
    //{name: "琴团长", __proto__: person}

寄生式继承

  • 重点:就在原型式继承的基础上,给浅复制对象增加方法,增强了这个浅复制对象的能力。
    function createOther(original) {
        let clone = Object.create(original);
        // 以某种方式增强这个对象
        clone.say = function() {
            console.log("sayHi");
        } 
        return clone;
    }

寄生组合继承

function Cat(name) {
     // 核心,把父类的实例方法属性指向子类;                     
    Animal.call(this, this.name);                         
}
// 核心,利用空对象作为中介;
var F = function(){};
 // 核心,将父类的原型赋值给空对象F;                                       
F.prototype = Animal.prototype; 
 // 核心,将F的实例赋值给子类;                           
Cat.prototype = new F(); 
// 修复子类Cat的构造器指向,防止原型链的混乱;                                 
Cat.prototype.constructor = Cat;                            
    function Father(name) {
        this.name = name;
    }

    Father.prototype.sayName = function() {
        console.log(this.name);
    }

    function inheritPrototype(f_constructor, c_constructor) {
        // 创建父类原型的浅复制,(原型式继承) 
        let prototype = Object.create(f_constructor.prototype);
        // 修正原型上构造函数的指向
        prototype.constructor = child;
        // 将子类的原型替换为这个原型
        c_constructor.prototype = prototype;
        
        
    }

    // 继承父类上的属性, (构造函数继承)
    function Child(name, age) {
        Father.call(this, name);
        this.age = age;
    }

    inheritPrototype(Father, Child);

ES6 class extends关键字继承

  • 重点:子类必须在 constructor 方法中调用 super 方法,其次就算你子类不写 constructor 方法,但是编译的时候还是会默认给你加上。
  • 和 ES5 其他各种思路的区别:
    其实上面各种形式,但凡只要使用到了构造函数的继承,都是子类的实例 (this) 调用父类的构造函数。但是 ES6 的 extends 的核心概念就是父类实例对象的属性和方法,加到this上面,然后再用子类的构造函数修改 this
class Animal {
    constructor(name) {
        this.name = name;
    }

    sayName() {
        console.log(this.name)
    }
}

class Dog extends Animal {
    constructor(name, age) {
        // 这个 super 就相当于调用父类的构造函数,调用完成之后就得到了与父类属性和方法
        // 否则如果不调用super子类就得不到this对象
        // 简而言之,调用super就能通过父类的构造函数创建子类的 this 对象
        super(name);
        this.age = age;
    }

    wangwang() {
        console.log("wangwang~~~~");
    }
}

const dog1 = new Dog("teddy", 18);

dog1.sayName();
dog1.wangwang();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • ES5继承 构造函数、原型和实例的关系:每一个构造函数(函数对象)都有一个prototype属性,指向函数的原型对...
    江平路阅读 227评论 0 0
  • 面向对象的语言支持两种继承方式,接口继承和实现继承js无法实现接口继承,只支持实现继承,主要通过原型链来实现。具体...
    jadefan阅读 120评论 0 2
  • 原型式继承 原型式继承和之前的继承方式不太相同,原理上相当于对对象进行一次浅拷贝,父对象中属性如果是引用类型的值,...
    火星的天秤座阅读 332评论 0 1
  • 前言 ES6之前,没有严格意义上的class继承, 其实JS(ES5)主要是依靠原型链来实现继承的。 既然要实现继...
    Mica_马超阅读 965评论 0 2
  • 1.1 原型链继承 原型继承: 将子类B的原型对象 重写成父类A的一个实例。 B.prototype = new ...
    九四年的风阅读 585评论 0 0