js的继承方式

1 类式继承 子类的原型对象
// 继承的第一种方式,类式继承
function SuperClass(){
    this.name = 'demo'
    this.books = ['js','java','html','css']
    this.superValue = true
}
// 父类方法
SuperClass.prototype.getSuperValue = function(){
    return this.superValue
}
// 声明子类 
function SubClass(){
    this.subValue = false
}
// 继承父类
SubClass.prototype = new SuperClass()
SubClass.prototype.getSubValue = function () {
    return this.subValue
}
var instance = new SubClass();
var instance2 = new SubClass();

console.log(instance.getSuperValue()) // 输出 true
console.log(instance.getSubValue()) // 输出 false
console.log(instance instanceof SuperClass) // 输出 true
console.log(instance instanceof SubClass) // 输出 true
console.log(SubClass instanceof SuperClass) // 输出 true
console.log(instance2.books) // ['js','java','html','css']
instance.books.push('linux')
instance.name = 'jack'
console.log(instance.name) // jack
console.log(instance2.books) // ['js','java','html','css','linux']
console.log(instance2.name) // demo
// 类式继承,即子类的原型是父类的一个实例,

// 特点:
// 实例可继承的属性有:实例的构造函数的属性,
// 父类构造函数属性,父类原型的属性。(新实例不会继承父类实例的属性!)

// 缺点1 
// 由于子类通过其原型prototype对父类实例化,继承了父类。
// 所以父类中的共有属性要是引用类型,就会在子类中被所有实例共用,
// 因此一个子类的实例更改子类原型从父类构造函数中继承来的共有属性
// 就会直接影响到其他子类

// 缺点2:
// 由于子类实现的继承是靠其原型prototype对父类的实例化实现的,
// 因此在创建父类的时候,是无法向父类传递参数的,因而在实例化父类的时候也
// 无法对父类构造函数内的属性进行初始化

2 构造函数继承 创建即继承
// 构造函数继承
// 声明父类
function SuperClass(id){
    this.books = ['js','html','css']
    this.id = id
}
// 声明父类原型方法
SuperClass.prototype.showBooks = function (){
    console.log(this.books)
}
// 子类
function SubClass(id){
    SuperClass.call(this,id)
    // SuperClass.apply(this,[id])
}

var instance = new SubClass(10)
var instance2 = new SubClass(11)

instance.books.push('linux')
console.log(instance.books) //  ['js','html','css','linux']
console.log(instance.id) // 10
console.log(instance2.books)  // ['js','html','css']
console.log(instance2.id) // 11

instance.showBooks() // 报错

// call,apply 方法可以改变函数的作用环境,不同的是参数传入方式不一样。
// 在子类中调用call,apply方法就是将子类中的变量在父类中执行一遍,由于父类中
// 是给this绑定属性的,因此子类自然也就继承了父类的共有属性。由于这种类型的继承
// 没有涉及到原型prototype,所以父类的原型方法不会被子类继承

重点:用.call()和.apply()将父类构造函数引入子类函数
    (在子类函数中做了父类函数的自执行(复制))
特点:1、只继承了父类构造函数的属性,没有继承父类原型的属性。
    2、解决了原型链继承缺点1、2、3。
    3、可以继承多个构造函数属性(call多个)。
    4、在子实例中可向父实例传参。
缺点:1、只能继承父类构造函数的属性。
    2、无法实现构造函数的复用。(每次用每次都要重新调用)
    3、每个新实例都有父类构造函数的副本,臃肿。

3 组合继承 (类式继承和构造函数继承的组合)
// 组合继承
// 声明父类
function SuperClass(id){
    this.books = ['js','html','css']
    this.id = id
}
// 声明父类原型方法
SuperClass.prototype.showBooks = function (){
    console.log(this.books)
}
// 子类
function SubClass(id,name){
SuperClass.call(this,id)
    // SuperClass.apply(this,[id])
    this.name = name
}
// 类式继承,子类原型继承父类
SubClass.prototype = new SuperClass()
// 子类原型方法
SubClass.prototype.getName = function() {
    console.log(this.name)
}

var instance = new SubClass(1,'js')
instance.books.push('设置')
instance.getName() // js
instance.showBooks() // ["js", "html", "css", "设置"]

var instance2 = new SubClass(2,'hello')
instance2.getName() // hello 
instance2.showBooks() // ["js", "html", "css"]

重点:结合了两种模式的优点,传参和复用
特点:1、可以继承父类原型上的属性,可以传参,可复用。
    2、每个新实例引入的构造函数属性是私有的。
缺点:调用了两次父类构造函数(耗内存),
    子类的构造函数会代替原型上的那个父类构造函数。
4 原型式继承 (类式继承的封装)
 // 原型式继承
 // 原型是继承,类式继承的封装
 function inheritObject(o){
     // 声明一个过渡函数对象
     function F(){}
     // 过渡对象的原型继承父对象
     F.prototype = o
     // 返回过渡对象的一个实例
     return new F()
 }
 var book = {   
     name: 'js book',
     alikebook: ['css book','html book']
 }
 var newBook = inheritObject(book)
 newBook.name = 'ajax book'
 newBook.alikebook.push('xml book')

var otherBook = inheritObject(book)
otherBook.name = 'flash book'
otherBook.alikebook.push('as book')

console.log(newBook.name)
console.log(newBook.alikebook)

console.log(otherBook.name)
console.log(otherBook.alikebook)

console.log(book.name)
console.log(book.alikebook)

//重点:用一个函数包装一个对象,然后返回这个函数的调用,
//这个函数就变成了个可以随意增添属性的实例或对象。
//object.create()就是这个原理。
//特点:类似于复制一个对象,用函数来包装。
//缺点:1、所有实例都会继承原型上的属性。
//    2、无法实现复用。(新实例属性都是后面添加的)
5 寄生式继承
// 寄生式继承 即原型式继承的封装
function inheritObject(o){
    // 声明一个过渡函数对象
    function F(){}
    // 过渡对象的原型继承父对象
    F.prototype = o
    // 返回过渡对象的一个实例
    return new F()
}
var book = {
    name: 'js book',
    alikebook: ['css book','html book']
}
function createBook(obj){
    var o = inheritObject(obj)
    o.getName = function(){
        console.log(this.name)
    }
    return o
}

var newbook = createBook(book)
newbook.name = 'ajax book'
console.log(newbook.name)
newbook.getName()
newbook.alikebook.push('linux book')
console.log(newbook.alikebook)

var newbook2 = createBook(book)
newbook2.name = 'demo book'
console.log(newbook2.name)
newbook2.getName()
newbook2.alikebook.push('java book')
console.log(newbook2.alikebook)


重点:就是给原型式继承外面套了个壳子。
优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),
     这个函数顺理成章就成了创建的新对象。
缺点:没用到原型,无法复用。

6 寄生组合式继承
// 寄生组合式继承
// 原型式继承
function inheritObject(obj){
    function F(){}
    F.prototype = obj
    return new F()
}
// 寄生式继承-->继承原型
function inheritPrototype(sub, father){
    // 复制一份父类的原型副本保存在变量中
    var p = inheritObject(father.prototype)
    // 修正因为重写子类原型导致子类的constructor属性被修改
    p.constructor = sub
    // 设置子类的原型
    sub.prototype = p
}
// 定义父类
function superClasss(name){
    this.name = name
    this.colors = ['red','blue','green']
}
// 父类原型
superClasss.prototype.getName = function(){
    console.log(this.name)
}
// 定义子类
function subClass(name,id){
    superClasss.call(this,name)
    this.id = id
}
// 寄生式继承父类原型
inheritPrototype(subClass,superClasss)
// 子类新增原型方法
subClass.prototype.getId = function(){
    console.log(this.id)
}

var instance = new subClass('demo',12)
var instance2 = new subClass('demo2',2020)
instance.colors.push('pink')
console.log(instance.colors)['red','blue','green','pink']
console.log(instance2.colors)['red','blue','green']
console.log(instance instanceof subClass) //true
console.log(instance instanceof superClasss) // true
console.log(subClass instanceof superClasss) // false
console.log(subClass.prototype instanceof superClasss)// true

// 组合继承中,通过构造函数继承属性和方法是没有问题的。
// 然而在对父类原型的继承时,我们只需要父类的原型不需要再执行一次构造函数。
// 为了修复组合继承的问题,因此寄生组合式继承的最大改变就是对子类原型的处理。

// 父类原型对象的副本,不可以直接赋值给子类。因为对父类原型对象复制
// 得到的复制对象p中的constructor指向的不是subClass子类对象,因此
// 寄生式继承中要对复制对象p做一次增强,修复其constructor属性指向不正确的问题,
// 最后将得到的复制对象p赋值给子类原型,这样子类的原型就继承了父类的原型并且没有
// 执行父类的构造函数

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