菜单

JavaScript 深入之bind的套实现

2018年11月15日 - JavaScript

JavaScript 深入之bind的拟实现

2017/05/26 · JavaScript
· bind

原稿出处: 冴羽   

已去简书,原因参见
http://www.jianshu.com/p/0f12350a6b66。

bind

同词话介绍 bind:

bind() 方法会创建一个初函数。当是新函数被调用时,bind()
的第一个参数将作为其运行时之
this,之后的一律队列参数将会当传递的实参前传出作为它们的参数。(来自于 MDN
)

通过我们可以率先得出 bind 函数的少独特点:

  1. 回去一个函数
  2. 好传参数

虽人微言轻,但也只要来友好的千姿百态。

返回函数的依样画葫芦实现

从第一单特性开始,我们举个例子:

var foo = { value: 1 }; function bar() { console.log(this.value); } //
返回了一个函数 var bindFoo = bar.bind(foo); bindFoo(); // 1

1
2
3
4
5
6
7
8
9
10
11
12
var foo = {
    value: 1
};
 
function bar() {
    console.log(this.value);
}
 
// 返回了一个函数
var bindFoo = bar.bind(foo);
 
bindFoo(); // 1

关于指定 this 的针对性,我们可用 call 或者 apply 贯彻,关于 call 和
apply
的依样画葫芦实现,可以查看《JavaScript深入的call和apply的效仿实现》。我们来形容第一版本的代码:

// 第一版 Function.prototype.bind2 = function (context) { var self =
this; return function () { self.apply(context); } }

1
2
3
4
5
6
7
8
// 第一版
Function.prototype.bind2 = function (context) {
    var self = this;
    return function () {
        self.apply(context);
    }
 
}

章好于自家之 Github
https://github.com/mqyqingfeng/Blog
查看

传参的学实现

紧接下去看第二触及,可以传参数。这个就算闹硌被人费解了,我以 bind
的时刻,是否可以传参呢?我于推行 bind
返回的函数的当儿,可免可以传参呢?让咱们看个例证:

var foo = { value: 1 }; function bar(name, age) {
console.log(this.value); console.log(name); console.log(age); } var
bindFoo = bar.bind(foo, ‘daisy’); bindFoo(’18’); // 1 // daisy // 18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var foo = {
    value: 1
};
 
function bar(name, age) {
    console.log(this.value);
    console.log(name);
    console.log(age);
 
}
 
var bindFoo = bar.bind(foo, ‘daisy’);
bindFoo(’18’);
// 1
// daisy
// 18

函数需要传 name 和 age 两独参数,竟然还可以 bind 的时段manbetx2.0手机版,只招一个
name,在实行回来的函数的时节,再传另一个参数 age!

即可咋办?不着急,我们就此 arguments 进行处理:

// 第二版本 Function.prototype.bind2 = function (context) { var self =
this; // 获取bind2函数自第二只参数到最终一个参数 var args =
Array.prototype.slice.call(arguments, 1); return function () { //
这个时的arguments是指bind返回的函数传入的参数 var bindArgs =
Array.prototype.slice.call(arguments); self.apply(context,
args.concat(bindArgs)); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 第二版
Function.prototype.bind2 = function (context) {
 
    var self = this;
    // 获取bind2函数从第二个参数到最后一个参数
    var args = Array.prototype.slice.call(arguments, 1);
 
    return function () {
        // 这个时候的arguments是指bind返回的函数传入的参数
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(context, args.concat(bindArgs));
    }
 
}

构造函数效果的拟实现

做到了这片点,最为难之组成部分及啦!因为 bind 还有一个风味,就是

一个绑定函数也能采用new操作符创建对象:这种表现就像把原本函数当成构造器。提供的
this 值被忽略,同时调用时的参数为提供给学函数。

也就是说当 bind 返回的函数作为构造函数的下,bind 时指定的 this
值会失效,但传播的参数还奏效。举个例子:

var value = 2; var foo = { value: 1 }; function bar(name, age) {
this.habit = ‘shopping’; console.log(this.value); console.log(name);
console.log(age); } bar.prototype.friend = ‘kevin’; var bindFoo =
bar.bind(foo, ‘daisy’); var obj = new bindFoo(’18’); // undefined //
daisy // 18 console.log(obj.habit); console.log(obj.friend); // shopping
// kevin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var value = 2;
 
var foo = {
    value: 1
};
 
function bar(name, age) {
    this.habit = ‘shopping’;
    console.log(this.value);
    console.log(name);
    console.log(age);
}
 
bar.prototype.friend = ‘kevin’;
 
var bindFoo = bar.bind(foo, ‘daisy’);
 
var obj = new bindFoo(’18’);
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin

专注:尽管当大局与 foo 中还宣示了 value 值,最后还返回了
undefind,说明绑定的 this 失效了,如果大家探听 new
的模拟实现,就见面掌握这个时候的 this 已经指向了 obj。

(哈哈,我就是吧自家之生一样首文章《JavaScript深入系列的new的模仿实现》打广告)。

因此我们得以经过改返回的函数的原型来贯彻,让咱描绘一下:

// 第三版 Function.prototype.bind2 = function (context) { var self =
this; var args = Array.prototype.slice.call(arguments, 1); var fbound =
function () { var bindArgs = Array.prototype.slice.call(arguments); //
当作为构造函数时,this 指向实例,self 指向绑定函数,因为下面一句
`fbound.prototype = this.prototype;`,已经修改了 fbound.prototype 为
绑定函数的 prototype,此时结果吧 true,当结果为 true 的时刻,this
指向实例。 // 当作为一般函数时,this 指向 window,self
指向绑定函数,此时结果吧 false,当结果为 false 的时,this 指向绑定的
context。 self.apply(this instanceof self ? this : context,
args.concat(bindArgs)); } // 修改返回函数的 prototype 为绑定函数的
prototype,实例就可以继续函数的原型中之值 fbound.prototype =
this.prototype; return fbound; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 第三版
Function.prototype.bind2 = function (context) {
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
 
    var fbound = function () {
 
        var bindArgs = Array.prototype.slice.call(arguments);
        // 当作为构造函数时,this 指向实例,self 指向绑定函数,因为下面一句 `fbound.prototype = this.prototype;`,已经修改了 fbound.prototype 为 绑定函数的 prototype,此时结果为 true,当结果为 true 的时候,this 指向实例。
        // 当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,this 指向绑定的 context。
        self.apply(this instanceof self ? this : context, args.concat(bindArgs));
    }
    // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承函数的原型中的值
    fbound.prototype = this.prototype;
    return fbound;
}

假若对原型链稍有疑惑,可以查看《JavaScript深入的从原型到原型链》。

构造函数效果的优化实现

然而于斯写法被,我们一直将 fbound.prototype =
this.prototype,我们一直改动 fbound.prototype 的时候,也会见直接改动函数的
prototype。这个时刻,我们得经一个空函数来展开转账:

// 第四版 Function.prototype.bind2 = function (context) { var self =
this; var args = Array.prototype.slice.call(arguments, 1); var fNOP =
function () {}; var fbound = function () { var bindArgs =
Array.prototype.slice.call(arguments); self.apply(this instanceof self ?
this : context, args.concat(bindArgs)); } fNOP.prototype =
this.prototype; fbound.prototype = new fNOP(); return fbound; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 第四版
Function.prototype.bind2 = function (context) {
 
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
 
    var fNOP = function () {};
 
    var fbound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(this instanceof self ? this : context, args.concat(bindArgs));
    }
    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();
    return fbound;
 
}

顶之结束,大之问题且曾缓解,给协调一个赞!o( ̄▽ ̄)d

其三个小问题

属下去处理些稍问题:

1.apply 这段代码跟 MDN 上的有点有两样

当 MDN 中文版讲 bind 的法实现时,apply 这里的代码是:

self.apply(this instanceof self ? this : context || this,
args.concat(bindArgs))

1
self.apply(this instanceof self ? this : context || this, args.concat(bindArgs))

大多矣一个有关 context 是否在的判定,然而此是错的!

推个例:

var value = 2; var foo = { value: 1, bar: bar.bind(null) }; function
bar() { console.log(this.value); } foo.bar() // 2

1
2
3
4
5
6
7
8
9
10
11
var value = 2;
var foo = {
    value: 1,
    bar: bar.bind(null)
};
 
function bar() {
    console.log(this.value);
}
 
foo.bar() // 2

如上代码正常情况下会打印 2,如果换成了 context || this,这段代码就会见打印
1!

据此这里不应有进行 context 的判定,大家查看 MDN
同样内容的英文版,就无在这个论断!

2.调于是 bind 的莫是函数咋办?

老,我们如果报错!

if (typeof this !== “function”) { throw new
Error(“Function.prototype.bind – what is trying to be bound is not
callable”); }

1
2
3
if (typeof this !== "function") {
  throw new Error("Function.prototype.bind – what is trying to be bound is not callable");
}

3.本身要是在线上之所以

这就是说别忘了举行只门当户对:

Function.prototype.bind = Function.prototype.bind || function () { …… };

1
2
3
Function.prototype.bind = Function.prototype.bind || function () {
    ……
};

自最好是用es5-shim啦。

末代码

故最好末之代码就是:

Function.prototype.bind2 = function (context) { if (typeof this !==
“function”) { throw new Error(“Function.prototype.bind – what is trying
to be bound is not callable”); } var self = this; var args =
Array.prototype.slice.call(arguments, 1); var fNOP = function () {}; var
fbound = function () { self.apply(this instanceof self ? this : context,
args.concat(Array.prototype.slice.call(arguments))); } fNOP.prototype =
this.prototype; fbound.prototype = new fNOP(); return fbound; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Function.prototype.bind2 = function (context) {
 
    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind – what is trying to be bound is not callable");
    }
 
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var fNOP = function () {};
 
    var fbound = function () {
        self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
    }
 
    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();
 
    return fbound;
 
}

深刻系列

JavaScript深入系列目录地址:https://github.com/mqyqingfeng/Blog。

JavaScript深入系列预计写十五首左右,旨在救助大家捋顺JavaScript底层知识,重点讲解如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。

假如发荒唐或不谨慎的地方,请务必与指正,十分谢谢。如果喜欢还是持有启发,欢迎star,对笔者吧是同等种植鞭策。

本系列:

  1. JavaScirpt 深入之于原型到原型链
  2. JavaScript
    深入的词法作用域和动态作用域
  3. JavaScript 深入的实施上下文栈
  4. JavaScript 深入之变量对象
  5. JavaScript 深入的图域链
  6. JavaScript 深入之从 ECMAScript 规范解读
    this
  7. JavaScript 深入之实施上下文
  8. JavaScript 深入的闭包
  9. JavaScript 深入之参数按值传递
  10. JavaScript
    深入之call和apply的模拟实现

    1 赞 收藏
    评论

manbetx2.0手机版 1

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图