Apa yang terjadi dalam kode ini dengan objek Number yang menahan properti dan menambah nomor?

246

Tweet terbaru berisi cuplikan JavaScript ini.

Dapatkah seseorang tolong jelaskan apa yang terjadi di dalamnya langkah demi langkah?

> 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

Secara khusus, tidak jelas bagi saya:

  • mengapa hasilnya dis.call(5)adalah Numberdengan beberapa jenis [[PrimitiveValue]]properti, tetapi hasil five++dan five * 5tampaknya hanya angka biasa 5dan 25(bukan Numbers)
  • mengapa five.wtfproperti hilang setelah five++kenaikan
  • mengapa five.wtfproperti tidak lagi dapat diselesaikan setelah five++kenaikan, meskipun five.wtf = 'potato?'penugasan tampaknya menetapkan nilai.
Nathan Long
sumber
6
Haha saya melihat tweet itu !! Itu sangat aneh, saya pikir karena Anda melipatgandakannya, itu tidak terlalu mempengaruhi objek, tetapi ++tampaknya mempengaruhi tipe yang mendasarinya
Callum Linington
8
Baris mana yang tidak Anda mengerti? Prevs Postkenaikan? Setelah perkalian, itu lagi-lagi sebuah Numberobjek yang tidak memiliki wtfproperti ... Tapi tetap saja itu adalah objectkarenanya ia dapat memiliki properties..
Rayon
25
Ketika Anda menelepon disdengan dis.call(5), ini membungkus nomor primitif 5menjadi objek bertipe Numberyang berisi 5, sehingga objek ini dapat dikembalikan sebagai thisobjek. The ++bertobat itu kembali ke nomor primitif yang tidak dapat berisi properti, sehingga wtfmulai terdefinisi.
GSerg
5
@ Rayon ... dan itu telah berubah dengan ES6 . Jadi ke depan semua ini akan berhenti terjadi.
GSerg

Jawaban:

278

OP disini. Lucu melihat ini di Stack Overflow :)

Sebelum melangkah melalui perilaku, penting untuk memperjelas beberapa hal:

  1. Nilai angka dan objek Angka ( a = 3vs a = new Number(3)) sangat berbeda. Satu adalah primitif, yang lain adalah objek. Anda tidak dapat menetapkan atribut ke primitif, tetapi Anda bisa untuk objek.

  2. Pemaksaan di antara keduanya adalah implisit.

    Sebagai contoh:

    (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. Setiap Ekspresi memiliki nilai balik. Ketika REPL membaca dan mengeksekusi ekspresi, inilah yang ditampilkannya. Nilai pengembalian seringkali tidak berarti apa yang Anda pikirkan dan menyiratkan hal-hal yang tidak benar.

Baiklah, ini dia.

Gambar asli dari kode JavaScript

Janji.

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

Tentukan fungsi disdan panggil dengan 5. Ini akan menjalankan fungsi dengan 5sebagai konteks ( this). Di sini ia dipaksa dari nilai Number ke objek Number. Sangat penting untuk dicatat bahwa jika kita dalam mode ketat ini tidak akan terjadi .

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

Sekarang kita mengatur atribut five.wtfuntuk 'potato', dan dengan lima sebagai objek, cukup yakin ia menerima tugas sederhana .

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

Dengan fivesebagai objek, saya memastikan masih dapat melakukan operasi aritmatika sederhana. Bisa. Apakah atributnya masih melekat? Iya.

Giliran.

> five++
5
> five.wtf
undefined

Sekarang kita periksa five++. Trik dengan kenaikan postfix adalah bahwa seluruh ekspresi akan mengevaluasi terhadap nilai asli dan kemudian meningkatkan nilai. Sepertinya fivemasih lima, tapi benar-benar ekspresi dievaluasi menjadi lima, kemudian diatur fiveke 6.

Tidak hanya fivediatur 6, tetapi dipaksa kembali ke nilai Number, dan semua atribut hilang. Karena primitif tidak dapat memiliki atribut, five.wtftidak ditentukan.

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

Saya lagi mencoba untuk menetapkan kembali atribut wtfke five. Nilai pengembalian menyiratkan itu tetap, tetapi sebenarnya bukan karena fivemerupakan nilai angka, bukan objek angka. Ekspresi dievaluasi 'potato?', tetapi ketika kami memeriksa kami melihat itu tidak ditugaskan.

Prestise.

> five
6

Sejak peningkatan postfix, fivetelah 6.

Matius
sumber
70
Apakah Anda memperhatikan dengan seksama?
Waddles
3
@Nathan Long Well, di Jawa, yang JavaScript pinjam sejak awal, semua primitif memiliki kelas yang setara. intdan Integer, misalnya. Saya berasumsi ini adalah agar Anda dapat membuat suatu fungsi doSomething(Object), dan masih dapat memberikannya primitif. Primitif akan dikonversi ke kelas terkait mereka dalam kasus ini. Tapi JS tidak terlalu peduli dengan tipe, jadi alasannya mungkin sesuatu yang lain
Suppen
4
@ Eric Hal pertama yang ++dilakukan adalah menerapkan ToNumber ke nilai . Bandingkan kasus serupa dengan string: jika sudah x="5", maka x++kembalikan nomornya 5.
apsillers
2
Sungguh semua keajaiban terjadi dis.call(5), paksaan terhadap objek, saya tidak akan pernah menyangka itu.
mcfedr
3
@ gman kecuali bahwa ada banyak pengaruh yang jauh lebih terperinci, termasuk penamaan potongan besar dari pustaka standar, perilaku tipe objek standar, dan bahkan fakta bahwa ia menggunakan sintaks kurung kurawal dan titik koma (bahkan jika titik koma adalah opsional) berasal dari fakta bahwa ia dirancang agar tidak asing bagi programmer Java. Ya, ini membingungkan bagi pemula. Itu tidak berarti pengaruhnya tidak ada.
Jules
77

Ada dua cara berbeda untuk merepresentasikan angka:

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

Yang pertama adalah primitif, yang kedua adalah objek. Untuk semua maksud dan tujuan keduanya berperilaku sama, kecuali mereka terlihat berbeda ketika dicetak ke konsol. Satu perbedaan penting adalah bahwa, sebagai objek, new Number(5)menerima properti baru seperti halnya dataran {}, sedangkan primitif 5tidak:

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

Adapun bagian awal dis.call(5), silakan lihat Bagaimana kata kunci "ini" berfungsi? . Anggap saja argumen pertama callyang digunakan sebagai nilai this, dan bahwa operasi ini memaksa angka ke dalam Numberbentuk objek yang lebih kompleks . * Kemudian ++memaksa itu kembali ke bentuk primitif, karena operasi penambahan +menghasilkan primitif baru.

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

Sebuah Numberobjek menerima sifat baru.

> five++

++menghasilkan 6nilai primitif baru ...

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

... yang tidak memiliki dan tidak menerima atribut khusus.

* Perhatikan bahwa dalam modus ketat yang thisargumen akan diperlakukan berbeda dan akan tidak dikonversi ke Number. Lihat http://es5.github.io/#x10.4.3 untuk detail implementasi.

tipuan
sumber
2
@Pharap pasti jauh lebih baik di C / C ++ yang bisa Anda lakukan #define true false. Atau di Jawa, di mana Anda bisa mendefinisikan kembali apa artinya angka. Ini adalah bahasa padat yang bagus. Sebenarnya, setiap bahasa memiliki "trik" serupa di mana Anda mendapatkan hasil yang berfungsi seperti yang dimaksudkan tetapi mungkin terlihat aneh.
VLAZ
1
@ Vld Ok izinkan saya ulangi, itu sebabnya saya benci mengetik bebek.
Pharap
59

Ada Paksaan di Dunia JavaScript - Kisah Detektif

Nathan, Anda tidak tahu apa yang telah Anda temukan.

Saya sudah menyelidiki ini selama berminggu-minggu sekarang. Semuanya dimulai pada malam badai Oktober lalu. Saya tidak sengaja menemukan Numberkelas - Maksud saya, mengapa di dunia ini JavaScript memiliki Numberkelas?

Saya tidak siap untuk apa yang akan saya cari tahu selanjutnya.

Ternyata JavaScript, tanpa memberi tahu Anda, telah mengubah angka Anda menjadi objek dan objek Anda menjadi angka tepat di bawah hidung Anda.

JavaScript berharap tidak ada yang akan menangkap, tetapi orang-orang telah melaporkan perilaku aneh yang tak terduga, dan sekarang terima kasih untuk Anda dan pertanyaan Anda. Saya punya bukti yang saya butuhkan untuk membuat hal ini terbuka lebar.

Inilah yang kami temukan sejauh ini. Saya tidak tahu apakah saya harus mengatakan ini kepada Anda - Anda mungkin ingin mematikan JavaScript Anda.

> function dis() { return this }
undefined

Ketika Anda membuat fungsi itu, Anda mungkin tidak tahu apa yang akan terjadi selanjutnya. Semuanya tampak baik-baik saja, dan semuanya baik-baik saja - untuk saat ini.

Tidak ada pesan kesalahan, hanya kata "tidak terdefinisi" dalam output konsol, persis apa yang Anda harapkan. Bagaimanapun, ini adalah deklarasi fungsi - tidak seharusnya mengembalikan apa pun.

Tapi ini baru permulaan. Apa yang terjadi selanjutnya, tidak ada yang bisa memprediksi.

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

Ya saya tahu, Anda mengharapkan 5, tetapi bukan itu yang Anda dapatkan, apakah itu - Anda punya sesuatu yang lain - sesuatu yang berbeda.

Hal yang sama terjadi pada saya.

Saya tidak tahu harus bagaimana. Itu membuatku gila. Saya tidak bisa tidur, saya tidak bisa makan, saya mencoba untuk meminumnya, tetapi Mountain Dew tidak akan membuat saya lupa. Itu tidak masuk akal!

Saat itulah saya mengetahui apa yang sebenarnya terjadi - itu adalah paksaan, dan itu terjadi tepat di depan mata saya, tetapi saya terlalu buta untuk melihatnya.

Mozilla mencoba menguburnya dengan meletakkannya di tempat yang mereka tahu tidak akan dilihat siapa pun - dokumentasi mereka .

Setelah berjam-jam membaca dan membaca kembali secara berulang dan membaca kembali, saya menemukan ini:

"... dan nilai-nilai primitif akan dikonversi menjadi objek."

Itu ada di sana polos seperti yang bisa dibilang dalam font Open Sans. Itu call()fungsinya - bagaimana saya bisa sebodoh itu ?!

Nomor saya sama sekali bukan angka. Saat saya menyebarkannya call(), itu menjadi sesuatu yang lain. Itu menjadi ... sebuah objek.

Awalnya saya tidak percaya. Bagaimana ini bisa benar? Tetapi saya tidak bisa mengabaikan bukti yang meningkat di sekitar saya. Itu ada di sana jika Anda hanya melihat:

> five.wtf = 'potato'
"potato"

> five.wtf
"potato"

wtfitu benar. Angka tidak dapat memiliki properti khusus - kita semua tahu itu! Ini hal pertama yang mereka ajarkan padamu di akademi.

Kita seharusnya tahu saat kita melihat output konsol - ini bukan jumlah yang kami pikir itu. Ini adalah seorang penipu - sebuah benda yang lewat sebagai nomor manis kami yang tidak bersalah.

Ini adalah ... new Number(5).

Tentu saja! Itu masuk akal. call()punya pekerjaan untuk dilakukan, dia harus menjalankan fungsi, dan untuk itu dia perlu mengisi this, dia tahu dia tidak bisa melakukan itu dengan angka - dia butuh objek dan dia bersedia melakukan apa pun untuk mendapatkannya, bahkan jika itu berarti memaksa nomor kita. Ketika call()melihat nomornya 5, dia melihat peluang.

Itu adalah rencana yang sempurna: tunggu sampai tidak ada yang melihat dan menukar nomor kami dengan objek yang terlihat seperti itu. Kami mendapat nomor, fungsinya dipanggil, dan tidak ada yang lebih bijak.

Itu benar-benar rencana yang sempurna, tetapi seperti semua rencana, bahkan yang sempurna, ada lubang di dalamnya, dan kami akan jatuh ke dalamnya.

Lihat, yang call()tidak mengerti adalah bahwa dia bukan satu-satunya di kota yang bisa memaksa angka. Bagaimanapun, ini adalah JavaScript - paksaan ada di mana-mana.

call() mengambil nomor saya, dan saya tidak akan berhenti sampai saya menarik topeng dari penipu kecilnya dan mengekspos dia ke seluruh komunitas Stack Overflow.

Tapi bagaimana caranya? Saya membutuhkan rencana. Tentu itu terlihat seperti angka, tetapi saya tahu tidak, pasti ada cara untuk membuktikannya. Itu dia! Itu terlihat seperti angka, tetapi bisakah itu bertindak seperti nomor?

Saya bilang fivesaya perlu dia menjadi 5 kali lebih besar - dia tidak bertanya mengapa dan saya tidak menjelaskan. Saya kemudian melakukan apa yang akan dilakukan oleh programmer yang baik: Saya mengalikannya. Tentunya tidak mungkin dia bisa menipu jalan keluar dari ini.

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

Sial! Tidak hanya fivemultiply saja wtfmasih ada. Sialan orang ini dan kentangnya.

Apa yang sedang terjadi? Apakah saya salah tentang semua ini? Apakah fivebenar-benar angka? Tidak, saya pasti kehilangan sesuatu, saya tahu itu, ada sesuatu yang harus saya lupakan, sesuatu yang sangat sederhana dan mendasar sehingga saya benar-benar mengabaikannya.

Ini kelihatannya tidak bagus, saya telah menulis jawaban ini selama berjam-jam dan saya masih belum bisa menjelaskan maksud saya. Saya tidak dapat melanjutkan ini, akhirnya orang-orang akan berhenti membaca, saya harus memikirkan sesuatu dan saya harus memikirkannya dengan cepat.

Tunggu, itu saja! fivebukan 25, 25 adalah hasilnya, 25 adalah angka yang sama sekali berbeda. Tentu saja, bagaimana saya bisa lupa? Angka tidak berubah. Ketika Anda melipatgandakan 5 * 5tidak ada yang ditugaskan untuk apa pun yang Anda buat nomor baru 25.

Pasti itu yang terjadi di sini. Entah bagaimana ketika saya mengalikan five * 5, fiveharus dipaksa menjadi angka dan angka itu harus menjadi nomor yang digunakan untuk perkalian. Ini hasil dari perkalian yang dicetak ke konsol, bukan nilai fiveitu sendiri. fivetidak pernah ditugaskan apa pun - jadi tentu saja itu tidak berubah.

Jadi, bagaimana saya bisa fivemenetapkan sendiri hasil operasi. Saya mendapatkannya. fiveBahkan sebelum sempat berpikir, saya berteriak "++".

> five++
5

Aha! Saya memilikinya! Semua orang yang tahu 5 + 1adalah 6, ini adalah bukti yang saya butuhkan untuk mengekspos bahwa fiveitu bukan angka! Itu penipu! Penipu buruk yang tidak tahu bagaimana cara menghitung. Dan saya bisa membuktikannya. Begini cara bilangan real bertindak:

> num = 5
5
> num++
5

Tunggu? Apa yang sedang terjadi disini? desah aku begitu terjebak dalam penghancuran fivesehingga aku lupa bagaimana operator pos bekerja. Ketika saya menggunakan ++di akhir fivesaya katakan kembalikan nilai saat ini, lalu kenaikan five. Ini nilai sebelum operasi terjadi yang akan dicetak ke konsol. numsebenarnya 6dan saya bisa membuktikannya:

>num
6

Saatnya untuk melihat apa yang fivesebenarnya:

>five
6

... persis seperti apa yang seharusnya. fivebagus - tapi saya lebih baik. Jika fivemasih ada objek yang berarti masih memiliki properti wtfdan saya berani bertaruh semua yang tidak.

> five.wtf
undefined

Aha! Saya benar. Saya memilikinya! fivedulu nomor - itu bukan obyek lagi. Saya tahu trik multiplikasi tidak akan menghemat waktu ini. Lihat five++benar-benar five = five + 1. Berbeda dengan perkalian, ++operator memberikan nilai five. Lebih khusus, ia memberikannya hasil five + 1yang sama seperti dalam kasus penggandaan mengembalikan angka abadi yang baru .

Aku tahu aku memilikinya, dan hanya untuk memastikan dia tidak bisa keluar dari situ. Saya punya satu tes lagi di lengan baju saya. Jika saya benar, dan fivebenar-benar nomor sekarang, maka ini tidak akan berhasil:

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

Dia tidak akan membodohiku saat ini. Saya tahu potato?akan dicetak ke konsol karena itu adalah hasil tugas. Pertanyaan sebenarnya adalah, apakah wtfmasih ada?

> five.wtf
undefined

Seperti yang saya duga - tidak ada - karena angka tidak dapat ditetapkan sebagai properti. Kami belajar bahwa tahun pertama di akademi;)

Nathan terima kasih. Terima kasih atas keberanian Anda mengajukan pertanyaan ini, akhirnya saya bisa melupakan semua ini dan beralih ke kasus baru.

Seperti ini tentang fungsinya toValue(). Ya Tuhan. Tidaaaak!

Luis Perez
sumber
9
Lupakan saja Jake; itu Javascript.
Seth
Mengapa kita memanggil fungsi konstruktor "kelas" di 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

01mendeklarasikan fungsi disyang mengembalikan objek konteks. Apa yang thismewakili perubahan tergantung pada apakah Anda menggunakan mode ketat atau tidak. Seluruh contoh memiliki hasil yang berbeda jika fungsi tersebut dinyatakan sebagai:

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

Ini dirinci dalam bagian 10.4.3 dalam spesifikasi ES5

  1. Jika kode fungsi adalah kode yang ketat, atur ThisBinding ke thisArg.
  2. Lain jika thisArg adalah null atau tidak terdefinisi, atur ThisBinding ke objek global.
  3. Lain jika Type (thisArg) bukan Object, atur ThisBinding ke ToObject (thisArg).

02adalah nilai pengembalian dari deklarasi fungsi. undefinedharus jelas di sini.

03variabel fivediinisialisasi dengan nilai pengembalian disketika dipanggil dalam konteks nilai primitif 5. Karena distidak dalam mode ketat, saluran ini identik dengan panggilan five = Object(5).

04Nilai Number {[[PrimitiveValue]]: 5}balik ganjil adalah representasi objek yang membungkus nilai primitif5

05yang fiveobjek wtfproperti ditugaskan nilai string'potato'

06 adalah nilai pengembalian tugas dan harus jelas.

07yang fiveobjek wtfproperti sedang diperiksa

08seperti five.wtfyang sebelumnya diatur untuk 'potato'kembali ke 'potato'sini

09yang fiveobjek sedang dikalikan dengan nilai primitif 5. Ini tidak berbeda dengan objek lain yang dikalikan dan dijelaskan di bagian 11.5 dari spesifikasi ES5 . Dari catatan khusus adalah bagaimana benda dilemparkan ke nilai numerik, yang dibahas dalam beberapa bagian.

9.3 ToNumber :

  1. Biarkan primValue menjadi ToPrimitive (argumen input, Nomor petunjuk).
  2. Kembali ToNumber (primValue).

9.1 ToPrimitive :

Kembalikan nilai default untuk Objek. Nilai default suatu objek diambil dengan memanggil metode internal [[DefaultValue]] objek, melewati petunjuk opsional PreferredType. Perilaku metode internal [[DefaultValue]] ditentukan oleh spesifikasi ini untuk semua objek ECMAScript asli di 8.12.8 .

8.12.8 [[Nilai Default]] :

Biarkan valueOf menjadi hasil memanggil metode internal [[Get]] objek O dengan argumen "valueOf".

  1. Jika IsCallable (valueOf) benar maka,

    1. Biarkan val menjadi hasil dari memanggil metode internal [[Panggil]] dari ValueOf, dengan O sebagai nilai ini dan daftar argumen kosong.
    2. Jika val adalah nilai primitif, kembalikan val.

Ini semua adalah cara bundaran untuk mengatakan bahwa valueOffungsi objek dipanggil dan nilai pengembalian dari fungsi tersebut digunakan dalam persamaan. Jika Anda ingin mengubah valueOffungsi, Anda dapat mengubah hasil operasi:

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

10karena fungsi fives valueOftidak berubah, ia mengembalikan nilai primitif yang dibungkus 5sehingga five * 5mengevaluasi 5 * 5yang menghasilkan25

11yang fiveobjek wtfproperti dievaluasi lagi meskipun telah berubah dari ketika ditugaskan pada 05.

12 'potato'

13yang Postfix Kenaikan Operator disebut pada five, yang mendapat nilai numerik ( 5, kita bahas bagaimana sebelumnya), toko nilai sehingga dapat dikembalikan, menambahkan 1untuk nilai ( 6), memberikan nilai untuk five, dan kembali nilai yang disimpan ( 5)

14 seperti sebelumnya, nilai yang dikembalikan adalah nilai sebelum ditambahkan

15yang wtfproperti dari nilai primitif ( 6) disimpan di variabel fivediakses. Bagian 15.7.5 dari spesifikasi ES5 mendefinisikan perilaku ini. Angka mendapatkan properti dari Number.prototype.

16 Number.prototypetidak memiliki wtfproperti, jadi undefineddikembalikan

17 five.wtfdiberi nilai 'potato?'. Penugasan didefinisikan dalam 11.13.1 dari spesifikasi ES5 . Pada dasarnya nilai yang diberikan dikembalikan tetapi tidak disimpan.

18 'potato?' dikembalikan oleh operator penugasan

19lagi five, yang memiliki nilai 6diakses, dan sekali lagi Number.prototypetidak memiliki wtfproperti

20 undefined seperti yang dijelaskan di atas

21 five diakses

22 6 dikembalikan seperti yang dijelaskan dalam 13

zzzzBov
sumber
17

Sederhana saja.

function dis () { return this; }

Ini mengembalikan thiskonteksnya. Jadi, jika Anda melakukannya call(5)Anda melewati angka sebagai objek.

The callfungsi tidak menyediakan argumen, argumen pertama yang Anda berikan adalah konteks this. Biasanya jika Anda ingin pada itu pada konteks, Anda memberikan {}begitu dis.call({}), yang berarti thisdalam fungsi adalah kosong this. Namun, jika Anda lulus5 sepertinya akan dikonversi ke objek. Lihat .call

Jadi kembalinya adalah object

Ketika Anda melakukannya five * 5, JavaScript melihat objek fivesebagai tipe primitif, sehingga setara dengan 5 * 5. Menariknya, lakukan '5' * 5, masih sama 25, jadi JavaScript jelas casting di bawah tenda. Tidak ada perubahan pada yang mendasarinyafive tipe yang dilakukan pada baris ini

Tetapi ketika Anda melakukannya ++akan mengkonversi objek ke numbertipe primitif sehingga menghapus .wtfproperti. Karena Anda memengaruhi tipe yang mendasarinya

Callum Linington
sumber
Pengembaliannya adalah Angka .
GSerg
++mengonversi dan menetapkannya kembali. ++sama dengan variable = variable + 1. Jadi, Anda kehilanganwtf
Rajesh
The *vs ++tidak membingungkan saya sama sekali. Memiliki fungsi yang tidak mengharapkan argumen dan mengembalikan this, tetap memiliki fungsi tersebut menerima argumen dan mengembalikan sesuatu yang seperti, tetapi tidak tepat, argumen - yang tidak masuk akal bagi saya.
Nathan Long
Memperbarui @NathanLong untuk menunjukkan apa yang dis.call()dilakukannya.
Callum Linington
5 ++ berperilaku sama seperti dalam bahasa C jadi tidak ada yang mengejutkan. thishanyalah sebuah pointer ke objek sehingga tipe primitif dikonversi secara implisit. Mengapa fungsi tanpa argumen tidak dapat memiliki setidaknya konteks? Argumen pertama callatau binddigunakan untuk mengatur konteks. Juga, fungsi adalah penutupan yang berarti mereka memiliki akses ke lebih dari sekadararguments
Rivenfall
10

Nilai-nilai primitif tidak dapat memiliki properti. Tetapi ketika Anda mencoba untuk mengakses ke properti pada nilai primitif, itu transparan transstype ke objek Nomor sementara.

Begitu:

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

Deklarasikan fungsi dis. Fungsi mengembalikan konteksnya

function dis() { return this }
undefined

Panggil disdengan konteks 5. Nilai-nilai primitif dikotak bila diteruskan sebagai konteks dalam mode ketat ( MDN ). Jadi fivesekarang adalah objek (nomor kotak).

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

Nyatakan wtfproperti pada fivevariabel

five.wtf = 'potato'
"potato"

Nilai dari five.wtf

five.wtf
"potato"

fivekotak 5, jadi itu nomor dan objek pada saat yang sama (5 * 5 = 25). Itu tidak berubah five.

five * 5
25

Nilai dari five.wtf

five.wtf
"potato"

Buka kotak di fivesini. fivesekarang hanya primitif number. Mencetak 5, dan kemudian menambahkan 1ke five.

five++
5

fiveadalah bilangan primitif 6sekarang, tidak ada properti di dalamnya.

five.wtf
undefined

primitif tidak dapat memiliki properti, Anda tidak dapat mengatur ini

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

Anda tidak dapat membaca ini, karena belum disetel

five.wtf
undefined

fiveadalah 6karena pasca incrementing atas

five
6
Maks
sumber
7

Pertama sepertinya ini sedang dijalankan melalui konsol nodejs.

1.

    function dis() { return this }

menciptakan fungsi dis (), tetapi karena itu tidak ditetapkan sebagai vartidak ada nilai untuk kembali begitu undefinedjuga output, meskipun dis()didefinisikan. Pada sidenote, thistidak dikembalikan karena fungsinya tidak dijalankan.

2.

    five = dis.call(5)

Pengembalian ini javascript ini Numberobjek karena Anda hanya mengatur fungsi dis()'s thisnilai primitif lima.

3.

   five.wtf = 'potato'

Pengembalian pertama "potato"karena Anda hanya mengatur properti wtfdari fiveke 'potato'. Javascript mengembalikan nilai variabel yang Anda atur, membuatnya mudah untuk mem-rantai berbagai variabel dan mengaturnya ke nilai yang sama seperti ini:a = b = c = 2 .

4.

    five * 5

Ini kembali 25karena Anda baru saja mengalikan angka primitif 5ke five. Nilai fiveditentukan oleh nilai Numberobjek.

5.

    five.wtf

Saya melewatkan baris ini sebelumnya karena saya akan mengulanginya di sini. Itu hanya mengembalikan nilai propertiwtf yang Anda tetapkan di atas.

6.

    five++

Seperti yang dikatakan @Callum, ++akan mengkonversi tipe ke numberdari nilai yang sama dari objekNumber {[[PrimitiveValue]]: 5}} .

Sekarang karena fivea number, Anda tidak dapat mengatur properti lagi sampai Anda melakukan sesuatu seperti ini:

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

atau

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

Perhatikan juga bahwa cara kedua akan memiliki perilaku yang berbeda daripada menggunakan metode pertama karena itu mendefinisikan objek generik daripada Numberobjek yang dibuat sebelumnya.

Saya harap ini membantu, javascript suka mengasumsikan sesuatu, sehingga bisa membingungkan ketika mengubah dari Numberobjek ke primitif number. Anda dapat memeriksa apa jenis sesuatu dengan menggunakan typeofkata kunci, menulis jenis lima setelah Anda menginisialisasi itu kembali 'object', dan setelah Anda melakukannya five++kembali 'number'.

@deceze menjelaskan perbedaan antara objek Number dan primitive number dengan sangat baik.

Patrick Barr
sumber
6

Lingkup JavaScript dibuat dari Konteks Eksekusi. Setiap Konteks Eksekusi memiliki Lingkungan Leksikal (nilai eksternal / cakupan global), Lingkungan Variabel (nilai cakupan lokal), dan ini mengikat .

The mengikat ini adalah bagian yang sangat penting dari Konteks Eksekusi. Menggunakan calladalah salah satu cara untuk mengubah pengikatan ini , dan dengan begitu akan secara otomatis membuat objek untuk mengisi pengikatan.

Function.prototype.call () (dari MDN)

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

thisArg
Nilai ini disediakan untuk panggilan bersenang-senang. Perhatikan bahwa ini mungkin bukan nilai aktual yang dilihat oleh metode: jika metode ini adalah fungsi dalam kode mode non-ketat, null dan undefined akan diganti dengan objek global dan nilai primitif akan dikonversi menjadi objek . (penekanan milikku)

Setelah terbukti bahwa 5 diubah menjadi new Number(5), sisanya harus agak jelas. Perhatikan bahwa contoh lain juga akan berfungsi selama mereka adalah nilai primitif.

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

masukkan deskripsi gambar di sini

Travis J
sumber
4

Beberapa konsep menjelaskan apa yang terjadi

5 adalah angka, nilai primitif

Number {[[PrimitiveValue]]: 5} adalah turunan dari Number (sebut saja objek wrapper)

Setiap kali Anda mengakses properti / metode pada nilai primitif, mesin JS akan membuat pembungkus objek dari tipe yang sesuai ( Numberuntuk 5, Stringuntuk 'str'dan Booleanuntuk true) dan menyelesaikan panggilan akses / metode properti pada pembungkus objek tersebut. Inilah yang terjadi ketika Anda melakukannya true.toString()misalnya.

Saat melakukan operasi pada objek, mereka dikonversi ke nilai primitif (dengan menggunakan toStringatau valueOf) untuk menyelesaikan operasi tersebut - misalnya saat melakukan

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

stringakan memegang rangkaian string dari mystrdan obj.toString()dan numberakan menahan penambahan 3dan obj.valueOf().

Sekarang untuk menyatukan semuanya

five = dis.call(5)

dis.call(5)berperilaku seperti (5).dis()jika 5benar-benar punya metode dis. Untuk menyelesaikan pemanggilan metode, pembungkus objek dibuat dan pemanggilan metode diselesaikan. Pada titik ini lima poin ke pembungkus objek di sekitar nilai primitif 5.

five.wtf = 'potato'

Menetapkan properti pada objek, tidak ada yang mewah di sini.

five * 5

Ini sebenarnya five.valueOf() * 5mendapatkan nilai primitif dari objek wrapper. fivemasih menunjuk ke objek awal.

five++

Ini sebenarnya five = five.valueOf() + 1. Sebelum baris ini fivememegang pembungkus objek di sekitar nilai 5, sedangkan setelah titik ini fivememegang nilai primitif6 .

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

fivebukan lagi obyek. Masing-masing baris menciptakan instance Nomor baru untuk menyelesaikan .wtfakses properti. Mesin virtual independen, jadi menyetel properti pada satu tidak akan terlihat pada yang lain. Kode ini sepenuhnya sama dengan yang ini:

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