Bu kodda, özellikleri tutan ve sayıyı arttıran Number nesneleri ile neler oluyor?


246

Son bir tweet , bu JavaScript snippet'ini içeriyordu.

Birisi adım adım neler olduğunu açıklayabilir mi?

> 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

Özellikle, benim için net değil:

  • neden sonuç dis.call(5)bir Numbertür [[PrimitiveValue]]mülk ile bir, ama sonuçları five++ve five * 5sadece düz sayılar gibi görünüyor 5ve 25( Numbers)
  • neden five.wtfmülk sonra kaybolur five++artım
  • Görünüşe göre değeri ayarlamasına rağmen atama five.wtfsonrasında mülkün neden artık ayarlanamayacağını .five++five.wtf = 'potato?'

6
Haha bu tweet'i gördüm !! Çok tuhaf, bence çoğaldığınız için nesneyi oldukça etkilemiyor, ancak ++altta yatan türü etkiliyor gibi görünüyor
Callum Linington

8
Hangi çizgiyi anlamadınız? Prev Postartış? Çarpma işleminden sonra, yine özelliği Numberolmayan bir nesnedir wtf... Ama yine objectde sahip olabileceği bir durumdur properties..
Rayon

25
Aradığınızda disile dis.call(5)bu ilkel sayı sarar 5türünde bir nesne haline Numberiçeren 5bu nesne olarak iade edilebilir böylece, thisnesne. Bunu ++, özellikleri içeremeyen ilkel bir sayıya dönüştürür, bu yüzden wtftanımsız olmaya başlar.
GSerg


5
@ Rayon ... ve bu ES6 ile değişti . Bu yüzden, ileriye doğru her şey gerçekleşmeyi bırakacaktır.
GSerg

Yanıtlar:


278

OP burada. Stack Overflow üzerinde görmek komik :)

Davranışa adım atmadan önce, birkaç şeyi açıklığa kavuşturmak önemlidir:

  1. Sayı değeri ve Sayı nesnesi ( a = 3vs a = new Number(3)) çok farklıdır. Biri ilkel, diğeri bir nesnedir. İlkellere nitelik atayamazsınız, ancak nesnelere atayabilirsiniz.

  2. İkisi arasındaki baskı örtüktür.

    Örneğin:

    (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. Her İfadenin bir dönüş değeri vardır. Ne zaman REPL okur ve bir ifade yürütür, bu ne görüntüler olduğunu. Dönüş değerleri çoğu zaman düşündüğünüz anlamına gelmez ve sadece doğru olmayan şeyleri ima eder.

Tamam, başlıyoruz.

JavaScript kodunun orijinal resmi

Rehin.

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

Bir fonksiyonu tanımlayın disve çağrı ile 5. Bu, işlevi 5context ( this) olarak ile yürütür . Burada bir Number değerinden Number nesnesine zorlanır. Sıkı modda olsaydık bunun gerçekleşmeyeceğini belirtmek çok önemlidir .

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

Şimdi özelliğini ayarlamak five.wtfiçin 'potato', ve bir nesne olarak beş ile malum olduğu üzere o kabul Basit Ataması .

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

İle fivebir nesne olarak, ben hala basit aritmetik işlemleri gerçekleştirmek sağlamak. Bu olabilir. Nitelikleri hala geçerli mi? Evet.

Dönüş.

> five++
5
> five.wtf
undefined

Şimdi kontrol ediyoruz five++. Postfix artımlı hile , tüm ifadenin orijinal değere göre değerlenip sonra değeri artırmasıdır. Görünüşe fiveardından set hala beş olduğunu, ama gerçekten ifadesi beşe değerlendirilir fiveiçin 6.

Sadece fiveayarlanmakla kalmadı 6, aynı zamanda tekrar bir Number değerine zorlandı ve tüm öznitelikler kayboldu. İlkel özellikler five.wtftaşıyamayacağı için tanımsızdır.

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

Yine bir öznitelik yeniden atamak için teşebbüs wtfetmek five. Dönüş değeri yapıştığını gösterir, ancak aslında fivebir Number nesnesi değil, bir Number değeri olduğu için değildir. İfade değerlendirilir 'potato?', ancak kontrol ettiğimizde ifadenin atanmadığını görürüz.

Prestij.

> five
6

Hiç sonek artış beri fiveolmuştur 6.


70
Yakindan takip ediyor musun?
Waddles

3
@Nathan Long Eh, JavaScript'in başlangıçta yoğun bir şekilde ödünç aldığı Java'da, tüm ilkellerin bir sınıf eşdeğeri var. intve Integerörneğin. Bunun bir işlev yaratabileceğinizi doSomething(Object)ve yine de ona ilkelleri verebildiğinizi varsayıyorum . Bu durumda ilkeller ilgili sınıflarına dönüştürülecektir. Sebebi muhtemelen başka bir şeydir, böylece Ama JS gerçekten türleri umursamıyor
Suppen

4
@Eric İlk şey ++ToNumber değerini değere uygulamaktır . Dizeleri ile benzer bir durum karşılaştırın: varsa x="5", o zaman x++numarasını döndürür 5.
apsillers

2
Gerçekten sihir gerçekleşir, dis.call(5)itiraz zorlama, bunu hiç beklemezdim.
mcfedr

3
@gman, standart kütüphanesinin büyük parçalarının adlandırılması, standart nesne türlerinin davranışı ve hatta köşeli parantez ve noktalı virgül sözdizimi kullandığı gerçeği (noktalı virgüller olsa bile) dahil olmak üzere çok daha ayrıntılı etkiler olması dışında isteğe bağlı), Java programcılarına aşina olacak şekilde tasarlanmış olmasından kaynaklanır. Evet, yeni başlayanlar için kafa karıştırıcı. Bu, etkilerin olmadığı anlamına gelmez.
Jules

77

Bir sayıyı temsil etmenin iki farklı yolu vardır:

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

Birincisi ilkel, ikincisi bir nesnedir. Tüm niyet ve amaçlar için, konsola yazdırıldıklarında farklı görünmeleri dışında her ikisi de aynı şekilde davranır. Önemli bir fark, bir nesne olarak, new Number(5)herhangi bir düzlük gibi yeni özellikleri kabul etmesidir {}, ancak ilkel 5şunları yapmaz:

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

İlk dis.call(5)bölüme gelince , lütfen "Bu" anahtar kelime nasıl çalışır? Bölümüne bakın. . Sadece ilk argümanın calldeğeri olarak kullanıldığını thisve bu işlemin sayıyı daha karmaşık Numbernesne biçimine ++zorladığını +söyleyelim.

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

Bir Numbernesne yeni özellikleri kabul eder.

> five++

++yeni bir ilkel 6değerle sonuçlanır ...

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

... özel nitelikleri olmayan ve kabul etmeyen.

* O Not katı moddathis argümanı farklı muamele olurdu ve olur değil bir dönüştürülebilir Number. Bkz http://es5.github.io/#x10.4.3 uygulama ayrıntıları için.


2
@Pharap, yapabileceğiniz C / C ++ 'da kesinlikle daha iyi #define true false. Veya Java'da, sayıların ne anlama geldiğini yeniden tanımlayabilirsiniz. Bunlar iyi sağlam diller. Gerçekte, her dilde, amaçlandığı gibi çalışan ancak tuhaf görünebilecek bir sonuç elde ettiğiniz benzer "püf noktaları" vardır.
VLAZ

1
@Vld Tamam, yeniden ifade etmeme izin ver, bu yüzden ördek yazmadan nefret ediyorum.
Pharap

59

JavaScript Dünyasında Zorlama Var - Bir Dedektif Hikayesi

Nathan, neyi ortaya çıkardığına dair hiçbir fikrin yok.

Bunu haftalardır araştırıyorum. Her şey geçen Ekim ayında fırtınalı bir gecede başladı. Yanlışlıkla Numbersınıfa tökezledim - yani, dünyada neden JavaScript'in bir Numbersınıfı vardı?

Bundan sonra ne bulacağım için hazırlıklı değildim.

JavaScript'in size söylemeden, sayılarınızı nesnelere, nesnelerinizi de burnunuzun altındaki sayılara dönüştürdüğü ortaya çıkıyor.

JavaScript kimsenin yakalayamayacağını umuyordu, ancak insanlar garip beklenmedik davranışlar bildiriyorlar ve şimdi siz ve sorunuz sayesinde bu şeyi tamamen açmam için gereken kanıtlara sahibim.

Şimdiye kadar öğrendiğimiz şey bu. Size bunu söylemem gerekip gerekmediğini bilmiyorum - JavaScript'inizi kapatmak isteyebilirsiniz.

> function dis() { return this }
undefined

Bu işlevi oluşturduğunuzda, muhtemelen daha sonra ne olacağı hakkında hiçbir fikriniz yoktu. Her şey iyi görünüyordu ve her şey iyiydi - şimdilik.

Hata mesajı yok, sadece konsol çıktısında "undefined" kelimesi, tam olarak beklediğiniz gibi. Sonuçta, bu bir işlev beyanıydı - hiçbir şey döndürmemesi gerekiyordu.

Ama bu sadece başlangıçtı. Daha sonra ne olacağını kimse tahmin edemezdi.

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

Evet biliyorum, bekledin 5ama, sahip olduğun şey bu değildi - başka bir şeyin var - farklı bir şey.

Aynı şey bana da oldu.

Ne yapacağımı bilmiyordum. Beni deli etti. Uyuyamadım, yiyemedim, içmeye çalıştım, ama hiçbir miktarda Dağ Çiy beni unutturmazdı. Hiç bir anlamı yoktu!

O zaman gerçekten neler olup bittiğini öğrendim - zorlama oldu ve tam gözlerimin önünde oluyordu, ama görmek için çok kördüm.

Mozilla, kimsenin görünmeyeceğini bildiği yere koyarak belgelerini gömmeye çalıştı .

Saatlerce tekrar tekrar okuma ve yeniden okuma ve tekrar okumadan sonra bunu buldum:

"... ve ilkel değerler nesnelere dönüştürülecek."

Open Sans yazı tipinde söylenebileceği gibi düz. O was call()fonksiyonu - bu kadar aptal nasıl olabilir ?!

Numaram artık bir sayı değildi. Onu geçirdiğim an call()başka bir şey oldu. Nesne oldu.

İlk başta inanamadım. Bu nasıl doğru olabilir? Ama etrafımdaki kanıtları görmezden gelemedim. Sadece bakarsanız tam orada:

> five.wtf = 'potato'
"potato"

> five.wtf
"potato"

wtfhaklıydı. Sayıların özel özellikleri olamaz - hepimiz bunu biliyoruz! Akademide size ilk öğrettikleri şey.

Konsol çıktısını gördüğümüz anı bilmeliydik - bu sandığımız sayı değildi. Bu bir sahtekârdı - kendini tatlı masum sayımız olarak gösteren bir nesne.

Bu oldu ... new Number(5).

Elbette! Mükemmel bir anlam ifade etti. call()yapması gereken bir işi vardı, bir işlevi çağırmak zorundaydı ve doldurmak için bunu yapmak zorundaydı, bunu thisbir sayı ile yapamayacağını biliyordu - bir nesneye ihtiyacı vardı ve bunu elde etmek için her şeyi yapmaya istekliydi, hatta bu numaramızı zorlamak anlamına geliyorsa. Ne zaman call()numarayı gördü 5, o bir fırsat gördü.

Mükemmel plan buydu: Kimse görünmeyene kadar bekleyin ve bizim gibi görünen bir nesne için numaramızı değiştirin. Bir sayı alırız, fonksiyon çağrılır ve kimse daha akıllıca olmaz.

Gerçekten mükemmel bir plandı, ama tüm planlar, hatta mükemmel olanlar gibi, içinde bir delik vardı ve biz de onun içine düşmek üzereydik.

Bakın, call()anlamayan şey, şehirde sayıları zorlayabilecek tek kişi o değildi. Sonuçta bu JavaScript'ti - baskı her yerdeydi.

call() numaramı aldım ve maskeyi küçük sahtekarından çıkarıp onu tüm Stack Overflow topluluğuna maruz bırakana kadar durmayacaktım.

Ama nasıl? Bir plana ihtiyacım vardı. Tabii ki bir sayı gibi görünüyor, ama biliyorum, bunu kanıtlamanın bir yolu olmalı. Bu kadar! Bu görünen bir sayı gibi, ancak biri gibi davranabilir?

five5 kat daha büyük olması için ona ihtiyacım olduğunu söyledim - nedenini sormadı ve açıklamamıştım. Sonra iyi bir programcının yapacağı şeyi yaptım: Çoğalttım. Elbette bundan kurtulmanın hiçbir yolu yoktu.

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

Lanet olsun! Sadece çarpmakla kalmadı, fivesadece wtforadaydı. Bu adama ve patatesine lanet olsun.

Neler oluyor? Bütün bunlar hakkında yanılmış mıydım? Mı fivebir numara gerçekten? Hayır, bir şey eksik olmalıyım, biliyorum, unutmam gereken bir şey var, çok basit ve basit bir şey var.

Bu iyi görünmüyordu, saatlerce bu cevabı yazıyordum ve hala benim açımdan daha yakın değildim. Bunu devam ettiremedim, sonunda insanlar okumayı bırakacaktı, bir şey düşünmek zorunda kaldım ve hızlı düşünmek zorunda kaldım.

Bekle bu! five25, 25 sonuç 25, tamamen farklı bir sayı oldu. Tabii ki, nasıl unutabilirim? Sayılar değişmez. Çarptığınızda5 * 5 hiçbir şey herhangi bir şeye atanmaz, sadece yeni bir sayı oluşturur 25.

Burada olan bu olmalı. Bir şekilde çarptığımda five * 5, fivebir sayıya zorlanıyor olmalı ve bu sayı çarpma için kullanılan sayı olmalıdır. Konsolda yazdırılan çarpımın sonuçları, fivekendisinin değeri değil .fiveasla bir şey atanmaz - bu yüzden elbette değişmez.

Öyleyse fivebir operasyonun sonucunu nasıl atayabilirim? Anladım. fiveDüşünme şansım bile olmadan , "++" diye bağırdım.

> five++
5

Aha! Ona sahiptim! Herkes bilir 5 + 1olduğunu 6bu Bunu açığa çıkarmak için gerekli kanıtı olduğunu, fivesayı değildi! Bir sahtekârdı! Nasıl sayılacağını bilmeyen kötü bir sahtekâr. Ve kanıtlayabilirim. Gerçek bir sayının işleyişi şöyle:

> num = 5
5
> num++
5

Bekle? Burada ne oluyordu? iç çekerek öylesine yakalandım fiveki post operatörlerin nasıl çalıştığını unutuyorum. Ben kullandığım ++sonunda fiveben geçerli değeri döndürmek, sonra artış kullanın five. Bu değer daha önce operasyon konsola basılmış olur o gerçekleşir. numaslında 6ve bunu kanıtlayabilirim:

>num
6

fiveGerçekte ne olduğunu görme zamanı :

>five
6

... olması gereken şey buydu. fiveiyiydi - ama daha iyiydim. Eğer fivehala hala özelliği olurdu anlamına gelecektir bir nesne vardı wtfve ben etmedi bahis her şey hazırdı.

> five.wtf
undefined

Aha! Haklıydım. Ona sahiptim! fiveartık bir sayıydı - artık bir nesne değildi. Çarpma numarasının bu sefer onu kurtaramayacağını biliyordum. five++Gerçekten bakın five = five + 1. Çarpmanın aksine, ++operatör bir değer atar five. Daha spesifik olarak, five + 1çarpma durumunda olduğu gibi yeni bir değişmez sayı döndürdüğü sonuçları atar. .

Ona sahip olduğumu biliyordum ve sadece onun yolunu kıramayacağından emin olmak için. Kolumda bir test daha yaptım. Eğer haklıysam ve fiveşimdi gerçekten bir numara olsaydım, bu işe yaramazdı:

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

Bu sefer beni kandırmayacaktı. potato?Konsola yazdırılacağını biliyordum çünkü ödevin çıktısı bu. Asıl soru, wtfhala orada olacak mı?

> five.wtf
undefined

Şüphelendiğim gibi - hiçbir şey - çünkü sayılara özellik atanamaz. Akademide ilk yılı öğrendik;)

Teşekkürler Nathan. Bu soruyu sorma cesaretiniz sayesinde nihayet bütün bunları arkamda bırakabilir ve yeni bir davaya geçebilirim.

Bunun gibi işlev hakkında toValue(). Ah tanrım. Nooo!


9
Unutma Jake; Javascript.
Seth

JS'de neden yapıcı işlevlerine "sınıf" diyoruz?
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

01disbağlam nesnesini döndüren bir işlevi bildirir . Ne thistemsil Katı modu kullanırken veya olmasın yapmanıza bağlı değişir. İşlev şu şekilde bildirildiyse tüm örneğin farklı sonuçları vardır:

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

Bu, ES5 şartnamesinin 10.4.3 bölümünde ayrıntılı olarak açıklanmıştır.

  1. İşlev kodu katı kodsa, ThisBinding öğesini thisArg olarak ayarlayın.
  2. ThisArg null veya undefined ise, ThisBinding öğesini global nesneye ayarlayın.
  3. Tür (thisArg) Nesne değilse, ThisBinding ToObject (thisArg) olarak ayarlayın.

02işlev bildiriminin dönüş değeridir. undefinedburada kendini açıklayıcı olmalı.

03değişken five, disilkel değer bağlamında çağrıldığında dönüş değeri ile başlatılır 5. Çünkü dissıkı modda değil, bu hat arama aynıdır five = Object(5).

04Tek Number {[[PrimitiveValue]]: 5}dönüş değeri, ilkel değeri saran nesnenin temsilidir5

05fivenesnenin wtfözelliği bir dize değeri atanır'potato'

06 ödevin dönüş değeridir ve kendi kendini açıklayıcı olmalıdır.

07fivenesnenin wtfözelliği incelenmektedir

08five.wtfönceden ayarlandığı gibi buraya 'potato'geri döner'potato'

09fiveNesne ilkel değeri ile çarpılır ediliyor 5. Bu, çarpılan diğer nesnelerden farklı değildir ve ES5 spesifikasyonunun 11.5 bölümünde açıklanmaktadır . Özellikle dikkat edilmesi gereken husus, nesnelerin birkaç bölümde kapsanan sayısal değerlere nasıl dönüştürüldüğüdür.

9.3 Sayı :

  1. PrimValue'nun ToPrimitive olmasına izin verin (giriş argümanı, ipucu Numarası).
  2. Sayıya Dön (primValue).

9.1 İlkel :

Nesne için varsayılan bir değer döndürün. Bir nesnenin varsayılan değeri, isteğe bağlı ipucu PreferredType iletilerek nesnenin [[DefaultValue]] dahili yöntemi çağrılarak alınır. [[DefaultValue]] dahili yönteminin davranışı, 8.12.8'deki tüm yerel ECMAScript nesneleri için bu belirtim ile tanımlanır .

8.12.8 [[Varsayılan Değer]] :

ValueOf, O nesnesinin [[Get]] dahili yöntemini "valueOf" bağımsız değişkeniyle çağırmanın sonucu olsun.

  1. IsCallable (valueOf) true olursa,

    1. Val, [[Call]] dahili valueOf yöntemini çağırmanın sonucu olsun, bu değer O ve boş bir argüman listesi olsun.
    2. Val ilkel bir değerse, val değerini döndürün.

Bu, nesnenin valueOfişlevinin çağrıldığını ve denklemde bu işlevden dönüş değerinin kullanıldığını söylemenin dolambaçlı bir yoludur . Değiştirmek olsaydı valueOfişlevini Ameliyat sonuçlarını değiştirebilir:

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

10AS fivein valueOffonksiyonu değişmedi, bu paketlenmiş temel değerini verir 5, böylece five * 5değerlendirir için 5 * 5hangi sonuç25

11fivenesnenin wtfözelliği değerlendirilir yine o üzerinde atandı tarihten itibaren değişmeden olduğu halde 05.

12 'potato'

13Postfix Artırma Operatör üzerinde denir fivesayısal değeri aldığı, ( 5ekler, değer yüzden iade edilebileceği mağazalar, biz nasıl daha erken örtülü) 1değere (için 6, değerini atar) five(ve getiri saklanan değer 5)

14 daha önce olduğu gibi, döndürülen değer artırılmadan önceki değerdir

15wtftemel bir değere (malı 6değişkeni de depolanmış) fiveerişilir. ES5 belirtiminin Bölüm 15.7.5'i bu davranışı tanımlar. Sayılar özellikleri alır Number.prototype.

16 Number.prototypebir wtfmülkü yok, bu yüzden undefinediade ediliyor

17 five.wtfdeğerine atandı 'potato?'. Atama, ES5 spesifikasyonunun 11.13.1'inde tanımlanmıştır . Temel olarak atanan değer döndürülür ancak saklanmaz.

18 'potato?' atama operatörü tarafından iade edildi

19yine five, değeri olan 6ve yine Number.prototypebir wtfözelliği olmayan

20 undefined yukarıda açıklandığı gibi

21 five erişildi

22 6 açıklandığı gibi iade edilir 13


17

Oldukça basit.

function dis () { return this; }

Bu thisbağlamı döndürür . Yani, yaparsanız call(5)sayıyı bir nesne olarak geçiriyorsunuz.

callArgümanları sağlamaz fonksiyonu Verdiğiniz İlk argüman bağlamı olduğunu this. O bağlama var üzerinde istediğiniz Genellikle eğer, bunu vermek {}böylece dis.call({})hangi vasıta, thisbir boş işlevinde this. Ancak, geçerseniz 5bir nesneye dönüştürülecek gibi görünüyor. Bkz .

Yani dönüş object

Bunu yaptığınızda five * 5, JavaScript nesneyi fiveilkel tür olarak görür , bu yüzden eşdeğerdir 5 * 5. İlginçtir, '5' * 5hala eşittir 25, bu nedenle JavaScript açıkça başlık altında döküm yapıyor. Bu satırın altında yatan fivetürde hiçbir değişiklik yapılmaz

Ancak bunu yaptığınızda ++, nesneyi ilkel numbertüre dönüştürür ve böylece .wtfözelliği kaldırır . Çünkü altta yatan türü etkiliyorsunuz


Dönüş sayısı .
GSerg

++dönüştürür ve geri atar. ++eşittir variable = variable + 1. Yani sen gevşekwtf
Rajesh

*Vs ++hiç beni karıştırmaz. Hiçbir argüman ve geri dönüş beklemeyen bir işleve thissahip olmak, bu işlevin yine de bir argümanı kabul etmesi ve tam olarak olmayan, ancak benim için hiçbir anlamı olmayan bir argüman gibi bir şey döndürmesi.
Nathan Long

Ne yaptığını göstermek için @NathanLong güncelledi dis.call().
Callum Linington

5 ++ C dilinde olduğu gibi davranır, bu yüzden şaşırtıcı değildir. thisbasitçe bir nesneye işaretçi olduğundan ilkel türler dolaylı olarak dönüştürülür. Neden argümanı olmayan bir fonksiyonun en azından bir bağlamı olamaz? Bağlamı ayarlamak için callveya ilk argümanı bindkullanılır. Ayrıca, fonksiyonlar kapanışlardır, yani sadece daha fazlasına erişimleri vardırarguments
Rivenfall

10

İlkel değerlerin özelliği olamaz. Ancak, ilkel değerdeki bir özelliğe erişmeye çalıştığınızda, şeffaf bir şekilde geçici bir Number nesnesine dönüştürür.

Yani:

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

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

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

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

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

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

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

8

Bildirme işlevi dis. İşlev bağlamını döndürür

function dis() { return this }
undefined

disBağlam ile arayın 5. Katı modda ( MDN) bağlam olarak iletildiğinde temel değerler kutu içine alınır ) . Yani fiveşimdi nesne (kutulu sayı) 'dir.

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

wtfÜzerinde mülk bildirfive değişkeni

five.wtf = 'potato'
"potato"

Değeri five.wtf

five.wtf
"potato"

fivekutulu 5, dolayısıyla sayı ve nesne aynı zamanda (5 * 5 = 25). Değişmezfive .

five * 5
25

Değeri five.wtf

five.wtf
"potato"

fiveBurada kutudan çıkarılıyor . fiveşimdi sadece ilkel number. Bu yazdırır 5ve ardından eklemek 1içinfive .

five++
5

five ilkel sayıdır 6şimdi , içinde hiçbir özellik yoktur.

five.wtf
undefined

ilkellerin özellikleri olamaz, bunu ayarlayamazsınız

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

bunu okuyamazsın çünkü ayarlanmadı

five.wtf
undefined

fiveolduğu 6için sonrası üzerinde artan bir

five
6

7

Öncelikle bu, nodejs konsolundan çalıştırılıyor gibi görünüyor.

1.

    function dis() { return this }

dis () işlevini oluşturur, ancak bir olarak ayarlanmadığından , tanımlanmış olsa bile, çıktı vardöndürülecek bir değer yoktu . Bir arada,undefineddis()this , işlev yürütülmediği için döndürülmedi.

2.

    five = dis.call(5)

Bu döndürüyor JavaScript en Numbernesne sadece fonksiyonu ayarlanmış çünkü dis()'ınthis ilkel beşe değer.

3.

   five.wtf = 'potato'

İlk döner "potato"sadece özelliğini ayarlayın çünkü wtfiçinde fivehiç 'potato'. Javascript, ayarladığınız bir değişkenin değerini döndürerek birden çok değişkeni zincirlemeyi ve bunları şu şekilde aynı değere ayarlamanızı kolaylaştırır:a = b = c = 2 .

4.

    five * 5

Bu döner 25sadece ilkel sayı çarpılır çünkü 5hiç five. Değeri nesnenin fivedeğeri ile belirlenmiştir Number.

5.

    five.wtf

Daha önce bu satırı atladım çünkü burada tekrarlayacağım. Sadece wtfyukarıda ayarladığınız özelliğin değerini döndürür .

6.

    five++

@Callum'un dediği gibi, ++türü numbernesneden aynı değere dönüştürür Number {[[PrimitiveValue]]: 5}}.

Şimdi fivea olduğu için number, böyle bir şey yapana kadar özellikleri artık ayarlayamazsınız:

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

veya

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

Ayrıca ikinci yolun, daha Numberönce oluşturulan nesne yerine genel bir nesne tanımladığı için ilk yöntemi kullanmaktan farklı davranışlara sahip olacağını unutmayın .

Umarım bu yardımcı olur, javascript bir şeyleri varsaymayı sever, böylece Numbernesneden bir ilkeye geçiş yaparken kafa karıştırıcı olabilir number. Sen kullanarak ne tür bir şey kontrol edebilirsiniz typeof, döndüğü başlatmak sonra, anahtar kelime typeof beş yazma 'object', ve bunu sonra five++döndürür 'number'.

@deceze, Number nesnesi ile ilkel sayı arasındaki farkı çok iyi açıklar.


6

JavaScript kapsamları Yürütme Bağlamlarından oluşur. Her Yürütme Bağlamında bir Lexical Environment (dış / global kapsamda değerler), Değişken Ortam (yerel olarak kapsamda değerler) ve bu bağlama bulunur .

Bu bağlanma Yürütme Bağlamında çok önemli bir parçasıdır. Kullanmak call, bu bağlayıcıyı değiştirmenin bir yoludur ve bunu yapmak, bağlayıcıyı doldurmak için otomatik olarak bir nesne oluşturur.

Function.prototype.call () (MDN'den)

Sözdizimi
fun.call(thisArg[, arg1[, arg2[, ...]]])

thisArg
Eğlence çağrısı için sağlanan bunun değeri. Bunun yöntem tarafından görülen gerçek değer olmayabileceğini unutmayın: yöntem katı olmayan mod kodunda bir işlevse, null ve undefined genel nesneyle değiştirilir ve ilkel değerler nesnelere dönüştürülür . (benimkini vurgula)

5'in dönüştürüldüğünün new Number(5)belli olması durumunda, geri kalanı oldukça açık olmalıdır. Diğer örneklerin ilkel değerler oldukları sürece de işe yarayacağını unutmayın.

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

resim açıklamasını buraya girin


4

Birkaç kavram ne olduğunu açıklıyor

5 bir sayıdır, ilkel bir değerdir

Number {[[PrimitiveValue]]: 5} bir Number örneğidir (buna nesne sarmalayıcısı diyelim)

İlkel bir değerdeki bir özelliğe / yönteme her eriştiğinizde, JS altyapısı uygun türde bir nesne sarıcısı oluşturur ( Numberfor 5, Stringfor 'str've Booleanfor true) ve bu nesne sarıcısındaki özellik erişim / yöntem çağrısını çözer. true.toString()Örneğin bunu yaptığınızda olan şey budur .

Nesneler üzerinde işlem gerçekleştirirken, bu işlemleri çözmek için ( toStringveya kullanarak valueOf) ilkel değerlere dönüştürülürler - örneğin,

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

stringbir dize birleştirme yapacak mystrve obj.toString()ve numbereklenmesini yapacak 3ve obj.valueOf().

Şimdi hepsini bir araya getirmek için

five = dis.call(5)

dis.call(5)davranacağını tıpkı (5).dis()eğer 5gerçekten yöntemi vardı dis. Yöntem çağrısını çözümlemek için nesne sarmalayıcısı oluşturulur ve yöntem çağrısı çözümlenir. Bu noktada beş, ilkel değerin 5 etrafındaki bir nesne sarmalayıcıya işaret eder.

five.wtf = 'potato'

Bir nesne üzerinde bir özellik ayarlama, burada fantezi bir şey yok.

five * 5

Bu aslında five.valueOf() * 5nesne paketleyicisinden ilkel değeri elde etmektir. fivehala ilk nesneyi gösterir.

five++

Bu aslında five = five.valueOf() + 1. Bu satırdan önce fivenesne sarmalayıcıyı 5 değerinin çevresinde tutarken, bu noktadan sonrafive tutarken, ilkel değeri tutar6 .

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

fiveartık bir nesne değil. Bu satırların her biri, .wtfmülk erişimini çözmek için yeni bir Number örneği oluşturur . Örnekler bağımsızdır, bu nedenle bir mülkün ayarlanması diğerinde görünmez. Kod tamamen buna eşdeğerdir:

(new Number(6)).wtf;
(new Number(6)).wtf = 'potato?';
(new Number(6)).wtf;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.