关于call、apply和bind,请看这篇
call
func.call(context, arg1, arg2, ...)
参数说明:
context :在 func函数运行时指定的this值,当第一个参数为null、undefined的时候,默认指向window。
arg1, arg2:…指定的参数列表。
返回值:
使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回undefined。
apply(与call相似,只是参数不同而已)
func.apply(context, arr)
参数说明:
context :在 func函数运行时指定的this值,当第一个参数为null、undefined的时候,默认指向window。
arr 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。
bind
func.bind(context, arg1, arg2, ...)
参数说明:
context :在 func函数运行时指定的this值,当第一个参数为null、undefined的时候,默认指向window。
arg1, arg2:…指定的参数列表。
返回值:
返回一个原函数的拷贝,并拥有指定的this值和初始参数。
例如:
var name = '小王', age = 17; var obj = { name: '小张', age: this.age, myFun: function(fm, t) { console.log(this.name + ' 年龄' + this.age, ' 来自' + fm + '去往' + t); } } var db = { name: '德玛', age: 99 }
// 以下将obj的myFun方法应用到db对象上 obj.myFun.call(db,'成都','上海'); // 德玛 年龄 99 来自 成都去往上海 obj.myFun.apply(db,['成都','上海']); // 德玛 年龄 99 来自 成都去往上海 obj.myFun.bind(db,'成都','上海')(); // 德玛 年龄 99 来自 成都去往上海 obj.myFun.bind(db,['成都','上海'])(); // 德玛 年龄 99 来自 成都, 上海去往 undefined
区别
1、都是用来改变函数的this对象的指向的,this指向他们的第一个参数。
2、call跟apply的用法几乎一样,唯一的不同就是传递的参数不同,call只能一个参数一个参数的传入。
apply则只支持传入一个数组,哪怕是一个参数也要是数组形式。最终调用函数时候这个数组会拆成一个个参数分别传入。
3、bind方法是直接改变这个函数的this指向并且返回一个新的函数,之后再次调用这个函数的时候this都是指向bind绑定的第一个参数。bind传参方式跟call方法一致。
4、如果第一个参数为null或undefined,则this值会自动指向全局对象(浏览器中就是window对象)。
call/apply/bind的核心理念:借用方法。借助已实现的方法,改变方法中数据的this指向,减少重复代码,节省内存。
手写call、apply和bind
// 通过隐式绑定实现 Function.prototype.call = function(context, ...args) { // 如果没有传或传的值为空对象 context指向window context = context || window; // 给context添加一个方法 指向this context.func = this; if (typeof context.func !== 'function') { throw new TypeError('call must be called on a function'); } // 执行func let res = context.func(...args); // 删除方法,否则context就无缘无故多了个func delete context.func; return res; }
Function.prototype.apply = function(context, args) { context = context || window; context.func = this; if (typeof context.func !== 'function') { throw new TypeError('apply must be called on a function'); } let res = context.func(...args); delete context.func; return res; }
Function.prototype.bind = function(context, ...bindArgs) { context = context || window; // func 为调用 bind 的原函数 const func = this; if (typeof func !== 'function') { throw new TypeError('Bind must be called on a function'); } // bind 返回一个绑定 this 的函数 return function(...callArgs) { let args = bindArgs.concat(callArgs); if (this instanceof func) { // 意味着是通过 new 调用的 而 new 的优先级高于 bind return new func(...args); } return func.call(context, ...args); } }