注意:本文章是我的《You Don’t Know JS》的读书笔记。
在看backbone
源码的时候看到这么一小段,看上去很小,其实忽略了也没有太大理解的问题。可是不知道为何,我以为内心很难受。因此我以为必定要真正解决这个问题。这个问题就是原型。
就是下面这段代码:git
var Surrogate = function(){ this.constructor = child; }; Surrogate.prototype = parent.prototype; child.prototype = new Surrogate;
看懂了这一段简单的代码了吗?github
其实就着backbone
的注释,理解彻底没有问题。可是以后我不当心深究了一下,忽然发现本身对于这一个问题的理解还很不透彻。很惭愧,写了好一阵子的JavaScript
,可是一直都是以实用主义的角度来写的。不少时候真的把JavaScript
当成传统有class
的语言来写了。可是JavaScript
是一门很“狡猾”的语言,由于它的不少关键字仿佛都在透露本身是传统面向对象语言,instance
,constructor
,new
等等。然而事实上并非。解决这个问题的方法就是读书,我选了《You Don’t Know JS》。我快速过了相关的那一本,被这本书深深震撼到。本文是我的的学习笔记,也但愿能帮助到你。chrome
this
是函数执行的时候,内部产生的一个内部对象。在状况复杂的时候,this的指向会比较难把握。
this的指向经常不明,在书中主要列举了两种状况的混淆和四条规则,掌握了这二者,this就会比较明晰了。(还有不少很复杂的状况须要细致的分析)闭包
1,this
不是函数对象自己。函数自己也是一个对象,可是给这个对象添加属性并不能影响this
。除非foo.call(foo, i)
,这句代码的含义是:在foo对象上调用foo函数,并传递参数i
。这是一个特殊状况。
2,this
不是函数内部的做用域。所以在函数内部申明的变量若是不加其余操做,和this
根本不会有任何关系。app
这四条规则只有一个中心,就是call-site
。本质上就是指this
决定于调用的对象而不是在哪里声明。
1,直接调用函数。函数内部this
会指向全局。this
此时为window
。闭包调用亦是如此。
2,内部绑定调用。如obj.foo()
。此时foo
中的this
指的就是obj
对象。注意obj.foo()
,无论前面是否是还有其余调用,可是都不会改变this
此时为obj
。
3,直接绑定调用。主要是用了call
和apply
来调用。除此以外还有先bind
在调用的方式,这种方式很是强。直接绑定调用的this
就是指定的那个参数的对象。
4,new
方式调用。var bar = new foo()
这一句代码的做用之一就是把foo
函数中的this
所有赋到bar
上做为新的bar
对象的属性。ide
1,通常来讲直接绑定调用会比内部绑定调用有更高优先级。
2,new
在必定状况下能“覆盖”bind
调用。用这个特性能够实现函数currying
化。(函数传入多个参数,在bind的时候传入固定的变量)函数
function foo(val) { this.val = val; } var obj = {}; var bar = foo.bind(obj); bar("p1"); var baz = new bar("p2") console.log(baz.val); // p2
JavaScript
没有类,也不能建立实例。可是语法上确实很是很是面向对象,让人误解。new
就是总被误解。学习
var bar = new foo();
下面详述这一句代码执行的效果。这很是重要。
1,执行foo()
函数。是“构造方式调用”。注意,JavaScript
里面并无真正的构造函数。
2,链接foo.prototype
和新对象bar
的[[prototype]]
。(至关于把foo.prototype
总体搬到(引用)bar.__proto__
里面)
3,把foo
中的内部对象this
的属性给bar
。
4,foo
若是有返回对象就返回那个对象,没有就自动返回一个新的对象。对象特征如2,3所述。
事实上,new
作得事太多,不少时候使人讨厌。
注意:bar
和foo
函数对象自己的属性没有关系。this
在经典的教材上,JavaScript
的“继承”经常使用下面的代码:spa
Bar.prototype = new Foo();
这句代码很经典,可是并很差。它当然可以完成原型链的链接,可是也产生了不少没必要要的麻烦。Bar.prototype
上会带上Foo
的this
,反作用大。所以书上建议是这样作来完成继承的:
Bar.prototype = Object.create(Foo.prototype);
A = Object.create(B)
的做用是把B
对象的属性添加到A
的__proto__
上面,而后A
的[[prototype]]
与B
的链接。有个比较“直观”的说法,就是把B
的“属性”,添加到A.__proto__
上面,而后把B.__proto__
也添加进去(原型链延长的不严谨说法)。试着在chrome
里面console
一下,就会发现此时的A
只有一个__proto__
属性,里面是B
的属性和B.__proto__
。这样就很好地完成了“继承”,并且也没有反作用了。下面提供一个降级写法(原理很简单。按照以前的几条规则能够很轻易地读懂):
// Object.create if(!Object.create) { Object.create = function(o) { function F(){}; F.prototype = o; return new F(); } }
还须要注意的是constructor
属性。若是对象是函数,这个属性会自动出如今函数的prototype
里面,但有时候会被覆盖。
prototype
是函数对象的一个属性。其实能够理解成比较特殊的一个属性,一个对象。这个属性在用构造器方式调用(new
)的时候就用来链接新对象。链接到最后就是Object.prototype
,这是每个对象(不包括null
)中__proto__
的尽头,原型链的尽头。能够想象,对象的声明是new Object()
,那么新的对象原型链天然就会有Object.prototype
了。若是想摆脱这个Object.prototype
,能够用Object.create(null)
。
常常有这种状况,调用一个对象的属性的时候,若是在自己找不到就会沿着原型链找。直观而不严谨地说,就是从__proto__
一直找下去。这也揭露了a instanceof foo
的本质。就是沿着原型链找,看看在a
的原型链上有没有foo.prototype
的存在,有就返回true
。instanceof
这个英文很误导,由于JavaScript
里面根本上就算不上有实例这样的东西。
做者对于JavaScript
的面向对象有一个很是惊艳的解决方案,在那以前先看看之前的方案是怎么作的。
function Foo(who) { this.me = who; } Foo.prototype.identify = function() { return "I am " + this.me; }; function Bar(who) { Foo.call( this, who ); } Bar.prototype = Object.create( Foo.prototype ); Bar.prototype.speak = function() { alert( "Hello, " + this.identify() + "." ); }; var b1 = new Bar( "b1" ); var b2 = new Bar( "b2" ); b1.speak(); b2.speak();
一图胜千言:
做者给出的解决方案OLOO
(objects-linked-to-other-objects
),没有麻烦的new
,没有虚伪的constructor
,没有混淆视线的call
, apply
, bind
,原型链链接再也不赤裸裸。感受真心很棒(注意一点,JavaScript
对象的方法实际上是不是“属于”一个对象值得商榷,由于所谓方法其实和这个对象关系并无想象中大):
var Foo = { init: function(who) { this.me = who; }, identify: function() { return "I am " + this.me; } }; var Bar = Object.create( Foo ); Bar.speak = function() { alert( "Hello, " + this.identify() + "." ); }; var b1 = Object.create( Bar ); b1.init( "b1" ); var b2 = Object.create( Bar ); b2.init( "b2" ); b1.speak(); b2.speak();
仍然是一图胜千言:
最后来说讲这个:
var Surrogate = function(){ this.constructor = child; }; Surrogate.prototype = parent.prototype; child.prototype = new Surrogate;
很好懂了吧。构造形式调用Surrogate
函数,把this
上的属性交给child.prototype,这里是constructor
;把Surrogate.prototype
的内容搬到child.prototype.__proto__
上面。而Surrogate.prototype
引用自parent.prototype
。因此child
是这样的一个函数:以child
为构造函数,parent.prototype
为原型。
这个问题是老生常谈的问题了,可是我以为对于每个写JavaScript
的人来讲,这个问题是永远避不开的。最后再次推荐《You Don’t Know JS》这一套开源书。能够绝不夸张地说,这是自红宝书和犀牛书以后讲JavaScript
的最好书了。深度深得恰到好处,语言和例子清晰简明,我还要继续学习。
有关 this & Object Prototypes
,书的内容远远比我这篇文章丰富精彩,去读吧。(我也要复反复读)
若是有任何错误请轻喷,相互学习~