资讯专栏INFORMATION COLUMN

JS 继承的实现

张红新 / 594人阅读

摘要:构造函数继承遍历通过对象冒充实现继承,实际上是在构造函数中,通过获取父类中的所有属性,并保存到自身对象中,这样则可以调用父类的属性和方法了。

JS从诞生之初本就不是面向对象的语言。

如何在JS中实现继承,总结而言会有四种写法。

构造函数继承
function Animal(name) {
    this.name = name
        
    this.sayName = function() {
        console.log(this.name)
    }
}
    
function Dog(name, hobby) {
    // 遍历
    let ani = new Animal(name)
    for(let p in ani) {
        if (ani.hasOwnProperty(p)) {
            this[p] = ani[p]
        }
    }
        
    this.hobby = hobby
}
    
let dog1 = new Dog("xiaohei", "bone")
let dog2 = new Dog("fofo", "bone and fish")
console.log(dog1.sayName()) // xiaohei
console.log(dog2.sayName()) // fofo

通过对象冒充实现继承,实际上是在构造函数中,通过获取父类中的所有属性,并保存到自身对象中,这样则可以调用父类的属性和方法了。这里forin的方式遍历父类属性,因为forin会遍历公开的属性和方法,所以通过hasOwnProperty控制写入当前对象的范围。否则则会将所有属性全部变为私有属性。

这样做有一个缺点就是,无法访问父类中的公开方法和属性(prototype中的方法)

Animal.prototype.sayHobby = function() {
    console.log(this.hobby)
}
dog1.sayHobby() // VM2748:1 Uncaught TypeError: dog1.sayHobby is not a function at :1:6

代码优化

在子类中,既然是需要获取父类的私有属性,则可以使用callapply,当调用父类的方法的时候,改变当前上下文为子类对象,则子类对象就可以获取到了父类的所有私有属性。

function Animal(name) {
    this.name = name
        
    this.sayName = function() {
        console.log(this.name)
    }
}
    
function Dog(name, hobby) {
    // 更改构造函数的上下文
    Animal.call(this, name)
    
    this.hobby = hobby
}
    
let dog1 = new Dog("xiaohei", "bone")
let dog2 = new Dog("fofo", "bone and fish")
console.log(dog1.sayName()) // xiaohei
console.log(dog2.sayName()) // fofo
类式继承
function Animal(name) {
    this.name = name || "animal"
    this.types = ["cat", "dog"]
    
    this.sayTypes = function() {
        console.log(this.types.join("-"))
    }
}
Animal.prototype.sayName = function() {
    console.log(this.name)
}

function Dog(name) {
    this.name = name    
}
Dog.prototype = new Animal("animal")

let dog1 = new Dog("xiaohei")
dog1.sayName() // xiaohei

let dog2 = new Dog("feifei")
dog2.sayName() // feifei

这种继承方式是通过对子类的prototype.__proto__引用父类的prototype,从而可以让子类访问父类中的私有方法和公有方法。详情可以查看关键字new的实现。

类式继承会有两方面的缺点

引用陷阱-子类对象可以随意修改父类中的方法和变量,并影响其他子类对象

dog1.types.push("fish")
console.log(dog1.types) // ["cat", "dog", "fish"]
console.log(dog2.types) // ["cat", "dog", "fish"]

无法初始化构造不同的实例属性

这个主要是由于类式继承,是通过Dog.prototype = new Animal("animal")实现的,我们只会调用一次父类的构造函数。所以只能在子类中从写父类的属性,如上的name属性,在子类中需要重写一次。

组合继承

组合继承,即结合以上两种继承方式的优点,抛弃两者的缺点,而实现的一种组合方式

function Animal(name) {
    this.name = name
    this.types = ["dog", "cat"]
}
Animal.prototype.sayName = function() {
    console.log(this.name)
}

function Dog(name, hobby) {
    // 获取私有方法并调用父类的构造函数,并传递构造函数的参数,实现初始化不同的构造函数
    Animal.call(this, name)
    this.hobby = hobby
}
// 子类实例可以访问父类prototype的方法和属性
Dog.prototype = new Animal()
Dog.prototype.constructor = Dog
Dog.prototype.sayHobby = function() {
    console.log(this.hobby)
}

// test instance of dog1
let dog1 = new Dog("xiaohei", "bone")
dog1.sayName() // xiaohei
dog1.sayHobby() // bone
dog1.types.push("ant") // types: ["dog", "cat", "ant"]

// test instance of dog2
let dog2 = new Dog("feifei", "fish")
dog2.sayName() // feifei
dog2.sayHobby() // fish
dog2.types // ["dog", "cat"]

组合模式,解决了使用构造函数继承类式继承带来的问题,算是一种比较理想的解决继承方式,但是这里还有一些瑕疵,调用了两次父类(Animal)的构造函数。

所以为了解决这个问题,进行了优化,产生了

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101071.html

相关文章

  • JavaScript继承方式详解

    摘要:可以通过构造函数和原型的方式模拟实现类的功能。原型式继承与类式继承类式继承是在子类型构造函数的内部调用超类型的构造函数。寄生式继承这种继承方式是把原型式工厂模式结合起来,目的是为了封装创建的过程。 js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承) 类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念。...

    Yangyang 评论0 收藏0
  • js原型和继承

    摘要:举例说明组合继承组合继承利用原型链借用构造函数的模式解决了原型链继承和类式继承的问题。示例组合式继承是比较常用的一种继承方法,其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。 对js原型和继承的理解一直处于不懂-懂-不懂-懂-不懂。。。的无限循环之中,本来打算只是简单总结下js继承方式,可看了些网上的资料后,发现又不懂继承了。。。这篇文章只...

    Hujiawei 评论0 收藏0
  • JS面向对象之五 【继承

    摘要:首先为了模拟类创建对象的功能搞出了构造函数。也就是名字肤色肤色这里是继承里的自有属性生命值这里继承的共有属性的方法攻击力兵种美国大兵攻击防御死亡肤色 JS面向对象之五 【继承】 我们已经准备了很多前置知识,包括 原型链,对象和对象之间的关系 this,对象和函数之间的关系 new, 用函数批量创建特定的对象的语法糖 JS面向对象的前世今生 我们说,面向对象是一种写代码的套路。因为如...

    genefy 评论0 收藏0
  • JS面向对象程序设计之继承实现-组合继承

    摘要:实现思路使用原型链实现对原型方法和方法的继承,而通过借用构造函数来实现对实例属性的继承。继承属性继承方法以上代码,构造函数定义了两个属性和。 JS面向对象的程序设计之继承的实现-组合继承 前言:最近在细读Javascript高级程序设计,对于我而言,中文版,书中很多地方翻译的差强人意,所以用自己所理解的,尝试解读下。如有纰漏或错误,会非常感谢您的指出。文中绝大部分内容引用自《Java...

    antz 评论0 收藏0
  • 《javascript高级程序设计》 继承实现方式

    摘要:寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部已某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象。 这篇本来应该是作为写JS 面向对象的前奏,只是作为《javascript高级程序设计》继承一章的笔记 原型链 code 实现 function SuperType() { this.colors = [red,blu...

    cppprimer 评论0 收藏0
  • JS类与类继承

    摘要:首先捋清楚类和对象的关系类比如人类,指的是一个范围对象比如某个人,指的是这个范围中具体的对象中的作为构造函数时,就是一个类,通过操作符,可以返回一个对象。 JS中的类与类的继承 我们知道,JS中没有类或接口的概念,即不能直接定义抽象的类,也不能直接实现继承。不过,为了编程的方便,我们可以在 JS 中模拟类和继承的行为。首先捋清楚类和对象的关系: 类:比如人类,指的是一个范围; ...

    Tangpj 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<