Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于 JavaScript 的继承 #36

Open
ClarenceC opened this issue Mar 21, 2018 · 0 comments
Open

关于 JavaScript 的继承 #36

ClarenceC opened this issue Mar 21, 2018 · 0 comments
Labels
JavaScript JavaScript 知识点

Comments

@ClarenceC
Copy link
Owner

ClarenceC commented Mar 21, 2018

继承是 OO 思想中的重大特性,当然 JavaScript 也实现了这一特性,OO 语言当中都支持两种继承的方法:

  1. 接口继承 意思是高度抽象父类,让子类只继承接口,不需要具体实现。
  2. 实现继承 意思是子类继承之后,有具体的实现方法,或者实现是继承自父类的。

ECMAScript 继承和 JAVA OO 语言那些不一样,只支持实现继承,不支持抽象的接口继承。
而在 ES5 中实现继承都是基于原型链来实现继承的,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。先回一下 JavaScript 原型链概念,每个构造函数都有一个原型的对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。如果我们让原型对象等于另一个类型的实例就形成了原型链。

prototype

1. 原型链继承

看一下实现继承的基本方式 (ES5)

    // 构建父类构造函数
    function Father() {
        this.property = true
    }
    // 父类的原型链
    Father.prototype.getFatherValue = function() {
        return this.property
    }
    // 构建子类构造函数
    function Child() {
        this.subproperty = false
    }
    // 子类原型指向父类实例 (继承)
    Child.prototype = new Father()
    Child.prototype.getChildValue = function() { // 添加子类原型链
        return this.subproperty
    }
    var child1 = new Child()
    console.log(child.getFatherValue()) // true 能访问父类函数 父类函数访问父类属性
    console.log(child.getChildValue()) // false 访问子类函数返回子类属性

(ES6) 继承的实现, ES6 有关键字extends定义继承需然,最终使用的还是通过原型链来实现,但是有了语法糖可看性良好了很多。

    class Father {
        constructor() {
            this.property = true
        }
        getFatherValue() {
            return this.property
        }
    }

    class Child extends Father {
        constructor() {
            this.subproperty = false
        }
        getChildValue() {
            return this.subproperty
        }
    }
    var child1 = new Child()
    console.log(child1.getFatherValue()) // true
    console.log(child1.getChildValue()) // false

2. 借用构造函数

使用借用构造函数其实就是,在子类型构造函数的内部调用父类型构造函数。甚至可以在子类给父类传参,用来实现构造函数不会重写子类型的属性。但是借用构造函数并没有继承至父类,调用不了父类的方法,所以只能使用下面组合继承的方式。

(ES5)

    function Father() {
        this.colors = ["red","blue","green"]
    }
    function Child() {
        Father.call(this) // 通过子类调用父类构造函数实现继承
    }

    var instance1 = new Child() // 新建实例时单独创建父类
    instance1.colors.push("black")
    console.log(instance1.colors) // "red,blue,green,black"

    var instance2 = new Child()
    console.log(instance2.colors) // "red, blue, green"
    console.log(instance1.__proto__ === instance2.__proto__) // true 原型链都是指向 Child 构造函数
    console.log(instance1 instanceof Father) // false 而儿实际上原型链没有继承 Father

(ES6) 中借用借用父类的构造函数都是使用关键字 super 来实现的。

    class Father() {
        constructor() {
            this.colors = ["red","blue","green"]
        }
    }
    class Child() extends Father { // extends 的意思是继承 Father 的原型链
        constructor() {
            super()     // 没有 extends 不能使用 super 
        }
    }
    var instance1 = new Child()
    instance1.colors.push("black")
    console.log(instance1.colors) // "red,blue,green,black"
    var instance2 = new Child()
    console.log(instance2.colors) // "red, blue, green"
    console.log(instance1.__proto__ === instance2.__proto__) //true 原型都指向 Child 构造函数
    console.log(instance1 instanceof Father) // true ES6 通过 extends 实现了原型链

3. 组合继承

组合继承是指原型链和借用构造函数的方式结合在一起。

组合继承 (ES5)

    function Father() {
        this.name = name
        this.colors = ["red","blue","green"]
    }
    Father.prototype.sayName = function() {
        console.log(this.name)
    }
    function Child(name, age) {
        // 继承父类属性
        Father.call(this, name)
        this.age = age
    }
    // 子类原型链指向父类
    Child.prototype = new Father()
    Child.prototype.sayAge = function() {
        console.log(this.age)
    }

    var child1 = new Child("Nicholas", 29) // 生成对象
    child1.colors.push("black")
    console.log(child1.colors) //"red, blue, green, black"
    child1.sayName() // "Nicholas" 调用父类方法
    child1.sayAge() // 29 调用子类方法

    var child2 = new Child("Greg", 27)
    console.log(child2.color) //"red, blue, green"
    child2.sayName() // "Greg"
    child2.sayAge() // 27

组合继承 (ES6) 等同于上面 ES5 的写法,而且更简洁易懂。

    // 定义父类
    class Father {
        constructor(name) { // 父类构造函数
            this.name = name
            this.colors = ["red", "blue", "green"]
        }
        sayName() {
            console.log(this.name)
        }
    }
    // 定义子类
    class Child extends Father {
        constructor(name, age) { // 子类构造函数
            super(name) // 调用父类构造函数
            this.age = age
        }
        sayAge() {
            console.log(this.age)
        }
    }

    var child1 = new Child("Nicholas", 29)
    child1.colors.push("black")
    console.log(child1.colors) //"red, blue, green, black"
    child1.sayName() // "Nicholas"
    child1.sayAge() // 29

    var child2 = new Child("Greg", 27)
    console.log(child2.colors) //"red, blue, green"
    child2.sayName() // "Greg"
    child2.sayAge() // 27

4. 原型式继承

原型式继承又称道格拉斯继承,其实就是 Object.create() 方法的实现。通过实例对象的原型来创建新的对象实例。原型式继承是通过原生方法实现的,所以 ES6 暂时没有语法糖支持。

    function object(o) {
        function F(){}
        F.prototype = o
        return new F()
    }

不过使用 Object.create() 传参会相对复杂一些。

    var person = {
        name: "Nicholas",
        friends: ["Shelby", "Court", "Van"]
    }

    var anotherPerson = Object.create(person, {
        name: {
            value: "Greg"
        },
        friends: {
			value: [1,2,3]
		}
    })
    console.log(anotherPerson.name) // "Greg"
    console.log(anotherPerson.friends) // "[1,2,3]"

5.寄生式继承

寄生式继承是通过原型式继承,添加自己宝的属性或者函数。

    function customerCreate(obj) {
        var clone = Object.create(obj) // 通过 Object.create 创建对象
        // 添加自定义函数或者属性
        clone.sayHi = function() {
            console.log('Hi')
        }
        return clone // 返回对象
    }

6.寄生组合式继承

基本原理是通过通过 Object.create() 来实现父类的实例对象,再将子类型的原型继承父类的实例对象。这样创建的原型链,只会调用一次父类构造函数,并且避免在子类上面创建不性格多余的属性。

    function customerInheritCreate(Child, Father) {
        var proto = Object.create(Father.prototype) // 通过 Father 实例创建 Father 原型实例对象
        proto.constructor = Child // 把 proto 的构造函数指向 Child
        Child.prototype = proto // Child 原型链指向 proto 实例
    }

    function Father(name) {
        this.name = name
        this.colors = ["red","blue","green"]
    }
    Father.prototype.sayName = function() {
        console.log(this.name)
    }
    function Child(name, age) {
        Father.call(this, name)
        this.age = age
    }
    customerInheritCreate(Father, Child) // 构建原型链, ES6 extends不合适使用
    Child.prototype.sayAge = function() {
        console.log(this.age)
    }
@ClarenceC ClarenceC added the JavaScript JavaScript 知识点 label Mar 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
JavaScript JavaScript 知识点
Projects
None yet
Development

No branches or pull requests

1 participant