基元类型(数字、字符串等)是通过值传递的,但对象是未知的,因为它们既可以通过值传递(如果我们认为持有对象的变量实际上是对对象的引用),也可以通过引用传递(当我们认为对象的变量持有对象本身)。

虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有JavaScript规范的摘录,该规范定义了与此相关的语义?


当前回答

如果您想要像其他语言一样的(正常)函数参数行为(传递值的副本)然后在传递到函数之前克隆对象:

function run()
{
    var test = [];
    test.push(1);

    console.log('before: '+test); // 1

    changeVariable(_.clone(test)); // (Note: I am using lodash _.clone() function)
 
    console.log('after: '+test); // 1 
}


function changeVariable(test2) {
  var test1 = test2;
  test1.push(2); 
  console.log('inside func:', test1);  // inside func: [1,2]
}   


run();    

其他回答

函数内部的简单值不会改变函数外部的值(通过值传递),而复杂值会(通过引用传递)。

function willNotChange(x) {

    x = 1;
}

var x = 1000;

willNotChange(x);

document.write('After function call, x = ' + x + '<br>'); // Still 1000

function willChange(y) {

    y.num = 2;
}

var y = {num: 2000};

willChange(y);
document.write('After function call y.num = ' + y.num + '<br>'); // Now 2, not 2000

这样想:它总是通过价值传递。然而,对象的值不是对象本身,而是对该对象的引用。

下面是一个例子,传递一个数字(一个原始类型)

function changePrimitive(val) {
    // At this point there are two '10's in memory.
    // Changing one won't affect the other
    val = val * 10;
}
var x = 10;
changePrimitive(x);
// x === 10

对对象重复此操作会产生不同的结果:

function changeObject(obj) {
    // At this point there are two references (x and obj) in memory,
    // but these both point to the same object.
    // changing the object will change the underlying object that
    // x and obj both hold a reference to.
    obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
// x === { val: 100 }

再举一个例子:

function changeObject(obj) {
    // Again there are two references (x and obj) in memory,
    // these both point to the same object.
    // now we create a completely new object and assign it.
    // obj's reference now points to the new object.
    // x's reference doesn't change.
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}

对于编程语言律师,我已经阅读了ECMAScript 5.1的以下部分(它比最新版本更容易阅读),并在ECMASript邮件列表中询问了它。

TL;DR:一切都是通过值传递的,但Objects的财产是引用,标准中令人毛骨悚然地缺少Object的定义。

参数列表的构造

第11.2.4节“参数列表”说明了生成仅由1个参数组成的参数列表的以下内容:

生产ArgumentList:AssignmentExpression的计算如下:让ref是赋值表达式的结果。让arg为GetValue(ref)。返回唯一项为arg的列表。

本节还列举了参数列表包含0或>1个参数的情况。

因此,所有的都是通过引用传递的。

对象财产的访问

第11.2.1节“财产访问人”

生产MemberExpression:MemberExpression[Expression]的计算如下:让baseReference作为计算MemberExpression的结果。让baseValue为GetValue(baseReference)。让propertyNameReference作为表达式求值的结果。让propertyNameValue为GetValue(propertyNameReference)。调用CheckObject强制(baseValue)。让propertyNameString为ToString(propertyNameValue)。如果正在计算的语法生成包含在严格模式代码中,则让strict为true,否则让严格是假的。返回一个Reference类型的值,其基值为baseValue,其引用名称为propertyNameString,其严格模式标志为strict。

因此,对象的财产始终可用作参考。

关于参考

第8.7节“引用规范类型”中描述了引用在语言中不是真正的类型——它们仅用于描述删除行为、类型和赋值运算符。

“对象”的定义

5.1版中定义了“对象是财产的集合”。因此,我们可以推断,对象的值是集合,但是关于集合的值是什么,在规范中定义得很差,需要一些努力才能理解。

确定某个对象是否“通过引用传递”的一个简单方法是您是否可以编写“交换”函数。例如,在C中,您可以执行以下操作:

void swap(int *i, int *j)
{
    int t;
    t = *i;
    *i = *j;
    *j = t;
}

如果你不能在JavaScript中做到这一点,那就不是“通过引用传递”。

在JavaScript中向函数传递参数类似于传递参数(按C中的指针值):

/*
The following C program demonstrates how arguments
to JavaScript functions are passed in a way analogous
to pass-by-pointer-value in C. The original JavaScript
test case by @Shog9 follows with the translation of
the code into C. This should make things clear to
those transitioning from C to JavaScript.

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

This produces the output:

10
changed
unchanged
*/

#include <stdio.h>
#include <stdlib.h>

struct obj {
    char *item;
};

void changeStuff(int *num, struct obj *obj1, struct obj *obj2)
{
    // make pointer point to a new memory location
    // holding the new integer value
    int *old_num = num;
    num = malloc(sizeof(int));
    *num = *old_num * 10;
    // make property of structure pointed to by pointer
    // point to the new value
    obj1->item = "changed";
    // make pointer point to a new memory location
    // holding the new structure value
    obj2 = malloc(sizeof(struct obj));
    obj2->item = "changed";
    free(num); // end of scope
    free(obj2); // end of scope
}

int num = 10;
struct obj obj1 = { "unchanged" };
struct obj obj2 = { "unchanged" };

int main()
{
    // pass pointers by value: the pointers
    // will be copied into the argument list
    // of the called function and the copied
    // pointers will point to the same values
    // as the original pointers
    changeStuff(&num, &obj1, &obj2);
    printf("%d\n", num);
    puts(obj1.item);
    puts(obj2.item);
    return 0;
}