Непоследовательная область "use strict" в разных веб-браузерах (относительно аргументов.вызываемого и вызывающего)

ситуация:

я нашел кое-что странное относительно строгого режима в Javascript.

  • я использую внешнюю, стороннюю библиотеку JavaScript, которая
    • был сокращен,
    • более 4000 строк кода
    • is не используя use strict, и
    • использует arguments.callee.
  • я использую use strict в моем собственном коде, область действия функция.

когда я вызываю одну из функций, предоставляемых библиотекой, она выдает ошибку. Однако,

  • ошибка возникает только если я использую 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


в моем понимании:


вопрос:

Итак, все браузеры, кроме Chrome, неправильны? Или наоборот? Или это неопределенное поведение, так что браузеры могут реализовать его в любом случае?

1 ответов


предисловие

пару быстрых очков, прежде чем мы попадем в мясо этого:

  • все современные настольные браузеры поддерживают use strict...

нет, вовсе нет. IE8-довольно современный браузер (больше нет, в 2015 году), а IE9-это совсем довольно современный браузер. Ни один из них не поддерживает строгий режим (IE9 поддерживает его части). IE8 будет с нами долго время, потому что это так высоко, как вы можете пойти на Windows XP. Несмотря на то, что XP теперь категорически заканчивается (ну, вы можете купить специальный план "пользовательской поддержки" от MS), люди будут продолжать использовать его некоторое время.

  • на use strict является областью действия моей функции, поэтому все, что определено вне ее области действия, не затрагивается

не совсем. Спецификация накладывает ограничения на то, как даже нестрогий код использует функции, созданные в strict режим. Таким образом, строгий режим может выйти за пределы своей коробки. И в самом деле, это часть того, что происходит с кодом, который вы используете.

обзор

Итак, все браузеры, кроме Chrome, неправильны? Или наоборот? Или это неопределенное поведение, так что браузеры могут реализовать его в любом случае?

глядя в него немного, это выглядит так:

  1. Chrome получает его правильно путь,

  2. Firefox получает это право по-другому,

  3. ...и 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), особенно в многопоточных средах (и есть многопоточные движки JavaScript), а также как и рекурсивный код, как указывал Берги в комментариях к вопросу.

так в 5-м издании они явно избавились от него, указав, что ссылок caller свойство строгой функции вызовет ошибку. (Это в §13.2,Создание Объектов Функция шаг 19.)

на строго(tm), я просто не вижу уточнил он.

этот код также интересно играть с:Скопировать Видео | Живой Источник

<!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>