学习
实践
活动
专区
工具
TVP
写文章
专栏首页偏前端工程师的驿站JS魔法堂:再次认识Function.prototype.call

JS魔法堂:再次认识Function.prototype.call

一、前言                                                             

  大家先预计一下以下四个函数调用的结果吧!

var test = function(){
  console.log('hello world')
  return 'fsjohnhuang'
}
test.call() // ①
Function.prototype.call(test) // ②
Function.prototype.call.call(test) // ③
Function.prototype.call.call(Function.prototype.call, test) // ④

  揭晓:①、③和④. 控制台显示hello world,并返回fsjohnhuang。②. 返回undefined且不会调用test函数;

  那到底是啥回事呢?下面将一一道来。

二、从常用的call函数说起                                                        

  还是通过代码说事吧

var test2 = function(){
  console.log(this)
  return 'fsjohnhuang'
}
test2() // 控制台显示window对象信息,返回值为fsjohnhuang
test2.call({msg: 'hello world'}) // 控制台显示{msg:'hello world'}对象信息,返回值为fsjohnhuang

  test2.call实际上是调用 Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] ) ,而其作用我想大家都了解的,但其内部的工作原理是怎样的呢? 这时我们可以参考ECMAScript5.1语言规范。以下是参照规范的伪代码(各浏览器的具体实现均不尽相同)

Function.prototype.call = function(thisArg, arg1, arg2, ...) {
  /*** 注意:this指向调用call的那个对象或函数 ***/

  // 1. 调用内部的IsCallable(this)检查是否可调用,返回false则抛TypeError
  if (![[IsCallable]](this)) throw new TypeError()
  
  // 2. 创建一个空列表
  // 3. 将arg1及后面的入参保存到argList中
  var argList = [].slice.call(arguments, 1)

  // 4. 调用内部的[[Call]]函数
  return [[Call]](this, thisArg, argList)
}

  那现在我们可以分析一下 ①test.call() ,并以其为基础去理解后续的内容。它内部实现的伪代码如下:

test.call = function(thisArg, arg1, arg2, ...){
  if (![[IsCallable]](test)) throw new TypeError()

  var argList = [].slice.call(arguments, 1)
  return [[Call]](test, thisArg, argList)
}

  下面我们再来分析② Function.prototype.call(test) ,伪代码如下:

Function.prototype.call = function(test, arg1, arg2, ...){
  /***  Function.prototype是一个function Empty(){}函数  ***/

  if (![[IsCallable]](Function.prototype)) throw new TypeError()

  var argList = [].slice.call(arguments, 1)
  // 实际上就是调用Empty函数而已,那返回undefined是理所当然的
  return [[Call]](Function.prototype, test, argList)
}

三、Function.prototype.call.call内部究竟又干嘛了?                                       

  有了上面的基础那么Function.prototype.call.call就不难理解了。就是以最后一个call函数的thisArg作为Function.prototype.call的this值啦!伪代码如下:

// test作为thisArg传入
Function.prototype.call.call = function(test, arg1, arg2,...){
  if ([[IsCallable]](Function.prototype.call)) throw new TypeError()
  
  var argList = [].slice.call(arguments, 1)
  return [[Call]](Function.prototype.call, test, argList)
}

// test作为函数的this值
// 注意:入参thisArg的值为Function.prototype.call.call的入参arg1
Function.prototype.call = function(thisArg, arg1, arg2,...){
  if ([[IsCallable]](test)) throw new TypeError()

  var argList = [].slice.call(arguments, 1)
  return [[Call]](test, thisArg, argList)
}

四、见鬼的合体技——Function.prototype.call.call(Function.prototype.call, test) 

  看伪代码理解吧!

// test作为arg1传入
Function.prototype.call.call = function(Function.prototype.call, test){
  if ([[IsCallable]](Function.prototype.call)) throw new TypeError()
  
  var argList = [].slice.call(arguments, 1)
  return [[Call]](Function.prototype.call, Function.prototype.call, argList)
}

Function.prototype.call = function(test){
  if ([[IsCallable]](Function.prototype.call)) throw new TypeError()

  var argList = [].slice.call(arguments, 1)
  return [[Call]](Function.prototype.call, test, argList)
}

Function.prototype.call = function(thisArg){
  if ([[IsCallable]](test)) throw new TypeError()

  var argList = [].slice.call(arguments, 1)
  return [[Call]](test, thisArg, argList)
}

  这种合体技不就是比第三节的多了一个步吗?有必有吗?  

五、新玩法——遍历执行函数数组                            

Array.prototype.resolve = function(){
  this.forEach(Function.prototype.call, Function.prototype.call)
}
var cbs = [function(){console.log(1)}, function(){console.log(2)}]
cbs.resolve() 
// 控制台输出
// 1
// 2

   这是为什么呢?那先要看看 Array.prototype.forEach(fn, thisArg) 的内部实现了,伪代码如下:

Array.prototype.forEach = function(fn, thisArg){
  var item
  for (var i = 0, len = this.length; i < len; ++i){
    item = this[i]
    fn.call(thisArg, item, i, this)
  }
}

   大家再自行将编写 Function.prototype.call.call(Function.prototype.call, item, i,this) 的伪代码就明白了

六、总结                                          

  在项目中关于Function.prototype.call.call的用法确实少见,而且性能不高,本篇仅仅出于学习的目的,只希望再深入了解一下Function.prototype.call的内部原理而已。

七、参考                                           

在JavaScript的Array数组中调用一组Function方法

  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

Annotated ECMAScript 5.1

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:http://fsjohnhuang.cnblogs.com/复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • CSS魔法堂:再次认识font

    一、前言                                 文字承载着站点内涵,而良好的字体、排版则为用户提供舒适的阅读体验。本文打算对字体稍微深...

    ^_^肥仔John
  • JS读书心得:《JavaScript框架设计》——第12章 异步处理

    一、何为异步                                 执行任务的过程可以被分为发起和执行两个部分。 同步执行模式:任务发起后必须等待直...

    ^_^肥仔John
  • JS魔法堂:追忆那些原始的选择器

    一、前言                                                                            ...

    ^_^肥仔John
  • 魔改一波合成大西瓜!代码已开源~

    上了两次微博热搜的《合成大西瓜》,想必大家都玩过了。Guide 哥最近什么排队买饭、带薪拉屎的时候,都在玩这个游戏,有点上头!

    Guide哥
  • 奎特尔星球导航

    有朋友在公众号留言,提出「奎特尔星球」上的文章对于初学者路径不清晰,不知道按怎么的顺序阅读。

    张晓衡
  • 25岁发5篇顶刊的天才少年,高不可攀的Nature,在他这里咋就成了“随手发”

    新年伊始,2021年2月1日,被誉为“天才少年”的95后博士曹原再次以共同一作+通讯作者的身份在Nature上刊文!

    百味科研芝士
  • handsome 最终魔改

    本次魔改需修改源文件,望各位下手前先备份

    Qicloud
  • 新年伊始,25岁“天才少年”曹原再发一篇Nature!9个月的第三篇Nature!

    来源 | 微算云平台(Sapere Aude)、Nature、纳米人、安徽商报、新安晚报、科学网

    磐创AI
  • 博客顶栏菜单重写

    顶栏算是目前首页唯二还能看出来是Butterfly的版块了,另一个是侧栏按钮。因为之前的SAO-controller没有达到预期。

    Akilar
  • 看完这几道 JavaScript 面试题,让你与考官对答如流(中)

    IIFE或立即调用的函数表达式是在创建或声明后将被调用或执行的函数。 创建IIFE的语法是,将function (){}包裹在在括号()内,然后再用另一个括号(...

    前端小智@大迁世界
  • 【优化算法】变邻域搜索算法(VNS)求解TSP(附C++详细代码及注释)

    上次变邻域搜索的推文发出来以后,看过的小伙伴纷纷叫好。小编大受鼓舞,连夜赶工,总算是完成了手头上的一份关于变邻域搜索算法解TSP问题的代码。今天,就在此给大家双...

    短短的路走走停停
  • 一文理解 this、call、apply、bind

    记得差不多在两年多之前写过一篇文章 两句话理解js中的this[1],当时总结的两句话原话是这样的:

    木子星兮
  • 【JS 逆向百例】网洛者反爬练习平台第二题:JJEncode 加密

    本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即...

    K哥爬虫
  • 精灵之息——不一样的游戏

    之前是打算做个纯网游,是以r/place为原型的一个游戏,然后里面塞各种各样的『技术』(比如tensorflow.js)。

    沙因Sign
  • 一文理解 this、call、apply、bind

    记得差不多在两年多之前写过一篇文章 两句话理解js中的this,当时总结的两句话原话是这样的:

    木子星兮
  • 面试官问:JS的this指向

    面试官出很多考题,基本都会变着方式来考察this指向,看候选人对JS基础知识是否扎实。读者可以先拉到底部看总结,再谷歌(或各技术平台)搜索几篇类似文章,看笔者写...

    若川
  • Uniy3d开发VR程序的流程(Unity3D)

    &emsp; 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧。

    恬静的小魔龙
  • CSS魔法堂:不得不说的Containing Block

    前言 《CSS魔法堂:重新认识Box Model、IFC、BFC和Collapsing margins》中提到在没有floated兄弟盒子时,line box...

    ^_^肥仔John

扫码关注腾讯云开发者

领取腾讯云代金券