实现 call 函数
挑战介绍
本节我们来挑战一道大厂面试真题 —— 实现 call
函数。
挑战准备
新建一个 myCall.js
文件,在文件里写一个名为 myCall
的函数,并导出这个函数,如下图所示:
这个文件在环境初始化时会自动生成,如果发现没有自动生成就按照上述图片自己创建文件和函数,函数代码如下:
function myCall(fn, context) {
// 补充代码
}
module.exports = myCall;
挑战内容
请封装一个 myCall
函数,用来实现 call
函数的功能。
myCall
函数接收多个参数,第一个参数是 fn
,代表要执行的函数,第二个参数是 context
,代表需要显式改变的 this
指向,之后的参数都是 fn
的参数。
示例:
const person = {
userName: "zhangsan",
};
function fn() {
return this.userName;
}
myCall(fn, person); // 执行函数 fn,返回 'zhangsan'
// fn 传参数的情况
const obj = {
count: 10,
};
function fn(x, y, z) {
return this.count + x + y + z;
}
myCall(fn, obj, 1, 2, 3); // 执行函数 fn,返回 16
注意事项
- 文件名、函数名不可随意更改。
- 文件中编写的函数需要导出,否则将无法提交通过。
参考答案
function myCall(fn, context = window) {
context.fn = fn;// 把函数 fn 挂载到要指向的对象 context 上。
const args = [...arguments].slice(2);// 处理函数 fn 的参数,执行 fn 函数时把参数携带进去。
const res = context.fn(...args);// 执行 context.fn 获取返回值
delete context.fn;// 执行完了删除 context 上的 fn 函数,避免对传入对象的属性造成污染。
return res;// 返回结果
}
call
函数是为了改变 this
的指向,call
是写到 Function.prototype
上的方法,而本节我们要实现的 myCall
是把函数当作参数传递进去,两者只是调用形式不同,原理都是一样的。
扩展
首先我们来观察call函数的原生代码
var userName = "xxx";
const person = {
userName: "zhangsan",
};
function fn(type) {
console.log(type, "->", this.userName);
}
fn.call(0, "number");// number -> undefined
fn.call(1n, "bigint");// bigint -> undefined
fn.call(false, "boolean");// boolean -> undefined
fn.call("123", "string");// string -> undefined
fn.call(undefined, "undefined");// undefined -> xxx
fn.call(null, "null");// null -> xxx
const a = Symbol("a");// number -> undefined
fn.call(a, "symbol");// symbol -> undefined
fn.call([], "引用类型");// 引用类型 -> undefined
我们可以看到,undefined
和 null
指向了 window
,原始类型和引用类型都是 undefined
。这是因为原生类型和引用类型没有userName这个属性,所以输出都是undefined。
我们可以改造一下 myCall
函数,使其实现原始类型的兼容,代码如下:
Function.prototype.myCall = function (context = window) {
if (context === null || context === undefined) {
context = window; // undefined 和 null 指向 window
} else {
context = Object(context); // 原始类型就包装一下
}
context.fn = this;
const args = [...arguments].slice(1);
const res = context.fn(...args);
delete context.fn;
return res;
};l
还有二十多天会进行校内蓝桥杯的选拔赛,加油加油!!!
考试考的是我们的知识储备不是焦虑,每天学习一点点,进步一点点就好啦!