Що відбувається в цьому коді з об'єктами Number, що містять властивості та збільшують число?


246

Нещодавній твіт містив цей фрагмент JavaScript.

Чи може хтось пояснити, будь ласка, крок за кроком, що відбувається в ньому?

> function dis() { return this }
undefined
> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}
> five.wtf = 'potato'
"potato"
> five.wtf
"potato"
> five * 5
25
> five.wtf
"potato"
> five++
5
> five.wtf
undefined
> five.wtf = 'potato?'
"potato?"
> five.wtf
undefined
> five
6

Зокрема, мені незрозуміло:

  • чому результат dis.call(5)- це Numberз якоюсь [[PrimitiveValue]]властивістю, але результати five++і, five * 5здається, є просто простими числами 5та 25(а не Number)
  • чому five.wtfвластивість зникає після five++приросту
  • чому five.wtfвластивість більше не встановлюється навіть після five++збільшення, незважаючи на те, що five.wtf = 'potato?'призначення, очевидно, встановлює значення.

6
Ха-ха, я побачив, що твіт !! Це настільки химерно, я думаю, тому що ви помножуєте його, це не дуже впливає на об'єкт, але, ++здається, впливає на базовий тип
Callum Linington

8
Яку лінію ви не зрозуміли? Preпроти Postприросту? Після множення, це знову Numberоб'єкт, який не має wtfвластивості ... Але все-таки це, objectотже, він може мати properties..
Район

25
Коли ви зателефонуєте за disдопомогою dis.call(5), це 5перетворює примітивний номер у об'єкт типу, Numberякий містить 5, так що цей об'єкт може бути повернутий як thisоб'єкт. У ++перетворює його назад в примітивному число , яке не може містити властивості, так wtfпочинає бути невизначеним.
GSerg


5
@Rayon ... і це змінилося з ES6 . Тож якщо рухатись уперед, вся ця справа перестане відбуватися.
GSerg

Відповіді:


278

ОП тут. Смішно бачити це на стеку Overflow :)

Перш ніж перейти до поведінки, важливо уточнити кілька речей:

  1. Значення числа та об'єкта Числа ( a = 3vs a = new Number(3)) дуже відрізняються. Один - примітив, інший - об’єкт. Ви не можете призначити атрибути примітивам, але ви можете об'єктам.

  2. Примус між ними є неявним.

    Наприклад:

    (new Number(3) === 3)  // returns false
    (new Number(3) == 3)   // returns true, as the '==' operator coerces
    (+new Number(3) === 3) // returns true, as the '+' operator coerces
  3. Кожен вираз має повернене значення. Коли REPL читає та виконує вираз, це відображається. Повернені значення часто не означають те, що ви думаєте, і мають на увазі речі, які просто не відповідають дійсності.

Гаразд, ось ми йдемо.

Оригінальне зображення коду JavaScript

Застава.

> function dis() { return this }
undefined
> five = dis.call(5)
[Number: 5]

Визначте функцію disта зателефонуйте їй за допомогою 5. Це виконає функцію з 5контекстом ( this). Тут воно примусове від значення Числа до об'єкта Число. Дуже важливо зазначити, що якби ми були в суворому режимі, цього не сталося б .

> five.wtf = 'potato'
'potato'
> five.wtf
'potato'

Тепер ми встановлюємо атрибут five.wtfдо 'potato', і з п'яти в якості об'єкта, досить , що він приймає простого присвоювання .

> five * 5
25
> five.wtf
'potato'

З fiveоб'єктом я переконуюсь, що він може виконувати прості арифметичні операції. Це може. Чи досі її атрибути дотримуються? Так.

Черга.

> five++
5
> five.wtf
undefined

Тепер перевіряємо five++. Трюк із збільшенням постфікса полягає в тому, що весь вираз буде оцінено по відношенню до вихідного значення, а потім збільшить значення. Схоже, fiveце ще п’ять, але насправді вираз оцінюється на п'ять, а потім встановлюється fiveна 6.

Не тільки fiveвстановлено 6, але воно було примусово повернене у число, і всі атрибути втрачаються. Оскільки примітиви не можуть містити атрибути, five.wtfне визначено.

> five.wtf = 'potato?'
'potato?'
> five.wtf
undefined

Я знову спробувати передати атрибут wtfдо five. Зворотна величина означає, що вона дотримується, але насправді це не тому five, що це число, а не об'єкт числа. Вираз оцінюється на 'potato?', але коли ми перевіряємо, ми бачимо, що він не був призначений.

Престиж.

> five
6

З моменту збільшення поштового коду fiveбуло 6.


70
Ви пильно стежите?
Вади

3
@Nathan Long Ну, на Java, котрий JavaScript з самого початку сильно запозичив, усі примітиви мають еквівалент класу. intі Integer, наприклад. Я припускаю, що це так, що ви можете створити функцію doSomething(Object)та все ж мати можливість надати їй примітиви. Примітиви в цьому випадку будуть перетворені у відповідні класи. Але JS насправді не цікавить типів, тому причина, ймовірно, в чомусь іншому
Suppen

4
@Eric Перше, ++що потрібно - застосувати ToNumber до значення . Порівняйте подібний випадок із рядками: якщо у вас є x="5", то x++повертає число 5.
апспіллери

2
Дійсно, магія все відбувається dis.call(5), примус до заперечення я ніколи цього не очікував.
mcfedr

3
@ gman, за винятком того, що існує багато набагато детальніших впливів, включаючи найменування великих фрагментів його стандартної бібліотеки, поведінку стандартних типів об'єктів і навіть той факт, що він використовує синтаксис дужок і крапки з комою (навіть якщо крапки з комою є необов'язково) походить від того, що він був розроблений для ознайомлення з програмістами Java. Так, для початківців це заплутано. Це не означає, що впливів не існує.
Жуль

77

Існує два різні способи представлення числа:

var a = 5;
var b = new Number(5);

Перший - примітив, другий - предмет. За всіма намірами та цілями обидва поводяться однаково, за винятком того, що вони виглядають по-різному, коли друкуються на консолі. Важлива відмінність полягає в тому, що, як об'єкт, new Number(5)приймає нові властивості так само, як і будь-яка звичайна {}, в той час як примітивні 5не:

a.foo = 'bar';  // doesn't stick
b.foo = 'bar';  // sticks

Щодо початкової dis.call(5)частини, будь ласка, див. Як працює ключове слово "це"? . Скажемо лише, що перший аргумент, який callвикористовується як значення this, і що ця операція змушує число перейти в більш складну Numberоб’єктну форму. * Пізніше ++змушує повернути його в первісну форму, оскільки операція додавання +призводить до появи нового примітиву.

> five = dis.call(5)  // for all intents and purposes same as new Number(5)
Number {[[PrimitiveValue]]: 5}
> five.wtf = 'potato'
"potato"
> five.wtf
"potato"

NumberОб'єкт приймає нові властивості.

> five++

++призводить до нового примітивного 6значення ...

> five.wtf
undefined
> five.wtf = 'potato?'
"potato?"
> five.wtf
undefined

... який не має та не приймає власні атрибути.

* Зверніть увагу , що в строгому режиміthis аргумент буде розглядатися по- різному , і було б НЕ бути перетворений в Number. Детальні відомості про реалізацію див. На веб-сайті http://es5.github.io/#x10.4.3 .


2
@Знайдіть, будь ласка, краще в C / C ++, де ви можете це зробити #define true false. Або на Java, де ви можете просто переглянути, що означають цифри. Це хороші тверді мови. По правді кажучи, кожна мова має подібні "хитрощі", коли ви отримуєте результат, який працює за призначенням, але може виглядати дивним.
ВЛАЗ

1
@Vld Добре дозвольте перефразувати це, тому я ненавиджу набирати качок.
Фарап

59

У світі JavaScript є примус - історія детектива

Натане, ти поняття не маєш, що ти розкрив.

Я розслідую це вже тижнями. Все почалося у бурхливу ніч у жовтні минулого року. Я випадково натрапив на Numberклас - я маю на увазі, чому в світі у JavaScript був Numberклас?

Я не був готовий до того, що збирався дізнатися далі.

Виявляється, JavaScript, не розповідаючи, міняв ваші номери на об'єкти, а ваші об'єкти на номери прямо під носом.

JavaScript сподівався, що ніхто не піде назустріч, але люди повідомляють про дивну несподівану поведінку, і тепер завдяки вам і вашому запитанню я маю докази, що мені потрібно роздути цю річ.

Це ми дізналися поки що. Я не знаю, чи варто мені це навіть розповідати - ви можете вимкнути JavaScript.

> function dis() { return this }
undefined

Коли ви створили цю функцію, ви, мабуть, не мали уявлення, що буде далі. Все виглядало нормально, і все було добре - поки що.

Немає повідомлень про помилки, лише слово "невизначено" у виведенні консолі, саме те, що ви очікували. Зрештою, це була декларація функції - вона нічого не повинна повертати.

Але це було лише початком. Що сталося далі, ніхто не міг передбачити.

> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}

Так, я знаю, ви очікували 5, але це не те, що ви отримали, це було - у вас щось інше - щось інше.

Те саме трапилось і зі мною.

Я не знав, що з цього зробити. Це ганяло мене. Я не міг спати, не міг їсти, я намагався його випити, але жодна кількість гірської роси не змусила мене забути. Це просто не мало сенсу!

Ось тоді я дізнався, що насправді відбувається - це був примус, і це відбувалося прямо там перед моїми очима, але я був занадто сліпий, щоб бачити це.

Мозілла спробувала поховати його, поклавши там, де вони знали, що ніхто не шукатиме - їх документацію .

Після годин рекурсивного читання та перечитування та перечитування я виявив таке:

"... і примітивні значення будуть перетворені на об'єкти."

Це було прямо там, як це просто написано шрифтом Open Sans. Це була call()функція - як я можу бути таким дурним ?!

Мій номер уже не був номером. Щойно я передавав це call(), це стало чимось іншим. Це стало ... об’єктом.

Я не міг спочатку повірити. Як це могло бути правдою? Але я не міг ігнорувати докази, які збиралися навколо мене. Це прямо там, якщо ви просто подивіться:

> five.wtf = 'potato'
"potato"

> five.wtf
"potato"

wtfбув правий. Цифри не можуть мати власні властивості - ми всі це знаємо! Це перше, чого вони навчають тебе в академії.

Ми повинні були знати, коли ми побачили вихід консолі - це не та цифра, яку ми думали. Це був самозванець - предмет, що передається як наш солодкий невинний номер.

Це було ... new Number(5).

Звичайно! Це мало ідеальний сенс. call()він повинен був виконувати роботу, він повинен був викликати функцію, і для цього йому потрібно було заселити this, він знав, що не може цього зробити з числом - йому потрібен предмет і він був готовий зробити все, щоб його отримати, навіть якщо це означало примусити нашу кількість. Коли call()побачив номер 5, він побачив можливість.

Це був ідеальний план: зачекайте, поки ніхто не шукає, і поміняйте нам номер на предмет, який виглядає так само. Ми отримуємо число, функція викликається, і ніхто не буде мудрішим.

Це справді був ідеальний план, але, як і всі плани, навіть ідеальні, в ньому була дірка, і ми ось-ось впали прямо в неї.

Бачите, що call()не зрозумів, що він не єдиний у місті, який міг примусити число. Зрештою, це був JavaScript - примус був скрізь.

call() взяв свій номер, і я не збирався зупинятися, поки не зняв маску свого маленького самозванця і не виставив його всій спільноті Stack Overflow.

Але як? Мені потрібен був план. Звичайно, це схоже на число, але я знаю, що це не так, має бути спосіб довести це. Це воно! Це схоже на число, але чи може воно діяти як одне?

Я сказав, що fiveмені потрібно, щоб він став у 5 разів більшим - він не запитував, чому, і я не пояснив. Тоді я зробив те, що зробив би будь-який хороший програміст: я примножив. Звичайно, не було способу підробити вихід із цього.

> five * 5
25
> five.wtf
'potato'

Блін! Не тільки fiveрозмножувалося, просто чудово wtfбуло ще. Чорт цей хлопець та його картопля.

Що за чорт сталося? Чи помилявся я у всьому цьому? Це fiveдійсно число? Ні, я, мабуть, щось пропускаю, я це знаю, є щось, що я мушу забути, щось таке просте і базове, що я повністю його не помічаю.

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

Зачекайте, це все! fiveНе було 25, 25 - результат, 25 - зовсім інше число. Звичайно, як я міг забути? Числа незмінні. Коли ви множите, 5 * 5нічого не призначається нічому, ви просто створите нове число 25.

Це повинно бути, що відбувається тут. Якось, коли я множу five * 5, fiveтреба примушуватися до числа, і це число повинно бути тим, яке використовується для множення. Це результати того множення, яке друкується на консолі, а не саме значення five. fiveніколи нічого не присвоюють - тому, звичайно, це не змінюється.

Тож як мені змусити fiveпризначити собі результат операції. Зрозумів. Перш ніж fiveнавіть був шанс подумати, я закричав "++".

> five++
5

Ага! Я мав його! Кожен знає 5 + 1це 6, це було доказом мені потрібно було виставити , що fiveне було поруч! Це був самозванець! Поганий самозванець, який не вмів рахувати. І я міг це довести. Ось як діє реальна кількість:

> num = 5
5
> num++
5

Чекати? Що тут було? зітхання Я настільки захопився перебоями, fiveщо забув, як працюють поштові оператори. Коли я використовую ++в кінці слова, fiveя кажу повернути поточне значення, а потім приріст five. Це значення перед початком операції надрукується на консолі. numбуло насправді, 6і я міг це довести:

>num
6

Час подивитися, що fiveнасправді було:

>five
6

... це було саме те, що має бути. fiveбуло добре - але я був кращим. Якби fiveвсе-таки був об’єкт, це означало б, що він все-таки матиме власність, wtfі я був готовий зробити ставку на все, чого він не робив.

> five.wtf
undefined

Ага! Я був правий. Я мав його! fiveтепер було числом - це вже не об’єкт. Я знав, що фокус на множення не врятує цього разу. Бачити five++- це справді five = five + 1. На відміну від множення, ++оператор присвоює значення five. Більш конкретно, він присвоює їй результати, five + 1як і у випадку множення, повертає нове незмінне число .

Я знав, що маю його, і просто переконався, що він не зможе вирватися з нього. У мене був ще один тест на рукаві. Якби я мав рацію, і зараз fiveбув справді чисельним, то це не працювало б:

> five.wtf = 'potato?'
'potato?'

Цього разу він не збирався мене обманювати. Я знав, potato?що буде надрукований на консоль, тому що це результат завдання. Справжнє питання, чи wtfвсе ще буде там?

> five.wtf
undefined

Як я підозрював - нічого - тому що числам не можна присвоїти властивості. Ми дізналися, що перший рік в академії;)

Спасибі Натане. Завдяки вашій мужності в питанні цього питання я можу нарешті все це поставити за собою і перейти до нової справи.

Як і ця щодо функції toValue(). О боже мілий. Nooo!


9
Забудь про це Джейк; це Javascript.
Сет

Чому ми називаємо функції конструктора "класами" в JS?
evolutionxbox

27
01 > function dis() { return this }
02 undefined
03 > five = dis.call(5)
04 Number {[[PrimitiveValue]]: 5}
05 > five.wtf = 'potato'
06 "potato"
07 > five.wtf
08 "potato"
09 > five * 5
10 25
11 > five.wtf
12 "potato"
13 > five++
14 5
15 > five.wtf
16 undefined
17 > five.wtf = 'potato?'
18 "potato?"
19 > five.wtf
20 undefined
21 > five
22 6

01оголошує функцію, disяка повертає об'єкт контексту. Що thisвідображає зміни залежно від того, використовуєте ви суворий режим чи ні. Весь приклад має різні результати, якщо функція була оголошена як:

> function dis() { "use strict"; return this }

Це детально описано в розділі 10.4.3 специфікації ES5

  1. Якщо код функції є суворим кодом, встановіть ThisBinding на цейArg.
  2. В іншому випадку, якщо thisArg є нульовим або невизначеним, встановіть ThisBinding на глобальний об'єкт.
  3. В іншому випадку, якщо Type (thisArg) не є Object, встановіть ThisBinding на ToObject (thisArg).

02- це повернене значення декларації функції. undefinedтут слід роз'яснювати себе.

03змінна fiveініціалізується із поверненим значенням, disколи викликається в контексті примітивного значення 5. Оскільки disне знаходиться в суворому режимі, ця лінія ідентична виклику five = Object(5).

04Непарне Number {[[PrimitiveValue]]: 5}повернене значення - це представлення об'єкта, який обгортає примітивне значення5

05в fiveоб'єкта wtfнерухомості присвоюється строкове значення'potato'

06 - це повернене значення завдання і повинно бути зрозумілим.

07в fiveоб'єкті wtfнерухомість розглядаються в

08як five.wtfбуло встановлено раніше, 'potato'повертається 'potato'сюди

09fiveоб'єкт множиться на примітивному значення 5. Це не відрізняється від будь-якого іншого об'єкта, що множиться, і пояснюється в розділі 11.5 специфікації ES5 . Особливо слід зазначити, як об'єкти переносяться на числові значення, що висвітлюється в кількох розділах.

9.3 Кількість :

  1. Нехай primValue буде ToPrimitive (аргумент введення, номер підказки).
  2. Повернути ToNumber (primValue).

9.1 ДоПримітив :

Повернути для об'єкта значення за замовчуванням. Значення об'єкта за замовчуванням отримується за допомогою виклику внутрішнього методу [[DefaultValue]], передаючи необов'язковий підказку PreferredType. Поведінка внутрішнього методу [[DefaultValue]] визначається цією специфікацією для всіх власних об'єктів ECMAScript у 8.12.8 .

8.12.8 [[DefaultValue]] :

Нехай valueOf є результатом виклику внутрішнього методу [[Get]] об'єкта O з аргументом "valueOf".

  1. Якщо IsCallable (valueOf) відповідає дійсності,

    1. Нехай val є результатом виклику внутрішнього методу [[Виклик]] valueOf, причому O це значення і порожній список аргументів.
    2. Якщо val - примітивне значення, поверніть val.

Це весь круговий спосіб сказати, що valueOfфункція об'єкта викликається, а повернене значення з цієї функції використовується в рівнянні. Якщо ви змінили valueOfфункцію, ви можете змінити результати операції:

> five.valueOf = function () { return 10 }
undefined
> five * 5
50

10Оскільки функція fives valueOfбула незмінною, вона повертає завернуте примітивне значення 5так, що five * 5оцінює, до 5 * 5чого призводить25

11в fiveоб'єкті wtfнерухомість оцінюються знову , незважаючи на те , залишалися незмінними з моменту , коли він був призначений на 05.

12 'potato'

13Postfix оператор інкремента викликається five, яка отримує числове значення ( 5ми розглянули , як раніше), зберігає значення так , що воно може бути повернуто, додає 1до значення ( 6) присвоює значення fiveі повертає збережене значення ( 5)

14 як і раніше, повернене значення - це значення до його збільшення

15доступ до wtfвластивості примітивного значення ( 6), що зберігається у змінній, fiveмає доступ. Розділ 15.7.5 специфікації ES5 визначає таку поведінку. Числа отримують властивості Number.prototype.

16 Number.prototypeне має wtfвластивості, тому undefinedповертається

17 five.wtfприсвоюється значення 'potato?'. Призначення визначено в 11.13.1 специфікації ES5 . В основному призначене значення повертається, але не зберігається.

18 'potato?' було повернуто оператором призначення

19знову fiveмає доступ до цього значення, яке має значення 6, і знову Number.prototypeне має wtfвластивості

20 undefined як пояснено вище

21 five доступ до нього

22 6 повертається, як пояснено в 13


17

Це досить просто.

function dis () { return this; }

Це повертає thisконтекст. Отже, якщо ви це call(5)робите, ви передаєте число як об’єкт.

callФункція не надає аргументи, то перший аргумент ви даєте це контекст this. Зазвичай, якщо ви хочете, щоб це було в контексті, ви даєте це {}так dis.call({}), що означає, thisщо функція порожня this. Однак, якщо ви передасте, 5здається, він буде перетворений на об'єкт. Див. Дзвінок

Тож повернення є object

У цьому випадку five * 5JavaScript розглядає об'єкт fiveяк примітивний тип, тому він еквівалентний 5 * 5. Цікаво, що '5' * 5це все-таки дорівнює 25, тому JavaScript чітко піддається кришці. У fiveцьому рядку жодних змін базового типу не робиться

Але коли ви ++це зробите, ви перетворите об'єкт у примітивний numberтип, видаливши таким чином .wtfвластивість. Тому що ти впливаєш на базовий тип


Повернення - номер .
GSerg

++перетворює і присвоює його назад. ++дорівнює variable = variable + 1. Отже, ти розгубишсяwtf
Раджеш

*Проти ++не бентежить мене. Мати функцію, яка не очікує ніяких аргументів і повертає this, змушуючи цю функцію приймати аргумент у будь-якому випадку і повертати те, що є подібним, але не зовсім аргументом - для мене це не має сенсу.
Натан Лонг

Оновлено @NathanLong, щоб показати, що dis.call()робить.
Каллум Лінінгтон

5 ++ поводиться так само, як і в мові С, тому нічого дивного. thisпросто вказівник на об'єкт, тому примітивні типи перетворюються неявно. Чому функція без аргументів не могла мати хоча б контекст? 1-й аргумент callабо bindвикористовується для встановлення контексту. Крім того, функції - це закриття, а це означає, що вони мають доступ до більш ніж простоarguments
Rivenfall

10

Примітивні значення не можуть мати властивість. Але коли ви намагаєтеся отримати доступ до властивості за примітивним значенням, вона прозоро переходить на тимчасовий об'єкт Number.

Так:

> function dis() { return this }
undefined
// Like five.dis(), so dis return the temporaty Number object and 
// reference it in five
> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}

// Write the wtf attribut on the Number object referenced by five
> five.wtf = 'potato'
"potato"
// Read the wtf attribut on the Number object referenced by five
> five.wtf
"potato"

// Return 5*5 but dont change the reference of five
> five * 5
25
// Read the same wtf attribut on the Number object referenced by five
> five.wtf
"potato"

// Change the five reference to a new primitive value (5+1). Five
// reference a primitive now.
> five++
5

// Read the wtf attribut on a new temporary Number object construct from
// the primitive referenced by five. So wtf does not exist.
> five.wtf
undefined

// Write the wtf attribut on a new temporary Number object construct from
// the primitive referenced by five. But this object not referenced by
// five. It will be lost.
> five.wtf = 'potato?'
"potato?"

// Read the wtf attribut on a new temporary Number object construct from
// the primitive referenced by five. So wtf does not exist.
> five.wtf
undefined
> five
6

8

Оголосити функцію dis. Функція повертає свій контекст

function dis() { return this }
undefined

Дзвінок disз контекстом 5. Примітивні значення розміщуються у вікні, коли передаються як контекст у суворому режимі ( MDN ). Отож fiveтепер є об’єкт (номер в коробці).

five = dis.call(5)
Number {[[PrimitiveValue]]: 5}

Оголосити wtfвластивість fiveзмінною

five.wtf = 'potato'
"potato"

Значення five.wtf

five.wtf
"potato"

fiveє коробкою 5, тож це число і об'єкт одночасно (5 * 5 = 25). Це не змінюється five.

five * 5
25

Значення five.wtf

five.wtf
"potato"

Відключення fiveтут. fiveтепер просто примітивний number. Він друкує 5, а потім додає 1до five.

five++
5

fiveзараз примітивне число 6, у ньому немає властивостей.

five.wtf
undefined

примітиви не можуть мати властивості, ви не можете встановити це

five.wtf = 'potato?'
"potato?"

ви не можете прочитати це, оскільки воно не було встановлено

five.wtf
undefined

fiveце 6тому , що пост збільшення вище

five
6

7

По-перше, схоже, що це запускається через консоль nodejs.

1.

    function dis() { return this }

створює функцію dis (), але, оскільки вона не була встановлена ​​як a, varзначення повернення не undefinedбуло, як і вихід, навіть якщо dis()було визначено. На сторонній стороні thisне повернуто, оскільки функція не виконувалася.

2.

    five = dis.call(5)

Це повертає в JavaScript Numberоб'єкт , тому що ви просто встановіть функцію dis()«s thisзначення примітивних п'яти.

3.

   five.wtf = 'potato'

Перші повертається , "potato"тому що ви просто встановіть властивість wtfз fiveв 'potato'. Javascript повертає значення змінної ви встановили, що робить його легко для декількох змінних ланцюга і встановити їх на те ж значення , як це: a = b = c = 2.

4.

    five * 5

Це повертається, 25тому що ви просто помножили примітивне число 5на five. Значення fiveвизначалося значенням Numberоб'єкта.

5.

    five.wtf

Я пропустив цю лінію раніше, тому що я б її повторював тут. Він просто повертає значення властивості, wtfяке ви встановили вище.

6.

    five++

Як сказав @Callum, ++перетворить тип в numberте саме значення з об'єкта Number {[[PrimitiveValue]]: 5}}.

Тому що fiveце a number, ви більше не можете встановлювати властивості до нього, поки не зробите щось подібне:

    five = dis.call(five)
    five.wtf = "potato?"

або

    five = { value: 6, wtf: "potato?" }

Також зауважте, що другий спосіб матиме іншу поведінку, ніж використання першого методу, оскільки він визначає загальний об'єкт замість Numberоб'єкта, який був створений раніше.

Я сподіваюся, що це допомагає, JavaScript любить припускати речі, тож може заплутатися при переході від Numberоб'єкта до примітиву number. Ви можете перевірити, що таке щось, скориставшись typeofключовим словом, записавши typeof п'ять після ініціалізації, вона повернеться 'object', і після того, як ви five++це зробите, вона повернеться 'number'.

@deceze надзвичайно добре описує різницю між об'єктом Число та примітивним числом.


6

Області JavaScript складаються з контекстів виконання. Кожен контекст виконання має лексичне середовище (зовнішні / загальносвітові значення), змінну середу (значення, локально визначені), і цю обов'язковість .

Ця прив'язка є дуже важливою частиною контексту виконання. Використання call- це один із способів змінити цю прив'язку , і це автоматично створить об'єкт для заповнення прив'язки.

Function.prototype.call () (від MDN)

Синтаксис
fun.call(thisArg[, arg1[, arg2[, ...]]])

thisArg
Значення цього передбачено для дзвінка на розваги. Зауважте, що це може бути не фактичне значення, яке бачить метод: якщо метод є функцією в не строгому режимі коду, нульові та невизначені будуть замінені глобальним об'єктом, а примітивні значення перетворяться на об'єкти . (наголос мій)

Як тільки стане очевидним, що 5 перетворюється на new Number(5), решта має бути досить очевидною. Зауважте, що інші приклади також працюватимуть, поки вони є примітивними значеннями.

function primitiveToObject(prim){
  return dis.call(prim);
}
function dis(){ return this; }

//existing example
console.log(primitiveToObject(5));

//Infinity
console.log(primitiveToObject(1/0));

//bool
console.log(primitiveToObject(1>0));

//string
console.log(primitiveToObject("hello world"));
<img src="http://i.stack.imgur.com/MUyRV.png" />

введіть тут опис зображення


4

Пару понять пояснюють, що відбувається

5 - число, примітивне значення

Number {[[PrimitiveValue]]: 5} - це примірник числа (назвемо його обгорткою об'єкта)

Кожного разу, коли ви отримуєте доступ до властивості / методу на примітивному значенні, двигун JS створить обгортку об'єктів відповідного типу ( Numberдля 5, Stringдля 'str'і Booleanдля true) та вирішить виклик доступу / методу властивості на цій обгортці об'єкта. Це те, що відбувається, наприклад, коли ви робите true.toString().

Виконуючи операції над об'єктами, вони перетворюються на примітивні значення (використовуючи toStringабо valueOf), щоб вирішити ці операції - наприклад, під час виконання

var obj = { a : 1 };
var string = 'mystr' + obj;
var number = 3 + obj;

stringбуде проводити конкатенацію рядків mystrі obj.toString()і numberбуде додавати 3і obj.valueOf().

Тепер, щоб скласти все це разом

five = dis.call(5)

dis.call(5)поводиться так, як (5).dis()якщо б 5насправді був метод dis. Для вирішення виклику методу створюється обгортка об'єктів і на ньому вирішується виклик методу. У цей момент п’ять вказує на обгортку об'єкта навколо примітивного значення 5.

five.wtf = 'potato'

Встановлення властивості на об'єкт, тут нічого фантазійного.

five * 5

Це фактично five.valueOf() * 5отримання примітивного значення з обгортки об'єктів. fiveвсе ще вказує на початковий об’єкт.

five++

Це насправді five = five.valueOf() + 1. Перед цим рядком fiveутримується обгортка об'єкта навколо значення 5, тоді як після цієї точки fiveзберігається примітивне значення6 .

five.wtf
five.wtf = 'potato?'
five.wtf

fiveбільше не є об’єктом. Кожен із цих рядків створює новий екземпляр Числа, щоб вирішити .wtfдоступ до властивості. Екземпляри є незалежними, тому встановлення властивості на одному не буде видно на іншому. Код повністю еквівалентний цьому:

(new Number(6)).wtf;
(new Number(6)).wtf = 'potato?';
(new Number(6)).wtf;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.