ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# call bind apply ## this this永远指向最后调用他的对象 例如: ~~~ var name = "windowsName"; function fn() { var name = 'Cherry'; innerFunction(); function innerFunction() { console.log(this.name); // windowsName } } fn() ~~~ 这里最后调用fn的是window,所以指向window的name属性。 再看一个 ~~~ var name = "windowsName"; var a = { name : null, // name: "Cherry", fn : function () { console.log(this.name); // windowsName } } var f = a.fn; f(); ~~~ 这里虽然f是a.fn 但是f并没有被直接调用,最后调用f的依然是window,所以输出windowsName。 **箭头函数的this** > 箭头函数的 this 始终指向函数定义时的 this,而非执行时。,箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。 > ## call bind apply的区别: call apply区别在于,apply传参传入数组,call传参依次传入。 ~~~ func.call(context, arg1, arg2, ...) func.apply(context, [arg1, arg2, ...]) ~~~ > bind与这两者的区别很大,bind函数返回的是一个新的函数,而后两者则都是立即执行函数,使用的时候即调用了该函数,返回方法操作的结果。 ~~~ var name = 'Bob'; var person = { name: 'Alice', age: 29 } function sayName() { console.log(this.name); } var say = sayName.bind(person); say(); // Alice sayName(); // Bob ~~~ 同时需要注意,bind绑定的this,是不可以使用call apply改变的。 ## 关于null 在非严格模式下,第一个参数传入null 或 undefined会指向全局变量 也就是window。 ## call的实现 ~~~ Function.prototype.call2 = function (context) { var context = context || window; context.fn = this; var args = []; for (let i = 1; i < arguments.length; i++) { args.push('arguments[' + i + ']') } var result = eval('context.fn(' + args + ')'); delete context.fn; return result; } ~~~ ## apply的实现 ~~~ Function.prototype.apply = function (context, arr) { var context = Object(context) || window; context.fn = this; var result; if (!arr) { result = context.fn(); } else { var args = []; for (var i = 0, len = arr.length; i < len; i++) { args.push('arr[' + i + ']'); } result = eval('context.fn(' + args + ')') } delete context.fn return result; } ~~~ ## bind的实现 先来看一下bind怎么使用: ~~~ function test (a,b){ console.log(this.name); console.log(a); console.log(b); } const mine = { name:'aaa' } const t = test.bind(mine,1,2); t(); // 或这样 // const t = test.bind(mine,1); // t(2) ~~~ 所以说需要传入this指向,也可以继续传参,作为参数。所以初步考虑的实现是这样的。 ~~~ Function.prototype.bind = function (content) { var self = this; var outerArgs = Array.prototype.slice.call(arguments, 1);// 这里注意,因为bind在使用中,第一个参数时新指定的this指向,所以在这里,去掉这个参数,从index=1开始,这里全都是函数所需要的参数。 return function () { var innerArgs = Array.prototype.slice.call(arguments); var totalArgs = outerArgs.concat(innerArgs); return self.apply(content, totalArgs); } } ~~~ 当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效。 详情请看: ~~~ var value = 2; var foo = { value: 200 } function bar(name, age) { this.habit = 'shopping'; console.log('this.value------' + this.value); console.log('name------' + name); console.log('age------' + age); }; bar.prototype.friend = 'kevin'; var bindFoo = bar.bind(foo, 'ddddd'); var obj = new bindFoo('100'); console.log('obj.habit------' + obj.habit); console.log('obj.friend------' + obj.friend); // this.value------undefined // name------ddddd // age------100 // obj.habit------shopping // obj.friend------kevin ~~~ 所以我们使用这个bind实现方法: ~~~ 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); // 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值 // 以上面的是 demo 为例,如果改成 `this instanceof fBound ? null : context`,实例只是一个空对象,将 null 改成 this ,实例会具有 habit 属性 // 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs)); } fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; } ~~~ ## 参考 [this call apply bind](https://juejin.im/post/59bfe84351882531b730bac2) [call apply 实现](https://github.com/mqyqingfeng/Blog/blob/master/articles/%E6%B7%B1%E5%85%A5%E7%B3%BB%E5%88%97%E6%96%87%E7%AB%A0/JavaScript%E6%B7%B1%E5%85%A5%E4%B9%8Bcall%E5%92%8Capply%E7%9A%84%E6%A8%A1%E6%8B%9F%E5%AE%9E%E7%8E%B0.md)