• 浅谈js继承的几种方法


    先回顾一下上一讲的原型。

    // 原型
    function Person(name) {
    	this.name = name;
    }
    Person.prototype.sayName = function() {
    	alert(this.name)
    }
    var p1 = new Person('嘿嘿');
    console.log(p1)
    

    结果如下图:

    每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针(constructor),而实例都包含一个指向原型对象的内部指针(__proto __)。
    构造函数Person中定义的属性(即实例属性)被挂在到实例p1上,原型方法被挂载到原型对象上。

    原型链继承

    思想:让原型对象等于另一个类型的实例。

    function SuperType() {
    	this.property = true;
    } 
    
    SuperType.prototype.getSuperValue = function() {
    	return this.property;
    };
    
    function SubType() {
    	this.subproperty = false;
    }
    
    SubType.prototype = new SuperType();  // 实现继承;也就是让SubType.prototype.constructor指向构造函数SuperType
    
    console.log(SubType.prototype.constructor)
    
    SubType.prototype.getSubValue = function() {
    	return this.subproperty;
    }
    var instance = new SubType();
    alert(instance.getSuperValue());	// true
    

    默认的原型

    所有函数的默认原型都是Object实例,因此默认原型都会包含一个内部指针,指向Object.prototype。这也是所有自定义类型都会继承toString()和valueOf()等默认方法的根本原因。

    添加方法

    给原型添加方法的代码一定要放在替换原型的语句之后。

    function SuperType() {
    	this.property = true;
    } 
    
    SuperType.prototype.getSuperValue = function() {
    	return this.property;
    };
    
    function SubType() {
    	this.subproperty = false;
    }
    
    SubType.prototype = new SuperType();  
    
    // 添加新方法
    SubType.prototype.getSubValue = function() {
    	return this.subproperty;
    }
    // 覆盖SuperType中的方法
    SubType.prototype.getSuperValue = function() {
        return false
    }
    
    var instance = new SubType();
    alert(instance.getSuperValue());	// false
    

    通过原型链实现继承的时候,不能使用对象字面量创建原型方法。

    function SuperType() {
    	this.property = true;
    } 
    
    SuperType.prototype.getSuperValue = function() {
    	return this.property;
    };
    
    function SubType() {
    	this.subproperty = false;
    }
    
    SubType.prototype = new SuperType();  
    
    // 使用对象字面量添加新方法,会导致上一行代码无效
    SubType.prototype = {
        getSubValue: function() {
    	    return this.subproperty;
        },
        someOtherMethod: function() {
            return false
        }
    }
    
    var instance = new SubType();
    alert(instance.getSuperValue());	// error
    

    使用字面量添加方法,使得现在的原型包含的是一个Object实例,而非SuperType实例。所以SubType和SuperType之间已经没有联系了。

    原型链的问题

    原先实例的属性变成了现在的原型属性。

    function SuperType() {
    	this.colors = ['red', 'blue', 'green'];
    	// 相当于 this.colors = new Array('red', 'blue', 'green')
    } 
    function SubType() {}
    
    SubType.prototype = new SuperType();
    
    var instance1 = new SubType();
    console.log(instance1.colors);  // ["red", "blue", "green"]
    instance1.colors.push('yellow');
    console.log(instance1.colors);  // ["red", "blue", "green", "yellow"]
    
    var instance2 = new SubType();
    console.log(instance2.colors)   // ["red", "blue", "green", "yellow"]
    

    引用类型的实例属性要被放在构造函数中,因为每个引用类型值在实例上会重新创建一遍,创建后的引用类型值的属性或方法是不一样的。这里,让SubType.prototype等于SuperType的实例,也就让SuperType中的实例方法变成了原型方法, 被挂在到SubType.prototype上

    原型链实现继承还有一个问题:在创建子类型(SubType)的实例时,无法向超类型(SuperType)的构造函数中传递参数。

    借用构造函数继承

    在子类型构造函数中调用超类型构造函数。使用call或apply方法改变超类型构造函数this指向。

    function SuperType(name) {
    	this.colors = ['red', 'blue', 'green'];
    	this.name = name;
    } 
    function SubType() {
        SuperType.call(this, '呵呵')
    }
    var instance1 = new SubType();
    console.log(instance1.colors);  // ["red", "blue", "green"]
    instance1.colors.push('yellow');
    console.log(instance1.colors);  // ["red", "blue", "green", "yellow"]
    console.log(instance1.name);    // 呵呵
    
    var instance2 = new SubType();
    console.log(instance2.colors)   // ["red", "blue", "green"]
    

    借用构造函数的问题:在超类型原型中定义的方法,无法被子类型继承。

    function A() {
        this.name = '嘿嘿'
    }
    A.prototype.sayName = function() {
        alert(this.name)
    }
    function B() {
        A.call(this)
    }
    var b = new B();
    b.sayName();        //  error
    

    组合继承(最常用方法,掌握这个就行了)

    使用原型链实现对原型属性和方法的继承,使用构造函数实现对实例属性的继承。

    function SuperType(name) {
        this.name = name;
        this.colors = ['red', 'blue', 'green'];
    }
    SuperType.prototype.sayName = function() {
        alert(this.name);
    }
    function SubType(name, age) {
        SuperType.call(this, name);
        this.age = age;
    }
    
    // 继承方法
    SubType.prototype = new SuperType();
    
    SubType.prototype.constructor = SubType;    // 为了修复SubType.prototype的结构
    
    SubType.prototype.sayAge = function(){
        alert(this.age);
    }
    var instance1 = new SubType('小红', 18);
    instance1.colors.push('yellow');
    console.log(instance1.colors);
    instance1.sayName();
    instance1.sayAge();
    
    var instance2 = new SubType('老红', 80);
    console.log(instance2.colors);
    instance2.sayName();
    instance2.sayAge();
    

    原型式继承

    原型式继承就是基于已有的对象创建新对象.

    function object(o) {
        function F() {}
        F.prototype = o;    // 使一个类型的原型对象等于另一个对象
        return new F();
    }
    var person = {
        name: '小芳',
        friends: ['小明', '小华']
    }
    var anotherPerson = object(person);
    anotherPerson.name = '老方';
    anotherPerson.friends.push('老王');
    
    var yetAnotherPerson = object(person);
    yetAnotherPerson.name = '老明';
    yetAnotherPerson.friends.push('二狗子');
    
    console.log(person.friends)     // ["小明", "小华", "老王", "二狗子"]
    

    ES5为了规范原型式继承,新增Object.create()方法。Object.create()可接收两个参数:第一个参数是超类型的对象,第二个参数可选,为新对象添加属性。

    var person = {
        name: '小芳',
        friends: ['小明', '小华']
    }
    var anotherPerson = Object.create(person);
    anotherPerson.name = '老方';
    anotherPerson.friends.push('老王');
    
    var yetAnotherPerson = Object.create(person);
    yetAnotherPerson.name = '老明';
    yetAnotherPerson.friends.push('二狗子');
    
    console.log(person.friends)     // ["小明", "小华", "老王", "二狗子"]
    
    var person = {
        name: '小芳',
        friends: ['小明', '小华']
    }
    var anotherPerson = Object.create(person, {
        name: {
            value: '老方'
        }
    })
    console.log(anotherPerson.name)         // '老方'
    

    寄生式继承

    function CreateAnother(original) {
        var clone = Object(original);
        clone.sayHi = function() {
            alert('hi');
        }
        return clone
    }
    var person = {
        name: '小芳',
        friends: ['小明', '小华']
    }
    var anotherPerson = CreateAnother(person);
    anotherPerson.sayHi();
    anotherPerson.friends.push('老王');
    console.log(person.friends)         //  ["小明", "小华", "老王"]
    

    寄生组合式继承(最好的方法)

    function inheritPrototype(subType, superType) {
        var prototype = Object(superType.prototype);
        prototype.constructor = subType;  //  修正subType.prototype的constructor
        subType.prototype = prototype;
    }
    
    function SuperType(name) {
        this.name = name;
        this.colors = ['red', 'blue', 'green'];
    }
    
    SuperType.prototype.sayName = function() {
        alert(this.name)
    }
    function SubType(name, age) {
        SuperType.call(this, name);
        this.age = age;
    }
    inheritPrototype(SubType, SuperType);
    SubType.prototype.sayAge = function() {
        alert(this.age)
    }
    
    var p1 = new SubType()
    console.log(p1.colors)      // ['red', 'blue', 'green']
    

    注:以上内容总结自《javascript高级程序设计》

  • 相关阅读:
    Flush输出表格内容
    [From Linux Toy] inxi– bash script to get system information
    .NET中Mocking框架的对比
    Python中的对象和动态性 [菜鸟的理解,高手莫入]
    《正见 佛陀的证悟》读书摘记 (1)
    Quote for tomorrow
    不抱怨就会死
    web deploy + windows server 2008 r2 64bit + iis 7.5
    bootsnipp
    [转载] A Beginner's Guide to HTTP Cache Headers
  • 原文地址:https://www.cnblogs.com/renzhiwei2017/p/8312845.html
Copyright © 2020-2023  润新知