JavaScript继承--原型链继承详解

目录

一、回顾原型概念:

二、原型链继承

三、注意点:

四:原型链的问题:


一、回顾原型概念:

在学习原型链继承之前我们先简单回顾一下原型的概念

每个函数都有一个prototype属性,它默认指向一个空的Object对象(称为原型对象),原型对象里面有一个constructor属性,它指向函数对象。

function foo(){
    
}
console.log(foo.prototype.constructor == foo); //true

构造函数、原型、实例的关系:

每一个构造函数都有一个原型对象(Foo.prototype)、原型对象都包含一个指向构造函数的指针(constructor),而实例都包含一个指向原型对象的内部指针(_proto_)。

function Foo(){
    this.name="小白";
}
var foo = new Foo();
console.log(foo.name);

console.log(Foo.prototype.constructor === Foo);
console.log(foo.__proto__ === Foo.prototype);


 二、原型链继承

//    创建SuperType函数   并添加property属性
    function SuperType() {
        this.property = true;
    }

//    给其原型上添加一个方法 getSuperValue
    SuperType.prototype.getSuperValue = function () {
        return this.property;
    };

//    创建一个SubType函数 并添加subproperty属性
    function SubType(){
        this.subproperty = false;
    }

//    继承SuperType(重点)
    SubType.prototype = new SuperType();

    SubType.prototype.getSubValue = function () {
        return this.subproperty;
    };

    var instance = new SubType();
    alert(instance.getSuperValue());

解释:上面代码定义了两个类型:SuperType和SubType。每个类型分别有一个属性和一个方法。它们的区别主要是SubType继承了SuperType,而继承是通过创建SuperType的实例,并将该实例赋给SubType.prototype实现的。实现的本质是重写原型对象,将SubType的原型对象作为SuperType的实例。那么原来存在于SuperType的实例中的所有属性和方法,现在也存在于SubType.prototype中了。在确立了继承关系后,我们给SubType.prototype添加了一个方法,这样就在继承了SuperType的属性和方法的基础上又添加了一个新方法。

 在上面代码中我们没有使用SubType默认提供的原型,而是给它换了一个新原型,而这个原型就是SuperType的实例。于是新原型不仅具有一个SuperType的实例所拥有的的全部属性和方法,而且其内部还有一个指针,指向了SuperType的原型。

    console.log(SubType.prototype.__proto__ === SuperType.prototype); //true

最终结果就是instance指向SubType的原型,SubType的原型又指向SuperType的原型。

三、注意点:

1.  别忘记默认的原型:

所有函数的显式原型都指向一个空的Object对象但是Object除外,因此默认原型都会包含一个内部指针,指向Object.prototype。这也正是所有自定义类型都会继承toString()、valueOf()等默认方法的根本原因。 

2. 谨慎的定义方法:

子类型有时候需要重新超类型中的某个方法,或者需要添加超类型中不存在的某个方法。给原型添加方法的代码一定要放在替换原型的语句之后

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

3. 在通过原型链实现继承时,不能使用对象字面量创建原型方法。因为会重写原型链

    function SuperType() {
        this.property = true;
    }
    SuperType.prototype.getSuperValue = function () {
        return this.property;
    };
    function SubType(){
        this.subproperty = false;
    }
//    继承SuperType
    SubType.prototype = new SuperType();
//使用对象字面量添加新方法
    SubType.prototype = {
        getSubValue : function(){
            return this.subproperty;
        },
        someOtherMethod : function(){
            return false;
        }
    };
    var instance = new SubType();
    alert(instance.getSuperValue());

四:原型链的问题:

1、包含引用类型值的原型属性会被所有实例共享,这也是为什么会在构造函数中而不是原型对象中定义属性的原因。

    function SuperType(){
        this.colors = ["red","blue","green"];
    }
    function SubType() {

    }
//    继承了SuperType
    SubType.prototype = new SuperType();

    var instance = new SubType();
    instance.colors.push("black");
    alert(instance.colors);

    var instance2 = new SubType();
    alert(instance2.colors);

该例子中SuperType构造函数定义了一个colors属性,该属性包含一个数组(引用数据类型)。SuperType的每个实例都会有colors属性。当SubType通过原型链继承了SuperType之后,它也拥有一个colors属性。重点是SubType会共享这个属性所以我们看到在instance实例中修改了数组colors的值后instance2中的colors值也发生了改变。

2、在创建子类型的实例时,不能向超类型的构造函数中传递参数。

注:资料参考 《JavaScript高级程序设计》

猜你喜欢

转载自blog.csdn.net/weixin_43690348/article/details/112965496