新浦京娱乐场官网-301net-新浦京娱乐www.301net
做最好的网站

由此我们可以首先得出 bind 函数的两个特点

JavaScript 深切之bind的模拟完毕

2017/05/26 · JavaScript · bind

原稿出处: 冴羽   

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);
    }
 
}

传参的效仿完结

接下去看第二点,能够流传参数。那一个就有一些令人费解了,作者在 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深刻类别目录地址:。

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

本文由新浦京娱乐场官网-301net-新浦京娱乐www.301net发布于www.301net,转载请注明出处:由此我们可以首先得出 bind 函数的两个特点

您可能还会对下面的文章感兴趣: