Чому цей рядок дійсний у JavaScript?
var a = 0[0];
Після цього aє undefined.
"0"з new Number(0)об'єкта.
0[0]повернути значення
0["toString"]Це приголомшливо, дякую, що вказали на це.
Чому цей рядок дійсний у JavaScript?
var a = 0[0];
Після цього aє undefined.
"0"з new Number(0)об'єкта.
0[0]повернути значення
0["toString"]Це приголомшливо, дякую, що вказали на це.
Відповіді:
Коли ви робите 0[0], інтерпретатор JS стане першим 0в Numberоб'єкт , а потім спробувати отримати доступ до [0]властивості цього об'єкта , який undefined.
Немає синтаксичної помилки, оскільки синтаксис доступу до властивостей 0[0]дозволений граматикою мови в цьому контексті. Ця структура (використовуючи терміни в граматиці Javascript) є NumericLiteral[NumericLiteral].
Відповідна частина мовної граматики з розділу A.3 специфікації ES5 ECMAScript така:
Literal ::
NullLiteral
BooleanLiteral
NumericLiteral
StringLiteral
RegularExpressionLiteral
PrimaryExpression :
this
Identifier
Literal
ArrayLiteral
ObjectLiteral
( Expression )
MemberExpression :
PrimaryExpression
FunctionExpression
MemberExpression [ Expression ]
MemberExpression . IdentifierName
new MemberExpression Arguments
Отже, за цим прогресуванням можна слідувати за грамером:
MemberExpression [ Expression ]
PrimaryExpression [ Expression ]
Literal [ Expression ]
NumericLiteral [ Expression ]
І, Expressionзрештою, це може бути NumericLiteralі після граматики, ми бачимо, що це дозволено:
NumericLiteral [ NumericLiteral ]
Що означає, що 0[0]це дозволена частина граматики і, отже, не SyntaxError.
Тоді під час виконання дозволено читати властивість, яка не існує (вона буде просто прочитана як undefined), доки джерело, з якого ви читаєте, є об'єктом або має неявне перетворення на об'єкт. І числовий буквал дійсно має неявне перетворення в об'єкт (об'єкт Число).
Це одна з тих часто невідомих особливостей Javascript. Типи Number, Booleanі Stringв JavaScript , як правило , зберігається у вигляді примітивів (не розпустить об'єкти). Це компактне, незмінне представлення пам’яті (можливо, це зроблено таким чином для ефективності впровадження). Але Javascript хоче, щоб ви могли ставитись до цих примітивів, як до об'єктів із властивостями та методами. Отже, якщо ви спробуєте отримати доступ до властивості чи методу, який не підтримується безпосередньо на примітиві, Javascript тимчасово примусить примітив у відповідний тип об'єкта зі значенням, встановленим значенням примітиву.
Коли ви використовуєте об'єктоподібний синтаксис на примітиві, такому як 0[0], інтерпретатор розпізнає це як доступ до властивості примітиву. Його відповідь на це полягає в тому, щоб прийняти перший 0числовий примітив і примусити його до повномасштабного Numberоб'єкта, до якого він може отримати доступ до [0]властивості. У цьому конкретному випадку [0]властивість об’єкта «Число» - undefinedсаме тому це значення, яке ви отримуєте 0[0].
Ось стаття про автоматичне перетворення примітиву в об'єкт для роботи з властивостями:
Таємне життя примітивів Javascript
Ось відповідні частини специфікації ECMAScript 5.1:
Викидає TypeError, якщо значення є undefinedабо в nullіншому випадку повертається true.

- Нехай baseReference є результатом оцінки MemberExpression.
- Нехай baseValue буде GetValue (baseReference).
- Нехай властивістьNameReference є результатом оцінки виразів.
- Нехай propertyNameValue буде GetValue (propertyNameReference).
- Виклик CheckObjectCoercible (baseValue).
- Нехай propertyNameString буде ToString (propertyNameValue).
- Якщо синтаксичне виробництво, яке оцінюється, міститься в строгому режимі коду, нехай строгий буде істинним, інакше нехай строгий буде хибним.
- Поверніть значення типу Reference, базовим значенням якого є baseValue і чиє посилається ім'я властивостіNameString, а прапор суворого режиму суворий.
Оперативна частина цього питання - крок №5 вище.
Це описує, як, коли отримане доступ до значення є посиланням на властивість, воно вимагає ToObject(base)отримати об'єктну версію будь-якого примітиву.
Тут описується Boolean, Numberі Stringпримітиви перетворюються в формі об'єкта з [[PrimitiveValue]] внутрішнє властивість безлічі відповідно.
Як цікавий тест, якщо код був таким:
var x = null;
var a = x[0];
Він би все одно не кидав SyntaxError за час розбору, оскільки це технічно легальний синтаксис, але він би кидав TypeError під час виконання під час запуску коду, оскільки коли вищевказана логіка властивостей властивостей застосовується до значення x, воно буде викликати CheckObjectCoercible(x)або викликати ToObject(x)який обидва кинуть TypeError, якщо xє nullабо undefined.
0[1,2]також діє, що це означає? (Я поновлюю питання)
nullабо undefinedє абсолютно нормальним, навіть якщо ці властивості не існують.
0[2]
1,2але повертає 2.
Як і більшість мов програмування, JS використовує граматику для розбору коду та перетворення його у виконувану форму. Якщо в граматиці немає правила, яке може бути застосоване до певного фрагменту коду, воно видає SyntaxError. В іншому випадку код вважається дійсним, незалежно від того, має він сенс чи ні.
Відповідні частини граматики JS є
Literal ::
NumericLiteral
...
PrimaryExpression :
Literal
...
MemberExpression :
PrimaryExpression
MemberExpression [ Expression ]
...
Оскільки 0[0]відповідає цим правилам, він вважається дійсним виразом. Чи правильно це (наприклад, не кидає помилку під час виконання) - інша історія, але так, це так. Ось як JS оцінює такі вирази, як someLiteral[someExpression]:
someExpression(що може бути довільно складним)Number, рядки => Stringтощо)get propertyоперацію з результатом (2) з результатом імені властивості (1)Так 0[0]трактується як
index = 0
temp = Number(0)
result = getproperty(temp, index) // it's undefined, but JS doesn't care
delete temp
return result
Ось приклад правильного , але неправильного виразу:
null[0]
Це добре проаналізовано, але під час виконання інтерпретатор не працює на кроці 2 (тому що nullйого неможливо перетворити на об'єкт) і видає помилку часу виконання.
var x = null; var a = x[0];не генерує синтаксичну помилку, але видає TypeError під час виконання.
0[0]повернути значення замість невизначеного
Є ситуації, коли ви можете дійсно підписати номер у Javascript:
-> 0['toString']
function toString() { [native code] }
Хоча не відразу зрозуміло, чому ви хочете це зробити, підписка на Javascript еквівалентна використанню пунктирних позначень (хоча позначення крапок обмежує використання ідентифікаторів як ключів).
(0).toString(без виклику функції). Це властивість типу номера.
0доступ до його властивості, а оскільки він не існує, undefinedє більш правильним, як пояснено у jfriend00.
0[0]повернеться невизначеним. Цілком імовірно, що це буде, але це не повинно бути так
Я просто хотів би зазначити, що це дійсний синтаксис є жодним чином не є унікальним для Javascript. Більшість мов матимуть помилку виконання або помилку типу, але це не те саме, що синтаксична помилка. Javascript вирішує повернутись невизначеним у багатьох ситуаціях, коли інша мова може створити виняток, у тому числі під час підписки об'єкта, який не має властивості вказаного імені.
Синтаксис не знає тип виразу (навіть простий вираз на зразок числового літералу), і дозволить застосувати будь-який оператор до будь-якого виразу. Наприклад, спроба підписати undefinedабо nullвикликати TypeErrorв JavaScript. Це не синтаксична помилка - якщо вона ніколи не виконується (перебуваючи на неправильній стороні if-заяви), це не спричинить жодних проблем, тоді як помилка синтаксису за визначенням завжди виявляється під час компіляції (eval, Function тощо) , усі вважаються компілюючими).
Тому що це дійсний синтаксис і навіть дійсний код для інтерпретації. Ви можете спробувати отримати доступ до будь-якого властивості будь-якого об’єкта (і в цьому випадку 0 буде передано на об'єкт Число), і він дасть вам значення, якщо воно існує, інакше не визначене. Спроба отримати доступ до властивості невизначеного не працює, тому 0 [0] [0] призведе до помилки виконання. Це все ще буде класифіковано як дійсний синтаксис. Існує відмінність того, що є дійсним синтаксисом, а що не спричинить помилки виконання / компіляції.
Не тільки синтаксис є дійсним, результат не повинен бути, undefinedхоча в більшості, якщо не у всіх розумних випадках це буде. JS - одна з найбільш чистих об'єктно-орієнтованих мов. Більшість так званих мов ОО є орієнтованими на клас, в тому сенсі, що ви не можете змінити форму (вона прив'язана до класу) об'єкта, щойно створений, лише стан об'єкта. У JS ви можете змінити стан, а також форму об'єкта, і це ви робите частіше, ніж ви думаєте. Ця здатність створює досить незрозумілий код, якщо ви неправильно використовуєте його. Цифри незмінні, тому ви не можете змінити сам об'єкт, не його стан, ані форму, щоб ви могли це зробити
0[0] = 1;
що є дійсним виразом призначення, який повертає 1, але насправді нічого не присвоює, цифра 0незмінна. Що саме по собі дещо дивно. Ви можете мати правильний і правильний (виконуваний) вираз припущення, який нічого не призначає (*). Однак тип цифри є об'єктом, що змінюється, тому ви можете мутувати тип, і зміни будуть згортатися вниз по ланцюгу прототипу.
Number[0] = 1;
//print 1 to the console
console.log(0[0]);
//will also print 1 to the console because all integers have the same type
console.log(1[0]);
Звичайно, це далеко не категорія розумного використання, але мова вказана, щоб це допустити, оскільки в інших сценаріях розширення можливостей об'єктів має багато сенсу. Це, як додатки jQuery зачіпляються в об’єкт jQuery, щоб навести приклад.
(*) Він фактично присвоює значення 1 властивості об'єкта, проте немає жодного способу посилатися на цей (старий) об'єкт, і таким чином він буде зібраний на проході nexx GC
У JavaScript все є об'єктом, тому, коли інтерпретатор розбирає його, він трактує 0 як об'єкт і намагається повернути 0 як властивість. Те ж саме відбувається, коли ви намагаєтеся отримати доступ до 0-го елемента true або "" (порожній рядок).
Навіть якщо ви встановите 0 [0] = 1, він встановить властивість та його значення в пам'яті, але, поки ви отримуєте доступ до 0, він трактує як число (не плутайте тут трактування як об'єкт та число.)
true[0]або""[0]