Değişkenleri Javascript'te Referansa Göre Geçme


285

Değişkenleri JavaScript'te referans olarak nasıl iletirim? Ben birkaç işlemleri gerçekleştirmek istiyorum 3 değişken var, bu yüzden onları bir for döngüsüne koymak ve her birine işlemleri gerçekleştirmek istiyorum.

sözde kod:

myArray = new Array(var1, var2, var3);
for (var x = 0; x < myArray.length; x++){
    //do stuff to the array
    makePretty(myArray[x]);
}
//now do stuff to the updated vars

Bunu yapmanın en iyi yolu nedir?


31
'Referansla geç' hakkında konuşuyorsunuz, ancak örneğinizde işlev çağrısı yok, bu nedenle örneğinizde hiç geçiş yok. Lütfen ne yapmaya çalıştığınızı netleştirin.
jfriend00

1
Karışıklık için özür dilerim. Özel olarak bir işlev yazmam gerekmediği için 'referansla geç' kelimelerin kötü bir seçimiydi. makePretty(var1); makePretty(var2); makePretty(var3); ...
Yazmadan

arr = [var1, var2, var3]; for (var i = 0, len = arr.length; i < len; i++) { arr[i] = makePretty(arr[i]); }Yorumunuza dayanarak: - makePrettyGeri dönen değeri dizideki yuvaya kaydetmeniz yeterlidir .
dylnmc

Yanıtlar:


415

JavaScript'te "referansla geç" yoktur. Bir nesneyi iletebilirsiniz (diğer bir deyişle, bir nesneye başvuruyu geçerek aktarabilirsiniz) ve sonra nesne içeriğini değiştiren bir işleve sahip olabilirsiniz:

function alterObject(obj) {
  obj.foo = "goodbye";
}

var myObj = { foo: "hello world" };

alterObject(myObj);

alert(myObj.foo); // "goodbye" instead of "hello world"

Sayısal dizine sahip bir dizinin özelliklerini yineleyebilir ve isterseniz dizinin her hücresini değiştirebilirsiniz.

var arr = [1, 2, 3];

for (var i = 0; i < arr.length; i++) { 
    arr[i] = arr[i] + 1; 
}

"Tek tek referans" ın çok özel bir terim olduğunu belirtmek önemlidir. Değiştirilebilir bir nesneye bir referans iletmenin mümkün olduğu anlamına gelmez. Bunun yerine, bir işlevin çağıran bağlamda bu değeri değiştirmesine izin verecek şekilde basit bir değişkeni iletmenin mümkün olduğu anlamına gelir . Yani:

 function swap(a, b) {
   var tmp = a;
   a = b;
   b = tmp; //assign tmp to b
 }

 var x = 1, y = 2;
 swap(x, y);

 alert("x is " + x + ", y is " + y); // "x is 1, y is 2"

C gibi bir dilde ++, o dil çünkü bunu yapmak mümkün yapar (sıralama-) geçiş referans-ile-var.

edit - bu son zamanlarda (Mart 2015), bu durumda Java hakkında olsa da, aşağıda belirtilen benim gibi bir blog yazısı üzerinde Reddit'te tekrar havaya uçtu. Reddit yorumlarında ileri geri okurken başıma gelen bu karışıklığın büyük bir kısmı "referans" kelimesini içeren talihsiz çarpışmadan kaynaklanıyor. "Referansa göre geç" ve "değere göre geç" terimi, programlama dillerinde çalışmak için "nesneler" kavramının önüne geçer. Gerçekten nesnelerle ilgili değil; fonksiyon parametreleri ve özellikle fonksiyon parametrelerinin çağıran ortama nasıl "bağlandığı" (veya bağlanmadığı) ile ilgilidir. Özellikle,ve JavaScript'teki gibi görünecektir. Ancak, bir olur da çağıran ortamda nesne başvurusu değiştirmeye muktedir ve bu, o anahtar şey olamaz JavaScript yapmak. Bir referans kodu, referansın kendisini değil, referansa bir referansı geçecektir .

edit - işte konuyla ilgili bir blog yazısı. (C ++ 'ın gerçekten de pass-by referansı olmadığını açıklayan bu yazının yorumuna dikkat edin. Bu doğrudur. Ancak C ++' ın sahip olduğu şey, açıkça işlev noktasında düz değişkenlere referanslar oluşturma yeteneğidir. işaretçi oluşturma çağrısında veya bağımsız değişken türü imzası bunun yapılmasını gerektiren işlevleri çağırırken dolaylı olarak çağrılır. Bunlar JavaScript'in desteklemediği anahtar öğelerdir.)


8
Bir nesneye veya OP'nin aslında ne istediğine inandığım orijinal nesneyi veya diziyi değiştirmenize izin veren bir diziye bir başvuru iletebilirsiniz.
jfriend00

2
Peki, OP terminolojiyi kullandı ama gerçek kod herhangi bir "geçen" içermiyor gibi görünmüyor :-) Gerçekten ne yapmaya çalıştığından emin değilim.
Sivri

5
Bir başvuruyu değere göre iletmek , başvuruyu iletmeyle aynı şey değildir , ancak bunun gibi bazı senaryolarda da görünebilir.
Travis Webb

5
Kişisel blogger C ++, yaklaşık yanlıştır yapar geçmek referans-ile-ve görevi hiçbir mantıklı. Başlatma işleminden sonra bir referansın değerini değiştirememeniz önemsizdir; bunun 'referansla geç' ile hiçbir ilgisi yok. 'Referansla geç' semantiğinin C # üzerinden izlenebileceği ifadesi bir alarm zili çalmış olmalı.
EML

7
@Pointy Bu korkunç bir cevap. Yardıma ihtiyacım olsaydı, isteyeceğim son şey semantik konusunda beni eğiten biri. "Referanslı by pass" basitçe, "fonksiyon bir dereceye kadar argüman olarak iletilen değişkenin değerini değiştirebilir" anlamına gelir. (soru bağlamında) Gerçekten ortaya çıkardığınız kadar karmaşık değil.
crownlessking

110
  1. dizeler ve sayılar gibi ilkel tip değişkenleri her zaman değere göre iletilir.
  2. Diziler ve Nesneler, aşağıdaki koşullara dayalı olarak referans veya değere göre iletilir:

    • bir nesnenin veya dizinin değerini ayarlıyorsanız, bu değere göre iletilir.

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

    • bir nesnenin veya dizinin özellik değerini değiştiriyorsanız, Referansla Geç.

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

kod

function passVar(obj1, obj2, num) {
    obj1.prop = "laptop"; // will CHANGE original
    obj2 = { prop: "computer" }; //will NOT affect original
    num = num + 1; // will NOT affect original
}

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

passVar(object1, object2, number1);
console.log(object1); //output: Object {item:"laptop"}
console.log(object2); //output: Object {item:"bike"}
console.log(number1); //ouput: 10


21
Bu "referans ile geç" in anlamı değildir. Terimin gerçekten nesnelerle hiçbir ilgisi yoktur, ancak çağrılan bir işlevdeki parametreler ile çağrı ortamındaki değişkenler arasındaki ilişkiyle ilgilidir.
Sivri

12
Her şey her zaman değerden geçer. İlkel olmayanlarla, iletilen değer bir referanstır. Bir başvuruya atama, başvuruda - doğal olarak bir değer olduğu için - değişir ve bu nedenle başlangıçta başvurulan diğer nesneyi değiştiremez. Bir referansla gösterilen nesnenin mutasyona uğratılması, sivri uçlu nesneye mutasyon geçirerek tam olarak beklediğiniz gibi davranır. Her iki senaryo da mükemmel bir şekilde tutarlıdır ve JavaScript'in zaman içinde geriye dönüp işleve nasıl aktarıldığını değiştirerek çalışma biçimini sihirli bir şekilde değiştirmez.
Matthew,

9
Bu cevap, bir önceki soruyu açıklığa kavuşturdu, bu da daha fazla oy almasına rağmen (bu yazma zamanında 287 ve aynı zamanda kabul edilen cevaptır) JS'de referans olarak nasıl geçileceği konusunda yeterince açık değildi. Kısacası, bu cevaptaki kod işe yaradı, daha fazla oy toplayan cevap işe yaramadı.
Pap

25

Referans gibi değişken geçirme geçici çözümü:

var a = 1;
inc = function(variableName) {
  window[variableName] += 1;
};

inc('a');

alert(a); // 2


DÜZENLE

evet, aslında küresel erişim olmadan yapabilirsiniz

inc = (function () {
    var variableName = 0;

    var init = function () {
        variableName += 1;
        alert(variableName);
    }

    return init;
})();

inc();

@Phil, küresel / pencere değerlerine dikkat etmek iyidir, ancak bir noktada tarayıcıda yaptığımız her şey pencere nesnesinin bir alt öğesi veya alt öğesidir. Düğümlerde, her şey GLOBAL'in soyundan gelir. Derlenmiş nesne dillerinde, bu açık bir üst nesne değilse örtüktir, çünkü aksi takdirde yığın yönetimi daha karmaşık hale gelir (ve ne için?).
dkloke

1
@dkloke: Evet, nihayetinde pencere nesnesine dokunulmalıdır - jQuery pencere kullanır. $ / window.jQuery ve diğer yöntemler bunun altındadır. Küresel ad alanının kirletilmesinden bahsediyordum, burada birleştirici bir ad alanı altında olmaktan çok değişken ekliyorsunuz.
Phil

2
Bu yaklaşımın ne kadar kötü olduğunu ifade etmek için iyi kelimeler bulamıyorum; (
SOReader

Ben referans tarafından değişken geçmez bile son çözümü (düzenlenmiş versiyonu) seviyorum. Bu avantajdır, çünkü değişkenlere doğrudan erişebilirsiniz.
John Boe

11

Basit Nesne

var ref = { value: 1 };

function Foo(x) {
    x.value++;
}

Foo(ref);
Foo(ref);

alert(ref.value); // Alert: 3

Özel Nesne

Nesne rvar

function rvar (name, value, context) {
    if (this instanceof rvar) {
        this.value = value;
        Object.defineProperty(this, 'name', { value: name });
        Object.defineProperty(this, 'hasValue', { get: function () { return this.value !== undefined; } });
        if ((value !== undefined) && (value !== null))
            this.constructor = value.constructor;
        this.toString = function () { return this.value + ''; };
    } else {
        if (!rvar.refs)
            rvar.refs = {};
        if (!context)
            context = window;
        // Private
        rvar.refs[name] = new rvar(name, value);
        // Public
        Object.defineProperty(context, name, {
            get: function () { return rvar.refs[name]; },
            set: function (v) { rvar.refs[name].value = v; },
            configurable: true
        });

        return context[name];
    }
}

Değişken Beyanı

rvar('test_ref');
test_ref = 5; // test_ref.value = 5

Veya:

rvar('test_ref', 5); // test_ref.value = 5

Test Kodu

rvar('test_ref_number');
test_ref_number = 5;
function Fn1 (v) { v.value = 100; }
console.log("rvar('test_ref_number');");
console.log("test_ref_number = 5;");
console.log("function Fn1 (v) { v.value = 100; }");
console.log('test_ref_number.value === 5', test_ref_number.value === 5);
console.log(" ");

Fn1(test_ref_number);
console.log("Fn1(test_ref_number);");
console.log('test_ref_number.value === 100', test_ref_number.value === 100);
console.log(" ");

test_ref_number++;
console.log("test_ref_number++;");
console.log('test_ref_number.value === 101', test_ref_number.value === 101);
console.log(" ");

test_ref_number = test_ref_number - 10;
console.log("test_ref_number = test_ref_number - 10;");
console.log('test_ref_number.value === 91', test_ref_number.value === 91);

console.log(" ");
console.log("---------");
console.log(" ");

rvar('test_ref_str', 'a');
console.log("rvar('test_ref_str', 'a');");
console.log('test_ref_str.value === "a"', test_ref_str.value === 'a');
console.log(" ");

test_ref_str += 'bc';
console.log("test_ref_str += 'bc';");
console.log('test_ref_str.value === "abc"', test_ref_str.value === 'abc');

Test Konsolu Sonucu

rvar('test_ref_number');
test_ref_number = 5;
function Fn1 (v) { v.value = 100; }
test_ref_number.value === 5 true

Fn1(test_ref_number);
test_ref_number.value === 100 true

test_ref_number++;
test_ref_number.value === 101 true

test_ref_number = test_ref_number - 10;
test_ref_number.value === 91 true

---------

rvar('test_ref_str', 'a');
test_ref_str.value === "a" true

test_ref_str += 'bc';
test_ref_str.value === "abc" true 

5

Referans yoluyla herhangi bir (yerel, ilkel) değişkeni iletmek için yine başka bir yaklaşım, değişkeni "anında" kapatarak sarmaktır eval. Bu aynı zamanda "katı kullanın" ile de çalışır. (Not: evalJS optimize edicilerine uygun olmadığını unutmayın , ayrıca değişken adının etrafındaki tırnak işaretleri de öngörülemeyen sonuçlara neden olabilir)

"use strict"

//return text that will reference variable by name (by capturing that variable to closure)
function byRef(varName){
    return "({get value(){return "+varName+";}, set value(v){"+varName+"=v;}})";
}

//demo

//assign argument by reference
function modifyArgument(argRef, multiplier){
    argRef.value = argRef.value * multiplier;
}

(function(){

var x = 10;

alert("x before: " + x);
modifyArgument(eval(byRef("x")), 42);
alert("x after: " + x);

})()

Canlı örnek https://jsfiddle.net/t3k4403w/


Bu harika
hard_working_ant

3

Aslında oldukça hoş bir çözüm var:

function updateArray(context, targetName, callback) {
    context[targetName] = context[targetName].map(callback);
}

var myArray = ['a', 'b', 'c'];
updateArray(this, 'myArray', item => {return '_' + item});

console.log(myArray); //(3) ["_a", "_b", "_c"]

3

Ben şahsen çeşitli programlama dilleri tarafından sunulan "referans ile geçmek" işlevselliğini sevmiyorum. Belki de sadece fonksiyonel programlama kavramlarını keşfediyorum, ancak yan etkilere neden olan fonksiyonları gördüğümde (referansla geçirilen parametreleri değiştirmek gibi) her zaman tüylerim diken diken oluyor. Ben şahsen "tek sorumluluk" ilkesini benimsiyorum.

IMHO, bir işlev return anahtar sözcüğünü kullanarak yalnızca bir sonuç / değer döndürmelidir. Bir parametre / bağımsız değişkeni değiştirmek yerine, sadece değiştirilmiş parametre / bağımsız değişken değerini döndürür ve istenen yeniden atamaları çağrı koduna bırakırım.

Ancak bazen (umarım çok nadiren), aynı işlevden iki veya daha fazla sonuç değeri döndürmek gerekir. Bu durumda, elde edilen tüm değerleri tek bir yapıya veya nesneye dahil etmeyi tercih ederim. Yine, yeniden atamaların işlenmesi çağrı koduna kadar olmalıdır.

Misal:

Değişken parametrelerinin, argüman listesinde 'ref' gibi özel bir anahtar kelime kullanılarak destekleneceğini varsayalım. Kodum şöyle görünebilir:

//The Function
function doSomething(ref value) {
    value = "Bar";
}

//The Calling Code
var value = "Foo";
doSomething(value);
console.log(value); //Bar

Bunun yerine, aslında böyle bir şey yapmayı tercih ederim:

//The Function
function doSomething(value) {
    value = "Bar";
    return value;
}

//The Calling Code:
var value = "Foo";
value = doSomething(value); //Reassignment
console.log(value); //Bar

Birden çok değer döndüren bir işlev yazmak gerektiğinde, ben de başvuru tarafından geçirilen parametreleri kullanmazsınız. Yani böyle kodu önlemek:

//The Function
function doSomething(ref value) {
    value = "Bar";

    //Do other work
    var otherValue = "Something else";

    return otherValue;
}

//The Calling Code
var value = "Foo";
var otherValue = doSomething(value);
console.log(value); //Bar
console.log(otherValue); //Something else

Bunun yerine, aslında bir nesnenin içindeki her iki yeni değeri de bu şekilde döndürmeyi tercih ederim:

//The Function
function doSomething(value) {
    value = "Bar";

    //Do more work
    var otherValue = "Something else";

    return {
        value: value,
        otherValue: otherValue
    };
}

//The Calling Code:
var value = "Foo";
var result = doSomething(value);
value = result.value; //Reassignment
console.log(value); //Bar
console.log(result.otherValue);

Bu kod örnekleri oldukça basitleştirilmiştir, ancak kabaca bu tür şeyleri nasıl ele alacağımı kabaca gösterir. Çeşitli sorumlulukları doğru yerde tutmama yardımcı oluyor.

Mutlu kodlama. :)


Bir şey atama ve yeniden atama (herhangi bir tür denetimi olmadan) bana tüylerim diken diken verir. Sorumluluk gelince, ben doSomething (), o şey yapmak, o şey yapmak artı bir nesne artı yapmak ve değişkenlerimi özellikleri atamak bekliyoruz. Diyelim ki aranması gereken bir dizi var.İkinci bir diziye eşleşen öğeleri istiyorum ve kaç tane bulundu bilmek istiyorum. Standart bir çözüm şöyle bir işlevi çağırmak olacaktır: 'var foundArray; if ((findStuff (inArray, & foundArray))> 1) {// süreç bulunduArray} '. Bu senaryonun hiçbir yerinde arzu edilmeyen veya ihtiyaç duyulan yeni, bilinmeyen bir nesne yoktur.
Elise van Looij

@ElisevanLooij Örnek durumda, findStuffsadece ortaya çıkan diziyi döndürmeyi tercih ederim . Sen de bir beyan etmek zorunda foundArraydoğrudan kendisine çıkan dizi atamak diye, değişken: var foundArray = findStuff(inArray); if (foundArray.length > 0) { /* process foundArray */ }. Bu, 1) çağrı kodunu daha okunabilir / anlaşılır hale getirir ve 2) findStuffyöntemin dahili işlevselliğini (ve dolayısıyla test edilebilirliğini) önemli ölçüde basitleştirerek, farklı (yeniden) kullanım senaryolarında / senaryolarında çok daha esnek hale getirir.
Bart Hofland

@ElisevanLooij Ancak, yeniden atama (benim cevap gibi) gerçekten bir "kod kokusu" olduğunu kabul ediyorum ve aslında ben de mümkün olduğunca bu önlemek istiyorum. Cevabımı konu hakkındaki gerçek fikrimi daha iyi yansıtacak şekilde düzenlemeyi (hatta yeniden formüle etmeyi) düşüneceğim. Tepkiniz için teşekkürler. :)
Bart Hofland

2

Bu tür bir şey yapmak için sözdizimi ile oynuyorum, ancak biraz alışılmadık bazı yardımcılar gerektirir. Hiçbir şekilde 'var' kullanmamakla başlar, ancak yerel bir değişken yaratan ve anonim bir geri çağırma yoluyla kapsamı tanımlayan basit bir 'DECLARE' yardımcısı ile başlar. Değişkenlerin nasıl bildirildiğini kontrol ederek, onları her zaman referans olarak geçirilebilmeleri için nesnelere sarmayı seçebiliriz. Bu, Eduardo Cuomo'nun yukarıdaki cevabından birine benzer, ancak aşağıdaki çözüm dizeleri değişken tanımlayıcılar olarak kullanmayı gerektirmez. İşte konsepti göstermek için bazı minimum kod.

function Wrapper(val){
    this.VAL = val;
}
Wrapper.prototype.toString = function(){
    return this.VAL.toString();
}

function DECLARE(val, callback){
    var valWrapped = new Wrapper(val);    
    callback(valWrapped);
}

function INC(ref){
    if(ref && ref.hasOwnProperty('VAL')){
        ref.VAL++; 
    }
    else{
        ref++;//or maybe throw here instead?
    }

    return ref;
}

DECLARE(5, function(five){ //consider this line the same as 'let five = 5'
console.log("five is now " + five);
INC(five); // increment
console.log("five is incremented to " + five);
});

2

aslında çok kolay,

sorun, klasik argümanları geçtikten sonra başka bir salt okunur bölgeye yayıldığınızı anlamaktır .

çözümleri JavaScript'in nesne yönelimli tasarımını kullanarak argümanları iletmektir,

argümanları küresel / kapsamlı bir değişkene koymakla aynı, ama daha iyi ...

function action(){
  /* process this.arg, modification allowed */
}

action.arg = [ ["empty-array"],"some string",0x100,"last argument" ];
action();

iyi bilinen zincirin tadını çıkarmak için bir şeyler de vaat edebilirsiniz : İşte her şey, söz benzeri bir yapıya sahip

function action(){
  /* process this.arg, modification allowed */
  this.arg = ["a","b"];
}

action.setArg = function(){this.arg = arguments; return this;}

action.setArg(["empty-array"],"some string",0x100,"last argument")()

ya da daha iyisi .. action.setArg(["empty-array"],"some string",0x100,"last argument").call()


this.argyalnızca actionörnek ile çalışır . Bu çalışmıyor.
Eduardo Cuomo

1

Javascript, bir işlev içindeki dizi öğelerini değiştirebilir (nesneye / diziye başvuru olarak geçirilir).

function makeAllPretty(items) {
   for (var x = 0; x < myArray.length; x++){
      //do stuff to the array
      items[x] = makePretty(items[x]);
   }
}

myArray = new Array(var1, var2, var3);
makeAllPretty(myArray);

İşte başka bir örnek:

function inc(items) {
  for (let i=0; i < items.length; i++) {
    items[i]++;
  }
}

let values = [1,2,3];
inc(values);
console.log(values);
// prints [2,3,4]

1

JS güçlü bir tip olmadığından, bu sırtta göründüğü gibi sorunları birçok farklı şekilde çözmenizi sağlar.

Ancak, sürdürülebilirlik açısından Bart Hofland ile aynı fikirde olmalıydım. İşlev, bağımsız değişkenlerle bir şeyler yapmalı ve sonucu döndürmelidir. Onları kolayca yeniden kullanılabilir hale getirir.

Değişkenlerin referansla iletilmesi gerektiğini düşünüyorsanız, daha iyi hizmet verebilirsiniz.


1

Referans yoluyla tartışmayı bir kenara bırakırsak, belirtilen soruya hala bir çözüm arayanlar şunları kullanabilir:

const myArray = new Array(var1, var2, var3);
myArray.forEach(var => var = makePretty(var));

0

Ne demek istediğini tam olarak biliyorum. Swift'teki aynı şey sorun olmayacak. Alt satırda letdeğil kullanın var.

İlkellerin değere göre geçmesi, ancak var iyineleme noktasındaki değerin anonim işleve kopyalanmadığı gerçeği, en azını söylemek oldukça şaşırtıcıdır.

for (let i = 0; i < boxArray.length; i++) {
  boxArray[i].onclick = function() { console.log(i) }; // correctly prints the index
}
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.