图解Javascript原型链

jopen 9年前

 

本文尝试阐述Js中原型(prototype)、原型链(prototype chain)等概念及其作用机制。上一篇文章( 图解Javascript上下文与作用域 )介绍了Js中变量作用域的相关概念,实际上关注的一个核心问题是:“在执行当前这行代码时Js解释器可以获取哪些变量”,而原型与原型链实际上还是关于这一问题。

我们知道,在Js中一切皆为对象(Object),但是Js中并没有类(class);Js是基于原型(prototype-based)来实现的面向对象(OOP)的编程范式的,但并不是所有的对象都拥有prototype这一属性:

var a = {};    console.log(a.prototype);  //=> undefined    var b = function(){};    console.log(b.prototype);  //=> {}    var c = 'Hello';    console.log(c.prototype);  //=> undefined  

prototype是每个function定义时自带的属性,但是Js中function本身也是对象,我们先来看一下下面几个概念的差别:

1.function、Function、Object和{}

function是Js的一个关键词,用于定义函数类型的变量,有两种语法形式:

function f1(){      console.log('This is function f1!');  }  typeof(f1);  //=> 'function'    var f2 = function(){      console.log('This is function f2!');  }  typeof(f2);  //=> 'function'  

如果用更加面向对象的方法来定义函数,可以用Function:
var f3 = new Function("console.log('This is function f3!');");    f3();        //=> 'This is function f3!'    typeof(f3);  //=> 'function'    typeof(Function); //=> 'function'  

实际上Function就是一个用于构造函数类型变量的类,或者说是函数类型实例的构造函数(constructor);与之相似有的Object或String、Number等,都是Js内置类型实例的构造函数。比较特殊的是Object,它用于生成对象类型,其简写形式为{}:
var o1 = new Object();    typeof(o1);      //=> 'object'    var o2 = {};    typeof(o2);     //=> 'object'    typeof(Object); //=> 'function'  

2.prototypeVS__proto__

清楚了上面的概念之后再来看prototype:

Each function has two properties:lengthandprototype

prototype和length是每一个函数类型自带的两个属性,而其它非函数类型并没有(开头的例子已经说明),这一点之所以比较容易被忽略或误解,是因为所有类型的构造函数本身也是函数,所以它们自带了prototype属性:

// Node  console.log(Object.prototype);  //=> {}    console.log(Function.prototype);//=> [Function: Empty]    console.log(String.prototype);  //=> [String: '']  

除了prototype之外,Js中的所有对象(undefined、null等特殊情况除外)都有一个内置的[[Prototype]]属性,指向它“父类”的prototype,这个内置属性在ECMA标准中并没有给出明确的获取方式,但是许多Js的实现(如Node、大部分浏览器等)都提供了一个proto属性来指代这一[[Prototype]],我们通过下面的例子来说明实例中的proto是如何指向构造函数的prototype的:
var Person = function(){};    Person.prototype.type = 'Person';    Person.prototype.maxAge = 100;    var p = new Person();    console.log(p.maxAge);    p.name = 'rainy';    Person.prototype.constructor === Person;  //=> true    p.__proto__ === Person.prototype;         //=> true    console.log(p.prototype);                 //=> undefined  

上面的代码示例可以用下图解释:

图解Javascript原型链

Person是一个函数类型的变量,因此自带了prototype属性,prototype属性中的constructor又指向Person本身;通过new关键字生成的Person类的实例p1,通过__proto__属性指向了Person的原型。这里的__proto__只是为了说明实例p1在内部实现的时候与父类之间存在的关联(指向父类的原型),在实际操作过程中实例可以直接通过.获取父类原型中的属性,从而实现了继承的功能。

3. 原型链

清楚了prototype与__proto__的概念与关系之后我们会对“Js中一切皆为对象”这句话有更加深刻的理解。进而我们会想到,既然__proto__是(几乎)所有对象都内置的属性,而且指向父类的原型,那是不是意味着我们可以“逆流而上”一直找到源头呢?我们来看下面的例子:

// Node  var Obj = function(){};    var o = new Obj();    o.__proto__ === Obj.prototype;  //=> true    o.__proto__.constructor === Obj; //=> true    Obj.__proto__ === Function.prototype; //=> true    Obj.__proto__.constructor === Function; //=> true    Function.__proto__ === Function.prototype; //=> true    Object.__proto__ === Object.prototype;     //=> false    Object.__proto__ === Function.prototype;   //=> true    Function.__proto__.constructor === Function;//=> true    Function.__proto__.__proto__;               //=> {}    Function.__proto__.__proto__ === o.__proto__.__proto__; //=> true    o.__proto__.__proto__.__proto__ === null;   //=> true  

图解Javascript原型链

从上面的例子和图解可以看出,prototype对象也有__proto__属性,向上追溯一直到null。

4.new和instanceof

5. 原型与继承

http://pivotallabs.com/javascript-constructors-prototypes-and-the-new-keyword/

the Object type in ECMAScript is the base from which all other objects are derived

note that objects in the scope chain also have a prototype chain, so searching may include each object’s prototype chain

Each function has two properties:lengthandprototype

The prototype property is perhaps the most interesting part of the ECMAScript core. The prototype is the actual location of all instance methods for reference types, meaning methods such as toString() and valueOf() actually exist on the prototype and are then accessed from the object instances. This property is very important in terms of defining your own reference types and inheritance. (These topics are covered in Chapter 6.) In ECMAScript 5, the prototype property is not enumerable and so will not be found using for-in.

P185

How Prototypes Work

http://www.ruanyifeng.com/blog/2010/05/object-oriented javascript encapsulation.html

首先要区分prototype&[[Prototype]]/__proto__

  1. function Function Object {}

  2. Object的默认属性与方法

  3. prototype VS proto

  4. constructor ?

  5. prototype chain

  6. instanceof new

  7. 原型与继承