Javascriptでstrictモードに関して何か奇妙なものを見つけました。
use strict
をまったく使用しない、およびarguments.callee
を使用しています。use strict
を使用しています。ライブラリが提供する関数の1つを呼び出すと、エラーがスローされます。しかしながら、
use strict
を使用している場合にのみスローされます私は無関係なものをすべて削除し、コードを次のように減らしました( 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
をサポートしています( 使用できます を参照)。use strict
のスコープは関数内にあるため、スコープ外で定義されたものはすべて影響を受けません( このスタックオーバーフローの質問 を参照)。Chrome=を除くすべてのブラウザは間違っていますか?それとも逆ですか?または、ブラウザがどちらかの方法で実装することを選択できるように、この未定義の動作ですか?
この詳細に入る前に、いくつかの要点を説明します。
- すべての最新のデスクトップブラウザは
use strict
...をサポートしています.
いいえ、まったくありません。 IE8はかなり最新のブラウザです (2015年にはもうない)、IE9は かなり かなり現代的なブラウザ。どちらも厳密モードをサポートしていません(IE9はその一部をサポートしています)。 IE8はWindows XPと同じくらい高いので、長い間私たちと一緒にいます。 XPは現在、サポートが終了している(まあ、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
Function#caller
プロパティは、第3版の仕様では定義されていません。いくつかの実装はそれを提供し、他は提供しませんでした。それは 驚くほど悪い考え (申し訳ありません、それは主観的でしたね?)実装の問題(arguments.caller
よりもさらに1)、特にマルチスレッド環境(およびBergiが質問のコメントで指摘したように、マルチスレッドのJavaScriptエンジンです)。
したがって、第5版では、厳密な関数でcaller
プロパティを参照するとエラーがスローされるように指定することで、明示的にそれを取り除きました。 (これは §13.2、Creating Function Objects、Step 19 にあります。)
これはstrict関数に関するものです。ただし、厳密でない関数では、動作は特定されておらず、実装に依存します。そのため、これを正しく行うにはさまざまな方法があります。
デバッグセッションよりもインストルメント済みコードを参照する方が簡単なので、 これを使用 :
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));
}
V8(ChromeのJavaScriptエンジン)では、上記のコードで次のようになります。
1. arguments.callee 2からa5を取得します。何を手に入れましたか? [オブジェクト関数] 3。 a5.caller 4を取得します。現在a5とは何ですか? [オブジェクトnull]
そのため、foo.bar
からarguments.callee
関数への参照を取得しましたが、その非厳密関数でcaller
にアクセスすると、null
が得られました。ループは終了し、エラーは発生しません。
Function#caller
は厳密でない関数では指定されていないため、V [8]はfoo.bar
でcaller
にアクセスするために必要なすべてのことを実行できます。 null
を返すことは完全に合理的です(null
ではなくundefined
を見て驚いたのですが)。 (以下の結論でnull
に戻ります...)
SpiderMonkey(FirefoxのJavaScriptエンジン)はこれを行います:
1. arguments.callee 2からa5を取得します。何を手に入れましたか? [オブジェクト関数] 3。 a5.caller TypeErrorの取得:ストリクトモードの呼び出し元関数へのアクセスは検閲されます
foo.bar
からarguments.callee
を取得することから始めますが、その非厳密関数のcaller
へのアクセスはエラーで失敗します。
繰り返しになりますが、厳密でない関数でのcaller
へのアクセスは不特定の動作であるため、SpiderMonkeyの人々は望んだことを実行できます。返される関数が厳密な関数である場合、エラーをスローすることにしました。細かい線ですが、これは指定されていないため、歩くことは許可されています。
JScript(IE10のJavaScriptエンジン)はこれを行います。
1. arguments.callee からa5を取得します。2.何を取得しましたか? [オブジェクト関数] 3. a5.callerの取得 SCRIPT5043:関数または引数オブジェクトの 'caller'プロパティへのアクセスは、厳密モードでは許可されていません
他と同様に、foo.bar
からarguments.callee
関数を取得します。次に、非厳密関数のcaller
にアクセスしようとすると、厳密モードでは実行できないというエラーが表示されます。
これを「間違った」と呼びます(ただし、very小文字の「w」を使用します)、厳密モードでは実行できないことを示しているためです。厳密モードではinではありません。
ただし、これは(=)caller
アクセスが不特定の動作であるため、ChromeとFirefoxの動作が間違っているわけではないと主張できます。そのため、IE10の実装では、この未指定の動作はストリクトモードエラーをスローします。誤解を招く可能性があると思いますが、繰り返しますが、「間違っている」場合は、間違いなく間違いではありません。
ところで、IE9は間違いなくこれを間違っています:
1. arguments.calleeからa5を取得する 2。何を手に入れましたか? [オブジェクト関数] 3。 a5.callerを取得する 4。現在a5とは何ですか? [オブジェクト関数] 3。 a5.callerを取得する 4。現在a5とは何ですか? [オブジェクトnull]
厳密でない関数でFunction#caller
を許可し、厳密な関数で許可してnull
を返します。厳密な関数でcaller
にアクセスしていたので、2番目のアクセスでエラーがスローされたはずであることが仕様に明記されています。
上記すべての興味深い点は、厳密な関数、Chrome、Firefox、およびIE10でcaller
にアクセスしようとすると、エラーをスローするという明確に指定された動作に加えて、すべて(さまざまな方法で)厳密でない関数でcaller
にアクセスする場合でも、厳密な関数への参照を取得するためにcaller
を使用する。 Firefoxはエラーをスローしてこれを行います。 ChromeおよびIE10はnull
を返すことでそれを行います。それらはすべて、nonへの参照の取得をサポートしていますcaller
を介した-strict関数(非厳密関数に対して)、厳密な関数ではありません。
私はその動作がどこにも指定されていることを見つけることができません(しかし、非厳密関数のcaller
は完全に指定されていません...)。それはおそらく正しいことです(tm)、指定されていないようです。
このコードは、次のコードで遊ぶのも楽しいものです。 Live Copy | ライブソース
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Strict and Loose Function#caller</title>
<style>
p {
font-family: sans-serif;
margin: 0.1em;
}
.err {
color: #d00;
}
</style>
</head>
<body>
<script>
function display(msg, cls) {
var p = document.createElement('p');
if (cls) {
p.className = cls;
}
p.innerHTML = String(msg);
document.body.appendChild(p);
}
// The loose functions
(function () {
function loose1() {
display("loose1 calling loose2");
loose2();
}
loose1.id = "loose1"; // Since name isn't standard yet
function loose2() {
var c;
try {
display("loose2: looping through callers:");
c = loose2;
while (c) {
display("loose2: getting " + c.id + ".caller");
c = c.caller;
display("loose2: got " +
((c && c.id) || Object.prototype.toString.call(c)));
}
display("loose2: done");
}
catch (e) {
display("loose2: exception: " +
(e.message || String(e)),
"err");
}
}
loose2.id = "loose2";
window.loose1 = loose1;
window.loose2 = loose2;
})();
// The strict ones
(function() {
"use strict";
function strict1() {
display("strict1: calling strict2");
strict2();
}
strict1.id = "strict1";
function strict2() {
display("strict2: calling loose1");
loose1();
}
strict2.id = "strict2";
function strict3() {
display("strict3: calling strict4");
strict4();
}
strict3.id = "strict3";
function strict4() {
var c;
try {
display("strict4: getting strict4.caller");
c = strict4.caller;
}
catch (e) {
display("strict4: exception: " +
(e.message || String(e)),
"err");
}
}
strict4.id = "strict4";
strict1();
strict3();
})();
</script>
</body>
</html>