基元类型(数字、字符串等)是通过值传递的,但对象是未知的,因为它们既可以通过值传递(如果我们认为持有对象的变量实际上是对对象的引用),也可以通过引用传递(当我们认为对象的变量持有对象本身)。
虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有JavaScript规范的摘录,该规范定义了与此相关的语义?
基元类型(数字、字符串等)是通过值传递的,但对象是未知的,因为它们既可以通过值传递(如果我们认为持有对象的变量实际上是对对象的引用),也可以通过引用传递(当我们认为对象的变量持有对象本身)。
虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有JavaScript规范的摘录,该规范定义了与此相关的语义?
当前回答
我理解这一点的简单方法。。。
调用函数时,您将传递内容(引用或值),而不是变量本身。var var1=13;var var2={prop:2};//13和var2的内容(引用)正在此处传递foo(var1,var2);在函数内部,参数变量inVar1和inVar2接收传递的内容。函数foo(inVar1,inVar2){//更改inVar1和inVar2的内容不会影响外部变量inVar1=20;inVar2={prop:7};}由于inVar2收到了{prop:2}的引用,因此可以更改对象属性的值。函数foo(inVar1,inVar2){inVar2.prop=7;}
其他回答
它总是按值传递,但对于对象,变量的值是一个引用。因此,当您传递一个对象并更改其成员时,这些更改将在函数之外持续存在。这使得它看起来像是通过引用传递。但如果您实际更改了对象变量的值,您将看到更改不会持续,这证明它确实是通过值传递的。
例子:
函数changeObject(x){x={member:“bar”};console.log(“in changeObject:”+x.member);}函数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);/*更改没有持续*/console.log(“changeMember之前:”+x.member);changeMember(x);console.log(“after changeMember:”+x.member);/*更改持续存在*/
输出:
before changeObject: foo
in changeObject: bar
after changeObject: foo
before changeMember: foo
in changeMember: bar
after changeMember: bar
基本体通过值传递,对象通过引用传递。这与C、Visual Basic或Delphi等其他语言截然不同。我不能说它们如何准确地处理对象和原语,但我知道VisualBasic和Delphi可以(也应该)指定它们。
PHP在版本5之后做了类似的事情:所有对象都是通过引用传递的,但所有原语都可以通过引用传递,如果前面有一个与符号(&)。否则,基元按值传递。
所以在JavaScript中,如果我通过一个参数将一个对象X传递给一个函数,它仍然是X。如果你在函数(或任何其他对象,但这并不重要)内部更改数据,那么新值也可以在函数外部使用。
语义!!设置具体的定义必然会使一些答案和注释不兼容,因为即使使用相同的单词和短语,它们也不会描述相同的内容,但克服这种困惑至关重要(尤其是对于新程序员)。
首先,并非每个人都能理解抽象的多个层次。学习过第四代或第五代语言的较新程序员可能很难围绕汇编语言或C语言程序员熟悉的概念进行思考,而不是通过指向指针的指针来实现。通过引用传递不仅仅意味着能够使用函数参数变量更改被引用对象。
变量:引用内存中特定位置值的符号的组合概念。在讨论细节时,这个术语通常太过沉重,不能单独使用。
符号:用于引用变量(即变量名称)的文本字符串。
值:存储在内存中并使用变量符号引用的特定位。
内存位置:存储变量值的位置。(位置本身由与存储在该位置的值不同的数字表示。)
函数参数:在函数定义中声明的变量,用于引用传递给函数的变量。
函数参数:调用方传递给函数的函数外部的变量。
对象变量:其基本基本值不是“对象”本身,而是指向内存中存储对象实际数据的另一个位置的指针(内存位置值)。在大多数更高一代语言中,“指针”方面通过在各种上下文中自动取消引用而被有效隐藏。
原始变量:其值为实际值的变量。即使这个概念也会因为各种语言的自动装箱和类似对象的上下文而变得复杂,但一般的想法是变量的值是由变量符号表示的实际值,而不是指向另一个内存位置的指针。
函数参数和参数不是一回事。此外,变量的值不是变量的对象(正如许多人已经指出的,但显然被忽略了)。这些区别对于正确理解至关重要。
通过值传递或通过共享调用(对于对象):函数参数的值被COPIED到另一个由函数参数符号引用的内存位置(无论它是在堆栈上还是在堆上)。换句话说,函数参数收到了传递参数值的副本。。。AND(关键)参数的值从不被调用函数更新/更改/更改。记住,对象变量的值不是对象本身,而是指向对象的指针,因此逐个传递对象变量会将指针复制到函数参数变量。函数参数的值指向内存中完全相同的对象。对象数据本身可以通过函数参数直接更改,但函数参数的值从未更新,因此在整个函数调用过程中,甚至在函数调用之后,它都将继续指向同一对象(即使其对象的数据已更改,或者如果函数参数被完全分配了不同的对象)。仅仅因为被引用的对象可通过函数参数变量更新,就断定函数参数是通过引用传递的,这是不正确的。
通过引用调用/传递:函数参数的值可以/将由相应的函数参数直接更新。如果有帮助,函数参数将成为参数的有效“别名”——它们有效地引用同一内存位置的相同值。如果函数参数是一个对象变量,则更改对象数据的能力与传递值情况没有区别,因为函数参数仍然指向与参数相同的对象。但是在对象变量的情况下,如果函数参数设置为完全不同的对象,那么参数同样也会指向不同的对象——这在传递值的情况下不会发生。
JavaScript不通过引用传递。如果仔细阅读,你会发现所有相反的意见都误解了传递值的含义,他们错误地得出结论:通过函数参数更新对象数据的能力与“传递值”同义。
对象克隆/复制:创建新对象并复制原始对象的数据。这可以是深层副本或浅层副本,但重点是创建了一个新对象。创建对象副本是与传递值不同的概念。有些语言区分类对象和结构(或类似的),并且可能有不同的行为来传递不同类型的变量。但是JavaScript在传递对象变量时不会自动执行类似的操作。但是,缺少自动对象克隆并不意味着通过引用传递。
对于编程语言律师,我已经阅读了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版中定义了“对象是财产的集合”。因此,我们可以推断,对象的值是集合,但是关于集合的值是什么,在规范中定义得很差,需要一些努力才能理解。
字符串、数字等基本类型变量始终作为传递按价值计算。基于这两个条件,数组和对象作为引用传递或值传递。如果要使用新的Object或array更改该Object或array的值,则它将通过value传递。object1={item:“car”};数组1=[1,2,3];此处是将新对象或数组分配给旧对象或数组。您没有更改属性的值旧对象的。因此它是按值传递的。如果要更改对象或数组的属性值,则会通过引用传递。object1.key1=“汽车”;阵列1[0]=9;此处您正在更改旧对象的属性值。您没有将新对象或数组分配给旧对象或数组。因此它是通过引用传递的。
Code
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