最近在做面试题的过程中偶然碰到关于call函数的问题。然后再百度上查了查。偶然看到一篇文章:JavaScript中的call、apply、bind深入理解 抛开其对call函数基本概念的介绍还有其他原理的介绍。其中一段函数吸引了我。

function fn1(){
console.log(1);
}
function fn2(){
console.log(2);
} fn1.call(fn2); //输出 1 fn1.call.call(fn2); //输出 2

  对于 fn1.call(fn2);我能够理解,这段代码仅仅 使得 fn1对象的this指向了fn2;但是最终不影响fn1函数的执行。因为fn1中不包含对this的操作。不过 fn1.call.call(fn2);实在是令我费解。我一时半会没有领会笔者的表达方式。花了很长时间去领会。最终还是看其他大神的博客才得以有所体会。究其原因还是在于对 call 函数的原理的研究。call 函数执行的时候到底干了什么????直接粘贴代码(摘自CSDN:深入JS系列(一:call, apply, bind实现)):

Function.prototype.es3Call = function (context) {
var content = context || window;
content.fn = this;
var args = [];
// arguments是类数组对象,遍历之前需要保存长度,过滤出第一个传参
for (var i = 1, len = arguments.length ; i < len; i++) {
// 避免object之类传入
args.push('arguments[' + i + ']');
}
var result = eval('content.fn('+args+')');
delete content.fn;
return result;
}

  在本机上调试后发现,执行  fn1.call.call(fn2); 的结果与 fn1.es3Call.es3Call(fn2);的结果一致。说明其基本还原了call函数的原理。故结合原理代码总结就是:

    1:把传入的第一个参数作为 call 函数内部的一个临时对象 context;

    2:给 context 对象一个属性 fn , 我称呼其为实际执行函数 context.fn ;让 this 关键字(仅仅是关键字,而不是this对象)指向这个属性 ,即 context.fn = this ; 注意 : 在这里的 this 对象指向的是调用call()函数的函数对象。如 fn1.call(fn2);在执行 call 函数时,call 函数内部的this指向的是fn1;然而 fn1.call.call(fn2);在执行 call() 函数时(注意这里必须是打了小括号“()”才算执行函数,fn1.call访问的是一个对象),call函数内部的 this 指向的是 fn1.call 。

    3:将传入call函数的其他参数,放入临时数组arr[];

    4:利用 eval (笔者采用es3的方法实现,也可以利用其他方式实现)。执行 context.fn( [args] ) ; 实际就是执行 this( [args] );结合第2点。

    5:执行完成后再把 context.fn 删除。返回执行 this( [args] ) 的结果。

   总结上边 5 点之后,能够大概解释出 fn1.call.call(fn2);的执行结果为什么是 输出 2 了。

   首先 调用call 函数时,也就是 fn1.call.call(fn2) ;加粗部分;先将 fn2 作为 临时的 context 对象 。然后 将 fn1.call这个函数对象作为 实际执行函数属性 : context.fn = fn1.call;注意:fn1.call会通过原型链找到最终的对象。其本质为 Function.prototype.call; 然后检查其他参数,没有了。直接执行 fn1.call()函数 ,即 context.fn();此时函数的本质还是 Function.prototype.call 函数对象。不过执行这个函数的环境还是在 Function.prototype.call()中,只不过是第一次调用的call()函数中。第一次调用的call()函数将this关键字指向了 fn2 ;故而 在  fn1.call.call(fn2) ;加粗部分的 函数中执行的 call函数执行过程中的 this指向的是 fn2;传入的参数为空,故而 新的 call()函数对象 的this关键字 被替换为window; 而执行 this()时,就是执行 fn2();不涉及 this操作。故最终输出2。

  这样就能够较好的解释 fn1.call.call(fn2);的输出结果了。为了验证这个过程。可以这段代码查看各个最终执行函数的this对象的指向:

function func(){
console.log(this);
}
func.call(func); //输出func
func.call.call(func); //输出window

  至于 func 为什么指向 window MDN官网上有具体解释(如下图)。如果执行 func.call.call(func,2);还会出来结果 Number{2}。
  

   以上。就是我目前对 js 中call 函数的理解。

理解JavaScript Call()函数原理。的更多相关文章

  1. 理解javascript 回调函数

    ##回调函数定义 百度百科:回调函数 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数.回调函数不 ...

  2. 理解 JavaScript 回调函数并使用

    JavaScript中,函数是一等(first-class)对象:也就是说,函数是 Object 类型并且可以像其他一等对象(String,Array,Number等)一样使用.它们可以"保 ...

  3. JavaScript大杂烩2 - 理解JavaScript的函数

    JavaScript中的字面量 书接上回,我们已经知道在JavaScript中存在轻量级的string,number,boolean与重量级的String,Number,Boolean,而且也知道了之 ...

  4. 深入理解javascript 匿名函数和闭包

    代码如下: (function(){ //这里忽略jQuery所有实现 })(); (function(){ //这里忽略jQuery所有实现 })();  半年前初次接触jQuery的时候,我也像其 ...

  5. JavaScript Call函数原理

    call原理分析,一定要看最后的例子. 1.call使用例子 function add(c, d) { return this.a + this.b + c + d; } , b: }; consol ...

  6. 重新理解javascript回调函数

    把函数作为参数传入到另一个函数中.这个函数就是所谓的回调函数 经常遇到这样一种情况,某个项目的A层和B层是由不同的人员协同完成.A层负责功能funA,B层负责funcB.当B层要用到某个模块的数据,于 ...

  7. 理解JavaScript普通函数以及箭头函数里使用的this

    this 普通函数的this 普通函数的this是由动态作用域决定,它总指向于它的直接调用者.具体可以分为以下四项: this总是指向它的直接调用者, 例如 obj.func() ,那么func()里 ...

  8. 深入理解JavaScript的函数作用域

    什么是作用域 ? 作用域:一个变量可以生效的范围. 变量不是在所有地方都可以使用的,而这个变量的使用范围就是我们要说的作用域. 注意:在JavaScript中,划分作用域也是用大括号划分的, 但是在 ...

  9. 理解JavaScript中函数方法

    1.函数声明和函数表达式 通过字面量创建函数的方式有两种函数声明和函数表达式: 函数声明: function sum(x, y) { var result = x + y; return result ...

随机推荐

  1. fatal error C1003: error count exceeds number; stopping compilation解决方法

    [error]C1003: error count exceeds 100; stopping compilation ...winnt.h 在项目工程中添加#include<windows.h ...

  2. Aspose.Words 操作指北

    前言:   这段时间因为业务需求,需要做一个word 导出,表单式的那种,因为之前也做过NPOI 操作 Excel 这类型的问题,所以总以为word 导出应该挺简单的,一直没有研究这块,汗颜,天真大意 ...

  3. CentOS_mysql8.0_错误

    #参考资料 CSND:https://blog.csdn.net/y_server/article/details/78781177 博客园:http://www.cnblogs.com/testwa ...

  4. SpringBoot学习笔记(6)----SpringBoot中使用Servlet,Filter,Listener的三种方式

    在一般的运用开发中Controller已经大部分都能够实现了,但是也不排除需要自己实现Servlet,Filter,Listener的方式,SpringBoot提供了三种实现方式. 1. 使用Bean ...

  5. UE4自学随笔(一)

    本文及后续均为个人学习记录所用,难免毫无章法零零碎碎,希望看到此文的诸君勿怪. 一.Actor与Pawn Actor类 在UE4中,Actor类是可以放到游戏场景中的游戏对象的基本类型.你如果想放置任 ...

  6. BZOJ 2865 字符串识别(后缀数组+线段树)

    很容易想到只考虑后缀长度必须为\(max(height[rk[i]],height[rk[i]+1])+1\)(即\([i,i+x-1]\)代表的串只出现过一次)然后我正着做一遍反着做一遍,再取一个\ ...

  7. java String字符串操作 字符串加密等

    子串加密 1,设计思想 (1)输入一个字符串 (2)通过toCharArray()的方法将字符串转换成字符数组 (3)新建一个字符数组用来存储修改后的字符数组 2,程序流程图 3,源代码 packag ...

  8. vue-quill-editor-upload : 实现vue-quill-editor上传图片到服务器

    vue-quill-editor-upload git: https://github.com/NextBoy/vu... A plug-in for uploading images to your ...

  9. 转:强制Visual Studio以管理员身份运行

    Windows 8的一个既安全又蛋疼之处是UAC的行为被改变了.以往在Windows 7中,只要关闭了UAC,自己的帐号又是本机管理员组的,任何程序都会以管理员身份启动.然而,在Windows 8上, ...

  10. [Python]Use Flask-Admin with PostgreSQL

    This code recipe gives you an idea of how to use Flask-Admin with postgresql database. from flask im ...