11# JavaScript深入之bind的模拟实现
22
3+ ## bind
4+
35一句话介绍bind:
46
57> bind()方法会创建一个新函数。当这个新函数被调用时,bind()的第一个参数将作为它运行时的 this, 之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于MDN)
68
79由此我们可以首先得出bind函数的两个特点:
10+
8111 . 返回一个函数
9122 . 可以传入参数
1013
11- 我们从实现第一点开始,我们先举个例子:
14+ ## 返回函数的模拟实现
15+
16+ 从第一个特点开始,我们举个例子:
1217
1318``` js
1419var foo = {
@@ -25,10 +30,10 @@ var bindFoo = bar.bind(foo);
2530bindFoo (); // 1
2631```
2732
28- 关于指定this的指向,我们可以使用call或者apply实现,关于call和apply的模拟实现,可以查看《JavaScript深入之call和apply的模拟实现》,那么这个实现看起来很简单 :
33+ 关于指定this的指向,我们可以使用call或者apply实现,关于call和apply的模拟实现,可以查看《JavaScript深入之call和apply的模拟实现》,底部有相关链接。我们来写第一版的代码 :
2934
3035``` js
31- // 第一版的代码
36+ // 第一版
3237Function .prototype .bind2 = function (context ) {
3338 var self = this ;
3439 return function () {
@@ -38,7 +43,9 @@ Function.prototype.bind2 = function (context) {
3843}
3944```
4045
41- 接下看第二点,可以传入参数,这个就有点让人思考了,我在bind的时候,可以传参,我执行bind返回的函数的时候,可不可以传参呢?让我们看个例子:
46+ ## 传参的模拟实现
47+
48+ 接下来看第二点,可以传入参数。这个就有点让人费解了,我在bind的时候,可以传参,我执行bind返回的函数的时候,可不可以传参呢?让我们看个例子:
4249
4350``` js
4451var foo = {
@@ -59,12 +66,12 @@ bindFoo('18');
5966// 18
6067```
6168
62- 神呐,竟然都可以传参! 函数需要传name和age两个参数, 竟然还可以在bind的时候,只传一个name, 在执行返回的函数的时候,再传另一个参数age!
69+ 函数需要传name和age两个参数, 竟然还可以在bind的时候,只传一个name, 在执行返回的函数的时候,再传另一个参数age!
6370
6471这可咋办,不急,我们用arguments对象进行处理:
6572
6673``` js
67- // 第二版的代码
74+ // 第二版
6875Function .prototype .bind2 = function (context ) {
6976
7077 var self = this ;
@@ -80,11 +87,13 @@ Function.prototype.bind2 = function (context) {
8087}
8188```
8289
90+ ## 构造函数效果的模拟实现
91+
8392完成了这两点,最难的部分到啦!因为bind还有一个特点,就是
8493
8594> 一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
8695
87- 也就是说当bind返回的函数作为构造函数的时候,bind时指定的this值会失效,但传入的参数依然生效。
96+ 也就是说当bind返回的函数作为构造函数的时候,bind时指定的this值会失效,但传入的参数依然生效。举个例子:
8897
8998``` js
9099var value = 2 ;
@@ -114,77 +123,73 @@ console.log(obj.friend);
114123// kevin
115124```
116125
117- 注意:尽管全局声明了value值,最后依然返回了undefind,说明虽然bind时执行的this会失效,但也不会指向全局,而且obj继承了bar的属性以及原型中的属性。
126+ 注意:尽管在全局和foo中都声明了value值,最后依然返回了undefind,说明绑定的this失效了,如果大家了解new的模拟实现,就会知道这个时候的this已经指向了obj。
127+
128+ (哈哈,我这是为我的下一篇文章《JavaScript深入系列之new的模拟实现》打广告)。
118129
119130所以我们可以通过修改返回的函数的原型来实现,让我们写一下:
120131
121132``` js
122- // 第三版的代码
133+ // 第三版
123134Function .prototype .bind2 = function (context ) {
124135
125136 var self = this ;
126- // 获取bind2函数从第二个参数到最后一个参数
127137 var args = Array .prototype .slice .call (arguments , 1 );
128138
129139 var fbound = function () {
130- // 这个时候的arguments是指bind返回的函数传入的参数
140+
131141 var bindArgs = Array .prototype .slice .call (arguments );
132- // 当通过构造函数new出一个实例的时候,this指向这个实例
133- // 这个时候 this instanceof self 为 true
134- // self.apply(this) 构造的实例就可以拥有self的属性
142+ // 当通过构造函数new出一个实例的时候,会执行this instanceof self的判断,此时结果为true,将this指向构造的实例。
135143 self .apply (this instanceof self ? this : context, args .concat (bindArgs));
144+ // 如果想不明白为什么new的时候,会执行instanceof的判断以及为什么this instanceof self的结果会为true,就只能看下一篇文章《JavaScript深入系列之new的模拟实现》,虽然明天才发布……
136145 }
137- // 修改返回函数的prototype为函数的prototype,就可以继承bar原型中的值
146+ // 修改返回函数的prototype为函数的prototype,new的实例就可以继承函数的原型中的值
138147 fbound .prototype = this .prototype ;
139148 return fbound;
140149
141150}
142151
143152```
144153
145- 如果对原型链稍有困惑,可以查看《JavaScript深入之从原型到原型链》
154+ 如果对原型链稍有困惑,可以查看《JavaScript深入之从原型到原型链》。
146155
147- 但是在这个写法中,我们直接将fbound.prototype = this.prototype,这样我们直接修改fbound.prototype的时候,就会也直接修改了函数的prototype
156+ ## 构造函数效果的优化实现
148157
149- 这个时候,我们可以通过一个空函数来进行中转:
158+ 但是在这个写法中,我们直接将fbound.prototype = this.prototype,我们直接修改fbound.prototype的时候,也会直接修改函数的prototype。 这个时候,我们可以通过一个空函数来进行中转:
150159
151160``` js
152- // 第四版的代码
161+ // 第四版
153162Function .prototype .bind2 = function (context ) {
154163
155164 var self = this ;
156- // 获取bind2函数从第二个参数到最后一个参数
157165 var args = Array .prototype .slice .call (arguments , 1 );
158166
159167 var fNOP = function () {};
160168
161169 var fbound = function () {
162- // 这个时候的arguments是指bind返回的函数传入的参数
163170 var bindArgs = Array .prototype .slice .call (arguments );
164- // 当通过构造函数new出一个实例的时候,this指向这个实例
165- // 这个时候 this instanceof self 为 true
166- // self.apply(this) 构造的实例就可以拥有self的属性
167171 self .apply (this instanceof self ? this : context, args .concat (bindArgs));
168172 }
169- // 修改返回函数的prototype为函数的prototype,就可以继承bar原型中的值
170173 fNOP .prototype = this .prototype ;
171174 fbound .prototype = new fNOP ();
172175 return fbound;
173176
174177}
175178```
176179
177- 到此为止,大的问题都已经解决,o( ̄▽ ̄)d
180+ 到此为止,大的问题都已经解决,给自己一个赞!o( ̄▽ ̄)d
181+
182+ ## 三个小问题
178183
179184接下来处理些小问题:
180185
181- 1 . apply这段代码跟MDN上的稍有不同
186+ ** 1. apply这段代码跟MDN上的稍有不同**
182187
183188在MDN中文版讲bind的模拟实现时,apply这里的代码是:
184189
185190``` js
186191
187- self .apply (this instanceof self ? this : context || this , args .concat (bindArgs)
192+ self .apply (this instanceof self ? this : context || this , args .concat (bindArgs))
188193
189194```
190195
@@ -193,20 +198,6 @@ self.apply(this instanceof self ? this : context || this, args.concat(bindArgs)
193198举个例子:
194199
195200``` js
196- Function .prototype .bind = function (context ) {
197- var me = this ;
198- var args = Array .prototype .slice .call (arguments , 1 );
199- var F = function () {};
200- F .prototype = this .prototype ;
201- var bound = function () {
202- var innerArgs = Array .prototype .slice .call (arguments );
203- var finalArgs = args .contact (innerArgs);
204- return me .apply (this instanceof F ? this : context || this , finalArgs);
205- }
206- bound .prototype = new fNOP ();
207- return bound;
208- }
209-
210201var value = 2 ;
211202var foo = {
212203 value: 1 ,
@@ -220,37 +211,39 @@ function bar() {
220211foo .bar () // 2
221212```
222213
223- 以上代码会打印2,如果换成了 context || this,这段代码就会打印1!
214+ 以上代码正常情况下会打印2,如果换成了context || this,这段代码就会打印1!
224215
225216所以这里不应该进行context的判断,大家查看MDN同样内容的英文版,就不存在这个判断!
226217
227-
228- 2.调用bind的不是函数咋办?
218+ ** 2.调用bind的不是函数咋办?**
229219
230220不行,我们要报错!
221+
231222``` js
232223if (typeof this !== " function" ) {
233- throw new TypeError (" Function.prototype.bind - what is trying to be bound is not callable" );
224+ throw new Error (" Function.prototype.bind - what is trying to be bound is not callable" );
234225}
235226```
236227
237- 3.我要在线上用
228+ ** 3.我要在线上用**
229+
230+ 那别忘了做个兼容:
238231
239- 那别忘了做个兼容
240232``` js
241233Function .prototype .bind = Function .prototype .bind || function () {
242234 ……
243235};
244236```
245237
246- 当然最好是用[es5-shim](https://github.com/es-shims/es5-shim)啦
238+ 当然最好是用[ es5-shim] ( https://github.com/es-shims/es5-shim ) 啦。
247239
248240所以最最后的代码就是:
241+
249242``` js
250243Function .prototype .bind2 = function (context ) {
251244
252245 if (typeof this !== " function" ) {
253- throw new TypeError (" Function.prototype.bind - what is trying to be bound is not callable" );
246+ throw new Error (" Function.prototype.bind - what is trying to be bound is not callable" );
254247 }
255248
256249 var self = this ;
@@ -268,3 +261,19 @@ Function.prototype.bind2 = function (context) {
268261
269262}
270263```
264+
265+ ## 相关链接
266+
267+ [ 《JavaScript深入之从原型到原型链》] ( https://github.com/mqyqingfeng/Blog/issues/2 )
268+
269+ [ 《JavaScript深入之call和apply的模拟实现》] ( https://github.com/mqyqingfeng/Blog/issues/11 )
270+
271+ 《JavaScript深入系列之new的模拟实现》 (明天发布)
272+
273+ ## 深入系列
274+
275+ JavaScript深入系列目录地址:[ https://github.com/mqyqingfeng/Blog ] ( https://github.com/mqyqingfeng/Blog ) 。
276+
277+ JavaScript深入系列预计写十五篇左右,旨在帮大家捋顺JavaScript底层知识,重点讲解如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。
278+
279+ 如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。
0 commit comments