js继承的方式总结

  1. 对象冒充
  2. call()函数
  3. apply()函数
  4. 原型链
  5. 混合方式

对象冒充

        function Father(name,age,sex){
            this.name=name;
            this.age=age;
            this.sex=sex;
            this.say=function(){
                alert('hello world');
            }
        }
        function Son(name,age,sex){
            this.extend=Father;//将构造方法当成普通方法使用,赋值给属性
            this.extend(name,age,sex);//调用
            delete this.extend;//删除对Father的引用
            //所有新属性和新方法都必须在删除了新方法的代码行后定义。否则,可能会覆盖超类的相关属性和方法,因为this.extend创建了对Father的引用        
        }
        var son=new Son('son',18,'男');
        var father=new Father('father',38,'男');
        console.log(son.name);//son
        console.log(father.name);//father

实现原理:让父类的构造函数成为子类的方法,然后调用该子类的方法,通过this关键字给所有的属性和方法赋值。
该种实现方式可以实现多继承。
这里存在一个弊端,如果存在两个类 ClassA 和 ClassB 具有同名的属性或方法,ClassB 具有高优先级。因为它从后面的类继承。除这点小问题之外,用对象冒充实现多重继承机制轻而易举。

call()函数

        function Father(name,age,sex){
            this.name=name;
            this.age=age;
            this.sex=sex;
            this.say=function(){
                alert('hello world');
            }
        }
        function Son(name,age,sex,grade){
            Father.call(this,name,age,sex);
            this.grade=grade;
        }
        var son=new Son('key',18,'男',100);
        console.log(son.name);//key
        console.log(son.grade);//100

实现原理:改变函数内部的函数上下文this,使它指向传入函数的具体对象。
该种方式不能继承原型链,若想继承原型链,则采用第5种混合模式。

apply()函数

        function Father(name,age,sex){
            this.name=name;
            this.age=age;
            this.sex=sex;
            this.say=function(){
                alert('hello world');
            }
        }
        function Son(name,age,sex,grade){
            Father.apply(this,[name,age,sex]);//参数以数组的形式
            this.grade=grade;
        }
        var son=new Son('key',18,'男',100);
        console.log(son.name);//key
        console.log(son.grade);//100

实现原理:改变函数内部的函数上下文this,使它指向传入函数的具体对象。
该种方式不能继承原型链,若想继承原型链,则采用5混合模式,与call()函数的区别就是参数是以数组的形式传人。

原型链

        function Father(name,age,sex){
            this.name=name;
            this.age=age;
            this.sex=sex;
            this.say=function(){
                alert(this.name);
            }
        }
        function Son(grade){
            this.grade=grade;
        }
        /*调用 Father 的构造函数,没有给它传递参数。这在原型链中是标准做法。要确保构造函数没有任何参数。
        与对象冒充相似,子类的所有属性和方法都必须出现在 prototype 属性被赋值后,因为在它之前赋值的所有方法都会被删除。为什么?因为 prototype 属性被替换成了新对象,添加了新方法的原始对象将被销毁。*/
        Son.prototype=new Father();//直接将实例赋值给子类原型
        var son=new Son(100);
        son.name="key";
        console.log(son.name);//key
        son.say();//key

实现原理:使子类原型对象指向父类的实例以实现继承,即重写类的原型,弊端是不能直接实现多继承。

混合方式

        function Father(name) {
            this.name = name;
        }
        Father.prototype.say = function () {
            alert(this.name);
        };
        function Son(name, age) {
            Father.call(this, name);
            this.age = age;
        }
        Son.prototype = new Father();
        Son.prototype.sayAge = function () {
            alert(this.age);
        };
        var father = new Father("father");
        var son = new Son("son", 18);
        father.say();//father
        son.say();//son
        son.sayAge();//18

以下是w3cSchool中的原话,我觉得写的很好!
对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。开发者如何选择呢?答案很简单,两者都用。
我们曾经讲解过创建类的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承 prototype 对象的方法。

目录

浏览器兼容

  1. 所有主流浏览器都兼容。


本文转载:CSDN博客