先简单说下它们的作用?
它们三个的作用都是用于 改变函数运行时 this 的指向 。
function sayHello() {
console.log(this);
}
const xmo = { name: "xiaoming" };
const xms = "xiaoming";
sayHello(); // Window
sayHello.call(xmo); // {name: 'xiaoming'}
sayHello.call(xms); // String {'xiaoming'} 原始类型被包装
它们之间有什么区别?
call 和 apply 的唯一区别是参数不同。call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。
如果这两个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
call(thisArg, arg1, arg2, ...)apply(thisArg, argsArray)
bind() 方法与上面两个的区别在于:bind 改变 this 指向之后不会立即调用,而是返回一个新函数。
bind(thisArg[, arg1[, arg2[, ...]]])
bind() 返回的新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。如果使用 new 运算符构造绑定函数,则忽略该值。当使用 bind 在 setTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,或者 thisArg 是 null 或 undefined,执行作用域的 this 将被视为新函数的 thisArg。
function sayHello() {
console.log(this);
}
const xmo = { name: "xiaoming" };
sayHello(); // Window
const newSayHello = sayHello.bind(xmo);
newSayHello(); // {name: 'xiaoming'}
手写 call/apply/bind
call
/**
* 注意点
* 1. 非严格模式, 传入 null 或 undefined 为 thisArg 时,自动指向全局 window 对象
* 2. 原始值会被包装
*/
Function.prototype._call = function (context, ...args) {
context = Object(context ?? window);
const fn = Symbol();
context[fn] = this;
const result = context[fn](...args);
delete context[fn];
return result;
};
apply
// 与 call() 方法类似,传参不同
Function.prototype._apply = function (context, args) {
context = Object(context ?? window);
const fn = Symbol();
context[fn] = this;
const result = context[fn](...(args ?? []));
delete context[fn];
return result;
};
bind
/**
* 注意点:
* 1. bind() 返回一个新函数。bind 的第一个参数为指定的 this 指向,其余参数将作为新函数的参数
* 2. 如果使用 new 运算符构造绑定函数,则忽略 this(相当于 new 调用原方法,并传入其余参数)
* 3. 如果 bind() 的参数为空,或者 thisArg 参数为 null 或 undefined ,执行作用域的 this 将被视为新函数的 thisArg
* 4. 当使用 bind 在 setTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。
*/
Function.prototype._bind = function (thisArg, ...args1) {
const self = this;
const fbound = function (...args2) {
return self.apply(this instanceof self ? this : thisArg, [
...args1,
...args2,
]);
};
const fNOP = function () {};
fNOP.prototype = this.prototype;
fbound.prototype = new fNOP();
return fbound;
};