初探js中的对象原型(Prototype)

在JavaScript中,万物皆对象。每一个对象都有一个特殊的内部属性,[[Prototype]](原型)。它是对于其余对象的引用,也就是说它关联到别的对象(若是它不为空)。函数

在平常中,咱们除了常常听到原型这个词以外,还常常会听到原型链这个词,那么这两个词到底有什么关系呢,下面咱们就来探究一下。this

那首先咱们来看一下[[Prototype]]这个特殊的内部属性有什么用呢?咱们先来说一个关于属性访问的一些知识。看下面的代码:spa

var myObject = {
    a:1;
};
myObject.a; //1

在这里,咱们能够清楚的看到Object.a是一个属性访问(访问了Object的a属性),那么深刻一下,上面的代码实际上在Object上执行了一个[[Get]]操做。对一个对象执行默认的[[Get]]操做,会首先检查对象,寻找请求的属性,若是找到,就返回相应的值。若是没找到,这个时候对象的[[Prototype]]属性以及它的[[Prototype]]链就须要派上用场了。prototype

先看代码:code

var newObject = {
    a:1;
};
var myObject = Object.create(newObject);
myObject.a; //1

首先咱们能够看到在上面的代码中,a这个属性是不存在myObject这个对象里的,可是最后咱们仍是获得了a这个属性的值。对象

var myObject = Object.create(newObject);这行代码就是关键了。它建立了一个对象myObject关联到newObject。也就是说myObject的[[Prototype]]属性关联到了newObject这个对象。[[Get]]操做在对象自己没找到须要的属性后,便顺着这个关联去newObject这个对象里进行查找。
因此在对属性进行查询的时候,就会顺着这个一层又一层的关联,也就是所谓的[[Prototype]]链,对须要的属性进行查找。在[[Prototype]]链找到了就返回属性值或者在链条的末尾都没找到就返回undefined。那么链条的末尾是什么呢?每一个普通的[[Prototype]]链的最顶端,是内建的Object.prototype,也就是null。blog

开头讲到,[[Prototype]]这个属性是把一个对象关联到另外一个对象。那么这个关联有什么用呢?
常见的用法那就是所谓的原型继承委托了。继承

咱们经过代码来理解一下:图片

function Foo(a) {
    this.a = a;
}
Foo.prototype.myA = function() {
    return this.a;
};

function Bar(a,b) {
    Foo.call(this,a);
    this.b = b;
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.myB = function() {
    return this.b;
};

var Baz = new Bar( "1", "obj 1" );

Baz.myA(); // "1"
Baz.myB(); // "obj 1"

在上面的代码中,最重要的部分就是Bar.prototype = Object.create(Foo.prototype);。这个代码应该会比较眼熟,由于在上面的例子中咱们也用到了类似的代码,而且解释说是把一个对象关联到另外一个对象上。那么这里咱们是把新建立的Bar对象的内部的[[Prototype]]连接到了指定的对象Foo.prototypeip

Bar和Foo在这里既是对象也是函数。函数有一个性质:全部的函数默认都会获得一个能够指向任何对象的属性prototype

图片描述

指向的对象每每被称为“函数的原型”。每一个由调用new Foo()而建立的对象将最终被[[Prototype]]连接到所谓的“函数的原型”。
看段代码来深刻了解一下:

function Foo(a){
    this.a = a;
}
var bar = new Foo(2);
bar.a; //2
Object.getPrototypeOf(bar) === Foo.prototype; //true

那么为何调用new Foo()建立的对象能够连接到函数原型呢?这就跟它的实现有关了。
当函数前面加new调用时,会有如下事情完成:

  • 一个全新的对象被建立
  • 这个新的对象会被接入到原型链
  • 这个新的对象被设置为函数调用的this绑定
  • 除非函数有返回对象,不然这个被new调用的函数将自动返回这个新的对象

主要咱们要看的是第二点,其余的会在this绑定的文章中讲到。
因此在上面的例子中,调用new Foo()建立bar的时候,bar会获得一个内部的[[Prototype]]连接,连接到Foo.prototype所指向的对象。

在“原型继承”的例子代码中var Baz = new Bar( "1", "obj 1" );也是使用了new来建立对象,从而使baz对象连接到bar.prototype上.

到目前为止,咱们提到了2种方法来建立新对象并与其余对象关联:

  • Object.create()
  • 函数前面加new调用

表面上看好像均可以达到咱们的目的,可是第二种方法可能会有意想不到的反作用产生。假如说Foo()这个函数还能够改变状态或者向this添加数据属性等,那么在对象在被建立的时候就会有这些咱们可能自己并不须要的东西。

最后用一张图来总结一下原型继承:(图来源:You-Dont-Know-JS)
图片描述

从图中能够看出,箭头由右至左,由下至上。而[[Prototype]]机制,就是一种存在于对象上的内部连接,指向另外一个对象。

事实上,“原型继承”这个词很容易让人产生误会,由于JavaScript中没有类,也就没有继承。实际上,Bar与Foo不能说是继承的关系,用准确点的术语来讲,是委托。因此接下来咱们就要来了解一下委托了。
很简单,委托实际上就是原型链。好比说新建对象a连接到对象b,a能够使用b的方法,也就是a委托了b(来实现它想要实现的效果)。这样的话直接用Object.create就能够了,不须要再调用new函数了。var a = Object.create(a);

相关文章
相关标签/搜索