实现 call 函数

实现 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

我们可以看到,undefinednull 指向了 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

还有二十多天会进行校内蓝桥杯的选拔赛,加油加油!!!
考试考的是我们的知识储备不是焦虑,每天学习一点点,进步一点点就好啦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值