JavaScript bir referans by-pass ya da by-value dili mi?


1404

İlkel türler (sayı, dize vb.) Değere göre iletilir, ancak nesneler bilinmez, çünkü ikisi de değere göre iletilebilir (bir nesneyi tutan bir değişkenin aslında nesneye bir başvuru olduğunu düşünürsek) ) ve referansla iletildi (nesneye değişkenin nesnenin kendisini tuttuğunu düşündüğümüzde).

Sonunda gerçekten önemli olmasa da, sözleşmeleri geçen argümanları sunmanın doğru yolunun ne olduğunu bilmek istiyorum. JavaScript spesifikasyonundan, bununla ilgili anlambilimin ne olması gerektiğini tanımlayan bir alıntı var mı?


2
Sanırım yanlışlıkla by-value ve by-by-pass tanımlarınızı çevirdiniz ... "by-by-value (bir nesneyi tutan bir değişkenin aslında nesneye bir referans olduğunu düşünürsek) ve geçti -by-reference (nesneye değişkenin nesnenin kendisini tuttuğunu düşündüğümüzde) "
Niko Bellic

5
Evet. Sözdizimine bakılmaksızın, herhangi bir programlama dilinde herhangi bir işlev çağrısında, by-pass referansı, iletilen değişkenle ilişkili verilerin işleve iletildiğinde kopyalanmadığı ve dolayısıyla işlev tarafından iletilen değişkende yapılan herhangi bir değişikliğin korunacağı anlamına gelir. fonksiyon çağrısı sona erdikten sonra programda. Değere göre geçme, değişkenle ilişkilendirilen verilerin işleve aktarıldığında gerçekten kopyalandığı ve bu işlev tarafından bu değişkene yapılan herhangi bir değişikliğin, işlev döndüğünde işlev işlevinin kapsamının dışına çıktığında kaybolacağı anlamına gelir.
John Sonderson

5
Bu eski soru biraz toksiktir, çünkü çokça oylanan cevabı yanlıştır. JavaScript kesinlikle tam bir değerdir .
Sivri

6
@DanailNachev Terminoloji ne yazık ki kafa karıştırıcı. Mesele şu ki, "değere göre geç" ve "referansla geç" birçok daha modern programlama dili özelliğinden önce gelen terimlerdir. "Value" ve "reference" kelimeleri , işlev çağrısı ifadesinde göründüğü şekliyle parametreye özel olarak işaret eder. JavaScript , işlevi çağırmadan önce her zaman bir işlev çağrısı parametre listesindeki her bir ifadeyi değerlendirir , böylece parametreler her zaman değerdir. Kafa karıştırıcı kısım, nesnelere yapılan referansların yaygın JavaScript değerleri olmasıdır. Ancak bu onu "referansla geç" dili yapmaz.
Sivri

2
@DanailNachev "referans olarak geçmektedir" Özellikle varsa demektir var x=3, y=x; f(x); alert(y === x);o zaman fonksiyon f()uyarı raporu yapabilir falseve true. JavaScript'te bu mümkün değildir, bu nedenle referansla değil. Değiştirilebilir nesnelere referanslar iletmek mümkündür, ancak "referans ile geç" ifadesinin anlamı bu değildir. Dediğim gibi, terminolojinin çok kafa karıştırıcı olması utanç verici.
Sivri

Yanıtlar:


1598

JavaScript'te ilginç. Bu örneği düşünün:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);
console.log(obj2.item);

Bu çıktıyı üretir:

10
changed
unchanged
  • Eğer obj1o zaman değişen, hiç bir referans değildi obj1.itemüzerinde hiçbir etkisi olurdu obj1işlev dışından.
  • Eğer argüman uygun bir referans olsaydı, her şey değişecekti. numolurdu 100ve obj2.itemokurdu "changed".

Bunun yerine, durum, iletilen öğenin değere göre iletilmesidir. Ancak değere göre iletilen öğenin kendisi bir referanstır. Teknik olarak buna paylaşma çağrısı denir .

Eğer parametreyi kendisi (olduğu gibi değiştirirseniz Pratik açıdan bakıldığında, bu araçlar o numve obj2) o parametrenin içine beslenmiş öğeyi etkilemez. Ancak , parametrenin DAHİLİ'ni değiştirirseniz , bu da geri yayılır (olduğu gibi obj1).


32
Bu, C # ile tam olarak aynı (veya en azından anlamsal olarak). Nesnenin iki türü vardır: Değer (ilkel türler) ve Başvuru.
Peter Lee

53
Bence bu Java da kullanılır: referans değer.
Jpnh

295
asıl sebep, changeStuff, num, obj1 ve obj2 içinde referanslar olmasıdır. Eğer değiştirdiğinizde itemobj1 tarafından başvurulan nesnenin özelliği, başlangıçta "değişmeden" olarak ayarlandı item özelliğinin değerini değişiyor. Obj2'ye {item: "changing"} değeri atadığınızda, başvuruyu yeni bir nesneye değiştirirsiniz (işlevden çıkıldığında hemen kapsam dışına çıkar). Fonksiyonu adlandırırsanız ne olduğu daha belirgin hale gelir, numf, obj1f ve obj2f gibi şeyleri değiştirir. Sonra, parametrelerin dış değişken isimlerini gizlediğini görüyorsunuz.
jinglesthula

13
@BartoNaz Pek değil. İstediğiniz referansı değere göre iletmek yerine referansı iletmektir. Ancak JavaScript her zaman referansı değere geçirir, tıpkı diğer her şeyi değere göre geçirir gibi. (Karşılaştırma için, C #, JavaScript ve Java'ya benzer, değere göre pass-referansı davranışına sahiptir, ancak refanahtar kelimeyle referansla pass-referansı belirtmenizi sağlar .) Genellikle, işlevin yeni nesneyi döndürmesini ve işlevi çağırdığınız noktadaki ödev. Örneğin, foo = GetNewFoo();yerineGetNewFoo(foo);
Tim Goodman

55
Her ne kadar bu cevap en popüler olsa da biraz kafa karıştırıcı olabilir, çünkü “Eğer değere göre saf geçerse” yazıyor. JavaScript olan saf geçiş değer-ile-. Ancak iletilen değer bir referanstır. Bu, parametre geçişi ile sınırlı değildir. Değişkeni basitçe kopyalayabilir ve örneğinizle var obj1 = { item: 'unchanged' }; var obj2 = obj1; obj2.item = 'changed';aynı etkiyi gözlemleyebilirsiniz. Bu yüzden ben şahsen Tim Goodman'ın cevabına atıfta bulunuyorum
chiccodoro

476

Her zaman değere göre geçer, ancak nesneler için değişkenin değeri bir referanstır. Bu nedenle, bir nesneyi iletip üyelerini değiştirdiğinizde , bu değişiklikler işlevin dışında kalır. Bu, referans ile geçiş gibi görünmesini sağlar . Ancak, nesne değişkeninin değerini gerçekten değiştirirseniz, değişimin devam etmediğini göreceksiniz, bunun gerçekten değerden geçtiğini kanıtlarsınız.

Misal:

function changeObject(x) {
  x = { member: "bar" };
  console.log("in changeObject: " + x.member);
}

function changeMember(x) {
  x.member = "bar";
  console.log("in changeMember: " + x.member);
}

var x = { member: "foo" };

console.log("before changeObject: " + x.member);
changeObject(x);
console.log("after changeObject: " + x.member); /* change did not persist */

console.log("before changeMember: " + x.member);
changeMember(x);
console.log("after changeMember: " + x.member); /* change persists */

Çıktı:

before changeObject: foo
in changeObject: bar
after changeObject: foo

before changeMember: foo
in changeMember: bar
after changeMember: bar

14
@ daylight: Aslında yanılıyorsunuz; const ref tarafından değiştirilmişse changeObject yapmaya çalışmak sadece başarısız olmaktan ziyade bir hataya neden olur. C ++ 'da bir const başvurusuna yeni bir değer atamayı deneyin ve derleyici bunu reddeder. Kullanıcı terimleriyle, bu, değere göre geçiş ve sabit referansla geçiş arasındaki farktır.
deworde

5
@ daylight: Sürekli ref değil. İçinde changeObject, xyeni nesneye bir başvuru içerecek şekilde değiştirdim . Söylediğim şeye x = {member:"bar"};eşdeğerdir x = new Object(); x.member = "bar";bu arada C # için de geçerlidir.
Tim Goodman

2
@daylight: C # için, kullandığınız takdirde, fonksiyonun dışından görebilirsiniz refanahtar kelimeyi (yerine değeriyle referansı geçme varsayılan) referans ile başvuru geçebilir ve daha sonra bir noktaya kadar değişiklik new Object() olacak inat .
Tim Goodman

11
@adityamenon "Neden" yanıtı vermek zor, ama Java ve C # tasarımcılarının da benzer bir seçim yaptığını; bu sadece JavaScript tuhaflığı değil. Gerçekten, çok tutarlı bir şekilde değere göre, insanlar için kafa karıştırıcı olan şey, bir değerin referans olabileceğidir. C ++ içinde (değere göre) bir işaretçi geçirip üyeleri ayarlamak için kaydı silme işleminden çok farklı değildir. Kimse bu değişimin devam edeceğine şaşıracaktı. Ancak bu diller işaretçiyi soyutladığı ve sizin için sessizce silindiği için insanlar karışır.
Tim Goodman

41
Başka bir deyişle, buradaki kafa karıştırıcı şey, değere göre / referansa göre değil. Her şey değer, tam durak. Kafa karıştırıcı olan şey, bir nesneyi geçirememeniz ya da bir nesneyi bir değişkende saklayamamanızdır. Bunu her düşündüğünüzde , aslında o nesneye bir referans iletiyor veya saklıyorsunuz. Ancak üyelerine erişmeye gittiğinizde, değişkeninizin gerçek nesneyi elinde tuttuğu kurguyu sürdüren sessiz bir kayıt silme gerçekleşir.
Tim Goodman

150

Değişken nesneyi "tutmaz"; bir referansı vardır. Bu başvuruyu başka bir değişkene atayabilir ve şimdi her ikisi de aynı nesneye başvuruda bulunabilir. Her zaman değere göre geçer (bu değer bir referans olsa bile ...).

Parametre olarak geçirilen bir değişken tarafından tutulan değeri değiştirmenin bir yolu yoktur; bu, JavaScript'in referansla geçmesini desteklemesi durumunda mümkün olabilir.


2
Bu beni biraz karıştırıyor. Referans referansı geçmiyor mu?

8
Yazar, bir referansı geçerek bir referans değeri geçtiğiniz anlamına gelir (bunu düşünmenin başka bir yolu da bellek adresinin değerini geçmektir). Bu nedenle, nesneyi yeniden bildirirseniz, orijinal değişmez, çünkü farklı bir bellek konumunda yeni bir nesne oluşturuyorsunuz. Bir özelliği değiştirirseniz, orijinal nesne, onu orijinal bellek konumunda değiştirdiğiniz için değişir (yeniden atanmamış).
Huy-Anh Hoang

113

İki sentim ... Anlama şeklim bu. (Yanlışsam beni düzeltmekten çekinmeyin)

Değere / referansa göre geçiş hakkında bildiğiniz her şeyi atma zamanı.

Çünkü JavaScript'te, değere göre mi, referansla mı, yoksa neyle mi aktarıldığı önemli değil. Önemli olan mutasyona karşı bir fonksiyona aktarılan parametrelerin atanmasıdır.

Tamam, ne demek istediğimi açıklamak için elimden geleni yapayım. Diyelim ki birkaç nesneniz var.

var object1 = {};
var object2 = {};

Yaptığımız şey "atama" ... "object1" ve "object2" değişkenlerine 2 ayrı boş nesne atadık.

Şimdi diyelim ki object1'i daha çok seviyoruz ... Yani, yeni bir değişken "atarız".

var favoriteObject = object1;

Daha sonra, her ne sebeple olursa olsun, nesne 2'yi daha iyi sevmeye karar veriyoruz. Yani, sadece küçük bir yeniden görevlendirme yapıyoruz.

favoriteObject = object2;

Object1 veya object2'ye hiçbir şey olmadı. Hiçbir veriyi değiştirmedik. Tek yaptığımız, en sevdiğimiz nesnenin ne olduğunu yeniden atamaktı. Object2 ve favouriteObject öğelerinin her ikisinin de aynı nesneye atandığını bilmek önemlidir. Bu nesneyi bu değişkenlerden herhangi biriyle değiştirebiliriz.

object2.name = 'Fred';
console.log(favoriteObject.name) // Logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // Logs Joe

Tamam, şimdi dizeler gibi ilkellere bakalım mesela

var string1 = 'Hello world';
var string2 = 'Goodbye world';

Yine, bir favori seçiyoruz.

var favoriteString = string1;

FavouriteString ve string1 değişkenlerimiz 'Hello world' a atanmıştır. Şimdi, ya favori favorimizi değiştirmek istiyorsak ??? Ne olacak???

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'

Ah oh .... Ne oldu. FavoriteString'i değiştirerek string1'i değiştiremedik ... Neden ?? Çünkü dize neslimizi değiştirmedik . Tüm yaptığımız yeni bir dizeye favoriteString değişkeni "RE ASSIGN" idi . Bu temelde string1 ile bağlantısını kesti. Önceki örnekte, nesnemizi yeniden adlandırdığımızda hiçbir şey atamadık. (Şey, değişkenin kendisine değil , ... ancak, name özelliğini yeni bir dizeye atadık.) Bunun yerine, sadece 2 değişken ile temeldeki nesneler arasındaki bağlantıları tutan nesneyi değiştirdik. (Biz değiştirmek veya istemişti bile mutasyona dize nesne kendisi, sahip olamadık, çünkü dizeler aslında JavaScript'te değiştirilemez.)

Şimdi, işlevlere ve parametreleri iletmeye devam ... Bir işlevi çağırdığınızda ve bir parametre ilettiğinizde, aslında yaptığınız şey yeni bir değişkene bir "atama" dır ve sadece kullanarak atadığınız gibi çalışır eşittir (=) işareti.

Bu örnekleri ele alalım.

var myString = 'hello';

// Assign to a new variable (just like when you pass to a function)
var param1 = myString;
param1 = 'world'; // Re assignment

console.log(myString); // Logs 'hello'
console.log(param1);   // Logs 'world'

Şimdi, aynı şey, ama bir fonksiyonu ile

function myFunc(param1) {
    param1 = 'world';

    console.log(param1);   // Logs 'world'
}

var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString);

console.log(myString); // logs 'hello'

Tamam, şimdi bunun yerine nesneleri kullanarak birkaç örnek verelim ... önce işlev olmadan.

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;

// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl

console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'

// Now, let's reassign the variable
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object has no influence on the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

Şimdi, aynı şey, ama bir işlev çağrısı ile

function myFunc(otherObj) {

    // Let's mutate our object
    otherObj.firstName = 'Sue';
    console.log(otherObj.firstName); // Logs 'Sue'

    // Now let's re-assign
    otherObj = {
        firstName: 'Jack',
        lastName: 'Frost'
    };
    console.log(otherObj.firstName); // Logs 'Jack'

    // Again, otherObj and myObject are assigned to 2 very different objects
    // And mutating one object doesn't magically mutate the other
}

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);

console.log(myObject.firstName); // Logs 'Sue', just like before

Tamam, bu yazının tamamını okuduysanız, belki de artık işlev çağrılarının JavaScript'te nasıl çalıştığını daha iyi anlayabilirsiniz. Bir şeyin referansla mı yoksa değerle mi geçtiği önemli değil ... Önemli olan ödevle mutasyon arasında.

Bir değişkeni bir işleve her ilettiğinizde, tıpkı eşittir (=) işaretini kullandıysanız, parametre değişkeninin adı ne olursa olsun "Atarsınız".

Her zaman eşittir işaretinin (=) atama anlamına geldiğini unutmayın. JavaScript'te bir işleve parametre aktarmanın atama anlamına da geldiğini unutmayın. Bunlar aynıdır ve 2 değişken aynı şekilde bağlanmıştır (aynı nesneye atandıklarını saymadıkça, öyle değildirler).

"Bir değişkeni değiştirmenin" farklı bir değişkeni etkilediği tek zaman, temeldeki nesnenin mutasyona uğramasıdır (bu durumda değişkeni değiştirmediniz, ancak nesnenin kendisidir).

Nesneler ve ilkel öğeler arasında bir ayrım yapmanın bir anlamı yoktur, çünkü bir fonksiyonunuz yokmuş gibi çalışır ve sadece yeni bir değişkene atamak için eşittir işaretini kullanır.

Tek gotcha, işleve ilettiğiniz değişkenin adı, işlev parametresinin adıyla aynı olduğundadır. Bu olduğunda, işlevin içindeki parametreye, işleve özel tamamen yeni bir değişkenmiş gibi davranmanız gerekir (çünkü)

function myFunc(myString) {
    // myString is private and does not affect the outer variable
    myString = 'hello';
}

var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';

myFunc(myString);
console.log(myString); // Logs 'test'

2
Tüm C programcıları için char * 'ı düşünün. foo(char *a){a="hello";} hiçbir şey yapmaz, ancak bunu yaparsanız bir dize (char dizisi) değerine göre iletilen bir bellek konumu olduğu için foo(char *a){a[0]='h';a[1]='i';a[2]=0;}dışarıda değiştirilir a. C'deki değere göre yapıların (js nesnelerine benzer) geçirilmesine izin verilir, ancak önerilmez. JavaScript basitçe bu en iyi uygulamaları uygular ve gereksiz ve genellikle istenmeyen hamleyi gizler ... ve kesinlikle daha kolay okumayı sağlar.
technosaurus

2
Bu doğrudur - değere göre geçiş ve referans yoluyla geçiş terimlerinin programlama dili tasarımında anlamları vardır ve bu anlamların nesne mutasyonu ile hiçbir ilgisi yoktur. Her şey işlev parametrelerinin nasıl çalıştığıyla ilgilidir.
Sivri

2
Şimdi obj1 = obj2'nin hem obj1 hem de obj2'nin şimdi aynı referans konumuna işaret ettiğini ve artık obj2'nin iç kısımlarını değiştirirsem obj1'e başvurmanın aynı iç kısımları açığa çıkaracağı anlamına geldiğini anlıyorum. Bir nesneyi source = { "id":"1"}; copy = source /*this is wrong*/; copy.id="2", kaynak yaptığımda hala {"id": "1"} olacak şekilde nasıl kopyalarım ?
Machtyn

1
Karışıklığı umarım azaltmak için geleneksel tanımlarla bir cevap daha yayınladım. Geleneksel "değere göre geçme" ve "referansla geçme" tanımları, otomatik kayıt silme işleminden önceki bellek işaretçileri gününde tanımlanmıştır. Bir nesne değişkeninin değerinin aslında nesne değil, bellek işaretçisi konumu olduğu gayet iyi anlaşılmıştır. Mutasyon karşısında mutasyon tartışması belki de yararlı olsa da, geleneksel terimleri veya tanımlarını atmak gerekli değildir. Mutasyon, atama, by-pass değeri, referans by-pass vs. birbiriyle çelişmemelidir.
C Perkins

"Sayı" da "değişmez" midir?
ebram khalil

72

Aşağıdakileri göz önünde bulundur:

  1. Değişkenler bellekteki değerlere işaretçilerdir .
  2. Bir değişkeni yeniden atamak yalnızca o işaretçiyi yeni bir değere işaret eder.
  3. Bir değişkeni yeniden atamak asla aynı nesneyi işaret eden diğer değişkenleri etkilemez

Bu nedenle, "referans / değerle geç" unutun çünkü "referans / değere göre geç" üzerine takılmayın:

  1. Terimler, temelde gerçek uygulama değil , yalnızca bir dilin davranışını tanımlamak için kullanılır . Bu soyutlamanın bir sonucu olarak, iyi bir açıklama için gerekli olan kritik detaylar kaybolur, bu da kaçınılmaz olarak tek bir terimin gerçek davranışı yeterince tanımlamadığı ve ek bilgilerin sağlanması gereken mevcut duruma yol açar.
  2. Bu kavramlar başlangıçta özellikle javascript'i tanımlamak niyetiyle tanımlanmadı ve bu yüzden onları sadece karışıklığa eklediklerinde kullanmaya mecbur hissetmiyorum.

Sorunuzu cevaplamak için: işaretçiler geçer.


// code
var obj = {
    name: 'Fred',
    num: 1
};

// illustration
               'Fred'
              /
             /
(obj) ---- {}
             \
              \
               1


// code
obj.name = 'George';


// illustration
                 'Fred'


(obj) ---- {} ----- 'George'
             \
              \
               1


// code
obj = {};

// illustration
                 'Fred'


(obj)      {} ----- 'George'
  |          \
  |           \
 { }            1


// code
var obj = {
    text: 'Hello world!'
};

/* function parameters get their own pointer to 
 * the arguments that are passed in, just like any other variable */
someFunc(obj);


// illustration
(caller scope)        (someFunc scope)
           \             /
            \           /
             \         /
              \       /
               \     /
                 { }
                  |
                  |
                  |
            'Hello world'

Bazı son yorumlar:

  • İlkel nesnelerin nesneler olmasa da özel kurallar tarafından uygulandığını düşünmek cazip gelebilir , ancak ilkel öğeler basitçe işaretçi zincirinin sonudur.
  • Son bir örnek olarak, bir diziyi temizlemek için ortak bir girişimin neden beklendiği gibi çalışmadığını düşünün.


var a = [1,2];
var b = a;

a = [];
console.log(b); // [1,2]
// doesn't work because `b` is still pointing at the original array

Ek kredi için takip soruları;) Çöp toplama nasıl çalışır? Bir değişkeni bir milyon {'George', 1}değere çeviririm, ancak bir kerede yalnızca bir tanesini kullanırsam, diğerleri nasıl yönetilir? Başka bir değişkenin değerine bir değişken atadığımda ne olur? Daha sonra bir işaretçiyi mi yoksa sağ operandın işaretini mi gösteriyorum? Does var myExistingVar = {"blah", 42}; var obj = myExistingVar;sonucu objişaret {"blah", 42}veya hiç myExistingVar?
Michael Hoffmann

@MichaelHoffmann Bunlar kendi SO sorularını hak ediyor ve muhtemelen yönetebileceğimden daha iyi cevaplandırılıyor. Bununla birlikte, 1)tarif ettiğiniz gibi bir döngü işlevi için tarayıcı geliştirici araçlarında bir bellek profili çalıştırdım ve döngü işlemi boyunca bellek kullanımında ani artışlar gördüm. Bu, döngünün her yinelemesinde gerçekten yeni özdeş nesnelerin yaratıldığını gösteriyor gibi görünmektedir. Çiviler aniden düştüğünde, çöp toplayıcı bu kullanılmayan nesnelerin bir grubunu temizledi.
geg

1
@MichaelHoffmann 2)İlişkin şey gibi var a = baltta yatan JavaScript motoru kuşkusuz bunları kullanır rağmen, javascript, işaretçileri kullanmak için ve (eğer C olabildiğince) değişken bir işaretçi işaret asla böylece bir mekanizma sağlamaz. Yani ... "doğru operandın zirvesine" var a = bişaret edeceka
geg

Burada 1 numaralı soruyu sordum (özellikle her tarayıcıda uygulama muhtemelen farklı olduğu için) stackoverflow.com/q/42778439/539997 ve hala # 2 sorusunu nasıl yazacağımı düşünmeye çalışıyorum. Herhangi bir yardım takdir.
Michael Hoffmann

1
"Referans / değerle geç" i unutmaya gerek yok ! Bu terimler, tam olarak tarif etmeye çalıştığınız şeyi tanımlayan tarihsel anlamlara sahiptir. Tarihsel terimleri ve tanımları atarsak ve aslında ne anlama geldiğini öğrenmek için çok tembelleşirsek, kuşaklar arasında etkili bir şekilde iletişim kurma yeteneğini kaybederiz. Farklı diller ve sistemler arasındaki farklılıkları tartışmanın iyi bir yolu olmayacaktır. Bunun yerine, yeni programcıların geleneksel terimleri ve neden ve nereden geldiklerini öğrenmeleri ve anlamaları gerekir. Aksi takdirde, toplu olarak bilgi ve anlayışı kaybederiz.
C Perkins

24

Bir fonksiyonun dışındaki bir nesne, dış nesneye bir referans verilerek bir fonksiyona aktarılır.

Bu başvuruyu nesnesini değiştirmek için kullandığınızda, dışarıdaki nesne etkilenir. Ancak, işlevin içinde referansı başka bir şeye yönlendirmeye karar verdiyseniz, dışarıdaki nesneyi hiç etkilemediniz, çünkü yaptığınız tek şey referansı başka bir şeye yeniden yönlendirmekti.


20

Şöyle düşünün: Her zaman değere göre geçer. Ancak, bir nesnenin değeri nesnenin kendisi değil, o nesneye yapılan bir referanstır.

İşte bir örnek (bir ilkel tip)

function changePrimitive(val) {
    // At this point there are two '10's in memory.
    // Changing one won't affect the other
    val = val * 10;
}
var x = 10;
changePrimitive(x);
// x === 10

Bunu bir nesne ile tekrarlamak farklı sonuçlar verir:

function changeObject(obj) {
    // At this point there are two references (x and obj) in memory,
    // but these both point to the same object.
    // changing the object will change the underlying object that
    // x and obj both hold a reference to.
    obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
// x === { val: 100 }

Bir örnek daha:

function changeObject(obj) {
    // Again there are two references (x and obj) in memory,
    // these both point to the same object.
    // now we create a completely new object and assign it.
    // obj's reference now points to the new object.
    // x's reference doesn't change.
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}

19

Değere ve referansa göre kopyalama, aktarma ve karşılaştırma hakkında çok ayrıntılı bir açıklama "JavaScript: Kesin Kılavuz" kitabının bu bölümünde yer almaktadır .

Referans olarak nesneleri ve dizileri manipüle etme konusundan ayrılmadan önce, bir isimlendirme noktasını temizlememiz gerekir.

"Referansla geç" ifadesinin birkaç anlamı olabilir. Bazı okuyucular için, ifade, bir işlevin bağımsız değişkenlerine yeni değerler atamasına ve bu değiştirilmiş değerlerin işlevin dışında görünür olmasına izin veren bir işlev çağırma tekniğini ifade eder. Bu kitapta terim bu şekilde kullanılmamaktadır.

Burada, basitçe, bir nesneye veya diziye yapılan bir referansın - nesnenin kendisi değil - bir fonksiyona iletildiğini kastediyoruz. Bir işlev, nesnenin veya dizinin öğelerinin özelliklerini değiştirmek için başvuruyu kullanabilir. Ancak, işlev yeni bir nesneye veya diziye başvuru ile başvurunun üzerine yazarsa, bu değişiklik işlevin dışında görünmez.

Bu terimin diğer anlamını bilen okuyucular, nesnelerin ve dizilerin değere göre geçtiğini söyleyebilirler, ancak iletilen değer aslında nesnenin kendisinden ziyade bir referanstır.


Vay canına, bu inanılmaz derecede kafa karıştırıcı. Aklı başında kim tam tersini ifade etmek ve sonra da bu şekilde kullanmak için köklü bir terim tanımlar ? Bu soruya bu kadar çok yanıtın bu kadar karışık olması şaşırtıcı değil.
Jörg W Mittag

16

JavaScript her zaman değerlidir ; her şey değer tipindedir.

Nesneler değerdir ve nesnelerin üye işlevleri değerlerin kendisidir (işlevlerin JavaScript'te birinci sınıf nesneler olduğunu unutmayın). Ayrıca, JavaScript'teki her şeyin bir nesne olduğu kavramı ile ilgili olarak ; Bu yanlış. Dizgiler, semboller, sayılar, booleanlar, null'lar ve tanımsızlar ilkeldir .

Bazen, temel prototiplerinden miras alınan bazı üye işlevlerden ve özelliklerden yararlanabilirler, ancak bu sadece kolaylık sağlamak içindir. Bu, nesnelerin kendileri oldukları anlamına gelmez. Referans için aşağıdakileri deneyin:

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

Her iki uyarıda da tanımlanacak değeri bulacaksınız.


12
-1, her zaman değere göre geçmez. MDC'den: "Bir nesneyi (yani, Dizi veya kullanıcı tanımlı bir nesne gibi ilkel olmayan bir değer) parametre olarak iletirseniz, nesneye bir başvuru işleve iletilir."
Nick

37
@Nick: Her zaman değere göre geçer. Dönemi. Nesneye yapılan bir başvuru, değere göre işleve iletilir . Referans olarak geçmiyor. "Referans yoluyla geçme", değişkenin değerini değil, kendisini geçme olarak düşünülebilir; işlevin bağımsız değişkende yaptığı herhangi bir değişiklik (bunu tamamen farklı bir nesneyle değiştirmek dahil!) arayana yansıtılır. JS referansla geçmediği için JS'de bu son bit mümkün değildir - referansları değere göre geçer. Ayrım inceliklidir, ancak sınırlamalarını anlamak için oldukça önemlidir.
cHao

1
Gelecekteki istifleyiciler için ... Bu referansınız hakkında: x = "teste"; x.foo = 12;vb. Bir özellik kalıcı olmadığı için, bunun bir nesne olmadığı anlamına gelmez. MDN'nin dediği gibi: JavaScript'te neredeyse her şey bir nesnedir. Null ve undefined dışındaki tüm ilkel tipler nesne olarak değerlendirilir. Onlara özellikler atanabilir (bazı türlerin atanan özellikleri kalıcı değildir) ve nesnelerin tüm özelliklerine sahiptirler. bağlantı
slacktracer

9
MDN kullanıcı tarafından düzenlenen bir wiki'dir ve orada yanlıştır. Normatif referans ECMA-262'dir. Bkz. S. 8 Referansların nasıl çözüldüğünü açıklayan "Referans Spesifikasyon Türü" ve ayrıca bir Referansa AssignmentExpression'u açıklamak için kullanılan 8.12.5 "[[Put]]" ve nesne 9.9 ToObject için. İlkel değerler için Michael, şartnamede olduğu gibi ToObject'in ne yaptığını zaten açıkladı. Ama ayrıca bkz. 4.3.2 ilkel değer.
Garrett

1
@WonderLand: Hayır, değil. Hiçbir zaman referansla geçemeyen insanlar, referans ile geçip referans ile değere geçme arasındaki farkları asla anlayamazlar. Ama oradalar ve önemli. İnsanları yanlış bilgilendirmek umurumda değil, çünkü kulağa daha kolay geliyor.
cHao

12

JavaScript'te, değerin türü yalnızca bu değerin değer kopyala mı yoksa referans kopyayla mı atanacağını denetler .

İlkel değerler her zaman değer kopyası ile atanır / iletilir :

  • null
  • undefined
  • sicim
  • numara
  • boole
  • sembol ES6

Bileşik değerler her zaman referans kopya ile atanır / geçirilir

  • nesneleri
  • diziler
  • fonksiyon

Örneğin

var a = 2;
var b = a; // `b` is always a copy of the value in `a`
b++;
a; // 2
b; // 3

var c = [1,2,3];
var d = c; // `d` is a reference to the shared `[1,2,3]` value
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]

Yukarıdaki snippet'te, 2skaler bir ilkel olduğu için a, bu değerin bir başlangıç ​​kopyasını tutar ve değerin bbaşka bir kopyası atanır. Değiştirirken b, içindeki değeri hiçbir şekilde değiştirmezsiniz a.

Ancak her ikisi de cve birleşik değer olan daynı paylaşılan değere ayrı referanslardır [1,2,3]. Ne değerin ne cde ddaha fazlasının [1,2,3]değere sahip olmadığına dikkat etmek önemlidir - her ikisi de değere eşit eş referanslardır. Bu nedenle, .push(4)gerçek paylaşılan arraydeğerin kendisini değiştirmek ( ) için her iki başvuruyu kullanırken , bu yalnızca bir paylaşılan değeri etkiler ve her iki başvuru da yeni değiştirilen değere başvuruda bulunur [1,2,3,4].

var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]

// later
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]

Görevi b = [4,5,6]yaptığımızda a, hala referansta bulunduğu yeri ( [1,2,3]) etkilemek için kesinlikle hiçbir şey yapmıyoruz . Bunu yapmak için, bir referanstan ziyade bbir işaretçi olması agerekir, arrayancak JS'de böyle bir yetenek yoktur!

function foo(x) {
    x.push( 4 );
    x; // [1,2,3,4]

    // later
    x = [4,5,6];
    x.push( 7 );
    x; // [4,5,6,7]
}

var a = [1,2,3];

foo( a );

a; // [1,2,3,4]  not  [4,5,6,7]

Argümanı ailettiğimizde, areferansın bir kopyasını atar x. xve aaynı [1,2,3]değere işaret eden ayrı referanslardır . Şimdi, fonksiyonun içinde, değerin kendisini değiştirmek için bu referansı kullanabiliriz ( push(4)). Ancak atamayı yaptığımızda x = [4,5,6], bu ilk referansın nereye aişaret ettiğini etkilemez - yine de (şimdi değiştirilmiş) [1,2,3,4]değeri gösterir.

Bileşik bir değeri (gibi array) değer kopyalamasıyla etkili bir şekilde geçirmek için , el ile bir kopyasını oluşturmanız gerekir, böylece geçirilen referans hala orijinali göstermez. Örneğin:

foo( a.slice() );

Referans kopya ile iletilebilen bileşik değer (nesne, dizi vb.)

function foo(wrapper) {
    wrapper.a = 42;
}

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Burada, objskaler ilkel özellik için bir sarıcı görevi görür a. İletildiğinde foo(..), objbaşvurunun bir kopyası iletilir ve wrapperparametreye ayarlanır . Artık wrapperreferansı paylaşılan nesneye erişmek ve özelliğini güncellemek için kullanabiliriz. İşlev bittikten sonra obj.agüncellenen değeri görecektir 42.

Kaynak


Önce "Bileşik değerler her zaman referans-kopya ile atanır / geçirilir" ve sonra "başvurunun bir kopyasını x'e atar" ifadesini belirtirsiniz. "Bileşik değer" olarak adlandırdığınız durumda, gerçek değişken değeri referanstır (yani bellek işaretçisi). Aynen açıkladığınız gibi, referans kopyalanır ... böylece değişken değeri kopyalanır , yine REFERANSIN DEĞER olduğunu vurgular. Bu, JavaScript'in tüm türler için by-pass olduğu anlamına gelir. Değere göre aktarma, değişkenlerin değerinin bir kopyasını geçirme anlamına gelir. Değerin bir nesneye / diziye referans olması önemli değildir.
C Perkins

Yeni bir terminoloji (değer-kopya / referans-kopya) tanıtıyorsunuz ve bu da işleri daha karmaşık hale getiriyor. Sadece kopyalar var, nokta. Bir ilkel iletirseniz, gerçek ilkel verilerin bir kopyasını geçtiniz, bir nesneyi iletirseniz, nesnenin bellek konumunun bir kopyasını geçtiniz. Söylemen gereken tek şey bu. Daha fazlası sadece insanları karıştırır.
Scott Marcus

9

'performans' ve 'hız' ile ilgili ve basit bir programlama dilinde 'bellek yönetimi' kelimesiyle ilgili.

: JavaScript içinde iki tabaka değerleri koymak Tip 1 - objectsve type2 gibi değeri tüm diğer türleri stringve boolean& vb

hafızayı aşağıdaki karelerde olduğu gibi hayal ediyorsanız, her birinde sadece bir tip2 değeri kaydedilebilir:

resim açıklamasını buraya girin

her type2 değeri (yeşil) tek bir karedir , type1 değeri (mavi) bunlardan oluşan bir gruptur :

resim açıklamasını buraya girin

nokta şu ki, bir type2 değerini belirtmek istiyorsanız, adres düzdür, ancak aynı şeyi type1 değeri için yapmak istiyorsanız hiç de kolay değildir! :

resim açıklamasını buraya girin

ve daha karmaşık bir hikayede:

resim açıklamasını buraya girin

burada referanslar bizi kurtarabilir: resim açıklamasını buraya girin

buradaki yeşil ok tipik bir değişkenken, mor olan bir nesne değişkeni olduğundan, yeşil okun (tipik değişken) yalnızca bir görevi olduğundan (ve tipik bir değeri gösterir) değerini değerini ayırmamız gerekmez. böylece yeşil oku nereye giderse gitsin değeriyle ve tüm ödevlerde, işlevlerde vb.

ama mor okla aynı şeyi yapamayız, 'john' hücresini buraya veya başka birçok şeye taşımak isteyebiliriz ... bu yüzden mor ok yerine yapışacak ve ona atanan tipik oklar hareket edecek ...

çok kafa karıştırıcı bir durum, referans alınan değişkeninizin nasıl değiştiğini fark edemediğiniz yerdir, hadi çok iyi bir örneğe bakalım:

let arr = [1, 2, 3, 4, 5]; //arr is an object now and a purple arrow is indicating it
let obj2 = arr; // now, obj2 is another purple arrow that is indicating the value of arr obj
let obj3 = ['a', 'b', 'c'];
obj2.push(6); // first pic below - making a new hand for the blue circle to point the 6
//obj2 = [1, 2, 3, 4, 5, 6]
//arr = [1, 2, 3, 4, 5, 6]
//we changed the blue circle object value (type1-value) and due to arr and obj2 are indicating that so both of them changed
obj2 = obj3; //next pic below - changing the direction of obj2 array from blue circle to orange circle so obj2 is no more [1,2,3,4,5,6] and it's no more about changing anything in it but we completely changed its direction and now obj2 is pointing to obj3
//obj2 = ['a', 'b', 'c'];
//obj3 = ['a', 'b', 'c'];

resim açıklamasını buraya girin resim açıklamasını buraya girin


8

Bu, değere göre geçiş ve referansla geçiş (JavaScript) için biraz daha açıklamadır. Bu kavramda değişkeni referansla geçirme ve değişkeni referansla geçirme hakkında konuşurlar.

Değere göre geçiş (ilkel tip)

var a = 3;
var b = a;

console.log(a); // a = 3
console.log(b); // b = 3

a=4;
console.log(a); // a = 4
console.log(b); // b = 3
  • JavaScript'teki tüm ilkel türler için geçerlidir (dize, sayı, Boole, tanımsız ve boş).
  • a'ya bir bellek (örneğin 0x001) ayrılır ve b, bellekteki değerin bir kopyasını oluşturur (örneğin 0x002).
  • Bu nedenle, bir değişkenin değerinin değiştirilmesi, ikisi de iki farklı konumda bulunduğundan diğerini etkilemez.

Referans ile geçiş (nesneler)

var c = { "name" : "john" };
var d = c;

console.log(c); // { "name" : "john" }
console.log(d); // { "name" : "john" }

c.name = "doe";

console.log(c); // { "name" : "doe" }
console.log(d); // { "name" : "doe" }
  • JavaScript motoru nesneyi değişkene atar ve c(0x012) gibi bir belleğe işaret eder.
  • D = c olduğunda, bu adımda daynı yeri gösterir (0x012).
  • Her iki değişken için herhangi bir değişiklik değerinin değerini değiştirme.
  • Fonksiyonlar nesnelerdir

Özel durum, referansla geçiş (nesneler)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • Eşit (=) operatörü yeni bellek alanı veya adres ayarlar

Sözde özel durumda, bellek alanı tahsisine neden olan atama operatörü değil, nesne değişmezinin kendisidir. Curley braketi gösterimi yeni bir nesnenin oluşturulmasına neden olur. Özellik c, yeni nesnenin başvurusunun bir kopyasına ayarlanır.
georgeawg

6

JavaScript'teki referansları bildiklerimi paylaşma

JavaScript'te, bir değişkene bir nesne atarken, değişkene atanan değer nesneye bir referanstır:

var a = {
  a: 1,
  b: 2,
  c: 3
};
var b = a;

// b.c is referencing to a.c value
console.log(b.c) // Output: 3
// Changing value of b.c
b.c = 4
// Also changes the value of a.c
console.log(a.c) // Output: 4


1
Bu, daha önceki cevapların daha iyi açıklanmadığı hakkında hiçbir şey söylemeyen aşırı basit bir cevaptır. Dizileri neden özel bir durum olarak adlandırdığınız konusunda kafam karıştı.
Quentin

1
" nesneler referans olarak saklanır " yanıltıcıdır. Ne demek istediğimi bir değişkene bir nesne atarken, değişkene atanan değerin nesneye bir başvuru olmasıdır.
RobG

bu, bir işlevin içindeki nesneyi işlevin dışında güncelleştirmeyen bir nesnenin güncellenmesi sorununu gidermez. Referansın yerine değerler olarak göründüğü resmin tamamı budur. Bu nedenle -1
amaster

@amaster Bunu işaret ettiğiniz için teşekkürler! Bir düzenleme önerebilir
xameeramir

Haha, denedim ... önerdiğim düzenleme çok değişti
amd'ye

4

Semantik !! Somut tanımların belirlenmesi, bazı kelimeleri ve cümleleri kullanırken bile aynı şeyi tanımlamadıkları için bazı cevapları ve yorumları uyumsuz hale getirecektir, ancak karışıklığı aşmak çok önemlidir (özellikle yeni programcılar için).

Her şeyden önce, herkesin kavramadığı birçok soyutlama seviyesi vardır. 4. ya da 5. nesil dilleri öğrenmiş olan yeni programcılar, fikirlerini derlemeye aşina olan kavramlar ya da işaretçilerden işaretçilerden işaretçilere aşamalı olmayan C programcılarının etrafına sarmakta zorluk çekebilirler. Tek tek başvuru, bir işlev parametresi değişkeni kullanarak başvurulan bir nesneyi değiştirme yeteneği anlamına gelmez.

Değişken : Bellekteki belirli bir konumdaki bir değere başvuran bir sembol kavramı. Bu terim ayrıntıların tartışılmasında genellikle tek başına kullanılamayacak kadar yüklenir.

Sembol : Değişkene (yani değişkenin adı) atıfta bulunmak için kullanılan metin dizesi.

Değer : Bellekte saklanan ve değişkenin sembolü kullanılarak referans verilen belirli bitler.

Bellek konumu : Bir değişkenin değerinin depolandığı yer. (Konumun kendisi, konumda depolanan değerden ayrı bir sayı ile gösterilir.)

İşlev parametresi : Bir işleve tanımlanmış değişkenler, işleve iletilen değişkenlere başvuruda bulunmak için kullanılır.

İşlev argümanı : Arayan tarafından işleve iletilen işlevin dışındaki değişken.

Nesne değişkeni : Temel temel değeri "nesne" nin kendisi değil, değeri nesnenin gerçek verilerinin depolandığı bellekteki başka bir konuma bir işaretçi (bellek konumu değeri) olan değişken. Çoğu yüksek nesil dilde, "işaretçi" yönü, çeşitli bağlamlarda otomatik referans alma ile etkili bir şekilde gizlenir.

İlkel değişken : Değeri gerçek değer olan değişken . Bu kavram bile otomatik boks ve çeşitli dillerin nesne benzeri bağlamları ile karmaşık olabilir, ancak genel fikirler, değişkenin değerinin, başka bir bellek konumuna bir işaretçi yerine değişkenin sembolü ile temsil edilen gerçek değer olmasıdır.

İşlev argümanları ve parametreleri aynı şey değildir. Ayrıca, bir değişkenin değeri değişkenin nesnesi değildir (çeşitli insanlar tarafından zaten belirtildiği gibi, ancak göz ardı edildiği gibi). Bu ayrımlar doğru anlayış için kritik öneme sahiptir.

Değere göre aktarma veya Paylaşıma göre çağrı (nesneler için): İşlev bağımsız değişkeninin değeri, işlevin parametre sembolü tarafından başvurulan başka bir bellek konumuna COPIED (yığın veya yığın üzerinde olsun). Başka bir deyişle, function parametresi iletilen bağımsız değişkenin değerinin bir kopyasını aldı ... VE (kritik) bağımsız değişkenin değeri, çağıran işlev tarafından ASLA GÜNCELLENMEMİŞ / DEĞİŞTİRİLMEMİŞ / DEĞİŞTİRİLMEMEKTEDİR. Unutmayın, bir nesne değişkeninin değeri nesnenin kendisi DEĞİLDİR, bunun yerine nesneye işaretçi olduğundan, bir nesne değişkenini değere iletmek işaretçiyi işlev parametresi değişkenine kopyalar. İşlev parametresinin değeri bellekteki tam olarak aynı nesneyi gösterir. Nesne verilerinin kendisi doğrudan function parametresi ile değiştirilebilir, ancak fonksiyon bağımsız değişkeninin değeri ASLA GÜNCELLENMEMEKTEDİR, böylece aynıişlev çağrısında ve sonrasında bile (nesnenin verileri değiştirilse veya işlev parametresine tamamen farklı bir nesne atanmış olsa bile). Başvurulan nesnenin, fonksiyon parametresi değişkeni üzerinden güncellenebilmesi nedeniyle işlev bağımsız değişkeninin başvuru ile iletildiği sonucuna varmak yanlıştır.

Call / Pass-by-reference : İşlev bağımsız değişkeninin değeri, karşılık gelen işlev parametresi tarafından doğrudan güncelleştirilebilir / güncellenir. Eğer yardımcı olursa, işlev parametresi bağımsız değişken için etkili bir "diğer ad" olur - etkin olarak aynı bellek konumundaki aynı değere başvururlar. Bir işlev bağımsız değişkeni bir nesne değişkeni ise, işlev parametresi yine de aynı nesneyi göstereceğinden, nesnenin verilerini değiştirme yeteneği, değere göre geçiş durumundan farklı değildir. Ancak, nesne değişkeni durumunda, function parametresi tamamen farklı bir nesneye ayarlanırsa, bağımsız değişken de aynı şekilde farklı nesneyi gösterir - bu, değere göre geçme durumunda olmaz.

JavaScript referansla geçmiyor. Eğer yakından okursanız, tüm zıt fikirlerin, by-pass değeri ile kastedileni yanlış anladığını ve bir parametrenin verilerini function parametresi ile güncelleme yeteneğinin "by-value" ile eşanlamlı olduğu sonucuna varacaksınız.

Nesne klonlama / kopyalama : Yeni bir nesne oluşturulur ve orijinal nesnenin verileri kopyalanır. Bu derin bir kopya veya sığ bir kopya olabilir, ancak önemli olan yeni bir nesnenin yaratılmasıdır. Nesnenin bir kopyasını oluşturmak, değere göre ayrı bir kavramdır. Bazı diller sınıf nesnesi ve yapıları (veya benzerlerini) birbirinden ayırır ve farklı türdeki değişkenleri iletmek için farklı davranışlara sahip olabilir. Ancak JavaScript, nesne değişkenlerini iletirken otomatik olarak böyle bir şey yapmaz. Ancak otomatik nesne klonlamasının olmaması, referansla geçme anlamına gelmez.


4

JavaScript ilkel türleri değere göre ve nesne türlerini referans olarak geçirir

Şimdi, insanlar, Java ve ark. aslında. Mesele şu:

  1. Bir nesneyi iletmek nesneyi kopyalamaz.
  2. Bir işleve iletilen bir nesne, üyelerinin işlev tarafından değiştirilmesini sağlayabilir.
  3. Bir işleve iletilen ilkel bir değer, işlev tarafından değiştirilemez. Bir kopya çıkar.

Kitabımda buna referans olarak geçme deniyor.

- Brian Bi - Hangi programlama dilleri referans olarak geçiyor?


Güncelleme

İşte buna bir çürütme:

JavaScript'te "referansla geçer" yoktur.


@Amy Çünkü bu referansla değil, değere göre geçişi açıklar. Bu cevap farkı gösteren iyi bir cevaptır: stackoverflow.com/a/3638034/3307720
nasch

@nasch Farkı anlıyorum. # 1 ve # 2, ref-by-ref anlambilimini açıklamaktadır. # 3, değer bazında anlambilimi açıklamaktadır.
Amy

@Amy 1, 2 ve 3, hepsi by-pass değeriyle tutarlıdır. Başvuru ile geçiş yapmak için 4'e de ihtiyacınız olacaktır: referansı fonksiyon içinde yeni bir değere atamak (= operatörü ile) de referansı fonksiyonun dışında yeniden atar. Javascript için bu geçerli değildir, bu da sadece değere göre geçmesini sağlar. Bir nesneyi iletirken, nesneye bir işaretçi geçirirsiniz ve bu işaretçiyi değere göre geçirirsiniz.
17'de nasch

Genel olarak "referansla geçme" ile kastedilen bu değildir. Sorgumu tatmin ettin ve ben sana katılmıyorum. Teşekkürler.
Amy

"Kitabımda buna referans olarak geçme deniyor." - Her derleyici kitapta, tercüman kitapta, programlama dili teori kitabında ve bilgisayar bilimi kitabında yazılan kitaplarda böyle değildir.
Jörg W Mittag

3

Bunu anlamanın basit yolu ...

  • Bir işlevi çağırırken, değişkenlerin kendisini değil, bağımsız değişkenlerin içeriğini (başvuru veya değer) iletirsiniz.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
  • İşlev içinde, parametre değişkenleri inVar1ve inVar2iletilen içeriği alır.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
  • Başvurusu inVar2alındığından, { prop: 2 }nesnenin özelliğinin değerini değiştirebilirsiniz.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }

3

JavaScript'teki bir işleve argümanlar iletmek, parametreleri C'deki işaretçi değerine göre geçirmeye benzer:

/*
The following C program demonstrates how arguments
to JavaScript functions are passed in a way analogous
to pass-by-pointer-value in C. The original JavaScript
test case by @Shog9 follows with the translation of
the code into C. This should make things clear to
those transitioning from C to JavaScript.

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

This produces the output:

10
changed
unchanged
*/

#include <stdio.h>
#include <stdlib.h>

struct obj {
    char *item;
};

void changeStuff(int *num, struct obj *obj1, struct obj *obj2)
{
    // make pointer point to a new memory location
    // holding the new integer value
    int *old_num = num;
    num = malloc(sizeof(int));
    *num = *old_num * 10;
    // make property of structure pointed to by pointer
    // point to the new value
    obj1->item = "changed";
    // make pointer point to a new memory location
    // holding the new structure value
    obj2 = malloc(sizeof(struct obj));
    obj2->item = "changed";
    free(num); // end of scope
    free(obj2); // end of scope
}

int num = 10;
struct obj obj1 = { "unchanged" };
struct obj obj2 = { "unchanged" };

int main()
{
    // pass pointers by value: the pointers
    // will be copied into the argument list
    // of the called function and the copied
    // pointers will point to the same values
    // as the original pointers
    changeStuff(&num, &obj1, &obj2);
    printf("%d\n", num);
    puts(obj1.item);
    puts(obj2.item);
    return 0;
}

1
JavaScript'te durumun böyle olduğunu sanmıyorum: `` javascript var num = 5;
Danail Nachev

@DanailNachev: Bu teknik olarak doğru olsa da, fark sadece ECMAScript ilkellerinin olmadığı değişken nesneler için gözlemlenebilir.
Jörg W Mittag

3

Dil avukatlarının programlanması için, ECMAScript 5.1'in (son sürümden daha kolay okunur) aşağıdaki bölümlerini inceledim ve ECMAScript posta listesinde sormaya devam edin.

TL; DR : Her şey değere göre aktarılır, ancak Nesnelerin özellikleri referanstır ve Nesne tanımı standartta sürüngen değildir.

Tartışma Listelerinin Oluşturulması

Bölüm 11.2.4 "Bağımsız Değişken Listeleri", yalnızca 1 bağımsız değişkenden oluşan bir bağımsız değişken listesi oluştururken aşağıdakileri açıklar:

Üretim ArgumentList: AssignmentExpression aşağıdaki gibi değerlendirilir:

  1. Ref, Atama İfadesini değerlendirmenin sonucu olsun.
  2. Arg GetValue olsun (ref).
  3. Tek öğesi arg olan bir Liste döndür.

Bu bölüm ayrıca argüman listesinin 0 veya> 1 argümanına sahip olduğu durumları da numaralandırır.

Böylece, her şey referansla geçer.

Nesne Özelliklerine Erişim

Bölüm 11.2.1 "Mülkiyet Erişimcileri"

Üretim MemberExpression: MemberExpression [Expression] şu şekilde değerlendirilir:

  1. BaseReference öğesini MemberExpression değerlendirmesinin sonucu olsun.
  2. BaseValue öğesinin GetValue (baseReference) olmasına izin verin.
  3. Bırakın propertyNameReference, İfadeyi değerlendirmenin sonucu olsun.
  4. PropertyNameValue öğesinin GetValue (propertyNameReference) olmasına izin verin.
  5. CheckObjectCoercible (baseValue) öğesini çağırın.
  6. PropertyNameString öğesinin ToString (propertyNameValue) olmasına izin verin.
  7. Değerlendirilmekte olan sözdizimsel üretim katı mod kodunda bulunuyorsa, katı doğru olsun, aksi takdirde yanlış yanlış olsun.
  8. Temel değeri baseValue olan ve başvurulan adı propertyNameString olan ve katı mod bayrağı katı olan Reference türünde bir değer döndürün.

Böylece, Nesnelerin özellikleri her zaman referans olarak kullanılabilir.

Referans Üzerine

Bölüm 8.7 "Referans Spesifikasyon Türü" bölümünde referanslar dilde gerçek türler değildir - sadece silme, typeof ve atama operatörlerinin davranışlarını tanımlamak için kullanılır.

"Object" in tanımı

5.1 sürümünde "Nesne bir özellik topluluğudur" olarak tanımlanır. Bu nedenle, nesnenin değerinin koleksiyon olduğunu, ancak koleksiyonun değerinin ne olduğu konusunda spesifikasyonda kötü tanımlandığını ve anlaşılması için biraz çaba gerektirdiğini çıkarabiliriz .


Kaç kişinin değerin geçtiği argümanlar, referans tarafından iletilen argümanlar, tüm nesneler üzerindeki işlemler ve mülkleri üzerindeki operasyonlar arasındaki ayrımlardan kaçındığı beni şaşırtmaz. 1979'da, bilgisayar programımdan mezun oldum, MBA programım için CS seçmeli derslerini 15 saat kadar eklemeyi seçtim. Bununla birlikte, kısa bir süre sonra, bu kavramları kavramamın en azından bilgisayar bilimi veya matematik dereceleri olan meslektaşlarımın tuttuğu kadar iyi olduğu anlaşıldı. Assembler'ı inceleyin ve oldukça netleşecektir.
David A. Gray

3

MDN dokümanları çok ayrıntılı olmadan bunu açıkça açıklar:

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ı da değerlerdir ve özeldir: işlev başvurulan nesnenin özelliklerini değiştirirse, bu değişiklik işlevin dışında görünür, (...)

Kaynak: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description


1

Düşük seviyeli bir dilde, bir değişkeni referans olarak geçirmek istiyorsanız, işlevin oluşturulmasında belirli bir sözdizimi kullanmanız gerekir:

int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
  *age = *age + 1;
}

Bu &agebir referanstır myAge, ancak değeri istiyorsanız başvuruyu kullanarak dönüştürmeniz gerekir *age.

Javascript, bu dönüşümü sizin için yapan üst düzey bir dildir. Bu nedenle, nesneler başvuru ile iletilse de, dil başvuru parametresini değere dönüştürür. Kullanılacak gerekmez &, fonksiyon tanımı, referans olarak geçmek, ne *JS öyle sizin için değere başvuruyu dönüştürmek için, fonksiyon gövdesi üzerinde,.

Bu nedenle, bir işlevin içindeki bir nesneyi değiştirmeye çalıştığınızda, değerini (yani age = {value:5}) değiştirerek değişiklik devam etmez, ancak özelliklerini değiştirirseniz (yani age.value = 5) yapar.

Daha fazla bilgi edin


1

Bu cevapları birden çok kez okudum ama Barbara Liskov tarafından " Paylaşarak Ara" teknik tanımını öğrenene kadar gerçekten anlamadım

Paylaşma yoluyla çağrı anlambilimi, işlev içindeki işlev bağımsız değişkenlerine atamaların (başvuru anlambiliminden farklı olarak) görünmemesi nedeniyle referansa göre farklılık gösterir [alıntı gerekli], bu nedenle örneğin bir değişken geçirilmişse, mümkün değildir arayanın kapsamındaki değişken üzerinde bir atama simüle etmek için. Ancak, işlev arayanla aynı nesneye eriştiğinden (kopya yapılmaz), işlev içinde nesneler değiştirilebilirse, bu nesnelerdeki mutasyonlar, çağıran tarafından görülebilir; bu, çağrıdan değere göre farklılık gösterebilir semantik. İşlev içinde değiştirilebilir bir nesnenin mutasyonları, nesne kopyalanmadığı veya klonlanmadığı için arayan tarafından görülebilir - paylaşılır.

Diğer bir deyişle, parametre değerinin kendisine gidip erişirseniz parametre başvuruları değiştirilebilir. Öte yandan, bir parametreye atama değerlendirme sonrasında kaybolacaktır ve fonksiyon arayan tarafından erişilemez.


Hayır, bir nesnenin değiştirilebilir olup olmadığı gerçekten sorun değildir. Her şey her zaman değerden geçer. Sadece neyi geçtiğinize bağlıdır (bir değer veya referans). Bkz bu .
Scott Marcus

Açıkladığı şey referans BY-VALUE iletmektir. Yeni terminoloji ortaya koymak için bir neden yoktur.
Sanjeev

1

Çoğunlukla en basit yol

// Copy JS object without reference
var first = {"a":"value1","b":"value2"};
var clone = JSON.parse( JSON.stringify( first ) ); 

var second = ["a","b","c"];
var clone = JSON.parse( JSON.stringify( second ) ); 

JSON.parse( JSON.stringify( obj ) )nesneleri klonlamak için korkunç bir yoldur. Sadece yavaş değil, aynı zamanda veri kaybına da neden olabilir.
D. Pardal

0

Bulduk uzanan bir yöntem ve Underscore.js kütüphanesi I ya tamamen modifiye edilmiş veya ikame edilebilir bir parametre olarak bir nesne geçirmek istediğinizde çok yararlı.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}

0

Bulduğum en kısa açıklama AirBNB stil kılavuzundaydı :

  • İlkel : İlkel bir türe eriştiğinizde doğrudan değeri üzerinde çalışırsınız

    • sicim
    • numara
    • boole
    • boş
    • Tanımsız

Örneğin:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Karmaşık : Karmaşık bir türe eriştiğinizde, değerine bir başvuru üzerinde çalışırsınız

    • nesne
    • dizi
    • fonksiyon

Örneğin:

var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

İlkel tipler etkili bir şekilde değere, karmaşık tipler ise referansa göre geçirilir.


Hayır, her şey her zaman değerden geçer. Sadece neyi geçtiğinize bağlıdır (bir değer veya referans). Bkz bu .
Scott Marcus

-1

Kopyala olduğunu söyleyebilirim -

Bağımsız değişkenleri ve değişken nesneleri, işlev çağırma işleminin başlangıcında oluşturulan yürütme bağlamı sırasında oluşturulan nesneler olarak görürsünüz - ve işleve aktarılan gerçek değer / başvurunuz bu bağımsız değişkenler + değişken nesnelerde saklanır.

Basitçe söylemek gerekirse, ilkel tipler için, değerler fonksiyon çağrısının başlangıcında kopyalanır, nesne tipi için referans kopyalanır.


1
"kopyala pass" === değere göre geçiş
Scott Marcus

-1

Burada JavaScript'te "referansla geç" teriminin kullanımı hakkında bazı tartışmalar var , ancak sorunuza cevap vermek için:

Bir nesne, özel olarak belirtmeye gerek kalmadan referans olarak otomatik olarak iletilir

(Yukarıda belirtilen makaleden.)


7
Bağlantılı makale artık bu ifadeleri içermiyor ve "referansla geç" i tamamen kullanmaktan kaçınıyor.
C Perkins

Değer bir referanstır

-2

Bir şeyin "referansla geç" olup olmadığını belirlemenin kolay bir yolu, "takas" işlevi yazıp yazamayacağınızdır. Örneğin, C dilinde şunları yapabilirsiniz:

void swap(int *i, int *j)
{
    int t;
    t = *i;
    *i = *j;
    *j = t;
}

Bunun eşdeğerini JavaScript'te yapamazsanız, "referansla geç" değildir.


21
Bu gerçekten referans olarak geçmiyor. İşaretçileri işleve geçiriyorsunuz ve bu işaretçiler değere göre geçiriliyor. Daha iyi bir örnek, C ++ 'ın ve işleci veya C #' ref "anahtar kelimesidir, her ikisi de gerçekten referans olarak geçer.
Matt Greer

Daha da kolayı, her şeyin JavaScript'te değere göre aktarılmasıdır.
Scott Marcus


-3
  1. dize, sayı gibi ilkel tür değişkeni her zaman değere göre geçirilir.
  2. Dizi ve Nesne bu iki koşula dayanarak referans olarak veya değer bazında geçirilir.

    • o Object veya dizinin değerini yeni Object veya Array ile değiştiriyorsanız, Value by pass olur.

      object1 = {item: "car"}; array1=[1,2,3];

    burada eski nesneye yeni nesne veya dizi atarsınız. eski nesnenin özelliğinin değerini değiştirmezsiniz, bu yüzden değere göre geçer.

    • bir nesnenin veya dizinin özellik değerini değiştiriyorsanız, o zaman Referans ile geçer.

      object1.key1= "car"; array1[0]=9;

    Burada eski nesnenin özellik değerini değiştiriyorsunuz. eski nesneye yeni nesne veya dizi atamıyorsunuz. bu nedenle referans ile geçiliyor.

kod

    function passVar(object1, object2, number1) {

        object1.key1= "laptop";
        object2 = {
            key2: "computer"
        };
        number1 = number1 + 1;
    }

    var object1 = {
        key1: "car"
    };
    var object2 = {
        key2: "bike"
    };
    var number1 = 10;

    passVar(object1, object2, number1);
    console.log(object1.key1);
    console.log(object2.key2);
    console.log(number1);

Output: -
    laptop
    bike
    10

1
Atama operatörü bir işlev çağrısı ile karıştırılmamalıdır. Varolan bir değişkene yeni veriler atadığınızda, eski verilerin referans sayısı azalır ve yeni veriler eski değişkenle ilişkilendirilir. Temel olarak, değişken yeni verilere işaret eder. Aynısı özellik değişkenleri için de geçerlidir. Bu atamaların işlev çağrıları olmadığı için, değere göre ya da başvuruya göre ilgileri yoktur.
John Sonderson

1
Hayır, her şey her zaman değerden geçer. Sadece neyi geçtiğinize bağlıdır (bir değer veya referans). Bkz bu .
Scott Marcus

-3
  1. Temel öğeler (sayı, Boole vb.) Değere göre geçirilir.
    • Dizeler değişmez, bu yüzden onlar için önemli değil.
  2. Nesneler başvuru ile iletilir (başvuru değere göre iletilir).

Hayır, her şey her zaman değerden geçer. Sadece neyi geçtiğinize bağlıdır (bir değer veya referans). Bkz bu .
Scott Marcus

İkinci ifadeniz kendisiyle çelişiyor.
Jörg W Mittag

-5

İşlevler içindeki basit değerler, bu değerleri işlevin dışında değiştirmez (değere göre geçirilir), karmaşık olanlar ise (başvuru ile iletilir).

function willNotChange(x) {

    x = 1;
}

var x = 1000;

willNotChange(x);

document.write('After function call, x = ' + x + '<br>'); // Still 1000

function willChange(y) {

    y.num = 2;
}

var y = {num: 2000};

willChange(y);
document.write('After function call y.num = ' + y.num + '<br>'); // Now 2, not 2000

bu çok saçma, fonksiyonel seviye kapsamı nedeniyle değişecek, referansla geçildiği için kaldırılmıyor.
Parijat Kalia

Hayır, her şey her zaman değerden geçer. Sadece neyi geçtiğinize bağlıdır (bir değer veya referans). Bkz bu .
Scott Marcus
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.