一、前言                                                             

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

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

 

出处:http://www.cnblogs.com/fsjohnhuang/p/4160942.html

认识Function.prototype.call的更多相关文章

  1. Function.prototype.toString 的使用技巧

    Function.prototype.toString这个原型方法可以帮助你获得函数的源代码, 比如: function hello ( msg ){ console.log("hello& ...

  2. Object.prototype和Function.prototype一些常用方法

    Object.prototype 方法: hasOwnProperty 概念:用来判断一个对象中的某一个属性是否是自己提供的(主要是判断属性是原型继承还是自己提供的) 语法:对象.hasOwnProp ...

  3. Object.prototype 与 Function.prototype 与 instanceof 运算符

    方法: hasOwnProperty isPrototypeOf propertyIsEnumerable hasOwnProperty 该方法用来判断一个对象中的某一个属性是否是自己提供的( 住要用 ...

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

    一.前言                                大家先预计一下以下四个函数调用的结果吧! var test = function(){ console.log('hello w ...

  5. 一起Polyfill系列:Function.prototype.bind的四个阶段

    昨天边参考es5-shim边自己实现Function.prototype.bind,发现有不少以前忽视了的地方,这里就作为一个小总结吧. 一.Function.prototype.bind的作用 其实 ...

  6. Function.prototype.bind接口浅析

    本文大部分内容翻译自 MDN内容, 翻译内容经过自己的理解. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Glo ...

  7. JavaScript 函数绑定 Function.prototype.bind

    ECMAScript Edition5 IE9+支持原生,作用为将一个对象的方法绑定到另一个对象上执行. Function.prototype.bind = Function.prototype.bi ...

  8. Function.prototype.bind

    解析Function.prototype.bind 简介 对于一个给定的函数,创造一个绑定对象的新函数,这个函数和之前的函数功能一样,this值是它的第一个参数,其它参数,作为新的函数的给定参数. b ...

  9. 解析Function.prototype.bind

    简介 对于一个给定的函数,创造一个绑定对象的新函数,这个函数和之前的函数功能一样,this值是它的第一个参数,其它参数,作为新的函数的给定参数. bind的作用 bind最直接的作用就是改变this的 ...

  10. Function.prototype.call.apply结合用法

     昨天在网上看到一个很有意思的js面试题,就跟同事讨论了下,发现刚开始很绕最后豁然开朗,明白过来之后发现还是挺简单的,跟大家分享下!  题目如下: var a = Function.prototype ...

随机推荐

  1. FirstAFNetWorking

    // ViewController.h // FirstAFNetWorking // // Created by 张国锋 on 15/7/20. // Copyright (c) 2015年 张国锋 ...

  2. 一个mybatis错误导致无法启动项目的问题

    今天遇到Mybatis一个问题,导致项目一直起不来,查了很久发现是MapperXML的错,问题表现为: 系统始终起不来,但也不报错,始终卡到如下信息位置: 信息: Initializing Sprin ...

  3. BZOJ 1396:识别子串 SA+树状数组+单调队列

    1396: 识别子串 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 381  Solved: 243[Submit][Status][Discuss] ...

  4. momentum公式

    momentum对于w的更新公式: http://caffe.berkeleyvision.org/tutorial/solver.html

  5. SQLSTATE=42000 #42000

    在使用PowerDesigner生成数据库表的时候遇到了这个问题. 原来是在填写属性的类型的时候, 少了两个括号, 造成了mysql数据类型错误 本来应该是varchar(50)的,写成了varcha ...

  6. Python re module (regular expressions)

    regular expressions (RE) 简介 re模块是python中处理正在表达式的一个模块 r"""Support for regular expressi ...

  7. PAT (Basic Level) Practise (中文)- 1012. 数字分类 (20)

    http://www.patest.cn/contests/pat-b-practise/1012 给定一系列正整数,请按要求对数字进行分类,并输出以下5个数字: A1 = 能被5整除的数字中所有偶数 ...

  8. Framework的简单创建

    如果你想将你开发的控件与别人分享,一种方法是直接提供源代码文件.然而,这种方法并不是很优雅.它会暴露所有的实现细节,而这些实现你可能并不想开源出来.此外,开发者也可能并不想看到你的所有代码,因为他们可 ...

  9. linux下libnet编程 亲自测试可用

    linux下libnet编程 亲自测试可用 亲自测试  如果build包的时候 只要把类型改了 就能改成相应的协议. 0x0800 ip 0x0806 arp 0x86DD    IPv6 0x86e ...

  10. lua拷贝二进制文件的方法

    使用lua拷贝二进制文件相比文本文件复杂一点,方法如下 function copyFunc(targetPath,sourcePath) local rf = io.open(sourcePath,& ...