资讯专栏INFORMATION COLUMN

谈谈javascript的Function中那些隐藏的属性/方法:caller/callee/app

shery / 1638人阅读

摘要:在全局环境中调用函数是不会生成此属性,因为不符合此属性的存在意义价值见上条。函数递归时用起来比用函数名调用函数更带感方法这俩方法性质一样,只是用法稍有不同,因此放在一起来介绍。

javascript的Function中有不少不那么常用,又或者用了也是知其然而不知其所以然的属性/方法,本文就来谈谈这一系列属性/方法:caller/callee/apply/call/bind

caller属性

直接上DEMO比较好理解:

// caller demo {
function callerDemo() {
    if (callerDemo.caller) {
      var a = callerDemo.caller.toString();
      console.log(a);
    } else {
      console.log("this is a top function");
    }
}
function handleCaller() {
    callerDemo();
}

handleCaller();    //"function handleCaller() { callerDemo(); }"
callerDemo();    //"this is a top function"

我们先来按照思路一步一步来看这段代码:

首先我们看到定义了俩function:handleCaller和callerDemo,并且还可以看出handleCaller函数里是调用了callerDemo函数的。

在callerDemo函数里,我们看到了本文介绍的主角之一:caller属性,并且可以看出这caller属性是函数对象本身的一个成员属性。

在callerDemo函数里,有一段判断caller属性是否存在的代码,这段代码有什么意义呢?这就要看最后的结果了:直接调用callerDemo()发现此时callerDemo.caller是为空的,而反观通过调用handleCaller()并在其内部调用callerDemo()则callerDemo.caller不为空。这说明只有在函数里调用函数,才会生成caller属性,而直接在全局环境里调用函数则不会生成。

继续看var a = callerDemo.caller.toString();console.log(a);,这里打印出来的居然是handleCaller整个函数体,这说明,此时的callerDemo.caller实际上就是对于handleCaller这个函数对象的一个引用。

这么分析下来,caller属性就很容易明白了:

caller属性是帮助我们在当前函数里获取调用当前函数的某个未知函数,之所以称未知函数,是因为我们在写一个函数时,很可能根本不知道哪个函数会调用到我们的这个函数。

在全局环境中调用函数是不会生成此caller属性,因为不符合此属性的存在意义/价值(见上条)。

只有在当前函数的内部(上下文环境)才能调用当前函数的caller属性,不能从外部调用。

callee属性

还是先放代码:

function calleeDemo() { 
    console.log(arguments.callee); 
} 

有了上文对caller属性的认知,callee属性就很好理解了,它实际上就是对当前函数对象的一个引用。有以下的点需要注意:

callee属性隶属于Function的一个隐藏对象——arguments中,这个arguments对象大家应该不陌生,表示的就是当前函数传入的参数,一般用于函数不限制参数数量的传参。

caller属性一样,也是要在当前函数的内部(上下文环境)才有效。

可配合caller属性一起使用:arguments.callee.caller,这样就可以完全忽略到具体的函数名了。

函数递归时用起来比用函数名调用函数更带感!

apply/call方法

这俩方法性质一样,只是用法稍有不同,因此放在一起来介绍。还记得我上一篇文章《javascript如何判断变量的数据类型》中介绍的利用Object.prototype.toString.call来判断数据类型的方法么:

function type(obj) {
  return Object.prototype.toString.call(obj).slice(8, -1);    //换成用apply方法亦可
}

apply/call方法的意义在于借用其它对象的成员方法来对目标对象执行操作。

借用的过程中,apply/call方法会改变被借用的成员方法的上下文环境:把this这一与上下文环境高度相关的变量指向目标对象,而非原来的对象。看下面的这段代码:

function Point(x, y){
    this.x = x;
    this.y = y;
}
Point.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
}
var p = new Point(0,0);
var circle = {x:1, y:1, r:1};    //只是一个普通的Object对象
p.move.call(circle, 2, 1);    //借用了Point类对象中的move方法
//p.move.apply(circle, [2, 1]);    //等价于p.move.call(circle, 2, 1);

这里的circle只是一个普通的Object对象,不含任何自定义的成员方法,但通过apply/call方法,可以借用Point类对象定义的move方法来帮助circle达到目的(本例其实是圆心在坐标轴上的移动)。在借用Point类对象的move方法时,move方法中的this就不再指向p,而是指向circle了,达到了上下文环境改变的效果。
另外,从代码里也可以看出,call方法与apply方法的区别仅在于:call方法直接把需要传入的参数列在目标对象其后,而apply方法则以数组的形式整体传入。

bind方法

bind方法与apply/call方法也非常类似,相当于稍微再封装了一下,仍以上述DEMO作为案例:

function Point(x, y){
    this.x = x;
    this.y = y;
}
Point.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
}
var p = new Point(0,0);
var circle = {x:1, y:1, r:1};
// p.move.call(circle, 2, 1);
// p.move.apply(circle, [2, 1]);
var circleMove = p.move.bind(circle, 2, 1);    //此时并不执行move方法
circleMove();    //此时才执行

从上面这段DEMO可以看出,bind方法其实是给apply/call方法缓了一下,也可以说是封装了一下方便后续调用,其实质上相当于下面的这段代码:

function circleMove() {
    p.move.call(circle, 2, 1);
}
circleMove();
bind方法兼容性适应

bind方法,即Function.prototype.bind,属于ECMAScript 5,IE从IE 10版本才开始支持,那怎么做兼容性适应呢?

if(typeof Function.prototype.bind !== "function") {
  Function.prototype.bind = function() {
    var thatFunc = this;
    var args = [];
    for(var i = 0; i < arguments.length; i++) {
      args[i] = arguments[i];
    }

    return function() {
      var context = args.shift();
      thatFunc.apply(context, args);
    }
  }
}

其思路是利用apply方法来封装成bind方法。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78508.html

相关文章

  • 谈谈JavaScript严格模式你应该遵守那些

    严格模式 首先来了解一下严格模式是什么?严格模式是JavaScript中的一种限制性更强的变种方式,不是一个子集:它在语义上与正常代码有明显的差异,不支持严格模式的浏览器与支持严格模式的浏览器行为上也不一样,所以不要在未经严格模式特性测试情况下使用严格模式,严格模式可以与非严格模式共存,所以脚本可以逐渐的选择性加入严格模式 严格模式的目的 首先,严格模式会将JavaScript陷阱直接变成明显的错...

    MingjunYang 评论0 收藏0
  • 谈谈text-overflow那些坑和应对方法

    摘要:基本语法默认值,将溢出的文本裁减掉将溢出的文本用省略号来表示设置一个字符串用来表示溢出的文本兼容性上,除了外,其余两个属性兼容到了,所以大可放心使用。 原文地址:https://www.xksblog.top/the-p... text-overflow是CSS3中的属性,它规定了当文本溢出其包含元素时以何种方式显示。但在使用的时候,有时会发现这个text-overflow设置了属性...

    nodejh 评论0 收藏0
  • 前端面试:谈谈 JS 垃圾回收机制

    摘要:例如本地函数的局部变量和参数当前嵌套调用链上的其他函数的变量和参数全局变量还有一些其他的,内部的这些值称为根。例如,如果局部变量中有对象,并且该对象具有引用另一个对象的属性,则该对象被视为可达性,它引用的那些也是可以访问的,详细的例子如下。 最近看到一些面试的回顾,不少有被面试官问到谈谈JS 垃圾回收机制,说实话,面试官会问这个问题,说明他最近看到一些关于 JS 垃圾回收机制的相关的文...

    孙淑建 评论0 收藏0
  • 谈谈React那些小事

    摘要:在如今的前端框架界,三分天下的时代已经到来,而曾经一统天下的局面已一去不复返。三分天下,前端虽乱,但美其名曰繁荣。那些小事俗话说千里之堤毁于蚁穴,在开发中我们不能忙于进度而忽视了细节。 前言 说起React,那也是近一年多时间火起来的前端框架,其在Facebook的影响力和大力推广下,已然成为目前前端界的中流砥柱。在如今的前端框架界,React、Vue、Angular三分天下的时代已经...

    Berwin 评论0 收藏0
  • 前端面试大全

    摘要:一般情况下是全局对象。避免在页面的主体布局中使用,要等其中的内容完全下载之后才会显示出来,显示布局慢。对普通的网站有一个统一的思路,就是尽量向前端优化减少数据库操作减少磁盘。 HTML面试题 1.XHTML和HTML有什么区别 HTML是一种基本的WEB网页设计语言,XHTML是一个基于XML的置标语言最主要的不同: XHTML 元素必须被正确地嵌套。 XHTML 元素必须被关闭。...

    dingda 评论0 收藏0

发表评论

0条评论

shery

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<