知识点
- 函数中的
callee
、caller
属性 Error.prototype.stack
属性
题目
我们要实现一个函数where
,它会返回它被调用时的所在函数的名字,例如:
function main () {
where()
}
function a () {
function b () {
where() // => 'b'
}
b()
}
main(); // => 'main'
a(); // => 'b'
实现
我们立刻就会想到使用caller
这个属性
const where = function() {
return where.caller.name
}
没有问题,我们还可以利用argumengts.callee
这个属性令函数名where
解耦:
const where = function() {
return arguments.callee.caller.name
}
嗯,这样就可以了吗?
这样的实现存在一个问题,当函数在严格模式下运行时,访问caller
、argument.callee
都会导致错误。
那么还有没有另外的方法解决上面的问题呢?
Error.prototype.stack
其实我们可以利用Error.prototype.stack
来解决这个问题。
Error对象作为一个非标准的栈属性提供了一种函数追踪方式,无论这个函数被谁低啊用,处于什么模式,来自哪一行或那个文件,有什么参数,都可以通过这个属性反应很粗俩。
我们需要来看一下Stack是如何工作的:
每当有一个函数被调用,它就会被压如栈顶,调用结束时会被从栈顶移出
这种数据结构叫做后进先出(LIFO)
举个例子,有三个函数a
/b
/c
,形成如下的调用关系:
function a () {
b();
}
function b () {
c();
}
function c () {
console.trace('hell0')
}
在这个调用关系中,a
先被压如栈顶,随后是b
,随后是c
,c
调用完毕后首先从栈顶移出,然后是b
,最后是c
我们可以用console.trace
来展示这个行为:
也就是说,当函数被调用时,压入栈顶,执行完毕时,被弹出栈
Error.prototype.stack
就会反映出这种调用关系,还记得当我们的程序抛出一个错误时,控制台都打印了什么吗?
在错误描述语句下方,以at
开头的每一行都是一条调用栈的记录,而这些调用记录就存储在Error.prototype.stack
中
应用
所以,我们的那道题目就可以利用Error.prototype.stack
来看一下这个调用信息了
首先我们在函数内部构造出一个Error
对象,然后通过正则匹配它的stack
属性中的信息,来获取函数的直接调用者
const where = function() {
return new Error().stack.match(/at\s\S+/g)[1].split('at ')[1]
};
这样就OK了