Что происходит в этом коде с объектами 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)является a Numberс каким-то [[PrimitiveValue]]свойством, но результатом five++и, five * 5кажется, являются просто простые числа 5и 25(не Numbers)
  • почему 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

ОП здесь. Забавно видеть это на переполнении стека :)

Прежде чем перейти к поведению, важно уточнить несколько вещей:

  1. Числовое значение и номер объекта ( a = 3против 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)принуждении к объекту, я бы никогда этого не ожидал.
МакФедр
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)части, см. Как работает ключевое слово "this"? , Давайте просто скажем, что первый аргумент to 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
@Pharap, конечно, лучше в C / C ++, где вы можете сделать #define true false. Или в Java, где вы можете просто переопределить, что означают числа. Это хорошие твердые языки. По правде говоря, у каждого языка есть схожие «хитрости», когда вы получаете результат, который работает как задумано, но может выглядеть странно.
ВЛАЗ
1
@Vld Хорошо, позвольте мне перефразировать это, поэтому я ненавижу печатать утку.
Pharap
59

В мире JavaScript есть принуждение - детективная история

Натан, ты не представляешь, что ты раскрыл.

Я расследую это уже несколько недель. Все началось бурной ночью в октябре прошлого года. Я случайно наткнулся на Numberкласс - я имею в виду, почему в мире JavaScript был Numberкласс?

Я не был готов к тому, что я собирался узнать дальше.

Оказывается, JavaScript, не сказав вам, меняет ваши номера на объекты, а ваши объекты на числа прямо у вас под носом.

JavaScript надеялся, что никто не поймет, но люди сообщают о странном неожиданном поведении, и теперь, благодаря вам и вашему вопросу, у меня есть доказательства того, что мне нужно взорвать эту штуку.

Это то, что мы узнали до сих пор. Я не знаю, должен ли я даже сказать вам это - вы можете отключить JavaScript.

> function dis() { return this }
undefined

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

Никаких сообщений об ошибках, только слово «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() взял мой номер, и я не собирался останавливаться, пока не снял маску с его маленького самозванца и не открыл его всему сообществу переполнения стека.

Но как? Мне нужен был план. Конечно, это похоже на число, но я знаю, что это не так, должен быть способ доказать это. Это оно! Это похоже на число, но может ли оно действовать как единое целое?

Я сказал, что 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(). О Боже мой. Нееет!

Луис Перес
источник
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 на thisArg.
  2. Иначе, если thisArg имеет значение null или не определено, установите 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 ToNumber :

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

9.1 ToPrimitive :

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

8.12.8 [[DefaultValue]] :

Пусть valueOf будет результатом вызова внутреннего метода [[Get]] объекта O с аргументом "valueOf".

  1. Если IsCallable (valueOf) имеет значение true, тогда

    1. Пусть val будет результатом вызова внутреннего метода [[Call]] для valueOf, с O в качестве значения this и пустым списком аргументов.
    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свойству примитива value ( 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

zzzzBov
источник
17

Это довольно просто.

function dis () { return this; }

Это возвращает thisконтекст. Итак, если вы это call(5)сделаете, вы передаете номер как объект.

callФункция не предоставляет аргументы, то первый аргумент вы даете это контекст this. Обычно, если вы хотите, чтобы это было в контексте, вы даете это {}так dis.call({}), что означает, что thisв функции пусто this. Однако, если вы передадите, 5кажется, он будет преобразован в объект. См. Звонок

Так что возвращение object

Когда вы это делаете five * 5, JavaScript видит объект fiveкак примитивный тип, что эквивалентно 5 * 5. Интересно, что '5' * 5это все равно равняется 25, поэтому JavaScript явно находится под капотом. В fiveэтой строке не производится никаких изменений в базовый тип

Но когда вы это сделаете, ++он преобразует объект в примитивный numberтип, удаляя .wtfсвойство. Потому что вы влияете на базовый тип

Каллум Линнингтон
источник
Возвращение номер .
GSerg
++преобразует и присваивает его обратно. ++равно variable = variable + 1. Итак, вы проигралиwtf
Раджеш
*Против ++не смущает меня. Наличие функции, которая не ожидает никаких аргументов и возвращает ее this, в любом случае эта функция принимает аргумент и возвращает что-то похожее на аргумент, но не совсем - для меня это не имеет смысла.
Натан Лонг
Обновил его @NathanLong, чтобы показать, что dis.call()делает.
Каллум Линнингтон
5 ++ ведет себя так же, как в языке Си, так что ничего удивительного. thisэто просто указатель на объект, поэтому примитивные типы конвертируются неявно. Почему функция без аргументов не может иметь хотя бы контекст? Первый аргумент 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
Techniv
источник
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

five6теперь это примитивное число , в нем нет свойств.

five.wtf
undefined

примитивы не могут иметь свойства, вы не можете установить это

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

Вы не можете прочитать это, потому что это не было установлено

five.wtf
undefined

fiveэто 6потому , что пост приращения выше

five
6
Maxx
источник
7

Во-первых, похоже, что это выполняется через консоль nodejs.

1.

    function dis() { return this }

создает функцию dis (), но поскольку она не была задана как a, varне было никакого значения, которое нужно возвращать, поэтому undefinedвывод был, даже если dis()был определен. На sidenote, 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это number, вы не можете устанавливать для него свойства, пока не сделаете что-то вроде этого:

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

или

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

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

Надеюсь, это поможет, javascript любит предполагать что-то, поэтому он может запутаться при переходе от Numberобъекта к примитиву number. Вы можете проверить , что что - то типа с помощью typeofключевого слова, писать TypeOf пять после инициализации он возвращается 'object', и после того, как вы сделаете five++это возвращается 'number'.

@deceze очень хорошо описывает разницу между объектом Number и примитивным числом.

Патрик Барр
источник
6

Области применения JavaScript сделаны из контекстов выполнения. Каждый контекст выполнения имеет лексическую среду (внешние / глобальные значения), переменную среду (локальные значения) и привязку this .

Эта привязка является очень важной частью контекста выполнения. Использование call- это один из способов изменить эту привязку , и при этом автоматически будет создан объект для заполнения привязки.

Function.prototype.call () (из MDN)

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

thisArg
Значение this предусмотрено для вызова fun. Обратите внимание, что это может не быть фактическим значением, видимым методом: если метод является функцией в коде нестрого режима, 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" />

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

Трэвис Дж
источник
4

Пара концепций объясняет, что происходит

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;
Tibos
источник