资讯专栏INFORMATION COLUMN

对js原型的思考

Pocher / 3275人阅读

摘要:原型链上有两个关键词和比较重要,是设置构造函数的原型对象,是声明原型的构造函数,不管是对象还是函数,都有一个隐式属性用来构成一条完整原型链的指向。

思考
说到原型,不得不提到原型链,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方法获取。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93932.html

相关文章

  • JS面向象之二 【原型链】(象和象之间关系)

    摘要:面向对象之二原型链对象和对象之间的关系注意这个系列文章要经常站在之父的视角去思考。思考问题我们都知道都属于那么既然他们都是对象一定有某些相同之处吧对象和对象之间有什么关联呢如果说你没有思考过这个问题那么可以换一个更具体的问题。 JS面向对象之二 【原型链】(对象和对象之间的关系) 注意这个系列文章,要经常站在JS之父的视角去思考。 牢记我们的需求,我要在JS没有class的情况下,那么...

    Jochen 评论0 收藏0
  • 详解js继承(一)

    摘要:构造函数,实例构造函数,是用来创建对象的函数,本质上也是函数。这里刚好解释一下时,说到的,可以通过实例的访问构造函数,但是本质上是原型对象的属性。 前言 最近在学vue,到周末终于有空写一些东西了(想想又能骗赞,就有点小激动!)。在javascript基础中,除了闭包之外,继承也是一个难点。因为考虑到篇幅较长,所以打算分成两个部分来写。同样基于《javascript高级程序设计》,做一...

    Object 评论0 收藏0
  • JavaScript创世神话——一切源于

    摘要:但是,中并没有类的概念,而是通过构造函数替代了类的功能,为某一类的对象提供共同的属性和方法。一只名叫的狗,首先继承了构造函数的原型对象,而的原型对象中的有继承了函数的原型对象,函数对象中的有继承了的原型对象。 《圣经》里的第一章创世纪中其中有一段经典记载上帝是如何创造人的。神说:我们要照着我们的形象,按照我们的样式造人。不谋而合的是,JavaScript中似乎也遵循着上帝的旨意去创造程...

    enda 评论0 收藏0
  • instanceof使用中可能漏掉一点细节

    摘要:代码传送门如上代码示例,从其运行结果可知,是等于的原型的,是被对象后返回的函数,从验证结果来看,函数经过的一层包装,依然不会影响其原型检测的绑定。前言 在面向对象语言中,一般都有关键字 instanceof 来检测对象类型,更准确点来说是检测对象是哪个类型的实例。那么在 JS 中这个关键字又有什么不同之处呢?此文仅是一篇对 ES 标准中 instanceof 关键字的解读,并记录了在此过程中...

    lauren_liuling 评论0 收藏0
  • 前端思考 - 收藏集 - 掘金

    摘要:并尝试用为什么你统计的方式是错的掘金翻译自工程师的文章。正如你期望的,文中的前端开发单一职责原则前端掘金单一职责原则又称单一功能原则,面向对象五个基本原则之一。 单页式应用性能优化 - 首屏数据渐进式预加载 - 前端 - 掘金前言 针对首页和部分页面打开速度慢的问题,我们开始对单页式应用性能进行优化。本文介绍其中一个方案:基于 HTTP Chunk 的首屏数据渐进式预加载方案,该方案总...

    LinkedME2016 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<