小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
前言
函数的调用关系,谁调用了谁,我是谁,你清楚吗?
除了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;
}
可以看到,严格模式下,
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, 谁调用了我?
注意点:
- 严格模式下,不可用
- 全局作用域内被调用,caller为null
用途:
- 调用环境检查
- 调用栈信息收集
这就对应了前言的话,除了Error能获取调用栈信息, caller也不失为一种取巧的获取方式,当然有一定的局限性,又何妨,特定场景有作用,就够了。
今天你收获了吗?
下一章,我们一起来学下 callee吧!