对js原型的思索

思索

说到原型,不能不提到原型链,js中不管是对象照样要领(也是对象)都有个隐蔽属性_proto_,来示意原型链的下一个指向,平常对象是指向Object.prototype,要领是指向Function.prototype,组织函数new出来的对象指向想组织函数的prototype

原型链的思索

由于原型链的存在,当前对象或许要领可以共用原型链上的上级属性和要领

var obj = {};
obj.toString() //[object Object]

obj对象上是没有toString要领的,由于obj._proto_指向Object.prototype,详细上是挪用Object.prototype要领:Object.prototype.toString.apply(obj)

function Foo() {

}
Foo.toString();//function Foo() {}

要领的原型链是Foo->Function.prototype->Object.prototype,所以Foo挪用toString要领是援用Function上的toString要领,详细上是挪用Function.prototype要领:Function.prototype.toString.apply(obj)

有时刻我们为了继续父类(实在js并没有类这个观点),会经由历程原型继续去继续父类的一些要领,在此之前,先简朴叙说下经由历程组织函数实例化一个对象的历程,比方以下建立一个obj对象,

var obj = new Object();
  • 先建立一个空对象{}
  • 空对象{}._proto_指向Object.prototype
  • Object.apply({})
  • 再实行组织要领内里的代码

所以当es5继续要领时,可以挑选原型继续,经由历程修正prototype的值,如:
ES5的状况:

var Father = function(name) {
    this.name = name;
}

Father.prototype.say = function() {
    return "my name is " + this.name;
}

var Child = function(name) {
    this.name = name;
}

Child.prototype = new Father();

var child = new Child('Nico');
child.say();//my name is Nico

然则在上面原型继续的状况,我们还要对Child的组织函数的constructor做一个声明

Child.prototype.constructor = Child;

由于Child原型是Father实例的一个援用,当想修正Child原型的要领时,会被挂在Father的实例对象上。

ES6的状况:

class Father {
    constructor(name) {
        this.name = name;
    }
    say() {
        return "my name is " + this.name;
    }
}

class Child extends Father {
    constructor(name) {
        super(name)
    }
}

var child = new Child('Nico');
child.say()//my name is Nico

es6的class类中,Child类的原型组织器不用做分外声明,由于,es6的class的constructor指向本身

继续的思索

子类能运用父类的要领

除了上面说起到的原型链继续,另有比方组织函数继续:

function Veticle(name) {
    this.name = name || null

    this.drive = function() {
        console.log(this.name + " drives! --from Veticle ");
    }
}

function Car(name) {
    Veticle.call(this)
    this.name = name;
}

var car = new Car('a car');
car.drive();//a car drives! --from Veticle 

这类要领中心就经由历程转变组织函数的上下文(context),到达子类可以运用父类要领,然则这类要领不能运用父类原型要领。

除此之外,还可以遍历父类实例继续父类要领:

function Veticle(name) {
    this.name = name || null

    this.drive = function() {
        console.log(this.name + "drives! --from Veticle ");
    }
}

Veticle.prototype.getName = function() {
    return this.name;
}

function Car(name) {
    var veticle = new Veticle();
    for (var p in veticle) {
        console.log(p)
        Car.prototype[p] = veticle[p];
    }
    this.name = name;
}

var car = new Car('a car');
car.getName();//a car

这类要领可以猎取实例能挪用的一切要领,除了不可枚举(ES6 class要领是不可枚举的)

实在关于继续js已经有个很好的的完成要领叫寄生组合继续,也许道理是用组织函数继续实例属性,用原型继续原型要领,而寄生组合继续是在组合继续的基础上强化,二者的区别是前者避免了两次实例化父类,是现在比较好的es5完成继续的要领:

function Veticle(name) {
    this.name = name || null

    this.drive = function() {
        console.log(this.name + "drives! --from Veticle ");
    }
}

Veticle.prototype.getName = function() {
    return this.name;
}

function Car(name) {
    Veticle.call(this)

    this.name = name || 'car'
}

function inheritProto(subType, superType) {
    var prototype = Object.create(superType.prototype);
    subType.prototype.constructor = subType;
    subType.prototype = prototype;
}

inheritProto(Car, Veticle)

var car = new Car('siip');

总结

    js中有一些组织要领诸如Object、Function、String、Number等等,当当前对象挪用要领或猎取属性时,会顺着本身对象到组织函数原型,再到Object原型,最后到Null这么一条原型链上查找。原型链上有两个关键词prototype和constructor比较主要,prototype是设置组织函数的原型对象,constructor是声明原型的组织函数,不管是对象照样函数,都有一个隐式属性_proto_用来组成一条完全原型链的指向。
    继续有继续属性和继续要领,许多时刻es5完成继续比es6要轻微简朴一点,es6的class的原型要领是不可枚举的,有时刻,挂载要领时须要经由历程Object.getOwnPropertyNames要领猎取。

    原文作者:NicolaChin
    原文地址: https://segmentfault.com/a/1190000014141394
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞