Function之caller

730 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

函数的调用关系,谁调用了谁,我是谁,你清楚吗?

除了Error的stack获取信息外,你还知道哪些手段知道了?

今天就一起学习学习caller和callee这两个属性。

caller

获取指定函数的调用者。

在浏览器控制台,执行下面的代码:

function sum(num1, num2) {
    console.log("caller:",sum.caller);
    return num1 + num2;
}

sum(1, 2);
// caller: null

function doSum() {
    sum(1, 2)
}

doSum();
// caller: ƒ doSum() {
//    sum(1, 2)
// }

可以看到全局作用域下,返回的是null,反之是调用该函数的函数。

在nodejs(v12.14.1)环境下执行 node [文件名].js, 返回值如下:

// caller: [Function]
// caller: [Function: doSum]

我们稍微调整一下代码, 把输出的信息调整为sum.caller.toString()

function sum(num1, num2) {
    console.log("caller:",sum.caller.toString());
    return num1 + num2;
}

sum(1, 2);
// 输出结果:
/* 
caller: function (exports, require, module, __filename, __dirname) {
    function sum(num1, num2) {
        console.log("caller:",sum.caller.toString());
        return num1 + num2;
    }

    sum(1, 2);

    function doSum() {
        sum(1, 2)
    }

    doSum()
 }
*/

function doSum() {
    sum(1, 2)
}

doSum()
// 输出结果
/*
caller: function doSum() {
    sum(1, 2)
}
*/

可以看到使用node [文件名].js执行js文件的时候,最终被包裹成如下函数

function (exports, require, module, __filename, __dirname) {
    // 省略代码
}

严格模式的caller

修改sum函数为下面的代码,再执行代码:

function sum(num1, num2) {
    "use strict";
    console.log("caller:",sum.caller.toString());
    return num1 + num2;
}

截图_20211207101209.png 可以看到,严格模式下, caller, callee, arguments属性都是不可用的。

平时大家都使用了严格模式吗? 其实很多库,都还在使用arguments参数,因为实在是太香,太方便了。

caller的用途

跟踪调用信息

可以用于跟踪函数的调用层级关系,是不是很秀?

function getStack(fn) {
    const stacks = [];
    let caller  = fn.caller;
    while (caller) {
        stacks.unshift(caller.name);
        caller = caller.caller;
    }
    return stacks;
}

function a() {
    console.log("a")
    const stacks = getStack(a);
    console.log("stacks:", stacks);
}

function b() {
    a();
    console.log("b");
}


function c() {
    b();
    console.log("c")
}


c();  // stacks: (2) ['c', 'b']

检查被调用环境

function getCaller(fun) {
   const caller = fun.caller;
   if (caller == null) {
      console.log("caller is global context");
   } else{
      console.log("caller.name:" + caller);
  }
  return fun.caller
}

function add(){
    getCaller(add)
}

add() // caller is global context

小结

一句话总结: caller, 谁调用了我?

注意点:

  1. 严格模式下,不可用
  2. 全局作用域内被调用,caller为null

用途:

  1. 调用环境检查
  2. 调用栈信息收集

这就对应了前言的话,除了Error能获取调用栈信息, caller也不失为一种取巧的获取方式,当然有一定的局限性,又何妨,特定场景有作用,就够了。

今天你收获了吗?

下一章,我们一起来学下 callee吧!