理解javascript中的原型

概述

javascript的灵魂应该就是原型了吧,可以说在js里一切皆有原型的影子,js中用原型实现继承,使得我们在实例对象中除了可以访问实例对象自己的属性外,还可以访问到它的原型的属性,以及它的原型的原型的属性,只要在它的原型链里面我们就都能访问到。不过如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。JavaScript的每个对象都继承自另一个对象,后者称为“原型”(prototype)对象。只有null除外,它没有自己的原型对象。比如说我们定义一个简单的对象:

var o = {
  a:1,
  b:2
}
// 这里,我们不仅可以访问o自己的属性o.a,我们还可以访问的o的原型Object.prototype的所有属性,比如o.toString(),toString就是Object.prototype里的属性,Object.prototype也有自己的原型:null,null没有原型。

js提供了一个获取某个对象原型的方法Object.getPrototypeOf(),我们可以用这个方法来查看o的原型:

var oPrototype = Object.getPrototypeOf(o);
//我们会发现o的原型就等于下面的这个东西:
oPrototype === Object.prototype;

//而Object.prototype的原型等于null这个鬼东西
Object.getPrototypeOf(Object.prototype) === null;

js中创建对象的方法

js中有至少3种方法来创建对象(同时会生成所创建对象的原型链):

使用普通语法创建对象

var obj1 = {};

//这个时候obj1的原型链:
Object.getPrototypeOf(obj1) === Object.prototype;
Object.getPrototypeOf(Object.prototype) === null;

使用Object.create创建对象

var obj2 = Object.create(Object.prototype);

//这里的obj2其实和obj1是一毛一样的,{}其实相当于Object.create的语法糖,obj2的原型链也和obj1的一样,Object.create方法的第一个参数就是显式的指定要创建的对象的原型。

使用函数创建对象

//Object其实是一个函数

var obj3 = new Object();

//这里的obj3其实和obj1和2是一样的,用new来创建对象时,会把函数的prototype属性(这个示例中就是Object.prototype)作为创建对象的原型,也就是
Object.getPrototypeOf(obj3) === Object.prototype;

//事实上所有的函数都拥有一个prototype属性(事实上也只有函数才拥有这个属性),所有使用new来创建的对象,他们的原型均是该函数的prototype,比如:

var F = function(){

};
var a = new F();

//这是a的原型
Object.getPrototypeOf(a) === F.prototype;

//我们可以a被创建后或创建之前给F.prototype增加一些属性,由于a的原型是F.prototype,所以在a上就能访问到F.prototype的所有属性

F.prototype.test = function(){
  return 1;
}
F.prototype.value = 2;

console.log(a.test()); // 1
console.log(a.value); //2


//所有继承F的对象,都拥有F.prototype的所有属性
var b = new F();

b.test();// 1  
b.value; //2

理解js中常见类型的原型链

这个时候,我们可以看看js中常见的一些类型比如数组、函数的原型链。

var arr1 = [];
//上面这个定义与下面的这个等价:
var arr2 = new Array(); //Array其实就是个函数,js环境默认给Array函数的prototype添加了一些属性,比如join,push等,所以数组其实就是Array函数的一个实例化对象,所以我们可以在数组中使用Array.prototype中所有的属性

//arr1和arr2的原型链均如下:
Object.getPrototypeOf(arr1) === Array.prototype;
Object.getPrototypeOf(Array.prototype) === Object.prototype;
Object.getPrototypeOf(Object.prototype) === null;
//null其实是所有对象原型的老祖宗

我们再来看看函数:

var fun1 = function(arg){return 1;}
var fun2 = new Function("arg","return 1;");
//fun1和fun2是全等的(但对于js引擎来讲fun1的效率更高,所以不推荐用fun2这种来定义函数
// fun1和fun2的原型链均如下:
Object.getPrototypeOf(fun1) === Function.prototype;
Object.getPrototypeOf(Function.prototype) === Object.prototype;
Object.getPrototypeOf(Object.prototype) === null;

在这里,我们不能用Object.create()来创建函数对象和数组,为什么呢?

//Object.create这个方法的定义其实就是下面这个:
Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
  };

//所以我们只能说只要能用create来创建的对象就一定能用new来创建,反之则不然。因为使用new的话F这个函数里面可能要做一些处理,但是Object.create就无法做任何处理。