1、call、apply、bind 区别
答:call、apply、bind都是用来改变函数内部的this指向的,区别主要在于它们的参数传递方式和调用时机。
- call: 参数逐个传入,立即执行,函数执行结果,常用于普通函数调用;
function sayHi(name) { console.log('Hi, ' + name)};
sayHi.call(person, [arg1, arg2]); // 会马上打印 Hi, name
- apply: 参数数组传入,立即执行,函数执行结果,常用于参数不确定时;
sayHi.call(person, [arg1, arg2]); // 会马上打印 Hi, name
- bind:参数逐个传入,不执行,返回新函数,常用于函数延迟调用、柯里化;
const fn = sayHi.bind(person, arg1, arg2); // 不会马上打印 Hi, name,需要额外调用
fn(); // 你手动调用时,才执行
// 注:bind 多次绑定;只有第一次生效
const a = sayHi.bind(person1).bind(person2);
a(); // 绑定一次后 this 就定下来了,后面无效
2、call、apply、bind 底层原理
答:Function.prototype.call 实现模拟
Function.prototype.myCall = function (context, ...agrs) {
context = context || Window; // 如果为 null/undefined,默认为全局对象
const fnSymbol = Symbol(); // 创建唯一属性名,避免覆盖原方法
context[fnSymbol] = this; // 把函数挂到对象上
const result = context[fnSymbol](...args); // 执行函数
delete context[fnSymbol]; // 删除临时方法
return result; // 返回执行结果
};
// 使用
function sayHi(a, b) {
console.log(this.name, a, b);
}
const obj = { name: '张三' };
sayHi.myCall(obj, 'hello', 'world'); // 输出:张三 hello world
- apply 的底层原理,实现模拟(与 call 基本一样 唯一差别是参数传入方式不同(数组))
Function.prototype.myApply = function (context, argsArray) {
context = context || window;
const fnSymbol = Symbol();
context[fnSymbol] = this;
const result = argsArray ? context[fnSymbol](...argsArray) : context[fnSymbol]();
delete context[fnSymbol];
return result;
};
// 使用
sayHi.myApply(obj, ['hello', 'world']);
bind的底层实现(返回一个新的函数)不立即执行,只返回一个包装函数,而且 this 被永久绑定!
Function.prototype.myBind = function (context, ...args1) {
const fn = this;
return function (...args2) {
return fn.call(context, ...args1, ...args2); // 参数合并执行
};
};
// 用法
const boundSayHi = sayHi.myBind(obj, 'hello');
boundSayHi('world'); // 张三 hello world
Symbol是 ES6 引入的一种原始数据类型,用于创建独一无二的值。
const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false,永远不相等