1. 手写call函数
call函数用于调整调用函数的this指向,并且会立即执行,参数以剩余参数的形式传递
/**
* 手写 Function.prototype.call
*
* call 的作用:
* 1. 改变函数执行时的 this 指向
* 2. 立即执行函数
* 3. 可以传递多个参数(逐个传递)
*
* 实现原理:
* 1. 将函数设为对象的属性
* 2. 执行该函数
* 3. 删除该属性
*/
Function.prototype.myCall = function (context, ...args) {
if (typeof this !== 'function') {
throw new TypeError('this is not a function');
}
// 1. 如果当前没有传入context则设置为全局对象window
context = context || window;
// 2. 如果 context 是基本类型,需要包装成对象
// 例如: call(1) 应该变成 call(Number(1))
if (typeof context !== 'object') {
context = Object(context);
}
// 使用Symbol创建唯一属性名,避免属性命名冲突
const fn = Symbol();
// 让这个函数成为context的一个属性,这样函数执行的时候this就指向了context
context[fn] = this;
// 通过属性调用函数,此时函数内部的this就指向了context
const result = context[fn](...args);
// 删除临时属性,避免污染对象
delete context[fn];
return result;
};
2. 手写apply函数
作用基本和
call函数一样,区别在于参数传递以数组的形式传递
/**
* 手写 Function.prototype.apply
*
* apply 与 call 的区别:
* - call: 参数逐个传递 fn.call(obj, arg1, arg2, arg3)
* - apply: 参数以数组形式传递 fn.apply(obj, [arg1, arg2, arg3])
*
* 实现原理与 call 相同,只是参数处理方式不同
*/
Function.prototype.myApply = function (context, args = []) {
if (typeof this !== 'function') {
throw new TypeError('this is not a function');
}
context = context || window;
if (typeof context !== 'object') {
context = Object(context);
}
const fn = Symbol();
context[fn] = this;
const result = context[fn](...args);
delete context[fn];
return result;
};
3. 手写bind函数
bind函数也会修改this指向,但是并不会立即执行,可以分步传递参数,并且返回的函数还可以当作构造函数使用,但是在这种情况下,bind绑定的this就会失效,指向创建的实例对象
/**
* 手写 Function.prototype.bind
*
* bind 的特点:
* 1. 返回一个新函数,不会立即执行
* 2. 可以分步传递参数 (柯里化)
* 3. 返回的函数可以作为构造函数使用 (new)
* 4. 作为构造函数时,bind 绑定的 this 会失效
*
* 实现要点:
* 1. 返回新函数
* 2. 处理参数合并
* 3. 处理 new 调用的情况
*/
Function.prototype.myBind = function (context, ...args) {
// 保存原函数
const fn = this;
if (typeof fn !== 'function') {
throw new TypeError('Bind must be called on a function');
}
const boundFunction = function (...innerArgs) {
// 判断是否是new调用
// 如果是new调用,则this指向新创建的对象(实例对象)
// 如果不是new调用,则this指向绑定的context
const isNew = this instanceof boundFunction;
const allArgs = [...args, ...innerArgs];
return fn.apply(isNew ? this : context, allArgs);
};
// 维护原型链
// 使 boundFunction.prototype 指向原函数的 prototype
// 这样通过 new boundFunction 创建的实例可以访问原函数原型上的方法
if (fn.prototype) {
// 使用 Object.create 避免直接修改原函数的 prototype
boundFunction.prototype = Object.create(fn.prototype);
}
// 返回一个新函数
return boundFunction;
};