javaScript之原型

[[prototype]]

JavaScript中的对象有一个特殊的 [[prototype]] 内置属性,其实就是对于其他对象的引用。几乎所有的对象在创建时 [[prototype]] 属性都会被赋予一个非空的值。

Object.create(..)会创建一个对象并把这个对象的 [[prototype]] 关联到指定的对象。

var obj = {
	a: 2
};
var obj1 = Object.create(obj);
console.log(obj1.a);  //2

 

Object.prototype

所有普通的 [[prototype]] 链最终都会指向内置的 Object.prototype

 

"类" 函数

所有的函数默认都会拥有一个名为prototype的共有的不可枚举的属性,它会指向另一个对象。

function  Foo() {
	
}

Foo.prototype;

这个对象通常被称为Foo的原型。

这个对象是在调用 new Foo()时创建的,最后会被(有点武断地)关联到这个“Foo.prototype”对象上。

new Foo()这个函数调用实际上并没有直接创建关联,这个关联只是一个意外的副作用。new Foo()只是间接完成了我们的目标:一个关联到其他对象的新对象。

 

“构造函数”

最准确的解释: 所有带new的函数调用。

函数不是构造函数,但是当且仅当使用new时,函数调用会变成“构造函数调用”。

function  Foo() {
	
}
Foo.prototype.constructor === Foo; // true

var a = new Foo();
a.constructor === Foo; // true

Foo.prototype默认有一个公有并且不可枚举的属性.constructor,这个属性引用的是对象关联的函数。

通过“构造函数”调用new Foo()创建的对象也有一个.constructor属性,指向“创建这个对象的函数”。

实际上,.constructor引用同样被委托给了Foo.prototype,而Foo.prorotype.constructor默认指向Foo。

.constructor只是通过默认的 [[prototype]] 委托指向Foo

function  Foo() {
	
}
Foo.prototype = {};

var a = new Foo();
console.log(a.constructor === Foo); // false
console.log(a.constructor === Object); // true

a没有.constructor属性,所以它会委托 [[prototype]] 链上的Foo.prototype,但这个对象也没有.constructor属性;所以它继续委托,这次委托给委托链顶端的Object.prototype,这个对象有.constructor属性,指向内置的object(..)函数。

 

(原型)继承

// ES6之前需要创建一个新对象然后把旧对象抛弃掉,不能直接修改已有的默认对象Bar.prototype
Bar.prototype = Object.create( Foo.prototype);

// ES6开始可以直接修改现有的 Bar.prototype
Object.setPrototypeOf(Bar.prototype, Foo.prototype);

在传统的面向类环境中,检查一个实例的继承祖先(js中的委托关联)通常被称为内省(或反射)

a instanceof Foo;

instanceof 操作符的左操作数是一个对象,右操作符是一个函数,

回答的是:在a的整条[[prototype]]链中是否有指向Foo.prototype的对象。

Foo.prototype.isPrototypeOf(a);

isPrototypeOf 回答的是: 在a的整条 [[prototype]] 链中是否出现过Foo.prototype

 

在ES5中,标准地获取一个对象的 [[prototype]] 链的方法:

Object.getPrototypeOf(a);

绝大多数(不是所有!)浏览器也支持一种非标准的方法来访问内部 [[prototype]] 属性:

a.__proto__ === Foo.prototype;  // true

__proto__(在ES6之前并不是标准!)属性“神奇地”引用了内部的 [[prototype]] 对象,和其它的常用函数( .toString( ) 、.isPrototypeOf( ) , 等等)一样,存在于内置的Object.prototype中。(它们是不可枚举的)。

__proto__看起来很像一个属性,但实际上它更像一个getter/setter

.__proto__的实现大致上是这样:

Object.defineProperty(Object.prototype, "__proto__", {
    get: function() {
        return Object.getPrototypeOf(this);
    },
    set: functional(o) {
        Object.setPrototypeOf(this, o);
        return o;
    }
})

因此,访问(获取值)a.__proto__时,实际上是调用了a.__proto__()(调用getter函数)。虽然getter函数存在于Object.prototype对象中,但是它的this指向对象a,所以和Object.getPrototypeOf(a)结果相同。

 

对象关联

[[prototype]] 机制就是存在于对象中的一个内部链接,它会引用其他对象。

这个链接的作用:如果在对象上没有找到需要的属性或者方法引用,引擎会继续在 [[prototype]] 关联的对象上继续查找。 同理,如果在后者中也没有找到需要的引用就会继续查找它的 [[prototype]] , 以此类推。 这一系列对象的链接被成为“原型链”。

 

Object.create(..)会创建一个新对象并把它关联到我们指定的对象,这样我们就可以充分发挥 [[prototype]] 极致的威力(委托)并且避免不必要的麻烦(比如使用new的构造函数调用会生成.prototype和.constructor引用)

Object.create(null)会创建一个拥有空(null) [[prototype]] 链接的对象,这个对象无法进行委托。由于这个对象没有原型链,所以instanceof操作符无法进行判断,因此总是会返回false。这些特殊的空 [[prototype]] 对象通常被称作“字典”,它们完全不会受到原型链的干扰,因此非常适合用来存储数据。

 

Object.create() 的pollyfill代码

// 部分实现了 Object.create(..)的功能
if(!object.create){
    Object.create = function(o) {
        function F(){}
        F.prototype = o;
        return new F();
    }
}

这段polyfill代码使用了一个一次性函数F,我们通过改写它的 .prototype 属性使其指向想要关联的对象,然后再使用new F()来构造一个新对象进行关联。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章