首页 文章

Javascript是否通过引用传递?

提问于
浏览
291

Javascript是通过引用传递还是通过值传递?以下是Javascript:The Good Parts的示例 . 关于矩形函数的 my 参数我很困惑 . 它实际上是 undefined ,并在函数内部重新定义 . 没有原始参考 . 如果我从函数参数中删除它,则内部区域功能无法访问它 .

这是关闭吗?但是没有返回任何函数 .

var shape = function (config) {
    var that = {};
    that.name = config.name || "";
    that.area = function () {
        return 0;
    };
    return that;
};
var rectangle = function (config, my) {
    my = my || {};
    my.l = config.length || 1;
    my.w = config.width || 1;
    var that = shape(config);
    that.area = function () {
        return my.l * my.w;
    };
    return that;
};
myShape = shape({
    name: "Unhnown"
});
myRec = rectangle({
    name: "Rectangle",
    length: 4,
    width: 6
});
console.log(myShape.name + " area is " + myShape.area() + " " + myRec.name + " area is " + myRec.area());

10 回答

  • 34

    基元按值传递,对象通过“引用副本”传递 .

    具体来说,当您传递一个对象(或数组)时,您(无形地)传递对该对象的引用,并且可以修改该对象的内容,但如果您尝试覆盖该引用,它将不会影响该对象的副本调用者持有的引用 - 即引用本身按值传递:

    function replace(ref) {
        ref = {};           // this code does _not_ affect the object passed
    }
    
    function update(ref) {
        ref.key = 'newvalue';  // this code _does_ affect the _contents_ of the object
    }
    
    var a = { key: 'value' };
    replace(a);  // a still has its original value - it's unmodfied
    update(a);   // the _contents_ of 'a' are changed
    
  • 1

    想想这样:

    每当你在ECMAscript中创建一个对象时,这个对象就形成了一个神秘的ECMAscript通用的地方,没有人能够得到它 . 你得到的只是在这个神秘的地方引用那个对象 .

    var obj = { };
    

    甚至 obj 也只是对象的引用(位于那个特别美妙的地方),因此,你只能传递这个引用 . 实际上,访问obj的任何代码都将修改远离的对象 .

  • 52

    我的2分钱...... Javascript是通过引用还是值传递参数无关紧要 . 真正重要的是分配与变异 .

    我在这里写了一个更长,更详细的解释(Is JavaScript a pass-by-reference or pass-by-value language?

    当你传递任何东西时(无论是对象还是原语),所有javascript都会在函数内部分配一个新变量...就像使用等号(=)一样

    该参数在函数内的行为方式与您使用等号分配新变量时的行为完全相同 . 请参考这些简单示例 .

    var myString = 'Test string 1';
    
    // Assignment - A link to the same place as myString
    var sameString = myString;
    
    // If I change sameString, it will not modify myString, 
    // it just re-assigns it to a whole new string
    sameString = 'New string';
    
    console.log(myString); // logs 'Test string 1';
    console.log(sameString); // logs 'New string';
    

    如果我将myString作为参数传递给函数,它的行为就好像我只是将它分配给一个新变量 . 现在,让我们做同样的事情,但是使用函数而不是简单的赋值

    function myFunc(sameString) {
    
        // Re assignment.. again, it will not modify myString
        sameString = 'New string';
    }
    
    var myString = 'Test string 1';
    
    // This behaves the same as if we said sameString = myString
    myFunc(myString);
    
    console.log(myString); // Again, logs 'Test string 1';
    

    将对象传递给函数时可以修改对象的唯一原因是因为您没有重新分配...而是可以更改或改变对象....再次,它的工作方式相同 .

    var myObject = { name: 'Joe'; }
    
    // Assignment - We simply link to the same object
    var sameObject = myObject;
    
    // This time, we can mutate it. So a change to myObject affects sameObject and visa versa
    myObject.name = 'Jack';
    console.log(sameObject.name); // Logs 'Jack'
    
    sameObject.name = 'Jill';
    console.log(myObject.name); // Logs 'Jill'
    
    // If we re-assign it, the link is lost
    sameObject = { name: 'Howard' };
    console.log(myObject.name); // Logs 'Jill'
    

    如果我将myObject作为参数传递给函数,它就像我只是将它分配给一个新变量一样 . 同样,具有完全相同的行为但具有功能的相同事物 .

    function myFunc(sameObject) {
    
        // We mutate the object, so the myObject gets the change too... just like before.
        sameObject.name = 'Jill';
    
        // But, if we re-assign it, the link is lost
        sameObject = { name: 'Howard' };
    }
    
    var myObject = { name: 'Joe'; }
    
    // This behaves the same as if we said sameObject = myObject;
    myFunc(myObject);
    console.log(myObject.name); // Logs 'Jill'
    

    每次将变量传递给函数时,您都会“分配”参数的名称,就像使用等号(=)一样 .

    永远记住,等号(=)表示赋值 . 将参数传递给函数也意味着赋值 . 它们是相同的,2个变量以完全相同的方式连接 .

    修改变量影响不同变量的唯一时间是基础对象发生变异时 .

    在对象和基元之间进行区分是没有意义的,因为它的工作方式与没有函数并使用等号分配给新变量的方式完全相同 .

  • 4

    与C一样,最终,一切都按 Value 传递 . 与C不同,您实际上无法备份并传递变量的位置,因为它没有指针只是引用 .

    它所引用的都是对象,而不是变量 . 有几种方法可以实现相同的结果,但必须手动完成,而不仅仅是在调用或声明站点添加关键字 .

  • 14

    函数参数通过-value或by-sharing传递,但在Javascript中通过引用从不 NEVER

    按值调用

    原始类型按值传递:

    var num = 123, str = "foo";
    
    function f(num, str) {
      num += 1;
      str += "bar";
      console.log("inside of f:", num, str);
    }
    
    f(num, str);
    console.log("outside of f:", num, str);
    

    函数范围内的 Reassignments 在周围范围内不可见 .

    这也适用于 String ,它是一种复合数据类型,但是不可变:

    var str = "foo";
    
    function f(str) {
      str[0] = "b"; // doesn't work, because strings are immutable
      console.log("inside of f:", str);
    }
    
    f(str);
    console.log("outside of f:", str);
    

    呼叫共享

    对象,也就是说所有非基元的类型都是通过共享传递的 . 保存对象引用的变量实际上只包含此引用的副本 . 如果Javascript将采用逐个引用的评估策略,则该变量将保留原始引用 . 这是副共享和参考之间的关键区别 .

    这种区别的实际后果是什么?

    var o = {x: "foo"}, p = {y: 123};
    
    function f(o, p) {
      o.x = "bar"; // mutation
      p = {x: 456}; // reassignment
      console.log("o inside of f:", o);
      console.log("p inside of f:", p);
    }
    
    f(o, p);
    
    console.log("o outside of f:", o);
    console.log("p outside of f:", p);
    

    Mutating 表示修改现有 Object 的某些属性 . 变量绑定到的引用副本和引用此对象的引用副本保持不变 . 因此,在呼叫者的范围内可以看到突变 .

    Reassigning 表示替换绑定到变量的引用副本 . 由于它只是一个副本,因此保存相同引用副本的其他变量不受影响 . 因此,在呼叫者的范围内,重新分配不像在参考呼叫评估策略中那样可见 .

    进一步有关Ecmascript中evaluation strategies的信息 .

  • 6

    JavaScript是按值传递的 . 对于原语,传递原始值 . 对于Objects,传递Object的引用“value” .

    对象示例:

    var f1 = function(inputObject){
        inputObject.a=2;
    }
    var f2 = function(){
        var inputObject={"a":1};
        f1(inputObject); 
        console.log(inputObject.a);
    }
    

    调用f2导致打印出“a”值为2而不是1,因为传递了引用并且更新了引用中的“a”值 .

    原语示例:

    var f1 = function(a){
        a=2;
    }
    var f2 = function(){
        var a =1;
        f1(a); 
        console.log(a);
    }
    

    调用f2会导致打印出“a”值为1 .

  • 3

    实际上,Alnitak是正确的并且易于理解,但最终在JavaScript中,一切都是通过 Value 传递的 .

    对象的“ Value ”是什么?它是对象参考 .

    传入对象时,您将获得此值的副本(因此,Alnitak描述的“引用副本”) . 如果更改此值,则不会更改原始对象,而是更改该引用的副本 .

  • 10

    为了创建一个使用const的简单示例......

    const myRef = { foo: 'bar' };
    const myVal = true;
    
    function passes(r, v) {
      r.foo = 'baz';
      v = false;
    }
    
    passes(myRef, myVal);
    
    console.log(myRef, myVal); // Object {foo: "baz"} true
    
  • 534

    “全局”javascript变量是window对象的成员 . 您可以作为窗口对象的成员访问引用 .

    var v = "initialized";
    function byref(ref) {
     window[ref] = "changed by ref";
    }
    byref((function(){for(r in window){if(window[r]===v){return(r);}}})());
    // could also be called like... byref('v');
    console.log(v); // outputs changed by ref
    

    注意,上面的示例不适用于函数内声明的变量 .

  • 4

    如果没有纯粹主义,我认为在Javascript中通过引用模拟标量参数的最佳方法是使用对象,就像之前的答案所说的那样 .

    但是,我有点不同:

    我在函数调用中进行了对象分配,因此可以看到函数调用附近的引用参数 . 它增加了源的可读性

    在函数声明中,我将属性设置为注释,原因完全相同:可读性 .

    var r;
    
    funcWithRefScalars(r = {amount:200, message:null} );
    console.log(r.amount + " - " + r.message);
    
    
    function funcWithRefScalars(o) {  // o(amount, message)
      o.amount  *= 1.2;
      o.message = "20% increase";
    }
    

    在上面的示例中, null 清楚地表示输出参考参数 .

    出口:

    240 - 20% Increase
    

    在客户端, console.log 应替换为 alert .

    ★★★

    另一种可以更具可读性的方法:

    var amount, message;
    
    funcWithRefScalars(amount = [200], message = [null] );
    console.log(amount[0] + " - " + message[0]);
    
    function funcWithRefScalars(amount, message) {  // o(amount, message)
       amount[0]  *= 1.2;
       message[0] = "20% increase";
    }
    

    在这里,您甚至不需要创建新的虚拟名称,例如上面的 r .

相关问题