call 的初窥

举报
小妖现世 发表于 2021/07/15 01:20:42 2021/07/15
【摘要】 call 和 apply 是 Function 构造函数原型对象上的方法,所有的函数都可以调用 call 和 apply,作用是可以改变调用 call 和 apply 函数内部的 this 指向,并执行函数。call 的使用方法function fn() { console.log(this, arguments);}fn.call();// Window {postMessage: ƒ,...

callapplyFunction 构造函数原型对象上的方法,所有的函数都可以调用 callapply,作用是可以改变调用 callapply 函数内部的 this 指向,并执行函数。


call 的使用方法


function fn() {
  console.log(this, arguments);
}

fn.call();

// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
// Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]


不指定替换的 this,则调用 call 的函数在运行时决定 this 指向,当前案例中在浏览器中运行,则指向 window 对象。


function fn() {
  console.log(this, arguments);
}

fn.call('hello', '1', '2');

// String { "hello" }
// Arguments(2) ["1", "2", callee: ƒ, Symbol(Symbol.iterator): ƒ]


在上面案例中,callfn 内部的 this 更改为 hello 的基本包装类(对象),而 12 作为 fn 的参数,以 arguments 的形式被打印出来。


function fn1() {
  console.log(this, arguments);
}

function fn2() {
  console.log(this, arguments);
}

fn1.call.call(fn2, '1', '2');

// String { "1" }
// Arguments ["2", callee: ƒ, Symbol(Symbol.iterator): ƒ]


由于 call 是函数原型的方法,当然也可以被 call 自己调用,在上面的案例中,第一个 call 内部的 this 为调用者 fn1,通过第二个 call 将第一个 call 内部的 thisfn1 更改为 fn212 作为参数传递给第一个 call,而 1 又作为第一个 call 内部 this 指向的 fn2 内部的 this2 作为 fn2 的参数,最后执行 fn2,固有上面执行结果。


call 的实现原理


根据 call 方法的特点,来模拟实现一个自己封装的 call 方法,代码如下。


// context 参数为要替换的 this
Function.prototype.call = function (context) {
  // 将传入的 this 转换成对象,若没传则使用 window 作为 this
  context = context ? Object(context) : window;

  // 将调用 call 的函数作为属性赋值给传入的 this
  context.fn = this;

  var args = [];

  // 将传递给调用 call 函数的参数转化成字符串取值的形式
  for (var i = 1; i < arguments.length; i++) {
    // args ['arguments[1]', 'arguments[2]']
    args.push('arguments[' + i + ']');
  }

  // 利用 eval 执行 context.fn,并利用数组转换字符串的 toString 去掉 [ ]
  var result = eval('context.fn(' + args + ')');

  // 删除 context 上多余的 fn 属性
  delete context.fn;

  return result;
}


上面的实现方式重点解决两个问题:


  • 如何让调用 call 函数内部的 this 指向传入的 this,我们通过将传入 this 上加一个属性 fn,值为调用 call 的函数,在执行函数时并不直接调用 this,而是执行 context.fn,这样内部的 this 指向了调用者 context,即指向了传入要替换的 this
  • 如何将 call 调用时除第一个参数以外的参数列表作为调用 call 函数的参数依次传入,我们这里借用了 eval 提供执行环境,将要执行的代码拼接成字符串,这样就可以容易的将 argument 第二项后面的所有项通过循环的方式拼接。


context 添加的多余属性 fn,要在函数 context.fn 执行后删除。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。