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

当前回答

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

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

其他回答

原语是按值传递的。但如果你只需要读取一个原语的值(在函数被调用时,值是不知道的),你可以传递一个函数,在你需要它的时候检索它的值。

function test(value) {
  console.log('retrieve value');
  console.log(value());
}

// call the function like this
var value = 1;
test(() => value);

函数参数通过值或共享传递,但在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变量是窗口对象的成员。您可以将引用作为窗口对象的成员访问。

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

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

你可以这样想:

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

var obj = { };

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

对象总是按引用传递,原语总是按值传递。只要将参数保持在对象的相同地址即可。

下面是一些代码来说明我的意思(在JavaScript沙箱中试试,比如https://js.do/)。

不幸的是,你不能只保留参数的地址;还保留了所有原始成员值。

a = { key: 'bevmo' };
testRetain(a);
document.write(' after function ');
document.write(a.key);


function testRetain (b)
{
    document.write(' arg0 is ');
    document.write(arguments[0].key);
    b.key = 'passed by reference';
    var retain = b; // Retaining the original address of the parameter

    // Address of left set to address of right, changes address of parameter
    b = {key: 'vons'}; // Right is a new object with a new address
    document.write(' arg0 is ');
    document.write(arguments[0].key);

    // Now retrieve the original address of the parameter for pass by reference
    b = retain;
    document.write(' arg0 is ');
    document.write(arguments[0].key);
}

结果:

Arg0是bevmo Arg0是vons Arg0是在引用传递的函数之后通过引用传递