若是对call,apply,bind的应用和区别还不了解,能够去看我以前的文章了解下。 让你弄懂 call、apply、bind的应用和区别es6
若是出现错误,请在评论中指出,我也好本身纠正本身的错误bash
author: thomaszhouapp
bind实现
通常咱们会直接使用bind函数,可是此次咱们经过原生js来尝试实现这个函数函数
bind() 方法会建立一个新函数。当这个新函数被调用时,bind() 的第一个参数将做为它运行时的 this,以后的一序列参数将会在传递的实参前传入做为它的参数post
由此咱们能够首先得出 bind 函数的三个特色:优化
(1)返回一个函数
:咱们可使用 call 或者 apply 实现(2)能够传入参数
:咱们用 arguments 进行处理var foo = { value: 1 };
function bar(name, age) {
console.log(this.value);
console.log(name);
console.log(age);
}
var bindFoo = bar.bind(foo, 'daisy'); // (1)bindFoo是一个函数
bindFoo('18'); // (2)此处能够再次传入参数
// 1
// daisy
// 18
复制代码
先实现前两个特色:实现代码(version 1.0):
Function.prototype.bind2 = function (context) {
var self = this;
// 获取bind2函数从第二个参数到最后一个参数
var args = Array.prototype.slice.call(arguments, 1);
return function () {
// 这个时候的arguments是指bind返回的函数bindFoo调用时传入的参数
var bindArgs = Array.prototype.slice.call(arguments);
self.apply(context, args.concat(bindArgs));
}
}
复制代码
(3)一个绑定函数也能使用new操做符建立对象:这种行为就像把原函数当成构造器。提供的 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'); // this 已经指向了 obj
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin
复制代码
注意:尽管在全局和 foo 中都声明了 value 值,最后依然返回了 undefind,说明绑定的 this 失效了ui
先实现第三个特色:实现代码(version 2.0):
Function.prototype.bind2 = function (context) {
let self = this;
// self --> ƒ bar(){}
let args = Array.prototype.slice.call(arguments, 1);
let fbound = function () {
let bindArgs = Array.prototype.slice.call(arguments);
// (1) 看成为构造函数时,this --> 实例(fbound建立的的实例),self --> 绑定函数bar,结果为true,那么self指向实例
// (2) 看成为普通函数时,this -->window,self -->绑定函数,此时结果为false,那么 self指向绑定的 context。
self.apply(this instanceof self ? this : context, args.concat(bindArgs));
};
// 为了要让 this instanceof self为true,就要使建立的实例继承绑定函数bar,
// 惟一办法就是让建立实例的函数fbound的prototype指向bar函数的prototype
fbound.prototype = this.prototype;
return fbound;
};
复制代码
优化:实现代码(version 3.0):
version 2.0 直接将 fbound.prototype = this.prototype
,咱们直接修改fbound.prototype 的时候,也会直接修改函数bar的 prototype。这个时候,咱们能够经过一个空函数来进行中转,而后利用组合继承的方式来实现this
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 的原型方法指向 this 即函数bar的原型方法,继承 this 的属性
fNOP.prototype = this.prototype;
// 再将 fbound 即要返回的新函数的原型方法指向 fNOP 的实例化对象
// 这样,既能让 fBound 继承 this 的属性,在修改其原型链时,又不会影响到 this 的原型链
fbound.prototype = new fNOP();
return fbound;
}
复制代码
在上面的代码中,咱们引入了一个新的函数 fun,用于继承原函数的原型,并经过 new 操做符实例化出它的实例对象,供 fBound 的原型继承,至此,咱们既让新函数继承了原函数的全部属性与方法,又保证了不会由于其对原型链的操做影响到原函数spa
-------------优化三点-------------------------
(兼容性)当 Function 的原型链上没有 bind 函数时,才加上此函数
Function.prototype.bind = Function.prototype.bind || function () {
……
};
复制代码
只有函数才能调用 bind 函数,其余的对象不行。即判断 this 是否为函数。
if (typeof this !== 'function') {
throw new TypeError("NOT_A_FUNCTION -- this is not callable");
}
复制代码
对于参数传递的代码,能够用ES6的拓展运算符来替换
最终版本!!!
Function.prototype.bind = Function.prototype.bind || function (context, ...formerArgs) {
let self = this;
if (typeof this !== 'function') {
throw new TypeError("NOT_A_FUNCTION -- this is not callable");
}
let fNOP = function () {};
let fbound = function (...laterArgs) {
self.apply(this instanceof self ? this : context, formerArgs.concat(laterArgs));
// es6 : formerArgs.concat(laterArgs)
};
fNOP.prototype = this.prototype;
fbound.prototype = new fNOP();
return fbound;
};
复制代码