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

当前回答

原语通过值传递,对象通过“引用的副本”传递。

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

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

其他回答

你可以这样想:

无论何时在ECMAscript中创建一个对象,这个对象都是在一个神秘的ECMAscript通用的地方形成的,没有人能够得到。你只能得到这个神秘地方的那个物体的参考。

var obj = { };

即使obj也只是对对象的引用(它位于那个特殊的位置),因此,你只能传递这个引用。实际上,任何访问obj的代码段都会修改很远很远的对象。

“全局”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

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

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 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中评估策略的进一步信息。

没有纯粹主义,我认为在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。