Değişken atama JavaScript'te nasıl çalışır?


99

Yani geçen gün JavaScript'te toplu atamanın tam olarak nasıl çalıştığını görmek için oynuyordum.

İlk önce bu örneği konsolda denedim:

a = b = {};
a.foo = 'bar';
console.log(b.foo);

Sonuç, bir uyarıda görüntülenen "bar" idi. Bu yeterince adil ave bgerçekten aynı nesnenin takma adlarıdır. Sonra bu örneği nasıl basitleştirebilirim diye düşündüm.

a = b = 'foo';
a = 'bar';
console.log(b);

Bu hemen hemen aynı şey, değil mi? Peki bu sefer, ilk örneğin davranışından beklediğim gibi foodeğil bar.

Bu neden oluyor?

NB Bu örnek, aşağıdaki kodla daha da basitleştirilebilir:

a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);

a = 'foo';
b = a;
a = 'bar';
console.log(b);

(JavaScript'in dizeler ve tamsayılar gibi ilkelleri karmalara farklı şekilde davrandığından şüpheleniyorum. Karalamalar bir işaretçi döndürürken "çekirdek" ilkeller kendilerinin bir kopyasını döndürür)


Burada atamanın açıklanması: syntaxsuccess.com/viewarticle/…
TGH

Yanıtlar:


115

İlk örnekte, mevcut bir nesnenin bir özelliğini ayarlıyorsunuz. İkinci örnekte, yepyeni bir nesne atıyorsunuz.

a = b = {};

ave bşimdi aynı nesneye işaret ediyor. Yani yaptığınızda:

a.foo = 'bar';

Bu ayarlar b.fooberi kuyu olarak ave bnokta aynı nesneye.

Ancak!

Bunun yerine bunu yaparsanız:

a = 'bar';

bunun aşimdi farklı bir nesneye işaret ettiğini söylüyorsunuz . Bunun daha aönce işaret edilen şey üzerinde hiçbir etkisi yoktur .

JavaScript'te bir değişken atamak ve bir özellik atamak 2 farklı işlemdir. Değişkenleri nesnelere işaretçiler olarak düşünmek en iyisidir ve doğrudan bir değişkene atadığınızda, herhangi bir nesneyi değiştirmezsiniz, yalnızca değişkeninizi farklı bir nesneye yeniden işaretlersiniz.

Ancak, gibi bir özellik atamak a.foo, aişaret eden nesneyi değiştirir . Bu, elbette, bu nesneye işaret eden diğer tüm referansları da değiştirir çünkü hepsi aynı nesneyi işaret eder.


3
"Şu anda farklı bir nesneyi işaret ettiğini söylüyorsunuz." Hayır, nesne kelimesini kullanmayın. Dize, JavaScript'te bir nesne değildir.
Nosredna

10
Dizeler teknik olarak javascript türü "Nesne" değildir, ancak OO anlamında nesneler olarak düşünülebilir.
Alex Wayne

2
@Squeegy: dizeler ilkeldir, nesne değildir: dizelere rastgele özellikler atayamazsınız! Java'da autoboxing denen şey yüzünden sadece nesne gibi davranıyorlar
Christoph

11
Ancak dizelerin yöntemleri ve özellikleri vardır ve String'in prototipi kesinlikle değiştirilebilir. Kesinlikle nesne gibi davranırlar.
Cameron Martin

9
@ddlshack: Christoph'un açıkladığı gibi, bu otomatik kutudan kaynaklanıyor. Eğer aracılığıyla bir dizeyi tanımlamak için olsaydı var foo = new String('foo');, o zaman bu bir dize nesne olacaktır (ve typeofbu teyit edecektir). Ancak bunu bir dizge ile bildirirseniz, o zaman bunlar ilkel dizelerdir. Bakınız: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
Lèse majesté

26

Sorunuz zaten Squeegy tarafından tatmin edici bir şekilde yanıtlanmıştır - nesneler ve ilkellerle ilgisi yoktur, ancak aynı referans nesnede değişkenlerin yeniden atanması ve özelliklerin ayarlanması ile ilgisi vardır.

Cevaplarda ve yorumlarda JavaScript türleri hakkında çok fazla kafa karışıklığı var gibi görünüyor, bu yüzden işte JavaScript'in tür sistemine küçük bir giriş:

JavaScript'te, temelde farklı iki tür değer vardır: ilkel öğeler ve nesneler (ve 'karma' diye bir şey yoktur).

Dizeler, sayılar ve mantıksal yanı sıra nullve undefinedolan ilkel, nesneler özelliklere sahip olabilir herşeysin. Diziler ve işlevler bile normal nesnelerdir ve bu nedenle keyfi özellikleri tutabilirler. Sadece dahili [[Class]] özelliklerinde farklılık gösterirler (fonksiyonların ayrıca [[Call]] ve [[Construct]] adlı bir özelliği vardır, ancak hey, bu ayrıntılar).

İlkel değerlerin nesneler gibi davranmasının nedeni otomatik kutucuktur, ancak ilkellerin kendileri herhangi bir özelliği tutamaz.

İşte bir örnek:

var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);

Bu çıktı verecektir undefined: aözelliği atarken bir nesneye yükseltilen ilkel bir değeri tutar foo. Ancak bu yeni nesne hemen atılır, dolayısıyla değeri fookaybolur.

Şöyle düşünün:

var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created

Mozilla Foundation'ın "JavaScript'e yeniden giriş (JS eğitimi)" sayfası JavaScript nesnelerini "ad-değer çiftlerinin basit koleksiyonları olarak tanımlar. Bu nedenle, bunlar ..." ile benzerlik gösterir ve ardından bir sözlükler, karma , karma tablolar ve çeşitli programlama dillerinden karma haritalar. Aynı sayfa, karma tablo aramaları olarak nesne özelliği referanslarını açıklar. Yani nesneler bir 'hash' tablosu gibi her şeydir. Bu, diğer yararlı bilgileri geçersiz kılmaz, ancak Chris Lloyd'un orijinal karakterizasyonu yanlış değildi.
C Perkins

2

"Karma" olarak bahsettiğiniz şeyin aslında bir Nesne için kısayol sözdizimi olması dışında, az çok haklısınız.

İlk örnekte, a ve b'nin her ikisi de aynı nesneyi ifade eder. İkinci örnekte, değiştirmek bir başka bir şeye başvurmak için.


Öyleyse neden Object için çifte standart?
Chris Lloyd

Çifte standart değil. İlk örnekte, a ve b hala aynı nesneyi ifade ediyor, sadece o nesnenin bir özelliğini değiştiriyorsunuz. İkinci örnekte, a'yı farklı bir nesneye işaret ediyorsunuz.
Kevin

1
Hayır, aradaki fark, ikinci durumda, bir nesne ile değil, bir dizeyle uğraşmanızdır.
Nosredna

1
Açık olmak gerekirse: Bunun dizelerin kendilerinin bir kopyasını döndürmesiyle ilgisi yoktur. İki kod parçacığının farklı olmasının nedeni Kevin'in ikinci paragrafındadır (Squeegy'nin cevabında daha ayrıntılı olarak açıklanmıştır).
Chuck

Değişkende bir dizenin veya nesnenin olması önemli değil. Yeni, farklı bir değer atarsınız ve ardından değişken bu yeni, farklı değeri içerir.
STH

2

İşte cevabın benim versiyonum:

obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"

// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"

// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory

0

Siz a'yı yeni bir string nesnesine işaret edecek şekilde ayarlıyorsunuz, b ise eski string nesnesini işaret etmeye devam ediyor.


0

İlk durumda, değişkenin içerdiği nesnenin bazı özelliklerini değiştirirsiniz, ikinci durumda değişkene yeni bir değer atarsınız. Bunlar temelde farklı şeylerdir. Değişkenler ave bilk atama ile bir şekilde sihirli bir şekilde bağlantılı değiller, sadece aynı nesneyi içeriyorlar. bDeğişkene yeni bir değer atayana kadar ikinci örnekte de durum budur .


0

Fark, basit tipler ve nesneler arasındadır.

Nesne olan her şey (bir dizi veya işlev gibi) başvuruya göre aktarılır.

Basit tipteki her şey (bir dizi veya sayı gibi) kopyalanır.

Her zaman elimde bir copyArray işlevi vardır, bu nedenle aynı dizi için bir grup takma ad oluşturmadığımdan emin olabilirim.


Fark pek çok senaryoda göze çarpmaz, ancak Javascript aslında referansla geçmez veya atamaz. Referans değerlerini kopyalar.
Juan Pablo Califano

Bu adamlar bunu açıklamak için zaten iyi bir çalışma yaptılar, bu yüzden sadece bağlantıyı yapıştıracağım: stackoverflow.com/questions/40480/is-java-pass-by-reference (Java'ya atıfta bulunuyorum, ancak geçiş ve atama için anlambilim değerler / referanslar Javascript ile aynıdır)
Juan Pablo Califano

1
Aslında bu cevap yanlıştır, JavaScript'te her şey değere göre aktarılır. MDN'den, "Bir işlev çağrısının parametreleri, işlevin bağımsız değişkenleridir. Bağımsız değişkenler, işlevlere değere göre iletilir. İşlev bir bağımsız değişkenin değerini değiştirirse, bu değişiklik genel olarak veya çağıran işlevde yansıtılmaz. Ancak, nesne başvuruları değerler de ve bunlar özeldir: İşlev, belirtilen nesnenin özelliklerini değiştirirse, bu değişiklik işlevin dışında görünür. " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bittersweetryan

İlkel öğeler değişmez nesneler gibi davranır ( tam olarak katı moddaki gibi). Bu cevap doğru değil.
Ry-
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.