基本类型(数字,字符串等)按值传递,但对象未知,因为它们都可以按值传递(如果我们认为保存对象的变量实际上是对该对象的引用) )和按引用传递(当我们认为对象的变量包含对象本身时)。
尽管最后并没有什么大不了,但我想知道呈现通过惯例的参数的正确方法是什么。是否有JavaScript规范的摘录,其中定义了与此相关的语义?
基本类型(数字,字符串等)按值传递,但对象未知,因为它们都可以按值传递(如果我们认为保存对象的变量实际上是对该对象的引用) )和按引用传递(当我们认为对象的变量包含对象本身时)。
尽管最后并没有什么大不了,但我想知道呈现通过惯例的参数的正确方法是什么。是否有JavaScript规范的摘录,其中定义了与此相关的语义?
一种简单的确定是否“通过引用传递”的方法是是否可以编写“交换”函数。例如,在C中,您可以执行以下操作:
void swap(int *i, int *j)
{
int t;
t = *i;
*i = *j;
*j = t;
}
如果您无法在JavaScript中完成等效操作,则它不是“通过引用传递”。
我会说这是通过副本-
考虑参数和变量对象是在函数调用开始时创建的执行上下文中创建的对象-传递给函数的实际值/引用仅存储在此参数+变量对象中。
简而言之,对于基本类型,值在函数调用的开始被复制,对于对象类型,引用被复制。
根据这两个条件,数组和对象按引用传递或按值传递。
如果要使用新的对象或数组更改该对象或数组的值,则按值传递。
object1 = {item: "car"};
array1=[1,2,3];
在这里,您将新对象或数组分配给旧对象或数组。您没有更改旧对象的属性值,因此按值传递。
如果要更改对象或数组的属性值,则通过引用传递它。
object1.key1= "car";
array1[0]=9;
在这里,您正在更改旧对象的属性值。您没有将新对象或数组分配给旧对象,因此它通过引用传递。
码
function passVar(object1, object2, number1) {
object1.key1= "laptop";
object2 = {
key2: "computer"
};
number1 = number1 + 1;
}
var object1 = {
key1: "car"
};
var object2 = {
key2: "bike"
};
var number1 = 10;
passVar(object1, object2, number1);
console.log(object1.key1);
console.log(object2.key2);
console.log(number1);
Output: -
laptop
bike
10
MDN文档对其进行了清晰的解释,而不必太冗长:
函数调用的参数是函数的参数。参数通过值传递给函数。如果函数更改了参数的值,则此更改不会在全局或调用函数中反映出来。但是,对象引用也是值,并且它们很特殊:如果函数更改了所引用对象的属性,则该更改在函数外部是可见的(...)
来源:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description
我已经多次阅读了这些答案,但是直到我了解Barbara Liskov所说的“共享通话”的技术定义后,我才真正理解它。
通过共享进行调用的语义与通过引用进行调用的不同之处在于,调用者看不到对函数内函数自变量的分配(不同于通过引用语义)[需要引用],因此例如,如果传递了变量,则不可能在调用者范围内模拟对该变量的分配。但是,由于该函数可以访问与调用方相同的对象(不进行任何复制),因此,如果对象可变,则这些对象的突变(如果该对象是可变的)对调用方可见,这似乎与按值调用有所不同语义。调用者可以看到函数中可变对象的突变,因为该对象没有被复制或克隆,而是共享的。
也就是说,如果您访问并访问参数值本身,则参数引用是可变的。另一方面,对参数的赋值将在评估后消失,并且函数调用者将无法访问。
我发现的最简洁的解释是在AirBNB样式指南中:
基元:访问基元类型时,可以直接使用其值
例如:
var foo = 1,
bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
复杂:访问复杂类型时,需要引用其值
例如:
var foo = [1, 2],
bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
也就是说,有效的原始类型是通过值传递的,而复杂类型是通过引用传递的。
对于编程语言律师,我阅读了ECMAScript 5.1的以下部分(比最新版本更易于阅读),并在ECMAScript邮件列表中进行了询问。
TL; DR:一切都是通过值传递的,但是Objects的属性是引用,并且标准中非常缺少Object的定义。
第11.2.4节“自变量列表”在生成仅包含1个自变量的自变量列表时说如下:
生产ArgumentList:AssignmentExpression的评估如下:
- 令ref为评估AssignmentExpression的结果。
- 令arg为GetValue(ref)。
- 返回唯一项为arg的列表。
本节还列举了参数列表包含0或> 1个参数的情况。
因此,所有内容都通过引用传递。
第11.2.1节“属性访问器”
生产的MemberExpression:MemberExpression [Expression]的计算如下:
- 令baseReference为评估MemberExpression的结果。
- 令baseValue为GetValue(baseReference)。
- 令propertyNameReference为计算Expression的结果。
- 设propertyNameValue为GetValue(propertyNameReference)。
- 调用CheckObjectCoercible(baseValue)。
- 设propertyNameString为ToString(propertyNameValue)。
- 如果要评估的语法产生包含在严格模式代码中,则使strict为真,否则使strict为假。
- 返回类型为Reference的值,其基值为baseValue,其引用名称为propertyNameString,其strict模式标志为strict。
因此,对象的属性始终可以用作参考。
在第8.7节“引用规范类型”中对此进行了描述,引用不是该语言中的实类型-它们仅用于描述delete,typeof和赋值运算符的行为。
在5.1版中定义“对象是属性的集合”。因此,我们可以推断出,对象的值就是集合,但是关于什么是集合的值在规范中定义得很差,需要一些努力才能理解。
在低级语言中,如果要通过引用传递变量,则必须在创建函数时使用特定的语法:
int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
*age = *age + 1;
}
该&age
是一个参考myAge
,但如果你想要的值,你必须参考转换,使用*age
。
Javascript是为您执行此转换的高级语言。因此,尽管对象是通过引用传递的,但语言会将引用参数转换为值。您不需要&
在函数定义上使用,而在*
函数体上通过引用来传递它,也不需要在函数体上使用,来将引用转换为值,JS会为您完成。
这就是为什么当您尝试通过替换函数的值(即age = {value:5}
)来更改对象时,更改不会持续存在,但是如果您更改其属性(即age.value = 5
),则更改仍然存在。
在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;
}
这只是有关按值传递和按引用传递(JavaScript)的更多说明。在这个概念中,他们正在谈论通过引用传递变量和通过引用传递变量。
按值传递(原始类型)
var a = 3;
var b = a;
console.log(a); // a = 3
console.log(b); // b = 3
a=4;
console.log(a); // a = 4
console.log(b); // b = 3
通过引用传递(对象)
var c = { "name" : "john" };
var d = c;
console.log(c); // { "name" : "john" }
console.log(d); // { "name" : "john" }
c.name = "doe";
console.log(c); // { "name" : "doe" }
console.log(d); // { "name" : "doe" }
c
,并且它指向一些内存,例如(0x012)。d
指向相同的位置(0x012)。特殊情况,通过引用传递(对象)
c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
分享我对JavaScript中引用的了解
在JavaScript中,对象存储为引用:
var a = {
a: 1,
b: 2,
c: 3
};
var b = a;
// b.c is referencing to a.c value
console.log(b.c) // Output: 3
// Changing value of b.c
b.c = 4
// Also changes the value of a.c
console.log(a.c) // Output: 4
JavaScript总是按值传递;一切都是价值类型。
对象是值,对象的成员函数本身就是值(请记住,函数是JavaScript中的一流对象)。另外,关于JavaScript中的所有内容都是对象的概念;这是错误的。字符串,符号,数字,布尔值,null和undefineds是基元。
有时他们可以利用从其基本原型继承的一些成员函数和属性,但这只是为了方便。这并不意味着它们本身就是对象。请尝试以下操作以供参考:
x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);
在这两个警报中,您都将找到未定义的值。
在“ JavaScript:权威指南”一书的这一章中,有关于按值复制和传递以及比较的非常详细的解释。
在离开通过引用操作对象和数组的主题之前,我们需要弄清楚术语的点。
短语“通过引用”可以具有多种含义。对某些读者而言,该短语指的是一种函数调用技术,该技术允许函数为其参数分配新值并使这些修改后的值在函数外部可见。这不是本书中使用该术语的方式。
在这里,我们的意思是简单地将对对象或数组的引用(而不是对象本身)传递给函数。函数可以使用引用来修改对象或数组元素的属性。但是,如果函数用对新对象或数组的引用覆盖了引用,则该修改在函数外部不可见。
熟悉该术语其他含义的读者可能更喜欢说对象和数组是通过值传递的,但是传递的值实际上是引用,而不是对象本身。
通过引用外部对象将函数外部的对象传递到函数中。
当您使用该引用操纵其对象时,外部对象因此受到影响。但是,如果在函数内部您决定将引用指向其他对象,那么您根本不会影响外部对象,因为您所做的只是将引用重定向到其他对象。
它总是按值传递,但是对于对象,变量的值是一个引用。因此,当您传递对象并更改其成员时,这些更改会在函数外部持久存在。这使其看起来像通过引用传递。但是,如果您实际上更改了对象变量的值,则会看到该更改不会持续存在,证明它确实是按值传递的。
例:
function changeObject(x) {
x = { member: "bar" };
console.log("in changeObject: " + x.member);
}
function changeMember(x) {
x.member = "bar";
console.log("in changeMember: " + x.member);
}
var x = { member: "foo" };
console.log("before changeObject: " + x.member);
changeObject(x);
console.log("after changeObject: " + x.member); /* change did not persist */
console.log("before changeMember: " + x.member);
changeMember(x);
console.log("after changeMember: " + x.member); /* change persists */
输出:
before changeObject: foo
in changeObject: bar
after changeObject: foo
before changeMember: foo
in changeMember: bar
after changeMember: bar
变量不会“保留”对象;它具有参考。您可以将该引用分配给另一个变量,现在两者都引用同一个对象。它总是按值传递(即使该值是引用...)。
无法更改作为参数传递的变量所持有的值,如果JavaScript支持通过引用传递,则可以实现。
JavaScript很有趣。考虑以下示例:
function changeStuff(a, b, c)
{
a = a * 10;
b.item = "changed";
c = {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);
产生输出:
10
changed
unchanged
obj1
根本不是参考,那么更改obj1.item
不会obj1
对函数的外部产生影响。num
会100
并且obj2.item
会阅读"changed"
。相反,情况是传入的项目是按值传递的。但是,按值传递的项目本身就是参考。从技术上讲,这称为共享呼叫。
实际上,这意味着如果您更改参数本身(如num
和一样obj2
),则不会影响输入该参数的项目。但是,如果您更改参数的INTERNALS,则会传播回来(与一样obj1
)。
基元按值传递,对象按引用传递。这与C,Visual Basic或Delphi等其他语言完全不同。我不能说它们如何精确地处理对象和基元,但是我知道Visual Basic和Delphi可以(并且应该)指定它们。
从版本5开始,PHP进行了类似的操作:所有对象都通过引用传递,但是所有原语都可以通过引用传递(如果前面带有&符号,则可以通过引用传递)。否则,基元将按值传递。
因此,在JavaScript中,如果我通过参数将对象X传递给函数,则它仍然是X。如果要更改函数内部的数据(或其他任何对象,但这并不重要),则在功能。