手写 apply call bind
每个 Function 对象都存在 apply()、call()、bind()方法,其作用都是可以在特定的作用域中调用函数,等于设置函数体内 this 对象的值,以扩充函数赖以运行的作用域。
用法 区别
window.name = "A"; // 挂载到window对象的name
document.name = "B"; // 挂载到document对象的name
let s = {
// 自定义一个对象s
name: "C",
};
let rollCall = {
name: "Teacher",
sayName: function () {
console.log(this.name);
},
};
rollCall.sayName(); // Teacher
// apply
rollCall.sayName.apply(); // A // 不传参默认绑定window
rollCall.sayName.apply(window); // A // 绑定window对象
rollCall.sayName.apply(document); // B // 绑定document对象
rollCall.sayName.apply(s); // C // 绑定自定义对象
// call
rollCall.sayName.call(); // A // 不传参默认绑定window
rollCall.sayName.call(window); // A // 绑定window对象
rollCall.sayName.call(document); // B // 绑定document对象
rollCall.sayName.call(s); // C // 绑定自定义对象
// bind // 最后一个()是为让其执行
rollCall.sayName.bind()(); //A // 不传参默认绑定window
rollCall.sayName.bind(window)(); //A // 绑定window对象
rollCall.sayName.bind(document)(); //B // 绑定document对象
rollCall.sayName.bind(s)(); // C // 绑定自定义对象
实现
apply
funct.apply(thisArg,[argsArray])
thisArg
: 必选,在 funct
函数运行时使用的 this
值,this
可能不是该方法看到的实际值,如果这个函数处于非严格模式下,则指定为 null
或 undefined
时会自动替换为指向全局对象,原始值会被包装。 argsArray
: 可选,传递一个参数数组或者类数组对象,其中的数组元素将作为单独的参数传给 funct
函数,如果该参数的值为 null
或 undefined
,则表示不需要传入任何参数。
实现思路,类似于Function.prototype.apply()
,同样将_apply()
方法挂载到Function.prototype
,使得函数对象能够直接调用,在调用funct._apply()
时,在_apply()
方法中的 this
指向的是 funct 对象,将此 funct
对象作为一个变量赋予将要绑定的对象的一个属性中,使用将要绑定的对象来调用这个funct
,即可实现 this 指针指向将要绑定的对象,对于参数的处理,直接使用 ES6
的 Spread
运算符将数组展开作为参数传递。
window.a = 1; // 定义一个全局变量
var obj = { a: 2 }; // 定义一个对象用来绑定
var funct = function (b, c) {
console.log(this.a, b, c);
return 1;
}; // 定义一个函数用来执行
funct(1, 2); // 1 1 2 // 直接执行,相当于window.funct(1, 2),this绑定于window
funct.apply(obj, [1, 2]); // 2 1 2 // 使用apply将this绑定到obj对象
Function.prototype._apply = function (base, args) {
// 拓展Function原型
base = base || window; // 传递绑定的对象为null或undefined时指向window
base.fn = this; // 调用_apply时的this指向的是调用者也就是函数对象,将函数对象赋值给base对象的一个属性
var result = base.fn(...args); // 调用base.fn时,fn中的this指针指向的是base,并使用Spread操作符展开参数传参
delete base.fn; // 删除base对象的fn属性
return result; // 将返回值返回
};
funct._apply(obj, [1, 2]); // 2 1 2 // this绑定到了obj对象
call
funct.call(thisArg[, arg1[, arg2[, ...]]])
thisArg
: 必选,在 funct
函数运行时使用的 this
值,this
可能不是该方法看到的实际值,如果这个函数处于非严格模式下,则指定为 null
或 undefined
时会自动替换为指向全局对象,原始值会被包装。 arg1, arg2, ...: 可选,指定的参数列表。
实现思路,类似于 Function.prototype.call()
,同样将_call()
方法挂载到 Function.prototype,使得函数对象能够直接调用,在调用 funct._call()
时,在_call()
方法中的 this
指向的是 funct
对象,将此 funct
对象作为一个变量赋予将要绑定的对象的一个属性中,使用将要绑定的对象来调用这个 funct,即可实现
this
指针指向将要绑定的对象,对于参数的处理,使用 ES6
的 Rest
操作符来接收剩余参数,使用 ES6 的 Spread 运算符将数组展开作为参数传递。
window.a = 1; // 定义一个全局变量
var obj = { a: 2 }; // 定义一个对象用来绑定
var funct = function (b, c) {
console.log(this.a, b, c);
return 1;
}; // 定义一个函数用来执行
funct(1, 2); // 1 1 2 // 直接执行,相当于window.funct(1, 2),this绑定于window
funct.call(obj, 1, 2); // 2 1 2 // 使用call将this绑定到obj对象
Function.prototype._call = function (base, ...args) {
// 拓展Function原型,使用Rest操作符接收剩余参数
base = base || window; // 传递绑定的对象为null或undefined时指向window
base.fn = this; // 调用_call时的this指向的是调用者也就是函数对象,将函数对象赋值给base对象的一个属性
var result = base.fn(...args); // 调用base.fn时,fn中的this指针指向的是base,并使用Spread操作符展开参数传参
delete base.fn; // 删除base对象的fn属性
return result; // 将返回值返回
};
funct._call(obj, 1, 2); // 2 1 2 // this绑定到了obj对象
bind
funct.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg
: 必选,调用绑定函数时作为 this 参数传递给目标函数的值,如果使用 new 运算符构造绑定函数,则忽略该值,当作为回调提供时,作为 thisArg
传递的任何原始值都将转换为 object
,如果 bind
函数的参数列表为空,或者 thisArg
是 null
或 undefined
,执行作用域的 this
将被视为新函数的 thisArg
. arg1
, arg2
, ...: 可选,当目标函数被调用时,被预置入绑定函数的参数列表中的参数。 实现思路,类似于 Function.prototype.bind()
,同样将_bind()
方法挂载到 Function.prototype
,使得函数对象能够直接调用,利用箭头函数在词法上绑定 this
值的特性,返回一个指定了 this
的函数,倘若不使用箭头函数,也可以将 this
值分配给封闭的变量来构建闭包,然后是类似于 apply
方法的实现,来绑定 this
到指定的对象。
window.a = 1; // 定义一个全局变量
var obj = { a: 2 }; // 定义一个对象用来绑定
var funct = function (b, c) {
console.log(this.a, b, c);
return 1;
}; // 定义一个函数用来执行
funct(1, 2); // 1 1 2 // 直接执行,相当于window.funct(1, 2),this绑定于window
var bindFunct = funct.bind(obj, 1, 2); // 使用bind将this绑定到obj对象,bind方法返回一个原函数的拷贝,并拥有指定的this值和初始参数。
bindFunct(); // 2 1 2
Function.prototype._bind = function (base, ...args1) {
// 拓展Function原型,使用Rest操作符接收剩余参数
return (...args2) => {
// 箭头函数不会生成自身作用域下的this,会从自己的作用域链的上一层继承this
base = base || window; // 传递绑定的对象为null或undefined时指向window
base.fn = this; // 调用箭头函数时的this指向的是调用者也就是函数对象,将函数对象赋值给base对象的一个属性
var result = base.fn(...args1, ...args2); // 调用base.fn时,fn中的this指针指向的是base,并使用Spread操作符展开参数传参
delete base.fn; // 删除base对象的fn属性
return result; // 将返回值返回
};
};
var _bindFunct = funct._bind(obj, 1, 2); // 绑定对象
_bindFunct(); // 2 1 2