JavaScript是通过引用传递还是通过值传递?

下面是一个来自JavaScript: The Good Parts的例子。我对矩形函数的参数很困惑。它实际上是未定义的,并在函数内部重新定义。没有原始参考文献。如果我从函数参数中删除它,内部区域函数就不能访问它。

它是一个闭包吗?但是没有返回函数。

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());

当前回答

在人们试图证明这一点的例子中,我看不到参考传递。我只看到值传递。

对于包含对象引用的变量,引用就是那些变量的值,因此引用被传递,然后被值传递。

在这样的陈述中,

var a = {
  b: "foo",
  c: "bar"
};

“a”的值不是对象,而是(到目前为止唯一的)对它的引用。换句话说,对象不在变量a中——对它的引用在。我认为这对于只熟悉JavaScript的程序员来说似乎很难。但是对于懂Java、c#和C的人来说,这是很容易的。

其他回答

没有纯粹主义,我认为在JavaScript中通过引用模拟标量参数的最好方法是使用object,就像前面的答案告诉。

然而,我的做法有点不同:

我已经在函数调用内部进行了对象赋值,因此可以在函数调用附近看到引用形参。它增加了源代码的可读性。

在函数声明中,我像注释一样放置属性,出于同样的原因:可读性。

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。

函数参数通过值或共享传递,但在JavaScript中从来不通过引用!

"值

基元类型按值传递:

var num = 123, str = “foo”; 函数 f(num, str) { 数字 += 1; str += “bar”; console.log(“inside of f:”, num, str); } f(数字, str); console.log(“Out of f:”, num, str);

函数作用域中的重赋在周围的作用域中是不可见的。

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

Var STR = "foo"; 函数f(str) { Str [0] = "b";//不起作用,因为字符串是不可变的 Console.log ("in of f:", str); } f (str); Console.log ("outside of f:", str);

Call-by-Sharing

对象,也就是说所有不是基元的类型,都是通过共享传递的。保存对象引用的变量实际上只保存该引用的副本。如果JavaScript采用引用调用求值策略,则变量将保存原始引用。这是共享和引用之间的关键区别。

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

Var o = {x: "foo"}, p = {y: 123}; 函数f(o, p) { O.x = "bar";/ /突变 P = {x: 456};/ /重新分配 Console.log ("o within 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);

突变是指修改现有对象的某些属性。变量所绑定的引用副本和引用该对象的引用副本保持不变。因此,突变在调用者的作用域中是可见的。

重赋值意味着替换绑定到变量的引用副本。由于它只是一个副本,持有同一引用副本的其他变量不受影响。因此,重赋在调用者的作用域中是不可见的,就像在引用调用求值策略中那样。

关于ECMAScript中评估策略的进一步信息。

实际上,Alnitak是正确的,而且很容易理解,但最终在JavaScript中,一切都是通过值传递的。

对象的“值”是什么?它是对象引用。

当你传入一个对象时,你得到这个值的一个副本(因此Alnitak描述的“引用的副本”)。如果你改变了这个值,你不会改变原来的对象;您正在更改该引用的副本。

JavaScript是按值传递的。

对于原语,则传递原语的值。对于对象,传递对象的引用“值”。

带对象的示例:

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”值被更新。

使用primitive的示例:

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

调用f2会将“a”值打印为1。

“全局”JavaScript变量是窗口对象的成员。您可以将引用作为窗口对象的成员访问。

var v = "initialized";

function byref(ref) {
  window[ref] = "changed by ref";
}

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

注意,上面的例子不适用于函数中声明的变量。