菜单

JavaScript深入之bind的法实现

2018年11月16日 - 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 的当儿,只招一个
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 赞 收藏
    评论

图片 1

相关文章

发表评论

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

网站地图xml地图