一篇了解Javascript主要几种继承方法✨

63 阅读3分钟

哈喽,我是前端菜鸟JL😄 下面分享一下继承这个专题

前置知识

构造函数的属性

funcion A(name) {
    this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
    this.arr = [1]; // 实例引⽤属性 (该属性,强调私⽤,不共享)
    this.say = function () { // 实例引⽤属性 (该属性,强调复⽤,需要共享)
        console.log('hello')
    }
}
注意:数组和⽅法都属于‘ 实例引⽤ 属性’,但是数组强调私有、不共享的。⽅法需要复⽤、共享。
在构造函数中,⼀般很少有数组形式的引⽤属性,⼤部分情况都是: 基本属性 + ⽅法。

什么是原型对象 每个函数都有prototype,它就是原型对象,而通过new实例化出来的对象有个__proto__属性,指向原型对象

let a = new A()
a.__proto__ = A.prototype
// prototype的结构如下
A.prototype = {
    constructor: A,
    ...其他原型上的属性和方法
}

原型对象的作用

主要用途为每个实例对象存储分享的方法和属性

它仅仅是⼀个普通对象⽽已。并且所有的实例是共享同⼀个原型对象,因此有别于实例⽅法或属性,原型对象仅有⼀份。⽽实例有很多份,且实例属性和⽅法是独⽴的。

在构造函数中:为了属性(实例基本属性)的私有性、以及⽅法(实例引⽤属性)的复⽤、共享。我们提倡:

  • 将属性封装在构造函数中
  • 将方法定义在原型对象上
function A(name) {
    this.name = name // 属性定义在构造函数上,强调私有,不共享
}
A.prototype.say = function() { // 方法定义在原型对象上,强调共享,复用
    console.log('111')
}

继承

本质:复制,即重写原型对象,代之以一个新类型的实例

原型链继承

function Fn() {
    this.name = 'Jack'
}
function fn () {}
fn.prototype = new Fn() // 重点
let test = new fn()

console.log(test.name) // 'Jack'
缺点:多实例共享属性或方法,改动一个实例属性就会影响到其他实例

借用构造函数继承

function Fn() {
    this.name = 'Jack'
}
function fn () {
    Fn.call(this) // 重点,利用构造函数改变this指向
    // Fn.apply(this)
}
let test = new fn()
console.log(test.name) // 'Jack'
缺点:
只能继承父类的实例属性和方法,不能继承原型属性和方法
无法实现复用,每个子类都有父类实例函数的副本,影响性能

组合继承

实现:原型继承+借用构造函数继承

function Fn() {
    this.name = 'Jack'
}
Fn.prototype.say = function() { console.log('Mack') }
function fn() {
    Fn.call(this)
}
fn.prototype = new Fn()
 const test = new fn()
 console.log(test.say())
优点: 可以继承父类原型属性和方法
缺点:存在性能问题,Fn被调用两次

原型式继承

ES5之前

const Fn = { name: 'Jack' }
function createFn(Fn) {
    function fn() {}
    fn.prototype = Fn
    return new fn()
}
const test = createFn(Fn)

ES6之后

const Fn = { name: 'Jack' }
const test = Object.create(Fn)

优点:对一个对象进行浅拷贝创建另一个对象,同时继承该对象的原型属性 缺点:由于是浅拷贝,所有实例共享,如果是引用值,存在污染可能。

寄生继承

实现:在原型式继承的基础上,增强对象,返回构造函数

function createFn(Fn) {
    const clone = object(Fn) // 通过调用object()函数创建一个新对象 new Object(Fn)
    clone.sayHi = function() { // 添加方法增强对象
        console.log('hi')    
    }
    return clone // 返回这个对象
}

const person = { name: 'Jack' }
const test = createFn(person)
test.sayHi() // hi

缺点:(同原型式继承) 多实例共享相同属性,存在篡改可能 无法传递参数

寄生组合式继承(最常用,也是最成熟的方法,现在库实现)

实现:借用构造函数继承+原型式继承

function Fn() { this.name = 'Jack' }
Fn.prototype.say = function() { console.log('111') }
function fn() {
    Fn.call(this)
    this.age = 18
}

// 重点
fn.prototype = Object.create(Fn.prototype)
fn.prototype.constructor = fn

const test = new fn()

ES6继承

extends

class Test {
    constructor() {
        this.name = 'aaa'    
    }
}
class test extends Test {
    constructor() {
        super()    
    }
}
const test1 = new test()
test1.name // 'aaa'

ES5和ES6主要区别

  • ES5主要是先创建子类实例this,然后通过apply(this, argments)添加父类的方法
  • ES6则是先通过super调用父类构造函数,创建父类this,包含父类的属性和方法,然后子类继承父类this,同时可以通过constructor修饰ths,添加一些属性等。

结语

希望能给你带来帮助✨~

分享不易,点赞鼓励🤞