基元类型(数字、字符串等)是通过值传递的,但对象是未知的,因为它们既可以通过值传递(如果我们认为持有对象的变量实际上是对对象的引用),也可以通过引用传递(当我们认为对象的变量持有对象本身)。
虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有JavaScript规范的摘录,该规范定义了与此相关的语义?
基元类型(数字、字符串等)是通过值传递的,但对象是未知的,因为它们既可以通过值传递(如果我们认为持有对象的变量实际上是对对象的引用),也可以通过引用传递(当我们认为对象的变量持有对象本身)。
虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有JavaScript规范的摘录,该规范定义了与此相关的语义?
当前回答
我会说这是通过复印件传递的-
考虑参数和变量对象是在函数调用开始时创建的执行上下文中创建的对象,传递到函数中的实际值/引用只存储在这个参数+变量对象中。
简单地说,对于基元类型,值在函数调用开始时被复制,对于对象类型,引用被复制。
其他回答
MDN文档对其进行了清晰的解释,但并不过于冗长:
函数调用的参数是函数的参数。参数按值传递给函数。如果功能发生变化参数的值,此更改不会在全局或调用函数。但是,对象引用也是值,并且它们是特殊的:如果函数更改引用对象的财产,该更改在函数外部可见,(…)
资料来源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description
语义!!设置具体的定义必然会使一些答案和注释不兼容,因为即使使用相同的单词和短语,它们也不会描述相同的内容,但克服这种困惑至关重要(尤其是对于新程序员)。
首先,并非每个人都能理解抽象的多个层次。学习过第四代或第五代语言的较新程序员可能很难围绕汇编语言或C语言程序员熟悉的概念进行思考,而不是通过指向指针的指针来实现。通过引用传递不仅仅意味着能够使用函数参数变量更改被引用对象。
变量:引用内存中特定位置值的符号的组合概念。在讨论细节时,这个术语通常太过沉重,不能单独使用。
符号:用于引用变量(即变量名称)的文本字符串。
值:存储在内存中并使用变量符号引用的特定位。
内存位置:存储变量值的位置。(位置本身由与存储在该位置的值不同的数字表示。)
函数参数:在函数定义中声明的变量,用于引用传递给函数的变量。
函数参数:调用方传递给函数的函数外部的变量。
对象变量:其基本基本值不是“对象”本身,而是指向内存中存储对象实际数据的另一个位置的指针(内存位置值)。在大多数更高一代语言中,“指针”方面通过在各种上下文中自动取消引用而被有效隐藏。
原始变量:其值为实际值的变量。即使这个概念也会因为各种语言的自动装箱和类似对象的上下文而变得复杂,但一般的想法是变量的值是由变量符号表示的实际值,而不是指向另一个内存位置的指针。
函数参数和参数不是一回事。此外,变量的值不是变量的对象(正如许多人已经指出的,但显然被忽略了)。这些区别对于正确理解至关重要。
通过值传递或通过共享调用(对于对象):函数参数的值被COPIED到另一个由函数参数符号引用的内存位置(无论它是在堆栈上还是在堆上)。换句话说,函数参数收到了传递参数值的副本。。。AND(关键)参数的值从不被调用函数更新/更改/更改。记住,对象变量的值不是对象本身,而是指向对象的指针,因此逐个传递对象变量会将指针复制到函数参数变量。函数参数的值指向内存中完全相同的对象。对象数据本身可以通过函数参数直接更改,但函数参数的值从未更新,因此在整个函数调用过程中,甚至在函数调用之后,它都将继续指向同一对象(即使其对象的数据已更改,或者如果函数参数被完全分配了不同的对象)。仅仅因为被引用的对象可通过函数参数变量更新,就断定函数参数是通过引用传递的,这是不正确的。
通过引用调用/传递:函数参数的值可以/将由相应的函数参数直接更新。如果有帮助,函数参数将成为参数的有效“别名”——它们有效地引用同一内存位置的相同值。如果函数参数是一个对象变量,则更改对象数据的能力与传递值情况没有区别,因为函数参数仍然指向与参数相同的对象。但是在对象变量的情况下,如果函数参数设置为完全不同的对象,那么参数同样也会指向不同的对象——这在传递值的情况下不会发生。
JavaScript不通过引用传递。如果仔细阅读,你会发现所有相反的意见都误解了传递值的含义,他们错误地得出结论:通过函数参数更新对象数据的能力与“传递值”同义。
对象克隆/复制:创建新对象并复制原始对象的数据。这可以是深层副本或浅层副本,但重点是创建了一个新对象。创建对象副本是与传递值不同的概念。有些语言区分类对象和结构(或类似的),并且可能有不同的行为来传递不同类型的变量。但是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;
}
一切都是通过价值传递的。
基本类型按值传递(即,实际变量值的新副本传递给函数)。
复杂类型(对象)作为“对象指针”传递。因此,您传递的实际内容是一个指针,它是按值传递的(它是一个地址,一个像任何其他值一样的数值)。显然,如果您试图在函数内修改对象的属性,那么即使在该函数外,修改也会反映出来。这是因为您通过指向属性唯一副本的指针访问属性。
这里的混淆出现在“通过值传递指针”和“通过引用传递对象”上。
通过引用外部对象,将函数外部的对象传递到函数中。
当使用该引用操纵其对象时,外部的对象将受到影响。但是,如果在函数内部决定将引用指向其他对象,则根本不会影响外部对象,因为所做的只是将引用重新指向其他对象。