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, )
// 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, )
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, )
// 实际上就是调用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, )
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, )
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, )
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, )
return [[Call]](Function.prototype.call, test, argList)
} Function.prototype.call = function(thisArg){
if ([[IsCallable]](test)) throw new TypeError() var argList = [].slice.call(arguments, )
return [[Call]](test, thisArg, argList)
}
这种合体技不就是比第三节的多了一个步吗?有必有吗?
五、新玩法——遍历执行函数数组
Array.prototype.resolve = function(){
this.forEach(Function.prototype.call, Function.prototype.call)
}
var cbs = [function(){console.log()}, function(){console.log()}]
cbs.resolve()
// 控制台输出
// 1
//
这是为什么呢?那先要看看 Array.prototype.forEach(fn, thisArg) 的内部实现了,伪代码如下:
Array.prototype.forEach = function(fn, thisArg){
var item
for (var i = , 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
JS魔法堂:再次认识Function.prototype.call的更多相关文章
- JS魔法堂:jsDeferred源码剖析
一.前言 最近在研究Promises/A+规范及实现,而Promise/A+规范的制定则很大程度地参考了由日本geek cho45发起的jsDeferred项目(<JavaScript框架设计& ...
- JS魔法堂:不完全国际化&本地化手册 之 实战篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- JS魔法堂:属性、特性,傻傻分不清楚
一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; con ...
- JS魔法堂:那些困扰你的DOM集合类型
一.前言 大家先看看下面的js,猜猜结果会怎样吧! 可选答案: ①. 获取id属性值为id的节点元素 ②. 抛namedItem is undefined的异常 var nodes = documen ...
- JS魔法堂:追忆那些原始的选择器
一.前言 ...
- JS魔法堂:判断节点位置关系
一.前言 在polyfill querySelectorAll 和写弹出窗时都需要判断两个节点间的位置关系,通过jQuery我们可以轻松搞定,但原生JS呢?下面我将整理各种判断方法,以供日后查阅. 二 ...
- JS魔法堂:LINK元素深入详解
一.前言 我们一般使用方式为 <link type="text/css" rel="stylesheet" href="text.css&quo ...
- JS魔法堂:浏览器模式和文档模式怎么玩?
一.前言 从IE8开始引入了文档兼容模式的概念,作为开发人员的我们可以在开发人员工具中通过“浏览器模式”和“文档模式”(IE11开始改为“浏览器模式”改成更贴切的“用户代理字符串”)品味一番,它的出现 ...
- JS魔法堂:精确判断IE的文档模式by特征嗅探
一.前言 苦逼的前端攻城狮都深受浏览器兼容之苦,再完成每一项功能前都要左顾右盼,生怕浏览器不支持某个API,生怕原生API内含臭虫因此判断浏览器类型和版本号成了不可绕过的一道关卡,而特征嗅探是继浏览器 ...
- JS魔法堂:不完全国际化&本地化手册 之 理論篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
随机推荐
- Android Scroll分析——滑动效果产生
相对于在Android2.x版本上出现的长按.点击事件的效果,不得不说,滑动操作具有更好的用户体验.因此,从Android 4.X版本开始,出现了更多滑动操作的效果.越来越多第三方应用模仿这样的效果, ...
- ASP.NET 5 入门 (2) – 自定义配置
ASP.NET 5 入门 (2) – 自定义配置 ASP.NET 5 理解和入门 建立和开发ASP.NET 5 项目 初步理解ASP.NET5的配置 正如我的第一篇文章ASP.NET 5 (vNext ...
- 使用NPOI读写Excel、Word
NPOI 是 POI 项目的 .NET 版本.POI是一个开源的Java读写Excel.WORD等微软OLE2组件文档的项目. 使用 NPOI 你就可以在没有安装 Office 或者相应环境的机器上对 ...
- 使用ACE_Get_Opt解析命令行
当我们用C++开发一些C++控制台小工具时,会需要一些用户输入的参数来决定程序如何工作和执行,而用户输入参数的方式大部分都是采用命令行参数的方式. 比如上一篇文章 玩转Windows服务系列--命令行 ...
- Window Ghosting
最近工作中遇到Window Ghosting这个问题, 感觉挺有意思,这里简单记录下. 在XP时代我们的程序没有响应后只能通过任务管理器强制杀掉,但是Vista之后情况变了, 我们仍然可以拖动失去响应 ...
- AWS系列之一 亚马逊云服务概述
云计算经过这几年的发展,已经不再是是一个高大上的名词,而是已经应用到寻常百姓家的技术.每天如果你和互联网打交道,那么或多或少都会和云扯上关系.gmail.github.各种网盘.GAE.heroku等 ...
- AngularJS快速入门指南17:Includes
使用AngularJS,你可以在HTML中包含其它的HTML文件. 在HTML中包含其它HTML文件? 当前的HTML文档还不支持该功能.不过W3C建议在后续的HTML版本中增加HTML import ...
- ios 向工程里添加Fonts
ios 向工程里添加Fonts 1.网上搜索字体文件(后缀名为.ttf,或.odf),假如你加入的字体为微软雅黑 2.把字体库导入到工程的resouce中 3.然后在你的工程的Info.plist文件 ...
- Atitit 理解Monad attilax总结
Atitit 理解Monad attilax总结 但函数式编程最大的一个问题是,函数是一个数学抽象,在现实世界中不存在,1 那既然这样就够用了,还要 Monad 干嘛?Monad 的作用在这里就体现出 ...
- Linux常用命令01
Linux对于我们来说,就是跑程序的运行平台,简单的来说,就是服务器,自己也没怎么系统的学习Linux的命令,随着项目的需要, 比如要查找日志,哪里出问题了,哪里报错了,因此自己也慢慢地懂一些常用的L ...