JavaScript — определение объекта доступно перед выполнением кода в Safari

Объекты и функции, которые мне нужно выполнить только один раз при загрузке страницы, заключены в проверку undefined для объекта. В Chrome на Windows/Linux, который я обычно использую, код работает отлично, т.е. код выполняется только один раз. Но в Safari как на iPad, так и на MacBook проверка undefined не работает, т. е. в соответствии с браузером объект/функция уже объявлены, даже без выполнения кода!

Я упростил свой код, включив в него только цикл if, который проверяет, объявлена ​​ли уже вложенная функция. Поскольку это не должно было быть объявлено в первый раз, я включил someVariable внутри if, которого никогда не должно быть undefined.

Запустите ту же функцию в Chrome и Safari и увидите разницу.


if (typeof anObject == 'undefined') {

    function anObject(someParameter = 'someParameter') {
        var someProperty = 'someProperty';

        function someMethod(someParameter) {
            console.info(someParameter);
        }
    }

    console.info('Hi');
    var someVariable = 404;
}

В Chrome вы можете видеть ведение журнала консоли 'Hi', а также someVariable и 404. Но в Safari нет ведения журнала консоли, и someVariable становится неопределенным.

Если ставить точки останова, чтобы понять, что происходит - первая неопределенная проверка вообще никогда не работает. anObject определяется еще до объявления.

Я попытался найти различия в этом между V8 (движок Chrome JS) и JavaScriptCore (движок Safari), но не нашел ничего надежного. Я предполагаю, что это связано с выполнением и подъемом функций. Было бы лучше, если бы кто-нибудь мог объяснить мне причину этой разницы в исполнении. На iPad поведение такое же, как и на Chrome!

Обновления:

  1. Я нашел аналогичную проблему, связанную с другим исполнением. Кажется как это что-то связанное с подъемом функции, но не может найти какой-либо надежный источник еще. Подъем Javascript в Chrome и Firefox

  2. Похоже, что это на самом деле подъемное поведение сейчас. Это работает с использованием функционального выражения. В этом случае простая замена function anObject() на var anObject = function(). Делая это, я думаю, переменной не присваивается ссылка на функцию, даже если функция поднимается и оценивается перед выполнением.

  3. По рекомендации PhistucK я открыл проблему в системе отслеживания проблем WebKit (ошибка №199823), Хром Обсудить и TC39 ECMA262 Github (ошибка №1632).

  4. Это существующая ошибка Webkit, о которой сообщалось в 2016 году — Ошибка 163209 — [ES6]. Реализовать правила подъема функции Приложения B.3.3 для глобальной области видимости. Теперь я резюмировал исследование в своем ответе..

Я удивлен, что такая разница все еще существует. Используете ли вы последние версии всех протестированных вами браузеров? Я думаю, что ECMAScript 2015+ должен был стандартизировать то, как это работает (я помню разговоры о функциях внутри операторов if). Небольшое примечание: Chrome для iOS (но не для macOS) не использует V8, это в основном просто скин Safari с минимальными дополнениями (я думаю, веб-платежи, а также функции браузера, такие как синхронизация и автоматическое заполнение вещи и многое другое), сам веб-движок по-прежнему WebKit и JavaScriptCore.

PhistucK 15.07.2019 13:13

И я предлагаю вам сообщить о проблеме либо со всеми движками, либо с теми, которые вы считаете неправильными, и каждый из них, вероятно, прокомментирует, правильный ли их способ или нет. Если все они заявляют, что их путь правильный, вы отправляете жалобу в TC39 (комитет ECMAScript) о различиях, и они должны урегулировать спор. Как только они это сделают, вы можете прокомментировать проблемы, которые вы подали в связи с решением (или зарегистрировать новые проблемы для тех, которые не соответствуют требованиям). Да, это немного работы, но оно того стоит — вы исправите один кусочек мира.

PhistucK 15.07.2019 13:18

@PhistucK, большое спасибо за руководство. Я зарегистрировал проблему на Chromium Discuss. На самом деле я изначально планировал опубликовать это в различных обсуждениях движка, но потом подумал, что буду рассылать спам во многих формах. Я сделаю это сейчас. Спасибо, что развеяли сомнения относительно Chrome для iOS — теперь я понимаю, что проблема действительно в движках. Так же запилю вопрос с двигателями и ТС39. Я очень ценю, что вы нашли время, чтобы сообщить мне обо всем этом. Рад внести свою лепту сейчас. :) Chrome: 75.0.3770.100 Safari: версия 12.1.1 iPad Chrome: 75, iOS 12.3

Paras Lehana 15.07.2019 14:12

Это средства отслеживания проблем основных движков — webkit.org/bcrbug.combugzil.la и TC39 — github.com/tc39/ecma262/issues.

PhistucK 15.07.2019 14:24

Вы можете проверить Какова точная семантика функций блочного уровня в ES6? Похоже, в JavaScriptCore есть ошибка.

Bergi 16.07.2019 18:49
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
5
408
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Другой вопрос SO, который вы нашли, на самом деле отвечает на этот вопрос. Вкратце:

Declaring functions inside conditional statements is non-standard, so do not do that.

Учитывая, что это нестандартно, браузеры могут вести себя по-разному.

Кроме того, я не думаю, что ваша техника дает какую-то пользу. Просто удалите галочку «if undefined» и безоговорочно определите функции на верхнем уровне. В любом случае они будут назначены только один раз.

Да, уже заменил это на флаг. Просто пытаюсь найти причины, если таковые имеются. Это упрощенный код, как я уже упоминал. Исходный код фактически проверял множественные вызовы одного и того же файла с помощью этой проверки. Проще говоря, объект будет выполняться только один раз, независимо от того, сколько раз загружен файл.

Paras Lehana 15.07.2019 11:43

@Bergi, спасибо за отличное объяснение. Я опубликовал окончательный ответ, который обобщает исследование. Я думаю, вы можете улучшить его. Ценится. :)

Paras Lehana 17.07.2019 10:01
Ответ принят как подходящий

Это поведение связано с использованием небрежного режима в движках Webkit, в котором есть ошибка. Позвольте мне завершить исследование:

В частности, в этом примере есть три ключевых аспекта: в коде нестрогий режим функция — это объявлен внутри блока и ссылка до этого блока.

Как объясняется во введении к Приложение В.3.3, объявления функций внутри блочных операторов изначально не были частью спецификации языка; это было расширение, которое браузеры часто реализовывали, каждый по-своему. ES2015 стремился указать как можно большую часть этого поведения, но, поскольку различия между браузерами не были полностью согласованы, некоторый существующий код неизбежно оставался непереносимым.

"Here are things we were forced to specify because web browsers implemented this behavior and then pages start relying on it, but we aren't happy about it." - Annex B 3.3

В неаккуратный режим JavaScriptCore ведет себя не так, как обычно:

λ eshost -sx "if (typeof foo === 'undefined') { function foo() {} print('ok'); } else { print('hmm'); }"
#### ch, sm, v8, xs
ok

#### jsc
hmm

One solution is to use 'strict' mode:

λ eshost -sx "(function () { 'use strict'; if (typeof foo === 'undefined') { function foo() {} print('ok'); } else { print('hmm'); } })()"

#### ch, jsc, sm, v8, xs
ok

Кроме того, это, по-видимому, происходит только в Safari на верхнем уровне сценариев. В функциях, как и в

function g(){
  console.info(typeof f);
  {
    function f(){}
  }
}

g();

Safari соответствует спецификации. Это вполне может быть связано с тем, что поведение на верхнем уровне скриптов было указано только в ES2016, в 8582e81, в отличие от поведения в функциях, которое было указано в ES2015.

Источник: комментарии, опубликованные Росс Кирслинг и Кевин Гиббонс в выпуске GitHub №1632.

В 2016 году сообщалось об ошибке, связанной с этим поведением при подъеме, Webkit Issue #16309: [ES6]. Реализовать правила подъема функции Приложения B.3.3 для глобальной области видимости. Вот случай Тест262, который охватывает это.

To solve this, I have used Function Expressions:

То есть я заменил function anObject() на var anObject() = function(). Запустите этот код, чтобы понять поток сейчас:

if (typeof anObject == 'undefined') {

  if (typeof anObject == 'undefined') console.info('anObject not defined inside block')
  if (typeof someVariable == 'undefined') console.info('someVariable not defined as of now');

  var anObject = function(someParameter = 'someParameter') {
    var someProperty = 'someProperty';
  }

  console.info('anObject is now defined');
  var someVariable = 404;

  if (typeof someVariable == 'undefined') console.info('someVariable not defined as of now');

}

Что тут происходит?

Функции и переменные поднимаются на верхний уровень. Но такие движки, как V8 (Chrome), семантически определяют имя функции во время выполнения кода. Однако в неаккуратном режиме в браузерах Webkit, даже после стандартизации ECMA2015/16, имя функции определяется до выполнения. Обратите внимание, что на обоих движках функция фактически определена (поднята) раньше всего — это как раз о семантике относительно функции имя. В приведенном выше коде ссылка на анонимную функцию (поскольку у нее сейчас нет имени) назначается объекту во время выполнения, и это также будет нормально работать в Safari. Хорошее объяснение области действия блока и подъема на Какова точная семантика функций блочного уровня в ES6?.

Другие вопросы по теме