(1, eval) ('this') проти eval ('this') у JavaScript?


85

Я починаю читати шаблони JavaScript , деякі коди мене бентежать.

var global = (function () {
    return this || (1, eval)('this');
}());

Ось мої запитання:

Q1:

(1, eval) === eval?

Чому і як це працює?

Q2: Чому не просто

var global = (function () {
    return this || eval('this');
}());

або

 var global = (function () {
    return this;
}());

Я оновив назву, оскільки це конкретний випадок. Крім того, дужки для конкретного виду дужок : [] та {} повністю відрізняються :)

Відповіді:


104

Різниця між (1,eval)простим і старим evalполягає в тому, що перше - це цінність, а друге - значення. Це було б більш очевидно, якби це був інший ідентифікатор:

var x;
x = 1;
(1, x) = 1; //  syntax error, of course!

Це (1,eval)вираз, який дає eval(так само, як сказати, (true && eval)чи (0 ? 0 : eval)міг би), але це не посилання на eval.

Чому вам все одно?

Ну, специфікація Ecma вважає посилання на eval"прямий виклик eval", але вираз, який просто поступається, evalє непрямим - і непрямі виклики eval гарантовано виконуються в глобальному масштабі.

Речі, яких я досі не знаю:

  1. За яких обставин прямий виклик eval не виконується в глобальному масштабі?
  2. За яких обставин thisфункція в глобальному масштабі не може дати глобальний об'єкт?

Дещо більше інформації можна отримати тут .

РЕДАГУВАТИ

Мабуть, відповідь на моє перше запитання - "майже завжди". Пряме evalвиконання з поточної області дії. Розглянемо такий код:

var x = 'outer';
(function() {
  var x = 'inner';
  eval('console.log("direct call: " + x)'); 
  (1,eval)('console.log("indirect call: " + x)'); 
})();

Не дивно (хе-хе), це друкує:

direct call: inner
indirect call: outer

РЕДАГУВАТИ

Після подальших експериментів я тимчасово скажу, що thisне можна встановлювати значення nullабо undefined. Його можна встановити на інші хибні значення (0, ``, NaN, false), але лише дуже навмисно.

Я хочу сказати, що ваше джерело страждає на легку та оборотну інверсію краніо-прямої кишки, і, можливо, ви захочете провести тиждень програмування в Хаскелі.


3
Нічого собі, не знав цілого valueпроти цього lvalue(ну, на практиці, можливо, але не на словах). Як і правила оцінки ES5 (не те, що мені обґрунтовано потрібно буде використовувати evalколи-небудь). Дякую!
Stoive

Так, у evalнього багато неприємних гострих країв, і його слід використовувати лише в крайньому випадку, а потім, дуже, дуже обережно.
Malvolio

Я лише раз зіткнувся з допустимим використанням - оцінюючи тег скрипта, який був доданий до DOM черезinnerHtml
Stoive

1
lvalue мало що зумовлено визначенням прямого eval, оскільки він зазвичай посилається на вираз, який може відображатися в лівій частині завдання, звідси і назва lvalue на відміну від rvalue. Виклик eval є прямим лише за умов, перелічених у 15.1.2.1.1 специфікації, в якій зазначено, що ідентифікатор повинен бути evalі бути частиною вираження CallExpression і посилатися на стандартну evalфункцію.
chuckj

1
@Malvolio Ви, мабуть, натякаєте, що lvalues ​​мають щось спільне з прямим чи непрямим оцінюванням, чого вони не мають. Використання ідентифікатора, що викликається evalяк ціль виразу виклику, є особливим. Ви заявляєте, що ECMA розглядає посилання на evalспеціальні, які ні. Саме розміщення у виразі виклику є особливим, що вираз оцінює за стандартною evalфункцією. Наприклад, var eval = window.eval; eval('1');все ще є прямим eval і window.eval('1')навіть не є, хоча eval є значенням у цьому випадку.
chuckj

33

Фрагмент,

var global = (function () {  
    return this || (1, eval)('this');  
}());  

буде правильно оцінювати глобальний об'єкт навіть у суворому режимі. У нестрогому режимі значення thisє глобальним об'єктом, але в суворому режимі воно є undefined. Вираз (1, eval)('this')завжди буде глобальним об'єктом. Причиною цього є правила навколо прямих непрямих віршів eval. Прямі виклики до evalмають обсяг виклику, і рядок thisоцінюватиметься як значення thisв закритті. Непрямі evalоцінюються в глобальній області, ніби вони виконуються всередині функції в глобальній області. Оскільки ця функція сама по собі не є функцією суворого режиму, глобальний об'єкт передається як, thisа потім вираз 'this'обчислюється глобальним об'єктом. Вираз (1, eval)- це просто вигадливий спосіб змуситиeval бути непрямим і повертати глобальний об'єкт.

А1: (1, eval)('this')це не те саме, що eval('this')через особливі правила, що стосуються непрямих віршів прямих дзвінків до eval.

A2: Оригінал працює в суворому режимі, змінені версії ні.


12

До І кварталу:

Я думаю, що це хороший приклад оператора коми в JS. Мені подобається пояснення щодо оператора коми у цій статті: http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/

Оператор кома обчислює обидва її операнди (зліва направо) і повертає значення другого операнда.

До Q2:

(1, eval)('this')розглядається як непрямий виклик eval, який у ES5 виконує код глобально. Тож результатом буде глобальний контекст.

Див. Http://perfectionkills.com/global-eval-what-are-the-options/#evaling_in_global_scope


7

Q1: Кілька послідовних операторів javascript, розділених комою, приймають значення останнього оператора. Тому:

(1, eval)приймає значення останнього, яке є посиланням на eval()функцію. Очевидно, він робить це таким чином, щоб перетворити eval()виклик на непрямий виклик, який буде оцінено в глобальній області в ES5. Деталі пояснюються тут .

Q2: Має бути якесь середовище, яке не визначає глобального this, але визначає eval('this'). Це єдина причина, про яку я можу придумати це.


Можливо, хтось намагається ухилитися від гачка реєстрації, який забороняє /eval\(/g?
Stoive

@Stoive - так, я теж дивувався про щось подібне. Якщо це не гачок реєстрації, якийсь фільтр десь у процесі (можливо, мінімізація).
jfriend00 02

7
Це пов’язано зі строгим режимом ES5. AFAIK у суворому режимі ES5 будь-який evalкод виконується у власному контексті, а не в глобальному контексті чи контексті, що його закриває. Одним із шляхів цього є непряме посилання на нього, як це робить відповідний код.
Крістіан Санчес, 02


Оновив свою відповідь, включивши інформацію від CDSanchez та @Saxoier. Дякую.
jfriend00 02
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.