JS继承

JS继承的几种实现方式

继承是指子类继承父类的属性和方法,要实现继承,首先我们需要有一个父类

function Person (name){
        //属性
        this.name = name || 'person';
        this.firends = ['James','Curry'];
        //方法
        this.speak = function (){
            console.log('I can speak chinese!')
        }
 }
//原型方法
Person.prototype.skill= function (){
    console.log('running')
}
var person = new Person;
person.speak();     //"speak"
person.skill();     //"running"

原型链继承

原型链继承的本质就是就是将父类的实例作为子类的原型对象

function Chinese() {

}
//继承Person
Chinese.prototype = new Person;
Chinese.name = "lianqi";
//添加新方法
Chinese.prototype.speakChinese = function (){
    console.log('speak chinese')
}
//重写父类中的方法
Chinese.prototype.skill = function (){
    console.log('jump')
}
console.log(chinese.name);      //"lianqi"
chinese.speak();        //"speak"
chinese.speakChinese();        //"speak chinese"
chinese.skill();        //"jump"
console.log(chinese instanceof Chinese);        //true
console.log(chinese instanceof Person);     //true

子类有时需要重写父类中某个方法,或者需要添加子类中不存在的方法。不管怎么样给原型添方法的代码,一定要放在替换原型的语句之后。
还有一点需要注意,再通过原型链来实现继承时不能使用对象字面量创建原型方法,因为这样做会重写原型链,就不能调用父类的方法。如下面的例子

function Chinese() {

}
Chinese.prototype = {
    eat : function (){
        console.log('eat food')
    }

}
var chinese = new Chinese();
console.log(chinese instanceof Chinese);        //true
console.log(chinese instanceof Person);     //false
chinese.eat();        //"eat food"
chinese.speak();        //报错"chinese.speak is not a function"

原型链虽然很强大,可以用它来实现继承,但也存在一些问题:
1.重写子类的原型 等于 父类的一个实例,(父类的实例属相变成子类的原型属性)如果父类包含引用类型的属性,那么子类所有实例都会共享该属性(包含引用类型的原型属性会被实例共享)
2.在创建子类实例时,不能向父类的构造函数传递参数

借用构造函数继承

借用构造函数继承的基本思想相当简单,即在子类构造函数的内部使用call()或apply()方法调用父类构造函数,这样子类的实例就都会具有属于自己的引用类型的属性,如下代码:

function Chinese(name) {
    //使用call继承Person
    Person.call(this,name);
}
var chinese = new Chinese('lianqi');
console.log(chinese.name);       //"lianqi"
chinese.firends.push('Irving');
console.log(chinese.firends);       // ["James", "Curry", "Irving"]
var chinese01 = new Chinese('Kobe');
console.log(chinese01.firends);     // ["James", "Curry"]

借用构造函数继承的模式,解决了原型链继承中子类共享父类中引用类型属性的问题,而且创建子类实例时可以向父类的构造参数传递参数,还可以实现多继承(call多个父类对象)
借用构造函数继承的方式也存在问题:
1.方法都在构造函数中定义,因此函数复用就无从谈起了
2.子类实例只能继承父类的实例(私有)属性和方法,在父类原型中定义的方法,对子类是不可见的,因此不能继承父类的原型(公有)属性和方法

拷贝继承

拷贝继承的私有属性通过call来继承,公有属性通过函数extend()来继承,拷贝继承实现了子类实例对父类的私有属性和公有属性的继承。

function extend(newObj,oldObj){
    for (var attr in oldObj){
        newObj[attr] = oldObj[attr]
    }
}
function Chinese(){
    //子类继承父类的私有属性和方法
    Person.call(this)
}
//子类继承父类的公有属性和方法
extend(Chinese.prototype,Person.prototype);
var chinese = new Chinese('liaqi');
chinese.firends.push('Irving');
console.log(chinese.firends);        // ["James", "Curry", "Irving"]
chinese.skill();        //"running"

拷贝继承也有它存在的问题:
1.效率较低,内存占用高(因为要拷贝父类的属性)
2.无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)

组合继承

组合继承的原理是通过原型链实现对公有属性和方法的继承,通过借用构造函数来实现对私有属性和方法的继承。

    function Chinese(name) {
        //继承私有属性和方法
        Person.call(this,name);    //第二次调用父类Person()
    }
    //继承公有属性和方法
    Chinese.prototype = new Person();      //第一次调用父类Person()
    //手动设置子类的constructor
    Chinese.prototype.constructor = Chinese;
    Chinese.prototype.speakChinese = function (){
        console.log('speak chinese')
    };

    var chinese = new Chinese();
    chinese.firends.push('Irving');
    chinese.speak();     //"speak"
    chinese.speakChinese();     //"speak chinese"
    console.log(chinese.firends);       //["James", "Curry", "Irving"]

    var chinese01 = new Chinese();
    chinese.speak();     //"speak"
    console.log(chinese01.firends);      //["James", "Curry"]

组合继承可以向父类构造函数传参,避免了子类实例共享父类的引用类型的属性,每个子类实例还可以使用相同的方法。组合继承避免了原型链和借用构造函数的缺点,融合了他们的优点
组合继承的缺点是调用了两次父类构造函数

寄生式继承

寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,然后返回这个对象。

function createPerson(original) {
    //创建一个新对象
    var obj = Object.create(original);
    //给对象添加方法
    obj.speakChinese = function (){
        console.log('speak chinese')
    }
    //返回对象
    return obj
}
var person = {
    name: 'lianqi',
    friends: ['James','Curry']
}
var chinese = createPerson(person);
chinese.speakChinese();     //"speak chinese"

createPerson()函数将接受的参数作为新对象的基础返回新对象,新对象不仅拥有了person的属性和方法,还有了自己的方法speakChinese()。在主要考虑对象而不是自定义类型活构造函数的情况下,寄生式继承是一种有用的模式。

寄生组合式继承

寄生组合式继承不会初始化两次调用父类构造函数,避免了组合继承的缺点,通过把父类原型寄生在一个新类的原型上,再赋值给子类原型。这样做的高效体现在只调用了一次父类构造函数,并且因此避免了在子类原型上创建不必要的、多余的属性

//创建子类
function Chinese(){
    Person.call(this)
}

//创建一个新类,新类没有任何属性和方法
function Temp() {

}
//父类的原型属性和方法寄生在新类的原型
Temp.prototype = Person.prototype;
//再把新类的原型属性和方法赋值给子类的原型,新类就相当于一个临时中转站,这样就避免了调用创建子类实例调用两次父类构造函数
Chinese.prototype = Temp.prototype;
//为子类原型添加constructors属性,从而弥补因重写原型而失去的默认constructor属性
Chinese.prototype.constructor = Chinese;
var chinese = new Chinese();
chinese.speak();    //"speak"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,183评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,850评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,766评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,854评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,871评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,457评论 1 311
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,999评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,914评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,465评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,543评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,675评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,354评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,029评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,514评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,616评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,091评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,685评论 2 360

推荐阅读更多精彩内容

  • 继承的概念:子类可以使用父类共享的属性和方法,避免重复代码提高代码复用性。 原型链:子类可以共享父类的实例对象和实...
    浅秋_6672阅读 406评论 0 0
  • 原文链接 js的继承有6种方式,大致总结一下它们各自的优缺点,以及它们之间的关系。 1.原型链 js的继承机制不同...
    空_城__阅读 796评论 0 11
  • JS 实现继承的方法有:1.原型链继承2.构造函数继承3.组合继承(原型链继承 + 构造函数继承)4.原型式继承(...
    yun_154192阅读 725评论 0 0
  • 一、原型链 学过java的同学应该都知道,继承是java的重要特点之一,许多面向对象的语言都支持两种继承方式:接口...
    grain先森阅读 1,423评论 0 39
  • 继承6种套餐 参照红皮书,JS继承一共6种 1.原型链继承 核心思想:子类的原型指向父类的一个实例 Son.pro...
    灯不梨喵阅读 3,145评论 1 2