Skip to content

Commit af9247d

Browse files
committed
完善文章底部文字
1 parent a524518 commit af9247d

11 files changed

+154
-112
lines changed

JavaScript深入之bind的实现.md renamed to JavaScript深入之bind的模拟实现.md

Lines changed: 59 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
# JavaScript深入之bind的模拟实现
22

3+
## bind
4+
35
一句话介绍bind:
46

57
>bind()方法会创建一个新函数。当这个新函数被调用时,bind()的第一个参数将作为它运行时的 this, 之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于MDN)
68
79
由此我们可以首先得出bind函数的两个特点:
10+
811
1. 返回一个函数
912
2. 可以传入参数
1013

11-
我们从实现第一点开始,我们先举个例子:
14+
## 返回函数的模拟实现
15+
16+
从第一个特点开始,我们举个例子:
1217

1318
```js
1419
var foo = {
@@ -25,10 +30,10 @@ var bindFoo = bar.bind(foo);
2530
bindFoo(); // 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+
// 第一版
3237
Function.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
4451
var 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+
// 第二版
6875
Function.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
9099
var 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+
// 第三版
123134
Function.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+
// 第四版
153162
Function.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-
210201
var value = 2;
211202
var foo = {
212203
value: 1,
@@ -220,37 +211,39 @@ function bar() {
220211
foo.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
232223
if (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
241233
Function.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
250243
Function.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

Comments
 (0)