O que está acontecendo neste código com objetos Number mantendo propriedades e incrementando o número?

246

Um tweet recente continha esse trecho de JavaScript.

Alguém pode explicar o que está acontecendo nele passo a passo?

> 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

Em particular, não está claro para mim:

  • por que o resultado de dis.call(5)é um Numbercom algum tipo de [[PrimitiveValue]]propriedade, mas os resultados de five++e five * 5parecem ser apenas os números simples 5e 25(não Numbers)
  • por que a five.wtfpropriedade desaparece após o five++incremento
  • por que a five.wtfpropriedade não é mais configurável após o five++incremento, apesar da five.wtf = 'potato?'atribuição aparentemente definir o valor.
Nathan Long
fonte
6
Haha eu vi esse tweet !! É tão bizarro, acho que porque você multiplicá-lo, ele não chega a afetar o objeto, mas ++parece afetar o tipo subjacente
Callum Linington
8
Qual linha você não entendeu? Prevs Postincremento? Depois de multiplicação, É novamente um Numberobjeto que não tem wtfpropriedade ... Mas ainda é um object, portanto, pode ter properties..
Rayon
25
Quando você ligar discom dis.call(5), isso envolve o número primitivo 5em um objeto do tipo Numberque contém o 5, de modo que este objeto pode ser retornado como thisobjeto. O ++converte novamente em um número primitivo que não pode conter propriedades e, portanto, wtfcomeça a ser indefinido.
28916 GSerg
5
@ Rayon ... e isso mudou com o ES6 . Então, daqui para frente, tudo isso vai parar de acontecer.
28916 GSerg

Respostas:

278

OP aqui. Engraçado ver isso no Stack Overflow :)

Antes de avançar no comportamento, é importante esclarecer algumas coisas:

  1. O valor numérico e o objeto numérico ( a = 3vs a = new Number(3)) são muito diferentes. Um é um primitivo, o outro é um objeto. Você não pode atribuir atributos a primitivos, mas a objetos.

  2. A coerção entre os dois está implícita.

    Por exemplo:

    (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. Toda expressão tem um valor de retorno. Quando o REPL lê e executa uma expressão, é isso que ela exibe. Os valores de retorno geralmente não significam o que você pensa e implicam coisas que simplesmente não são verdadeiras.

OK, vamos lá.

Imagem original do código JavaScript

O penhor.

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

Defina uma função dise chame -a com 5. Isso executará a função com 5o contexto ( this). Aqui, ele é coagido de um valor Number a um objeto Number. É muito importante observar que, se estivéssemos no modo estrito, isso não teria acontecido .

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

Agora, definimos o atributo five.wtfcomo 'potato', e com cinco como objeto, com certeza ele aceita a atribuição simples .

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

Com fivecomo objeto, garanto que ele ainda pode executar operações aritméticas simples. Pode. Seus atributos ainda permanecem? Sim.

A vez.

> five++
5
> five.wtf
undefined

Agora vamos checar five++. O truque com o incremento do postfix é que a expressão inteira será avaliada em relação ao valor original e, em seguida, aumentará o valor. Parece que fiveainda tem cinco anos, mas na verdade a expressão foi avaliada em cinco e depois definida fivecomo 6.

Não apenas foi fivedefinido 6como foi coagido novamente a um valor Number e todos os atributos foram perdidos. Como os primitivos não podem conter atributos, five.wtfé indefinido.

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

Tento novamente atribuir um atributo wtfa five. O valor de retorno implica que ele adere, mas na verdade não é porque fiveé um valor Number, não um objeto Number. A expressão é avaliada como 'potato?', mas quando verificamos, vemos que ela não foi atribuída.

O prestígio.

> five
6

Desde o incremento do postfix, fivetem sido 6.

Mateus
fonte
70
Você está assistindo de perto?
Waddles 29/07
3
@ Nathan Long Bem, em Java, do qual o JavaScript emprestou muito no início, todas as primitivas têm uma classe equivalente. inte Integer, por exemplo. Suponho que seja para que você possa criar uma função doSomething(Object)e ainda poder dar a ela primitivas. As primitivas serão convertidas em suas classes correspondentes neste caso. Mas JS realmente não se preocupam com os tipos, então a razão é provavelmente algo mais
Suppen
4
@ Eric A primeira coisa a ++fazer é aplicar ToNumber ao valor . Compare um caso semelhante com seqüências de caracteres: se você tiver x="5", então x++retorna o número 5.
Apsillers
2
Realmente toda a mágica acontece dis.call(5), a coerção para objetar, eu nunca esperaria isso.
Mcfedr 29/07
3
@gman, exceto que há muitas influências muito mais detalhadas, incluindo a nomeação de grandes partes de sua biblioteca padrão, o comportamento dos tipos de objetos padrão e até o fato de que ele usa uma sintaxe de chaves e ponto e vírgula (mesmo que o ponto e vírgula seja opcional) deriva do fato de que ele foi projetado para ser familiar aos programadores Java. Sim, é confuso para iniciantes. Isso não significa que as influências não existem.
Jules
77

Existem duas maneiras diferentes de representar um número:

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

O primeiro é um primitivo, o segundo é um objeto. Para todos os efeitos, ambos se comportam da mesma forma, exceto que parecem diferentes quando impressos no console. Uma diferença importante é que, como objeto, new Number(5)aceita novas propriedades como qualquer planície {}, enquanto a primitiva 5não:

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

Quanto à dis.call(5)parte inicial , consulte Como funciona a palavra-chave "this"? . Digamos apenas que o primeiro argumento para callé usado como o valor de this, e que esta operação força o número na Numberforma de objeto mais complexa . * Posteriormente, o ++força de volta à forma primitiva, porque a operação de adição +resulta em uma nova primitiva.

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

Um Numberobjeto aceita novas propriedades.

> five++

++resulta em um novo 6valor primitivo ...

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

... que não possui e não aceita atributos customizados.

* Observe que, no modo estrito, o thisargumento seria tratado de maneira diferente e não seria convertido em a Number. Consulte http://es5.github.io/#x10.4.3 para obter detalhes da implementação.

deceze
fonte
2
@ Raspe com certeza melhor em C / C ++, onde você pode fazer #define true false. Ou em Java, onde você pode redefinir o significado dos números. Essas são boas linguagens sólidas. Na verdade, todos os idiomas têm "truques" semelhantes, nos quais você obtém um resultado que funciona como pretendido, mas pode parecer estranho.
VLAZ 30/07/19
1
@ Vld Ok, deixe-me reformular isso, é por isso que eu odeio digitar pato.
Pharap
59

Há coerção no mundo do JavaScript - uma história de detetive

Nathan, você não tem ideia do que descobriu.

Estou investigando isso há semanas. Tudo começou em uma noite de tempestade em outubro passado. Eu acidentalmente tropecei na Numberaula - quero dizer, por que diabos o JavaScript tinha uma Numberaula?

Eu não estava preparado para o que iria descobrir a seguir.

Acontece que o JavaScript, sem dizer a você, alterou seus números para objetos e seus objetos para números logo abaixo do seu nariz.

O JavaScript esperava que ninguém entendesse, mas as pessoas têm relatado um comportamento estranho e inesperado, e agora, graças a você e à sua pergunta, tenho a evidência de que preciso para estourar tudo isso.

Foi isso que descobrimos até agora. Eu não sei se eu deveria estar dizendo isso a você. Você pode desativar o JavaScript.

> function dis() { return this }
undefined

Quando você criou essa função, provavelmente não tinha ideia do que aconteceria a seguir. Tudo parecia bem, e tudo estava bem - por enquanto.

Nenhuma mensagem de erro, apenas a palavra "indefinido" na saída do console, exatamente o que você esperaria. Afinal, essa era uma declaração de função - não deveria retornar nada.

Mas isso foi apenas o começo. O que aconteceu depois, ninguém poderia ter previsto.

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

Sim, eu sei, você esperava um 5, mas não foi isso que você conseguiu, foi - você conseguiu outra coisa - algo diferente.

A mesma coisa aconteceu comigo.

Eu não sabia o que fazer disso. Isso me deixou louco. Eu não conseguia dormir, não conseguia comer, tentei beber, mas nenhuma quantidade de Mountain Dew me faria esquecer. Simplesmente não fazia sentido!

Foi quando eu descobri o que realmente estava acontecendo - era coerção, e estava acontecendo bem diante dos meus olhos, mas eu estava cego demais para vê-lo.

A Mozilla tentou enterrá-lo, colocando-o onde eles sabiam que ninguém iria olhar - sua documentação .

Após horas lendo e relendo e relendo recursivamente, encontrei o seguinte:

"... e valores primitivos serão convertidos em objetos."

Estava bem claro, como pode ser escrito na fonte Open Sans. Era a call()função - como eu poderia ser tão estúpido ?!

Meu número não era mais um número. No momento em que passei call(), tornou-se outra coisa. Tornou-se ... um objeto.

Eu não pude acreditar no começo. Como isso pode ser verdade? Mas eu não podia ignorar as evidências que estavam se acumulando ao meu redor. Está aí se você apenas olhar:

> five.wtf = 'potato'
"potato"

> five.wtf
"potato"

wtfestava certo. Os números não podem ter propriedades personalizadas - todos sabemos isso! É a primeira coisa que eles ensinam na academia.

Deveríamos saber o momento em que vimos a saída do console - este não era o número que pensávamos. Era um impostor - um objeto que se passava como nosso doce número inocente.

Este foi ... new Number(5).

Claro! Fazia todo o sentido. call()tinha um trabalho a fazer, tinha que invocar uma função e, para fazer isso, precisava preencher this, sabia que não podia fazer isso com um número - precisava de um objeto e estava disposto a fazer qualquer coisa para obtê-lo, até se isso significava coagir nosso número. Quando call()viu o número 5, viu uma oportunidade.

Era o plano perfeito: espere até que ninguém olhe e troque nosso número por um objeto parecido com ele. Nós obtemos um número, a função é invocada e ninguém seria o mais sábio.

Realmente era o plano perfeito, mas como todos os planos, mesmo os perfeitos, havia um buraco nele, e estávamos prestes a cair nele.

Veja, o call()que não entendia era que ele não era o único na cidade que conseguia coagir números. Afinal, isso era JavaScript - coerção estava em toda parte.

call() peguei meu número e não pararia até tirar a máscara de seu pequeno impostor e expô-lo a toda a comunidade Stack Overflow.

Mas como? Eu precisava de um plano. Claro que parece um número, mas sei que não, tem que haver uma maneira de provar isso. É isso aí! Ele parece como um número, mas pode agir como um?

Eu disse fiveque precisava que ele ficasse 5 vezes maior - ele não perguntou o porquê e eu não expliquei. Fiz então o que qualquer bom programador faria: eu me multipliquei. Certamente não havia como ele conseguir escapar disso.

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

Droga! Não só fivemultiplicar muito bem wtfainda estava lá. Maldito esse cara e sua batata.

Que diabos estava acontecendo? Eu estava errado sobre essa coisa toda? É fiverealmente um número? Não, devo estar perdendo alguma coisa, eu sei, há algo que devo estar esquecendo, algo tão simples e básico que estou completamente ignorando.

Isso não parecia bom, eu estava escrevendo essa resposta por horas e ainda não estava mais perto de fazer o meu argumento. Eu não conseguia continuar, eventualmente as pessoas paravam de ler, eu tinha que pensar em alguma coisa e tinha que pensar rápido.

Espere, é isso! fivenão foi 25, 25 foi o resultado, 25 foi um número completamente diferente. Claro, como eu poderia esquecer? Os números são imutáveis. Quando você multiplica, 5 * 5nada é atribuído a nada, basta criar um novo número 25.

Deve ser o que está acontecendo aqui. De alguma forma, quando eu multiplico five * 5, fivedeve estar sendo coagido a um número e esse número deve ser o usado para a multiplicação. São os resultados dessa multiplicação impressa no console, não o valor em fivesi. fivenunca recebe nada - então é claro que isso não muda.

Então, como faço para me fiveatribuir o resultado de uma operação. Deixa comigo. Antes fivemesmo de ter a chance de pensar, eu gritei "++".

> five++
5

Aha! Eu o peguei! Todo mundo sabe que 5 + 1é 6, essa era a evidência que eu precisava para expor que fivenão era um número! Foi um impostor! Um impostor ruim que não sabia contar. E eu pude provar isso. Veja como um número real age:

> num = 5
5
> num++
5

Esperar? O que estava acontecendo aqui? suspiro , fiquei tão preso em flagras fiveque esqueço como os operadores de correios funcionam. Quando eu uso o ++no final do fiveque estou dizendo, retorne o valor atual e depois aumente five. É o valor antes da operação que é impresso no console. numfoi de fato 6e eu pude provar:

>num
6

Hora de ver o que fiverealmente era:

>five
6

... era exatamente o que deveria ser. fivefoi bom - mas eu estava melhor. Se fiveainda fosse um objeto, isso significaria que ainda teria a propriedade wtfe eu estava disposto a apostar tudo que não tinha.

> five.wtf
undefined

Aha! Eu tinha razão. Eu o peguei! fiveera um número agora - não era mais um objeto. Eu sabia que o truque da multiplicação não salvaria desta vez. Ver five++é realmente five = five + 1. Diferentemente da multiplicação, o ++operador atribui um valor a five. Mais especificamente, atribui a ele os resultados dos five + 1quais, como no caso da multiplicação, retorna um novo número imutável .

Eu sabia que o tinha, e apenas para ter certeza de que ele não conseguiria se esquivar disso. Eu tinha mais um teste na manga. Se eu estivesse certo e fiverealmente fosse um número agora, isso não funcionaria:

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

Ele não ia me enganar dessa vez. Eu sabia potato?que seria impresso no console porque isso é resultado da tarefa. A verdadeira questão é: wtfainda estará lá?

> five.wtf
undefined

Assim como eu suspeitava - nada - porque números não podem ser atribuídos a propriedades. Aprendemos que no primeiro ano na academia;)

Obrigado Nathan. Graças à sua coragem em fazer esta pergunta, finalmente posso deixar tudo isso para trás e seguir para um novo caso.

Como este sobre a função toValue(). Oh meu Deus. Nããão!

Luis Perez
fonte
9
Esqueça Jake; é Javascript.
Seth
Por que chamamos funções de construtor "classes" em JS?
Evoluçãoxbox
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

01declara uma função disque retorna o objeto de contexto. O que thisrepresenta as mudanças dependendo de você estar usando o modo estrito ou não. O exemplo inteiro tem resultados diferentes se a função foi declarada como:

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

Isso está detalhado na seção 10.4.3 na especificação ES5

  1. Se o código da função for estrito, defina ThisBinding como thisArg.
  2. Caso contrário, se thisArg for nulo ou indefinido, defina ThisBinding como o objeto global.
  3. Caso contrário, se Type (thisArg) não for Objeto, defina ThisBinding como ToObject (thisArg).

02é o valor de retorno da declaração da função. undefineddeve ser auto-explicativo aqui.

03a variável fiveé inicializada com o valor de retorno de disquando chamado no contexto do valor primitivo 5. Como disnão está no modo estrito, essa linha é idêntica à chamada five = Object(5).

04O Number {[[PrimitiveValue]]: 5}valor de retorno ímpar é a representação do objeto que envolve o valor primitivo5

05o fiveobjeto wtfde propriedade é atribuído um valor de seqüência de'potato'

06 é o valor de retorno da atribuição e deve ser auto-explicativo.

07a propriedade fivedo objeto wtfestá sendo examinada

08como five.wtffoi definido anteriormente 'potato', retorna 'potato'aqui

09o fiveobjeto está sendo multiplicado pelo valor primitivo 5. Isso não é diferente de qualquer outro objeto que está sendo multiplicado e é explicado na seção 11.5 da especificação ES5 . É importante notar como os objetos são convertidos em valores numéricos, abordados em algumas seções.

9.3 ToNumber :

  1. Deixe primValue ser ToPrimitive (argumento de entrada, dica Número).
  2. Retornar ToNumber (primValue).

9.1 ToPrimitive :

Retorne um valor padrão para o objeto. O valor padrão de um objeto é recuperado chamando o método interno [[DefaultValue]] do objeto, passando a dica opcional PreferredType. O comportamento do método interno [[DefaultValue]] é definido por esta especificação para todos os objetos nativos do ECMAScript na versão 8.12.8 .

8.12.8 [[DefaultValue]] :

Seja valueOf o resultado da chamada do método interno [[Get]] do objeto O com o argumento "valueOf".

  1. Se IsCallable (valueOf) for verdadeiro,

    1. Seja val o resultado da chamada do método interno [[Call]] de valueOf, com O como este valor e uma lista de argumentos vazia.
    2. Se val for um valor primitivo, retorne val.

Essa é uma maneira indireta de dizer que a valueOffunção do objeto é chamada e o valor de retorno dessa função é usado na equação. Se você alterar a valueOffunção, poderá alterar os resultados da operação:

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

10Como fivea valueOffunção s não foi alterada, ele retorna o valor primitivo encapsulado, de 5modo que five * 5avalie a 5 * 5qual resultado25

11a propriedade fivedo objeto wtfé avaliada novamente, apesar de não ter sido alterada desde quando foi atribuída 05.

12 'potato'

13é chamado o Operador de incremento do Postfixfive , que obtém o valor numérico ( 5abordamos anteriormente), armazena o valor para que ele possa ser retornado, adiciona 1ao valor ( 6), atribui o valor fivee retorna o valor armazenado ( 5)

14 como antes, o valor retornado é o valor anterior ao incremento

15a wtfpropriedade do valor primitivo ( 6) armazenado na variável fiveé acessada. A seção 15.7.5 da especificação ES5 define esse comportamento. Os números obtêm as propriedades Number.prototype.

16 Number.prototypenão tem uma wtfpropriedade, então undefinedé retornado

17 five.wtfé atribuído um valor de 'potato?'. A atribuição é definida em 11.13.1 da especificação ES5 . Basicamente, o valor atribuído é retornado, mas não armazenado.

18 'potato?' foi devolvido pelo operador de atribuição

19novamente five, cujo valor 6é acessado, e novamente Number.prototypenão possui uma wtfpropriedade

20 undefined como explicado acima

21 five é acessado

22 6 é retornado como explicado em 13

zzzzBov
fonte
17

É bem simples.

function dis () { return this; }

Isso retorna o thiscontexto. Então, se você call(5)está passando o número como um objeto.

A callfunção não fornece argumentos, o primeiro argumento que você fornece é o contexto de this. Normalmente, se você quer que nele está no contexto, você dá-lo {}de modo dis.call({})que os meios thisna função é um vazio this. No entanto, se você passar 5, parece que será convertido em um objeto. Consulte .call

Então o retorno é object

Quando você faz isso five * 5, o JavaScript vê o objeto fivecomo o tipo primitivo, portanto é equivalente a 5 * 5. Curiosamente, '5' * 5ainda é igual 25, então o JavaScript está claramente sendo transmitido. Nenhuma alteração no fivetipo subjacente é feita nesta linha

Mas quando você faz ++isso, ele converte o objeto no numbertipo primitivo , removendo a .wtfpropriedade. Porque você está afetando o tipo subjacente

Callum Linington
fonte
O retorno é Number .
28916 GSerg
++converte e atribui de volta. ++é igual a variable = variable + 1. Então você perdewtf
Rajesh
O *vs ++não me confunde. Ter uma função que não espera argumentos e retornos this; ter essa função aceita um argumento de qualquer maneira e retorna algo que é como, mas não é exatamente, o argumento - que não faz sentido para mim.
Nathan Long
Atualizado @ NathanLong para mostrar o que dis.call()faz.
precisa
5 ++ se comporta da mesma forma que na linguagem C, então nada de surpreendente. thisé simplesmente um ponteiro para um objeto, para que tipos primitivos sejam convertidos implicitamente. Por que uma função sem argumentos não pode ter pelo menos um contexto? O primeiro argumento de callou bindé usado para definir o contexto. Além disso, as funções são fechamentos que significa que têm acesso a mais do que apenasarguments
Rivenfall
10

Os valores primitivos não podem ter propriedade. Mas quando você tenta acessar uma propriedade com valor primitivo, ela é alterada de forma transparente para um objeto Number temporário.

Assim:

> 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
fonte
8

Declarar função dis. Função retorna seu contexto

function dis() { return this }
undefined

Ligue discom contexto 5. Os valores primitivos são colocados na caixa quando passados ​​como contexto no modo estrito ( MDN ). Então fiveagora é objeto (número da caixa).

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

Declarar wtfpropriedade na fivevariável

five.wtf = 'potato'
"potato"

Valor de five.wtf

five.wtf
"potato"

fiveestá na caixa 5, portanto, é número e objeto ao mesmo tempo (5 * 5 = 25). Isso não muda five.

five * 5
25

Valor de five.wtf

five.wtf
"potato"

Desmarque fiveaqui. fiveagora é apenas primitivo number. Imprime 5e depois adiciona 1a five.

five++
5

fiveé um número primitivo 6agora, não há propriedades nele.

five.wtf
undefined

primitivas não podem ter propriedades, você não pode definir isso

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

você não pode ler isso, porque não foi definido

five.wtf
undefined

fiveé 6por causa do incremento pós acima

five
6
Maxx
fonte
7

Primeiro, parece que isso está sendo executado no console do nodejs.

1

    function dis() { return this }

cria a função dis (), mas como ela não foi definida como varnão havia valor a ser retornado, undefinedo resultado foi a saída, mesmo que tenha dis()sido definida. Em uma nota lateral, thisnão foi retornada porque a função não foi executada.

2)

    five = dis.call(5)

Isso retorna de javascript Numberobjeto porque você apenas definir a função dis()de thisvalor para os cinco primitivo.

3)

   five.wtf = 'potato'

O primeiro retorna "potato"porque você acabou de definir a propriedade wtfde fivecomo 'potato'. Javascript retorna o valor de uma variável que você definir, tornando mais fácil para variáveis múltiplas cadeias e configurá-los para o mesmo valor como este: a = b = c = 2.

4)

    five * 5

Isso retorna 25porque você acabou de multiplicar o número primitivo 5para five. O valor de fivefoi determinado pelo valor do Numberobjeto.

5)

    five.wtf

Eu pulei essa linha antes porque repetiria aqui. Apenas retorna o valor da propriedade wtfque você definiu acima.

6

    five++

Como o @Callum disse, ++converterá o tipo para numbero mesmo valor do objeto Number {[[PrimitiveValue]]: 5}}.

Agora, como fiveé um number, você não pode mais definir propriedades para ele até fazer algo assim:

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

ou

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

Observe também que a segunda maneira terá um comportamento diferente do uso do primeiro método, pois está definindo um objeto genérico em vez do Numberobjeto criado anteriormente.

Espero que isso ajude, o javascript gosta de assumir as coisas, para que fique confuso ao mudar do Numberobjeto para um primitivo number. Você pode verificar qual é o tipo de algo usando a typeofpalavra - chave, escrevendo um tipo de cinco depois de inicializá-lo 'object'e, em seguida five++, retornar 'number'.

@deceze descreve muito bem a diferença entre o objeto Number e o número primitivo.

Patrick Barr
fonte
6

Os escopos JavaScript são feitos de contextos de execução. Cada Contexto de Execução possui um Ambiente Lexical (valores com escopo externo / globalmente), um Ambiente Variável (valores com escopo local) e essa ligação .

A ligação this é uma parte muito importante do contexto de execução. Usar callé uma maneira de alterar essa ligação , e isso criará automaticamente um objeto para preencher a ligação.

Function.prototype.call () (do MDN)

Sintaxe
fun.call(thisArg[, arg1[, arg2[, ...]]])

thisArg
O valor disso fornecido para a diversão. Observe que esse pode não ser o valor real visto pelo método: se o método for uma função no código do modo não estrito, nulo e indefinido serão substituídos pelo objeto global e os valores primitivos serão convertidos em objetos . (ênfase minha)

Uma vez que é evidente que 5 está sendo convertido em new Number(5), o resto deve ser bastante óbvio. Observe que outros exemplos também funcionarão desde que sejam valores primitivos.

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" />

insira a descrição da imagem aqui

Travis J
fonte
4

Alguns conceitos explicam o que acontece

5 é um número, um valor primitivo

Number {[[PrimitiveValue]]: 5} é uma instância de Number (vamos chamá-lo de invólucro de objeto)

Sempre que você acessar uma propriedade / método em um valor primitivo, o mecanismo JS criará um wrapper de objeto do tipo apropriado ( Numberpara 5, Stringpara 'str'e Booleanpara true) e resolverá a chamada de acesso / método de propriedade nesse wrapper de objeto. É o que acontece quando você faz, true.toString()por exemplo.

Ao executar operações em objetos, elas são convertidas em valores primitivos (usando toStringou valueOf) para resolver essas operações - por exemplo, ao executar

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

stringvai realizar a concatenação de mystre obj.toString()e numbervai realizar a adição de 3eobj.valueOf() .

Agora, para juntar tudo

five = dis.call(5)

dis.call(5)se comporta exatamente como (5).dis()se 5realmente tivesse o método dis. Para resolver a chamada do método, o wrapper de objeto é criado e a chamada do método é resolvida nele. Nesse ponto, cinco pontos para um invólucro de objeto em torno do valor primitivo 5.

five.wtf = 'potato'

Definir uma propriedade em um objeto, nada sofisticado aqui.

five * 5

Na verdade, está five.valueOf() * 5obtendo o valor primitivo do wrapper de objeto. fiveainda aponta para o objeto inicial.

five++

Isto é realmente five = five.valueOf() + 1. Antes dessa linha, fiveo wrapper do objeto fica em torno do valor 5, enquanto após esse ponto, fiveo valor primitivo é mantido6 .

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

fivenão é mais um objeto. Cada uma dessas linhas cria uma nova instância de Number para resolver o .wtfacesso à propriedade. As instâncias são independentes, portanto, definir uma propriedade em uma não será visível em outra. O código é completamente equivalente a este:

(new Number(6)).wtf;
(new Number(6)).wtf = 'potato?';
(new Number(6)).wtf;
Tibos
fonte