Что происходит в этом коде с объектами Number, сохраняющими свойства и увеличивающими число?
Недавний твит содержал этот фрагмент 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
с каким-либо свойством a [[PrimitiveValue]]
, но результаты five++
и five * 5
кажутся просто равными числами 5
и 25
(не Number
s)
- почему свойство
five.wtf
исчезает после приращения five++
- почему свойство
five.wtf
больше не устанавливается после приращения five++
, несмотря на то, что назначение five.wtf = 'potato?'
, по-видимому, устанавливает значение.
Ответы
Ответ 1
OP здесь. Забавно видеть это при переполнении стека:)
Прежде чем перейти к поведению, важно прояснить несколько вещей:
-
Числовое значение и Числовой объект (a = 3
vs a = new Number(3)
) очень другой. Один из них примитивен, другой - объект. Вы не можете назначать атрибуты примитивам, но можете создавать объекты.
-
Принуждение между ними неявно.
Например:
(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
-
Каждое выражение имеет возвращаемое значение. Когда REPL читает и выполняет выражение, это то, что он отображает. Возвращаемые значения часто не означают, что вы думаете, и подразумеваете вещи, которые просто не соответствуют действительности.
Хорошо, мы идем.
![Original image of the JavaScript code]()
Залог.
> function dis() { return this }
undefined
> five = dis.call(5)
[Number: 5]
Определите функцию dis
и call с помощью 5
. Это будет выполнять функцию с 5
как контекст (this
). Здесь он принуждается от значения Number к объекту Number. Очень важно отметить, что мы были в строгом режиме этого не произошло бы.
> 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
, но он был возвращен обратно в значение Number, и все атрибуты были потеряны. Поскольку примитивы не могут содержать атрибуты, five.wtf
- undefined.
> five.wtf = 'potato?'
'potato?'
> five.wtf
undefined
Я снова пытаюсь переназначить атрибут wtf
на five
. Возвращаемое значение подразумевает, что оно хранится, но на самом деле это не потому, что five
- это значение Number, а не объект Number. Выражение оценивается как 'potato?'
, но когда мы проверяем, мы видим, что оно не было назначено.
Престиж.
> five
6
С момента приращения постфикса five
был 6
.
Ответ 2
Существует два разных способа представления числа:
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)
, см. Как работает ключевое слово "his" ?. Скажем, что первый аргумент 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"
Объект A Number
принимает новые свойства.
> five++
++
приводит к новому примитивному значению 6
...
> five.wtf
undefined
> five.wtf = 'potato?'
"potato?"
> five.wtf
undefined
... который не имеет и не принимает пользовательские атрибуты.
* Обратите внимание, что в строгом режиме аргумент this
обрабатывается по-разному и не будет преобразован в Number
. См. http://es5.github.io/#x10.4.3 для деталей реализации.
Ответ 3
Там принуждение в мире JavaScript - история детектива
Нафан, ты понятия не имеешь, что ты раскрыл.
Я изучаю это уже несколько недель. Все началось в бурный вечер в октябре прошлого года. Я случайно наткнулся на класс Number
- я имею в виду, почему в мире JavaScript имел класс Number
?
Я не был готов к тому, что я собираюсь узнать дальше.
Оказывается, JavaScript, не сообщая вам, меняет ваши номера на объекты и ваши объекты на цифры прямо под вашим носом.
JavaScript надеялся, что никто не поймает, но люди сообщают о странном неожиданном поведении, и теперь благодаря вам и вашему вопросу у меня есть доказательства того, что мне нужно широко развернуть эту вещь.
Это то, что мы выяснили до сих пор. Я не знаю, должен ли я даже сказать вам это: вы можете отключить свой JavaScript.
> function dis() { return this }
undefined
Когда вы создали эту функцию, вы, вероятно, понятия не имели, что будет дальше. Все выглядело прекрасно, и все было хорошо - пока.
Нет сообщений об ошибках, просто слово "undefined" в выводе на консоль, что вы ожидаете. В конце концов, это было объявление функции - оно ничего не должно возвращать.
Но это было только начало. Что случилось дальше, никто не мог предсказать.
> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}
Да, я знаю, вы ожидали 5
, но это не то, что вы получили, было ли это - у вас есть что-то еще - что-то другое.
То же самое случилось со мной.
Я не знал, что с этим делать. Это подтолкнуло меня. Я не мог спать, я не мог есть, я пытался выпить его, но никакая горная роса не заставила бы меня забыть. Это просто не имело никакого смысла!
Что, когда я узнал, что действительно происходит, это было принуждение, и это происходило прямо перед моими глазами, но я был слишком слеп, чтобы увидеть это.
Mozilla попыталась похоронить его, поставив его там, где они знали, что никто не будет выглядеть - их документация.
После нескольких часов рекурсивного чтения и повторного чтения и повторного чтения я нашел это:
"... и примитивные значения будут преобразованы в объекты."
Это было прямо там, как может быть написано в шрифте 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
is 6
, это было доказательством того, что мне нужно было показать, что five
не был числом! Это был самозванец! Плохой самозванец, который не знал, как считать. И я могу это доказать. Здесь, как действует действительное число:
> num = 5
5
> num++
5
Подождите? Что здесь происходило? вздох, я так увлекся тем, что забыл, как работают почтовые операторы. Когда я использую ++
в конце 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()
. О Боже мой. Нееет!
Ответ 4
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
- Если код функции является строгим кодом, установите значение ThisBinding для этогоArg.
- Если значение thisArg равно null или undefined, установите этот привязку для глобального объекта.
- В противном случае, если Type (thisArg) не является объектом, установите значение ThisBinding для ToObject (thisArg).
02
- это возвращаемое значение объявления функции. undefined
должен быть понятным здесь.
03
переменная five
инициализируется возвращаемым значением dis
при вызове в контексте примитивного значения 5
. Поскольку dis
не находится в строгом режиме, эта строка идентична вызову five = Object(5)
.
04
Возвращаемое значение нечетного Number {[[PrimitiveValue]]: 5}
представляет собой представление объекта, который обертывает примитивное значение 5
05
Свойству five
object wtf
присваивается строковое значение 'potato'
06
- это возвращаемое значение присваивания и должно быть самоочевидным.
07
проверяется свойство five
object wtf
08
поскольку five.wtf
ранее был установлен в 'potato'
, он возвращает 'potato'
здесь
09
объект five
умножается на примитивное значение 5
. Это ничем не отличается от любого другого объекта, который размножается и объясняется в разделе 11.5 спецификации ES5. Особо следует отметить, как объекты передаются в числовые значения, которые рассматриваются в нескольких разделах.
9.3 ToNumber:
- Пусть primValue является ToPrimitive (входной аргумент, номер подсказки).
- Возврат ToNumber (primValue).
9.1 ToPrimitive:
Возвращает значение по умолчанию для объекта. Значение по умолчанию для объекта извлекается, вызывая внутренний метод [[DefaultValue]] объекта, передавая необязательный подсказку PreferredType. Поведение внутреннего метода [[DefaultValue]] определяется этой спецификацией для всех собственных объектов ECMAScript в 8.12.8.
8.12.8 [[DefaultValue]]:
Пусть valueOf является результатом вызова внутреннего метода [[Get]] объекта O с аргументом "valueOf".
Это все окольный способ сказать, что функция object valueOf
вызывается, и в уравнении используется возвращаемое значение этой функции. Если вы хотите изменить функцию valueOf
, вы можете изменить результаты операции:
> five.valueOf = function () { return 10 }
undefined
> five * 5
50
10
как функция five
valueOf
не изменилась, она возвращает завернутое примитивное значение 5
, так что five * 5
оценивается как 5 * 5
, что приводит к 25
11
свойство five
object wtf
оценивается снова, несмотря на то, что оно не изменилось с момента его назначения на 05
.
12
'potato'
13
Оператор Postfix Increment Operator вызывается на 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
Ответ 5
Это довольно просто.
function dis () { return this; }
Возвращает контекст this
. Итак, если вы делаете call(5)
, вы передаете число как объект.
Функция call
не предоставляет аргументы, первый аргумент, который вы даете, представляет собой контекст this
. Обычно, если вы хотите его использовать в контексте, вы даете ему {}
so dis.call({})
, что означает, что this
в функции пуст this
. Однако, если вы пройдете 5
, кажется, что он будет преобразован в объект. См. .call
Таким образом, возврат object
Когда вы выполняете five * 5
, JavaScript видит объект five
как примитивный тип, поэтому он эквивалентен 5 * 5
. Интересно, что do '5' * 5
, он все равно равен 25
, поэтому JavaScript явно подбрасывается под капот. В этой строке не выполняется никаких изменений в базовом типе five
Но когда вы сделаете ++
, он преобразует объект в примитивный тип number
, удалив при этом свойство .wtf
. Поскольку вы затрагиваете базовый тип
Ответ 6
У примитивных значений не может быть свойства. Но когда вы пытаетесь получить доступ к свойству на примитивном значении, он прозрачно транслирует на временный объект 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
Ответ 7
Объявить функцию 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
Ответ 8
Прежде всего, похоже, что это выполняется через консоль nodejs.
1.
function dis() { return this }
создает функцию dis(), но поскольку она не была установлена как var
, для возврата не было значения, поэтому undefined
был результатом, хотя был определен dis()
. В sidenote this
не был возвращен, потому что функция не была выполнена.
2.
five = dis.call(5)
Это возвращает объект javascript Number
, потому что вы просто устанавливаете значение функции dis()
this
для примитива 5.
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
является Number
, вы больше не можете устанавливать свойства, пока не выполните следующее:
five = dis.call(five)
five.wtf = "potato?"
или
five = { value: 6, wtf: "potato?" }
Также обратите внимание, что второй способ будет иметь другое поведение, чем использование первого метода, поскольку он определяет общий объект вместо объекта Number
, который был создан ранее.
Надеюсь, это поможет, javascript любит принимать вещи, поэтому он может запутаться при смене объекта Number
на примитив Number
.
Вы можете проверить, что это такое, используя ключевое слово typeof
, записывая тип пять
после инициализации он возвращает 'object'
, а после five++
он возвращает 'number'
.
@deceze очень хорошо описывает разницу между объектом Number и примитивным числом.
Ответ 9
Области JavaScript создаются из контекстов выполнения. Каждый контекст выполнения имеет лексическую среду (внешние/глобально ограниченные значения), переменную среду (локально привязанные значения) и эту привязку.
Эта привязка является очень важной частью контекста выполнения. Использование call
- это один из способов изменить эту привязку, и это автоматически создаст объект для заполнения привязки с помощью.
Функция .prototype.call() (из MDN)
Синтаксис
fun.call(thisArg[, arg1[, arg2[, ...]]])
thisArg
Ценность этого обеспечила для удовольствия. Обратите внимание, что это может быть не фактическое значение, которое видит метод: если метод является функцией в коде нестрого режима, то null и undefined будут заменены глобальным объектом, а примитивные значения будут преобразованы в объекты. (акцент мой)
Как только очевидно, что 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" />
Ответ 10
Несколько понятий объясняют, что происходит
5
- это число, примитивное значение
Number {[[PrimitiveValue]]: 5}
- это экземпляр Number (пусть он вызывает обертку объекта)
Всякий раз, когда вы получаете доступ к свойству/методу на примитивном значении, 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
больше не является объектом. Каждая из этих строк создает новый экземпляр Number, чтобы разрешить доступ к свойству .wtf
. Экземпляры независимы, поэтому установка свойства на одном не будет видна на другом. Код полностью эквивалентен этому:
(new Number(6)).wtf;
(new Number(6)).wtf = 'potato?';
(new Number(6)).wtf;