图解 Javascript 原型链 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
irainy
V2EX    Javascript

图解 Javascript 原型链

  •  
  •   irainy 2015-07-20 18:25:05 +08:00 3330 次点击
    这是一个创建于 3811 天前的主题,其中的信息可能已经有所发展或是发生改变。

    >> 原文 <<

    本文尝试阐述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. functionFunctionObject{}

    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);与之相似有的ObjectStringNumber等,都是Js内置类型实例的构造函数。比较特殊的是Object,它用于生成对象类型,其简写形式为{}

    var o1 = new Object(); typeof(o1); //=> 'object' var o2 = {}; typeof(o2); //=> 'object' typeof(Object); //=> 'function' 

    2. prototype VS __proto__

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

    Each function has two properties: length and prototype

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

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

    除了prototype之外,Js中的所有对象(undefinednull等特殊情况除外)都有一个内置的[[Prototype]]属性,指向它“父类”的prototype,这个内置属性在ECMA标准中并没有给出明确的获取方式,但是许多Js的实现(如Node、大部分浏览器等)都提供了一个__proto__属性来指代这一[[Prototype]],我们通过下面的例子来说明实例中的__proto__是如何指向构造函数的prototype的:

    pre>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

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

    js __proto__

    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 

    js prototype chain

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

    new关键词的作用就是完成上图所示实例与父类原型之间关系的串接,并创建一个新的对象;instanceof关键词的作用也可以从上图中看出,实际上就是判断__proto__(以及__proto__.__proto__...)所指向是否父类的原型:

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

    参考

    1. Javascript constructors, prototypes, and the new keyword
    2. Javascript 面向对象编程
    3. Professional Javascript for Web Developers
    5 条回复    2015-07-21 15:01:19 +08:00
    sztimhdd
        1
    sztimhdd  
       2015-07-21 09:46:41 +08:00
    认真读了 一下子豁然开朗 感谢 @irainy 分享~

    有一处疑问,为什么下例中p.prototype是undefine呢?
    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 ???
    irainy
        2
    irainy  
    OP
       2015-07-21 10:01:48 +08:00
    @sztimhdd

    p is not a function.

    ```js
    typeof p
    //=> 'object'
    ```
    windyboy
        3
    windyboy  
       2015-07-21 11:22:46 +08:00
    清楚明了
    感谢!
    zonghua
        4
    zonghua  
       2015-07-21 14:19:30 +08:00
    有人能告诉我上面那种字符画的图是怎么做出来的吗?
    irainy
        5
    irainy  
    OP
       2015-07-21 15:01:19 +08:00
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2775 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 14:40 PVG 22:40 LAX 06:40 JFK 09:40
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86