js继承的方式

  1. 原型链继承
    代码示例:
        Person.prototype.age = 55;
        function Person() { }

        Student.prototype = new Person()
        function Student(name) {
            this.name = name
        }

        var zhangsan = new Student('zhangsan')



        function Father() {
            this.sex = 'male'
        }

        Father.prototype.age = 30
        Father.prototype.address = {
            province: '浙江省',
            city: '杭州市'
        }

        function Son() {
            this.school = '第一小学'
        }

        Son.prototype = new Father()

        var zhangsan = new Son()
        console.log(zhangsan.age) // 30
        var zhangsi = new Son()
        zhangsan.address.city = '绍兴市'
        console.log(zhangsi)

这个示例是让Son继承Father的实例/原型链属性/方法。

Son.prototype = new Father()这句话是什么意思呢?

我们知道
①原型链是通过__ proto 属性一层一层向上寻找的,
②实例化Son的时候,Son会把自身的prototype属性赋给实例对象(zhangsan)的
proto __属性。

Son.prototype = new Father()的目的就是让zhangsan通过__ proto __ 找到Father的实例,进而能访问到Father的自身和原型链上的属性和方法

如果不这样操作,那么zhangsan的 __ proto __ 向上一路找到Object.prototype去了。

这两张图就是改变Son.prototype指向前后的差别,可以看出如果不改变,根本没Father什么事,也就继承不了其相关属性和方法

js继承的方式_第1张图片
改变之前
js继承的方式_第2张图片
改变之后

接着上述代码,我们再创建一个Son的实例

         var zhangsi = new Son()
        zhangsan.address.city = '绍兴市'
        console.log(zhangsi.address.city) //绍兴市 

可以看出zhangsi的address也被改变了,但是我们明明只是操作的zhangsan的address啊

这就是此继承方式的弊端之一,如果父类实例/原型链上有引用类型属性,那么多个子类实例的其中一个实例对象改变这个引用类型属性,就会导致其他子类实例对象的相关属性也跟着改变。

接着上述代码,我们来给Father.prototype添加一个属性

Father.prototype.job = ‘teacher’

console.log(zhangsan.job) // teacher
console.log(zhangsi.job) // teacher

这个特点,说不上完全是优点还是缺点,优点是易于扩展,能在已经创建好的实例上添加属性/方法,缺点是所有子类实例都能拿到这个新增属性/方法,隐私性不好。

总结:
优点:
1.结构简单,易于理解
2.父类新增的属性/方法,子类实例对象都能访问到,扩展性好
缺点:
1.子类能改变父类原型中引用类型的属性,且一个实例对象改变,其他实例对象跟着改变
2.子类实现继承的时候无法通过构造函数传参
3.不能实现多继承,子类不能继承多个父类的属性/方法

  1. 构造函数继承
        function Father(age) {
            this.age = age;
        }
        Father.prototype.job = 'teacher'
        Father.prototype.run = function () {
            console.log('I run very fast');
        }
        function Son(age, name) {
            Father.call(this, age)
            this.name = name
        }
        Son.prototype.height = '180'
        Son.prototype.eat = function () {
            console.log('I eat a lot')
        }

        var zhangsan = new Son(18, 'zhangsan')
        console.log(zhangsan.age); // 18

        console.log(zhangsan.height); // 180
        console.log(zhangsan.eat); // function eat(){}

        console.log(zhangsan.job); // undefined
        console.log(zhangsan.run); // undefined

Son函数并没有age属性,但是Son的实例对象zhangsan却有age属性,说明age是继承而来。

这种方法的关键点在于,要在子类的构造函数中使用call/apply调用父类的构造函数

总结:
优点:
1.可以实现多继承(子类构造函数中调用多个父类构造函数)
2.创建子类实例时,可以向父类构造函数传参
缺点:
1.不能继承父类实例和原型上的方法/属性

  1. 组合继承
        function Father(age) {
            this.age = age;
        }
        Father.prototype.job = 'teacher'
        Father.prototype.run = function () {
            console.log('I run very fast');
        }
        function Son(age, name) {
            Father.call(this, age)
            this.name = name
        }
        Son.prototype = new Father()

        Son.prototype.height = '180'
        Son.prototype.eat = function () {
            console.log('I eat a lot')
        }

        var zhangsan = new Son(18, 'zhangsan')
        console.log(zhangsan.age); // 18

        console.log(zhangsan.height); // 180
        console.log(zhangsan.eat); // function eat(){}

        console.log(zhangsan.job); // teacher
        console.log(zhangsan.run); // function run(){}

这种方式的思路是使用原型链实现对原型属性和方法的继承,借用构造函数实现对函数实例属性和方法的继承。是最常用的一种方式。

  1. es6 class 继承
        class Father {
            constructor(age) {
                this.job = 'teacher'
                this.age = age
            }
            run() {
                console.log('I run very fast');
            }
        }

        class Son extends Father{
            constructor(age) {
                super(age)
                this.height = "180"
            }
            eat() {
                console.log('I eat a lot')
            }
        }

        var zhangsan = new Son(55)
        console.log(zhangsan.job) //teacher
        console.log(zhangsan.age) //55
        console.log(zhangsan.run); // function run(){}
        console.log(zhangsan.height); //180
        console.log(zhangsan.eat); //function eat(){}

es6语法引入class和extends关键字来实现继承,还可以通过static关键字定义静态属性/方法,虽然这只是语法糖级别的,但是更加语义化,向传统的后端语言靠近一步。

以上就是常见的js继承方式了,其他还有一些寄生式继承、寄生组合式继承等,我窃以为这几种方式不太常用,把文中说的几种形式彻底搞懂就足够大多数场景使用了。

个人总结,如有错误,请指正。

你可能感兴趣的:(js继承的方式)