开端

最近想实现一个小功能,通过一个字符串在对象上任意访问属性,还要支持表达式计算,前者可以直接通过属性访问器实现,但是要支持表达式计算,就得使上咱们的 eval 函数了。

看解决方案直接拉到文章底部。

然后直接踩坑。


众所周知 Function.prototype.callFunction.prototype.apply 方法可以在指定的上下文中调用函数。(JS 中的 this 只与被调用时的上下文有关)

咱马上就想出了代码,大概长这样:

1
eval.call(context, expression)

然后发现代码一直达不到效果,一番调试之后发现了其中的大坑。

初探

查了一下网上也少有文章提及 eval 函数中 this 的问题,大概是因为只有垃圾程序员才用 eval 这种不安全的函数吧。(x)

mdn web docs 也只是提及了一句:

如果你间接的使用 eval(),比如通过一个引用来调用它,而不是直接的调用 eval。从 ECMAScript 5 起,它工作在全局作用域下,而不是局部作用域中。

Function.prototype.callFunction.prototype.apply 算是间接调用吗?

naive_code

可它们都是 naive native code ,咱也不知道,咱也不敢问。那就来调试康康。

现象

间接调用跳转全局作用域

根据 mdn web docs 的说法,间接调用的方式会让 eval 的 this 跳转到全局作用域,看样子 Function.prototype.callFunction.prototype.apply 也是间接调用的一种。

解决

既然不能间接调用 eval,而直接调用 eval 取决的是 eval 所在位置的 this,那咱们是不是可以将 eval 的调用封装在一个函数中,再通过 Function.prototype.call 给这个函数指定 this,让这个带有 this 的函数去执行 eval 呢?

还真行。

eval_this

这样就可以成功在调用 eval 的时候指定 this 辣。