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的内部原理而已。

  尊重原创,转载请注明在:http://www.cnblogs.com/fsjohnhuang/p/4160942.html ^_^肥仔John

 

七、参考                                           

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

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

  Annotated ECMAScript 5.1

如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!

posted @ 2014-12-13 11:49 ^_^肥仔John 阅读( 3714) 评论( 1) 编辑 收藏
  
#1楼 3310112 2015/11/19 16:08:18 2015-11-19 16:08 jack_Meng  
如何编写 Function.prototype.call.call(Function.prototype.call, item, i,this) 的伪代码啊?本人初学,本来就开的有点晕,我只能跑一下你的代码。麻烦不吝赐教!~

公告

 
本文转自 ^_^肥仔John博客园博客,原文链接: http://www.cnblogs.com/fsjohnhuang/p/4160942.html,如需转载请自行联系原作者
 
相关文章
|
1月前
|
存储 JavaScript 前端开发
js开发:请解释什么是回调函数(callback function),并给出一个示例。
回调函数是JavaScript中处理异步编程的一种常见模式,常用于事件驱动和I/O操作。它们作为参数传递给其他函数,在特定条件满足或任务完成后被调用。例如,`asyncOperation`函数接受回调函数`handleResult`,在模拟的异步操作完成后,调用`handleResult`并传递结果。这使得程序员能在操作完成后执行后续任务。
22 1
|
9月前
|
JavaScript 前端开发
JS(第七课)认识function函数(一)
JS(第七课)认识function函数(一)
56 0
|
9月前
|
JavaScript 前端开发
javascript函数:function() {}()的实战案例理解
javascript函数:function() {}()的实战案例理解
44 0
|
9月前
|
JavaScript
JS(第七课)认识function函数(二)
JS(第七课)认识function函数(二)
45 0
|
11月前
|
JavaScript 前端开发 Java
JavaScript对象类型之function
JavaScript对象类型之function
84 0
|
11月前
|
JavaScript 前端开发
JS:JS中常见的 “函数名 is not a function” 错误
JS:JS中常见的 “函数名 is not a function” 错误
597 0
|
11月前
|
前端开发 JavaScript 安全
前端祖传三件套JavaScript的函数之Function() 构造器
在JavaScript中,函数是实现各种功能和业务逻辑的基本手段。除了常规的函数声明和函数表达式外,JavaScript还支持通过Function()构造器来进行函数定义。在这篇文章中,我们将介绍JavaScript中的Function()构造器,以及其使用方法和注意事项。
54 0
|
JSON 自然语言处理 JavaScript
深入理解Js里new Function语法
深入理解Js里new Function语法
|
JavaScript 前端开发
JavaScript之function基础定义与调用
引入 前端人都多多少少听说过一句话:“HTML是网页的骨架,CSS是网页的皮囊,而JS是网页的灵魂”。 而在我们学习JS这一“灵魂”的同时,如果要给JS也找一个“灵魂”,我觉得就是接下来我们会一起学习的函数 之前的学习文章里我也曾提到过,一般我们不会嵌套太多层循环,如果有需要,我们更倾向于写一些自定义函数,然后以调用的方式来达到相应的目的 在大型开发中,我们也往往会采用定义函数并调用组件的形式来让我们代码的可读性更强。
|
JavaScript 前端开发
JavaScript 然后遍历dom时报错 forEach is not a function
JavaScript 然后遍历dom时报错 forEach is not a function
315 2