我发现Javascript中的严格模式有些奇怪。
use strict
和arguments.callee
。use strict
,在函数范围内。当我调用库提供的其中一个函数时,会抛出错误。然而,
use strict
我删除了所有不相关的内容并将代码缩减为此内容(online demo on jsFiddle):
// This comes from the minified external JS library.
// It creates a global object "foo".
(function () {
foo = {};
foo.bar = function (e) {
return function () {
var a5 = arguments.callee;
while (a5) {
a5 = a5.caller // Error on this line in all browsers except Chrome
}
}
}("any value here");
})();
// Here's my code.
(function() {
"use strict"; // I enable strict mode in my own function only.
foo.bar();
alert("done");
})();
+-----------------------+-----+--------------------------------------------------------------+
| Browser | OS | Error |
+-----------------------+-----+--------------------------------------------------------------+
| Chrome 27.0.1453.94 m | Win | <<NO ERROR!>> |
| Opera 12.15 | Win | Unhandled Error: Illegal property access |
| Firefox 21.0 | Win | TypeError: access to strict mode caller function is censored |
| Safari 5.1.7 | Win | TypeError: Type error |
| IE 10 | Win | SCRIPT5043: Accessing the 'caller' property of a function or |
| | | arguments object is not allowed in strict mode |
| Chrome 27.0.1543.93 | Mac | <<NO ERROR!>> |
| Opera 12.15 | Mac | Unhandled Error: Illegal property access |
| Firefox 21.0 | Mac | TypeError: access to strict mode caller function is censored |
| Safari 6.0.4 | Mac | TypeError: Function.caller used to retrieve strict caller |
+-----------------------+-----+--------------------------------------------------------------+
注意:对于OS
,Win
= Windows 7,Mac
= Mac OS 10.7.5
use strict
(请参阅Can I use)。use strict
在我的函数范围内,因此在其范围之外定义的所有内容都不会受到影响(请参阅this Stack Overflow question)。那么,除了Chrome之外的所有浏览器都是错误的吗?或者反过来呢?或者这是未定义的行为,以便浏览器可以选择以任何一种方式实现它?
答案 0 :(得分:41)
在我们开始讨论这个问题之前,有几个快速的要点:
- 所有现代桌面浏览器均支持
use strict
...
不,一点也不。 IE8是一个相当现代的浏览器 (不再是2015年),IE9是一个相当相当现代的浏览器。它们都不支持严格模式(IE9支持部分模式)。 IE8将与我们在一起很长一段时间,因为它可以和Windows XP一样高。尽管XP现在已经过时了(嗯,你可以购买一个特殊的&#34;自定义支持&#34;来自MS的计划),人们会继续使用它一段时间。
use strict
在我的函数范围内,因此在其范围之外定义的所有内容都不会受到影响
不完全。该规范对非严格代码使用在严格模式下创建的函数的方式施加了限制。所以严格的模式可以到达它的框外。事实上,这是你正在使用的代码的一部分。
那么,除了Chrome之外的所有浏览器都是错误的吗?或者反过来呢?或者这是未定义的行为,以便浏览器可以选择以任何一种方式实现它?
稍微调查一下,看起来像是:
Chrome正在以某种方式正确行事,
Firefox以不同的方式正确行事,
...而且IE10的非常轻微错误。 :-)(IE9肯定是错的,虽然没有特别有害的方式。)
我没有看到其他人,我认为我们已经覆盖了地面。
从根本上造成麻烦的代码就是这个循环
var a5 = arguments.callee;
while (a5) {
a5 = a5.caller // Error on this line in all browsers except Chrome
}
...它依赖于函数对象的caller
属性。所以,让我们从那里开始吧。
Function#caller
{3}规范中从未定义Function#caller
属性。一些实现提供了它,其他实现没有。它是一个令人震惊的坏主意 (抱歉,这是主观的,不是吗?)一个实施问题(甚至比{{1更多) (),特别是在多线程环境中(并且有多线程JavaScript引擎),以及递归代码,正如Bergi在问题的评论中指出的那样。
所以在第5版中,他们通过指定在strict函数上引用arguments.caller
属性会引发错误来明确地消除它。 (这是§13.2, Creating Function Objects, Step 19。)
严格功能。但是,在非严格函数上,行为未指定且与实现有关。这就是为什么有很多不同的方法可以做到这一点。
回调检测代码比调试会话更容易,所以让我们use this:
caller
在V8(Chrome的JavaScript引擎)上,上面的代码为我们提供了这个:
1. Getting a5 from arguments.callee 2. What did we get? [object Function] 3. Getting a5.caller 4. What is a5 now? [object Null]
因此,我们从console.log("1. Getting a5 from arguments.callee");
var a5 = arguments.callee;
console.log("2. What did we get? " +
Object.prototype.toString.call(a5));
while (a5) {
console.log("3. Getting a5.caller");
a5 = a5.caller; // Error on this line in all browsers except Chrome
console.log("4. What is a5 now? " +
Object.prototype.toString.call(a5));
}
获得了对foo.bar
函数的引用,但随后访问该非严格函数的arguments.callee
给了我们caller
。循环终止,我们不会收到任何错误。
由于null
未指定非严格函数,因此允许V8执行其对Function#caller
上caller
的访问所需的任何操作。返回foo.bar
是完全合理的(虽然我很惊讶地看到null
而不是null
)。 (我们将在下面的结论中回到undefined
...)
SpiderMonkey(Firefox的JavaScript引擎)执行此操作:
1. Getting a5 from arguments.callee 2. What did we get? [object Function] 3. Getting a5.caller TypeError: access to strict mode caller function is censored
我们开始从null
获取foo.bar
,但是在非严格函数上访问arguments.callee
失败并显示错误。
由于非严格函数对caller
的访问权限是未指定的行为,因此SpiderMonkey人员可以按照自己的意愿行事。如果要返回的函数是严格函数,他们决定抛出错误。一条细线,但由于这是未指明的,他们可以让它走了。
JScript(IE10的JavaScript引擎)执行此操作:
1. Getting a5 from arguments.callee 2. What did we get? [object Function] 3. Getting a5.caller SCRIPT5043: Accessing the 'caller' property of a function or arguments object is not allowed in strict mode
与其他人一样,我们从caller
获取foo.bar
函数。然后尝试访问非严格函数arguments.callee
会给我们一个错误,说我们无法在严格模式下执行此操作。
我称之为&#34;错误&#34; (但是非常小写&#34; w&#34;)因为它说我们不能做我们在严格模式下做的事情,但我们&#39 ; 严格模式。
但你可以说,Chrome和Firefox的做法并没有错,因为(再次)caller
访问是未指定的行为。所以IE10人员决定他们实现这种未指定的行为会引发严格模式错误。我认为它具有误导性,但是,如果它错了,&#34;它肯定不是非常错误。
1. Getting a5 from arguments.callee 2. What did we get? [object Function] 3. Getting a5.caller 4. What is a5 now? [object Function] 3. Getting a5.caller 4. What is a5 now? [object Null]
它在非严格函数上允许caller
,然后在严格函数上允许它,返回Function#caller
。规范很清楚,第二次访问应该抛出一个错误,因为它在严格的函数上访问null
。
以上所有内容的有趣之处在于,如果您尝试在严格的功能,Chrome,Firefox和IE10上访问caller
,那么除了明确指定的抛出错误行为之外(以各种方式)阻止您使用caller
获取对严格函数的引用,即使在非严格函数上访问caller
时也是如此。 Firefox通过抛出错误来做到这一点。 Chrome和IE10通过返回caller
来完成此操作。它们都支持通过null
(在非严格函数上)引用非 -strict函数,而不是严格的函数。
我无法在任何地方找到指定的行为(但是,非严格函数的caller
完全未指定...)。它可能是正确的事情(tm),我只是没有看到它。
此代码也很有趣:Live Copy | Live Source
caller
答案 1 :(得分:0)
我必须使用一个较旧的Telerik JS库,该库无法轻松更新,并且今天早晨遇到了此错误。对于某些人来说,一种可行的解决方法可能是在调用松散模式函数之前,使用“ setTimeout” JS函数退出严格模式。
例如更改此:
function functionInStrictMode(){
looseModeFunction();
}
对于这样的事情:
function functionInStrictMode(){
setTimeout(looseModeFunction);
}
我猜想这可能可行,因为setTimeout将上下文还原为全局名称空间和/或离开了functionInStrictMode的范围。我不完全了解所有细节。可能会有更好的方法;我没有对此进行彻底的研究,但我想将其发布在这里进行讨论。