JavaScript是一种传递引用还是按值传递的语言?

Dan*_*hev 1311 javascript pass-by-reference pass-by-value

原始类型(Number,String等)按值传递,但是对象是未知的,因为它们可以是值传递(如果我们认为持有对象的变量实际上是对象的引用)并且通过引用传递(当我们认为对象的变量保存对象本身时).

虽然最后并不重要,但我想知道提交传递约定的参数的正确方法是什么.是否有JavaScript规范的摘录,它定义了与此相关的语义?

dew*_*rde 1531

它在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);
Run Code Online (Sandbox Code Playgroud)

这会产生输出:

10
changed
unchanged
Run Code Online (Sandbox Code Playgroud)
  • 如果它是纯粹的值传递,则更改obj1将不会影响obj1.item函数的外部.
  • 如果它是纯粹的参考传递,那么一切都会改变.obj1num,并100会读obj2.item.

相反,情况是传入的项目是按值传递的.但是通过值传递的项本身就是一个引用.从技术上讲,这称为分享呼叫.

实际上,这意味着如果您更改参数本身(与"changed"和一样num),则不会影响输入参数的项目.但是如果你更改参数的INTERNALS,它将传播回来(如同obj2).

  • 真正的原因是在changeStuff中,num,obj1和obj2是引用.当您更改obj1引用的对象的`item`属性时,您将更改最初设置为"未更改"的item属性的值.当您为obj2指定值{item:"changed"}时,您正在更改对新对象的引用(当函数退出时,它立即超出范围).如果你将函数命名为numf,obj1f和obj2f等函数,那么会发生什么变得更加明显.然后你看到params隐藏了外部var名称. (290认同)
  • 虽然这个答案是最受欢迎的,但它可能会有些混乱,因为它说"如果它是纯粹的价值传递".JavaScript*是纯粹的传值.但传递的值是参考.这根本不限于参数传递.您可以简单地通过`var obj1 = {item:'unchanged'}复制变量; var obj2 = obj1; obj2.item ='changed';`并且会观察到与你的例子中相同的效果.因此,我亲自参考蒂姆古德曼的答案 (53认同)
  • 我认为这也用在Java中:按值引用. (48认同)
  • 这与C#完全相同(或至少在语义上).Object有两种类型:Value(基本类型)和Reference. (30认同)
  • @BartoNaz不是真的.你想要的是通过引用传递引用,而不是通过值传递引用.但JavaScript总是按值传递引用,就像它通过值传递其他所有内容一样.(为了比较,C#具有类似于JavaScript和Java的pass-reference-by-value行为,但允许您使用`ref`关键字指定pass-reference-by-reference.)通常,您只需让函数返回新对象,并在您调用该函数的位置执行赋值.例如,`foo = GetNewFoo();`而不是`GetNewFoo(foo);` (13认同)
  • 我想你也可以通过将对象分配给某个包装器对象的属性来解决这个问题.然后传入包装器对象,并更新其属性以指向新对象.(当然,任何其他包含旧对象引用的变量仍然会保留对旧对象的引用.)但是,通常我建议只返回新对象 - 它会更清楚地传达您的意图,并保持您的功能没有副作用. (6认同)
  • 哎呀!JavaScript是一种纯粹的价值转换语言.只是在不处理原语时传递的值是引用. (5认同)
  • 它只是我或其他人觉得Javascript在Reference传递中非常"奇怪"......? (2认同)
  • Upvoted.我绊倒了这个,认为"通过引用传递"的事情是真的通过引用传递 - 我认为更新引用将覆盖原始更新(更新所有引用).现在我明白了.现在,我想我可以修改我的代码! (2认同)
  • @chiccodoro说"它是*纯*传递价值,除了你通过价值传递参考"在实际问这个问题的任何人的水平上令人困惑.在"观察到的行为"术语中,我希望"纯粹的"传值(在该术语中有任何意义)是深层复制.显然它在"实现JavaScript引擎"级别实现为"纯粹的传值",但是如果你在那个级别工作,你就不会问这个问题(希望可能会请) (2认同)
  • @jgmjgm不是“按引用传递”的意思。我承认这很令人困惑,但是术语是在编程语言具有诸如对象之类的东西之前创造出来的。“按引用传递”意味着被调用的环境可以在调用环境中更改变量的值。请注意,我写的是“变量的值”-我的意思不是对象的值,而是变量,就像JavaScript中用`var`声明的符号一样。因为这是不可能的,所以JavaScript没有引用传递。(C ++通过`&`参数进行排序。) (2认同)
  • 请参阅下面的Tim Goodman的答案。它是正确的。始终按值传递。 (2认同)

Tim*_*man 440

它总是按值传递,但对于对象,变量的值是引用.因此,当您传递一个对象并更改其成员时,这些更改将在该函数之外保留.这使它看起来像通过引用传递.但是,如果您实际更改了对象变量的值,您将看到更改不会持续存在,从而证明它确实是通过值传递的.

例:

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 */
Run Code Online (Sandbox Code Playgroud)

输出:

before changeObject: foo
in changeObject: bar
after changeObject: foo

before changeMember: foo
in changeMember: bar
after changeMember: bar
Run Code Online (Sandbox Code Playgroud)

  • 换句话说,这里令人困惑的事情不是传递值/传递引用.一切都是价值传递,完全停止.令人困惑的是,您无法传递对象,也无法将对象存储在变量中.每次你想*你正在做这件事,你实际上*传递或存储对该对象的引用.但是当你去访问它的成员时,会发生一个静默的解除引用,这会使你的变量保持实际对象的虚构永存. (37认同)
  • @daylight:实际上,你错了; 如果它被const ref传递,试图做changeObject会导致错误,而不仅仅是失败.尝试在C++中为const引用分配一个新值,编译器拒绝它.在用户术语中,这是传递值和传递const引用之间的差异. (13认同)
  • @adityamenon很难回答"为什么",但我会注意到Java和C#的设计者做出了类似的选择; 这不仅仅是一些JavaScript的奇怪之处.实际上,它非常一致地传递价值,让人们感到困惑的是价值可以作为参考.它与在C++中传递指针(通过值)然后解除引用以设置成员没有多大区别.没有人会对这种变化持续存在感到惊讶.但是因为这些语言抽象了指针并默默地为你解除引用,人们会感到困惑. (9认同)
  • @daylight:这不是常数.在`changeObject`中,我改变了`x`以包含对新对象的引用.`x = {member:"bar"};`相当于`x = new Object(); x.member ="bar";顺便说一下,我所说的C#也是如此. (5认同)
  • @daylight:对于C#,你可以从函数外部看到这个,如果你使用`ref`关键字,你可以通过引用传递引用(而不是通过值传递引用的默认值),然后将更改指向一个`new Object()`*将*持续存在. (2认同)
  • 我认为这和Java中的参数传递是一样的。 (2认同)

Sho*_*og9 144

变量不"保持"对象,它保存一个引用.您可以将该引用分配给另一个变量,现在它们都引用同一个对象.它总是按值传递(即使该值是参考值......).

没有办法改变作为参数传递的变量所持有的值,如果JS支持通过引用传递,这将是可能的.

  • 作者意味着通过传递引用,您传递的是一个引用值(另一种方式是认为它传递了内存地址的值).这就是为什么如果你重新声明对象,原始版本不会改变,因为你在不同的内存位置创建一个新对象.如果更改属性,则原始对象会更改,因为您在原始内存位置(未重新分配)更改了它. (5认同)
  • 这让我有点困惑.是不是通过参考传递参考? (2认同)

Ray*_*rea 97

我的2分......这是我理解的方式.(如果我错了,请随意纠正我)

是时候抛弃你所知道的关于通过值/参考传递的所有内容了.

因为在JavaScript中,无论是通过值传递还是通过引用或其他任何方式传递都无关紧要.重要的是突变与传递给函数的参数的赋值.

好的,让我尽力解释我的意思.假设你有几个对象.

var object1 = {};
var object2 = {};
Run Code Online (Sandbox Code Playgroud)

我们所做的是"赋值"......我们已经为变量"object1"和"object2"分配了2个单独的空对象.

现在,让我们说我们更喜欢object1 ......所以,我们"分配"一个新的变量.

var favoriteObject = object1;
Run Code Online (Sandbox Code Playgroud)

接下来,无论出于何种原因,我们都认为我们更喜欢对象2.所以,我们只是做一点重新分配.

favoriteObject = object2;
Run Code Online (Sandbox Code Playgroud)

object1或object2没有任何反应.我们根本没有改变任何数据.我们所做的就是重新分配我们最喜欢的对象.重要的是要知道object2和favoriteObject都被分配给同一个对象.我们可以通过这些变量之一来改变那个对象.

object2.name = 'Fred';
console.log(favoriteObject.name) // Logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // Logs Joe
Run Code Online (Sandbox Code Playgroud)

好的,现在让我们看一下像字符串这样的原语

var string1 = 'Hello world';
var string2 = 'Goodbye world';
Run Code Online (Sandbox Code Playgroud)

再次,我们选择一个最喜欢的.

var favoriteString = string1;
Run Code Online (Sandbox Code Playgroud)

我们的favoriteString和string1变量都分配给'Hello world'.现在,如果我们想改变我们最喜欢的字符串怎么办?会发生什么???

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'
Run Code Online (Sandbox Code Playgroud)

呃哦......发生了什么事.我们无法通过更改favoriteString更改string1 ...为什么?因为字符串是不可变的,我们没有改变它.我们所做的只是"RE ASSIGN"favoriteString到一个新的字符串.这基本上与string1断开了连接.在前面的示例中,当我们重命名对象时,我们没有分配任何内容.(嗯,实际上......我们做了,我们将name属性分配给一个新字符串.)相反,我们只是改变了保持2个变量和底层对象之间连接的对象.

现在,转到函数和传递参数....当你调用一个函数,并传递一个参数时,你实际上做的是"赋值"到一个新变量,它的工作方式与你简单地使用等号(=).

拿这些例子.

var myString = 'hello';

// Assign to a new variable (just like when you pass to a function)
var param1 = myString;
param1 = 'world'; // Re assignment

console.log(myString); // Logs 'hello'
console.log(param1);   // Logs 'world'
Run Code Online (Sandbox Code Playgroud)

现在,同样的事情,但有一个功能

function myFunc(param1) {
    param1 = 'world';

    console.log(param1);   // Logs 'world'
}

var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString);

console.log(myString); // logs 'hello'
Run Code Online (Sandbox Code Playgroud)

好的,现在让我们举几个使用对象的例子......首先,没有函数.

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;

// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl

console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'

// Now, let's reassign the variable
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object has no influence on the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';
Run Code Online (Sandbox Code Playgroud)

现在,同样的事情,但有一个函数调用

function myFunc(otherObj) {

    // Let's mutate our object
    otherObj.firstName = 'Sue';
    console.log(otherObj.firstName); // Logs 'Sue'

    // Now let's re-assign
    otherObj = {
        firstName: 'Jack',
        lastName: 'Frost'
    };
    console.log(otherObj.firstName); // Logs 'Jack'

    // Again, otherObj and myObject are assigned to 2 very different objects
    // And mutating one object doesn't magically mutate the other
}

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);

console.log(myObject.firstName); // Logs 'Sue', just like before
Run Code Online (Sandbox Code Playgroud)

好的,如果你仔细阅读整篇文章,也许你现在可以更好地理解函数调用在javascript中的工作原理.无论是通过引用还是通过值传递的东西都没关系......重要的是赋值与变异.

每次将变量传递给函数时,您都会"分配"参数变量的名称,就像使用等号(=)一样.

永远记住,等号(=)表示赋值.永远记住,将参数传递给函数也意味着赋值.它们是相同的,2个变量以完全相同的方式连接.

修改变量影响不同变量的唯一时间是基础对象发生变异时.

在对象和基元之间进行区分是没有意义的,因为它的工作方式与没有函数的情况完全相同,只是使用等号分配给新变量.

唯一的问题是当您传递给函数的变量名称与函数参数的名称相同时.当发生这种情况时,你必须将函数内部的参数视为一个私有函数的全新变量(因为它是)

function myFunc(myString) {
    // myString is private and does not affect the outer variable
    myString = 'hello';
}

var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';

myFunc(myString);
console.log(myString); // Logs 'test'
Run Code Online (Sandbox Code Playgroud)

  • 对于任何 C 程序员,想想 char*。`foo(char *a){a="hello";}` 什么都不做,但是如果你做了 `foo(char *a){a[0]='h';a[1]='i';a [2]=0;}` 它在外面被改变了,因为`a` 是一个内存位置,它是通过引用一个字符串(字符数组)的值传递的。允许在 C 中按值传递结构(类似于 js 对象),但不建议这样做。JavaScript 只是强制执行这些最佳实践,并隐藏了不必要的和通常不受欢迎的杂物......它确实使阅读更容易。 (2认同)
  • 这是正确的 - 术语 *pass-by-value* 和 *pass-by-reference* 在编程语言设计中具有含义,而这些含义与对象突变完全无关。这都是关于函数参数如何工作的。 (2认同)
  • 现在我明白 obj1 = obj2 意味着 obj1 和 obj2 现在都指向相同的引用位置,如果我修改 obj2 的内部结构,引用 obj1 将暴露相同的内部结构。我如何复制一个对象,这样当我做 `source = {"id":"1"}; copy = source /*这是错误的*/; copy.id="2"` 那源还是 {"id":"1"}? (2认同)

geg*_*geg 66

考虑以下:

  1. 变量是指向内存中值的指针.
  2. 重新分配变量只会将指针指向新值.
  3. 重新分配变量绝不会影响指向同一对象的其他变量

因此,忘记"通过引用/值传递"不要挂起"通过引用/值传递",因为:

  1. 这些术语仅用于描述语言的行为,而不一定是实际的底层实现.由于这种抽象,对于正确解释至关重要的关键细节会丢失,这不可避免地导致当前的情况,即单个术语无法充分描述实际行为,必须提供补充信息
  2. 这些概念最初并不是为了特别描述javascript而定义的,因此我不觉得在它们只会增加混乱时不得不使用它们.

回答你的问题:指针被传递.


// code
var obj = {
    name: 'Fred',
    num: 1
};

// illustration
               'Fred'
              /
             /
(obj) ---- {}
             \
              \
               1
Run Code Online (Sandbox Code Playgroud)


// code
obj.name = 'George';


// illustration
                 'Fred'


(obj) ---- {} ----- 'George'
             \
              \
               1
Run Code Online (Sandbox Code Playgroud)


// code
obj = {};

// illustration
                 'Fred'


(obj)      {} ----- 'George'
  |          \
  |           \
 { }            1
Run Code Online (Sandbox Code Playgroud)


// code
var obj = {
    text: 'Hello world!'
};

/* function parameters get their own pointer to 
 * the arguments that are passed in, just like any other variable */
someFunc(obj);


// illustration
(caller scope)        (someFunc scope)
           \             /
            \           /
             \         /
              \       /
               \     /
                 { }
                  |
                  |
                  |
            'Hello world'
Run Code Online (Sandbox Code Playgroud)

一些最终评论:

  • 人们很容易认为原语是由特殊规则强制执行而对象不是,但原语只是指针链的末端.
  • 作为最后一个示例,请考虑清除数组的常见尝试无法按预期工作的原因.


var a = [1,2];
var b = a;

a = [];
console.log(b); // [1,2]
// doesn't work because `b` is still pointing at the original array
Run Code Online (Sandbox Code Playgroud)

  • 没有必要_忘记“通过引用/值传递”_!这些术语具有历史意义,可以准确描述您试图描述的内容。如果我们抛弃了历史术语和定义,并且懒得去了解它们最初的含义,那么我们就失去了代际之间有效沟通的能力。没有什么好方法来讨论不同语言和系统之间的差异。相反,新程序员需要学习和理解传统术语以及它们的来源和原因。否则,我们就会集体失去知识和理解。 (3认同)

use*_*764 23

函数外部的对象通过提供对外部obejct的引用而传递给函数.当您使用该引用来操纵其对象时,外部对象因此受到影响.但是,如果在函数内部您决定将引用指向其他内容,则根本不会影响外部对象,因为您所做的只是将引用重定向到其他内容.


Phi*_*der 19

可以这样想:它总是按价值传递.但是,对象的值不是对象本身,而是对该对象的引用.

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

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
Run Code Online (Sandbox Code Playgroud)

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

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 }
Run Code Online (Sandbox Code Playgroud)

还有一个例子:

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}
Run Code Online (Sandbox Code Playgroud)


igo*_*gor 15

关于复制很详细的说明,传递和值与基准进行比较是在本章 "JavaScript权威指南"的书.

在我们通过引用离开操作对象和数组的主题之前,我们需要清除一个命名点.短语"通过引用传递"可以有几个含义.对于某些读者来说,该短语指的是一种函数调用技术,它允许函数为其参数赋值新值,并在函数外部显示这些修改后的值.这不是本书中使用的术语.这里,我们只是简单地将对象或数组的引用 - 而不是对象本身 - 传递给函数.函数可以使用引用来修改对象或数组元素的属性.但是,如果函数使用对新对象或数组的引用覆盖引用,则该修改在函数外部不可见.熟悉该术语其他含义的读者可能更喜欢说对象和数组是按值传递的,但传递的值实际上是一个引用而不是对象本身.

有一件事我还是想不通.检查下面的代码.有什么想法吗?

__PRE__


小智 14

Javascript总是按值传递,一切都是有价值的类型.对象是值,对象的成员函数本身就是值(请记住,函数是Javascript中的第一类对象).此外,关于Javascript中的所有内容都是对象的概念,这是错误的.字符串,符号,数字,布尔值,空值和未定义是原语.有时他们可以利用从基础原型继承的一些成员函数和属性,但这只是为了方便,并不意味着它们本身就是对象.请尝试以下内容以供参考

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);
Run Code Online (Sandbox Code Playgroud)

在两个警报中,您都会发现未定义的值.

  • @Nick:总是按值传递.期.对象的引用通过值*传递给函数.这不是通过引用传递."通过引用传递"几乎可以被认为是传递变量本身,而不是它的价值;*任何*更改函数对参数的更改(包括完全用不同的对象替换它!)将反映在调用者中.最后一点在JS中是不可能的,因为*JS不通过引用传递* - 它按值传递引用.区别是微妙的,但对于理解其局限性非常重要. (35认同)
  • -1,它并不总是按值传递.从MDC:"如果传递一个对象(即非原始值,如Array或用户定义的对象)作为参数,则对该对象的引用将传递给该函数." (12认同)
  • MDN是用户编辑的维基,它在那里是错误的.规范性参考文献是ECMA-262.参见S. 8"参考规范类型",它解释了如何解析引用,以及8.12.5"[[Put]]",用于解释AssignmentExpression到Reference,以及对象coersion 9.9 ToObject.对于原始值,Michael已经解释了ToObject的功能,如规范中所述.但另见s.4.3.2原始价值. (9认同)
  • @WonderLand:不,他不是。从来不会通过引用传递的人可能永远不会理解通过引用传递和通过值传递引用之间的区别。但它们就在那里,它们很重要。我不在乎误导人们只是因为这听起来更容易。 (2认同)

zan*_*ngw 12

在JavaScript中,值的类型控制是通过value-copy还是通过引用副本分配该.

始终通过value-copy分配/传递原始值:

  • null
  • undefined
  • 布尔
  • 符号 ES6

复合值始终由引用副本分配/传递

  • 对象
  • 阵列
  • 功能

例如

var a = 2;
var b = a; // `b` is always a copy of the value in `a`
b++;
a; // 2
b; // 3

var c = [1,2,3];
var d = c; // `d` is a reference to the shared `[1,2,3]` value
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]
Run Code Online (Sandbox Code Playgroud)

在上面的代码片段中,因为2是标量基元,a保存该值的一个初始副本,并为该值b分配另一个副本.更改时b,您无法更改其中的值a.

但两者cd是同一共享值单独的引用[1,2,3],这是一个复合值.重要的是要注意,既不cd"拥有" [1,2,3]值也不是"拥有" - 两者都是对值的等同对等引用.因此,当使用任一引用来修改(.push(4))实际的共享array值本身时,它只影响一个共享值,并且两个引用都将引用新修改的值[1,2,3,4].

var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]

// later
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]
Run Code Online (Sandbox Code Playgroud)

当我们进行赋值时b = [4,5,6],我们完全没有做什么影响a仍在引用([1,2,3])的位置.要做到这一点,b必须是指向a而不是引用array- 但JS中没有这样的功能!

function foo(x) {
    x.push( 4 );
    x; // [1,2,3,4]

    // later
    x = [4,5,6];
    x.push( 7 );
    x; // [4,5,6,7]
}

var a = [1,2,3];

foo( a );

a; // [1,2,3,4]  not  [4,5,6,7]
Run Code Online (Sandbox Code Playgroud)

当我们传入参数时a,它会将a引用的副本分配给x.x并且a是指向相同[1,2,3]值的单独引用.现在,在函数内部,我们可以使用该引用来改变值本身(push(4)).但是当我们进行赋值时x = [4,5,6],这决不会影响初始引用a指向的位置 - 仍然指向(现在修改的)[1,2,3,4]值.

array通过值复制有效地传递复合值(如a ),您需要手动复制它,以便传递的引用仍然不指向原始值.例如:

foo( a.slice() );
Run Code Online (Sandbox Code Playgroud)

可以通过引用副本传递的复合值(对象,数组等)

function foo(wrapper) {
    wrapper.a = 42;
}

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42
Run Code Online (Sandbox Code Playgroud)

这里,obj充当标量原始属性的包装器a.传递给时foo(..),传入obj引用的副本并设置为wrapper参数.我们现在可以使用该wrapper引用来访问共享对象,并更新其属性.函数完成后,obj.a将看到更新的值42.

资源


ash*_*deh 11

嗯,这是关于“性能”和“速度”以及编程语言中简单的“内存管理”这个词。

在 javascript 中,我们可以将值放在两层中:type1 -objectstype2 - 所有其他类型的值,例如string& boolean& 等

如果你把内存想象成下面的方块,在每个方块中只能保存一个 type2-value:

在此处输入图片说明

每个类型 2 值(绿色)都是一个正方形,而类型 1 值(蓝色)是一组它们

在此处输入图片说明

关键是,如果你想指示一个 type2-value,地址是简单的,但如果你想对 type1-value 做同样的事情,那根本就不容易!:

在此处输入图片说明

在一个更复杂的故事中:

在此处输入图片说明

所以这里的引用可以拯救我们: 在此处输入图片说明

虽然这里的绿色箭头是一个典型变量,紫色箭头是一个对象变量,所以因为绿色箭头(典型变量)只有一个任务(并且表示一个典型值)我们不需要将它的值与所以我们将带有值的绿色箭头移动到任何地方以及所有分配、函数等中......

但是我们不能用紫色箭头做同样的事情,我们可能想在这里移动“约翰”单元格或许多其他东西......,所以紫色箭头会坚持它的位置,只有分配给它的典型箭头才会移动...

一个非常令人困惑的情况是你无法意识到你引用的变量是如何变化的,让我们看一个很好的例子:

let arr = [1, 2, 3, 4, 5]; //arr is an object now and a purple arrow is indicating it
let obj2 = arr; // now, obj2 is another purple arrow that is indicating the value of arr obj
let obj3 = ['a', 'b', 'c'];
obj2.push(6); // first pic below - making a new hand for the blue circle to point the 6
//obj2 = [1, 2, 3, 4, 5, 6]
//arr = [1, 2, 3, 4, 5, 6]
//we changed the blue circle object value (type1-value) and due to arr and obj2 are indicating that so both of them changed
obj2 = obj3; //next pic below - changing the direction of obj2 array from blue circle to orange circle so obj2 is no more [1,2,3,4,5,6] and it's no more about changing anything in it but we completely changed its direction and now obj2 is pointing to obj3
//obj2 = ['a', 'b', 'c'];
//obj3 = ['a', 'b', 'c'];
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明 在此处输入图片说明


xam*_*mir 6

分享我对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
Run Code Online (Sandbox Code Playgroud)


Gia*_*ini 6

一切都是按值传递的。

基本类型按值传递(即实际变量值的新副本传递给函数)。

复杂类型(对象)作为“指向对象的指针”传递。因此,您传递的实际内容是一个按值传递的指针(它是一个地址,一个与其他值一样的数值)。显然,如果您尝试在函数内部修改对象的属性,则即使在该函数外部,该修改也会反映出来。这是因为您正在通过指向该属性的唯一副本的指针访问该属性。

“按值传递指针”和“按引用传递对象”是同一回事。


C P*_*ins 5

语义!!设置具体的定义必然会使一些答案和评论不兼容,因为即使使用相同的单词和短语,它们也不是描述相同的事物,但克服混淆至关重要(尤其是对于新程序员)。

首先,似乎并不是每个人都能掌握多个抽象层次。学习过第 4 代或第 5 代语言的新程序员可能难以将他们的思想围绕在汇编或 C 程序员熟悉的概念上,而不是通过指向指针的指针来分阶段进行。传递引用不仅仅意味着能够使用函数参数变量更改引用对象。

变量:符号的组合概念,它引用内存中特定位置的值。这个术语通常过于繁重,不能单独用于讨论细节。

符号:用于引用变量的文本字符串(即变量的名称)。

:存储在内存中并使用变量符号引用的特定位。

内存位置:存储变量值的位置。(位置本身由与存储在该位置的值分开的数字表示。)

函数参数:在函数定义中声明的变量,用于引用传递给函数的变量。

函数参数:函数外部的变量,由调用者传递给函数。

对象变量:其基本底层值不是“对象”本身的变量,而是其值是指向存储对象实际数据的内存中另一个位置的指针(内存位置值)。在大多数更高级的语言中,“指针”方面被各种上下文中的自动取消引用有效地隐藏了。

原始变量:其值为实际值的变量。甚至这个概念也可能因各种语言的自动装箱和类对象上下文而变得复杂,但一般的想法是变量的值是变量符号表示的实际值,而不是指向另一个内存位置的指针。

函数参数和参数不是一回事。此外,变量的值不是变量的对象(正如许多人已经指出的那样,但显然被忽略了)。这些区别对于正确理解至关重要。

按值传递或按共享调用(用于对象): 函数参数的值被复制到另一个内存位置,该位置由函数的参数符号引用(无论它是在堆栈上还是堆上)。换句话说,函数参数收到了传递参数值的副本......并且(关键)参数值从未被调用函数更新/更改/更改。请记住,对象变量的值不是对象本身,而是指向对象的指针,因此按值传递对象变量会复制指向函数参数变量的指针。函数参数的值指向内存中完全相同的对象。对象数据本身可以通过函数参数直接更改,但函数参数的值永远不会更新,因此它将继续指向相同的对象自始至终,甚至在函数调用之后(即使其对象的数据被更改,或者如果函数参数完全被分配了一个不同的对象)。仅仅因为被引用的对象可通过函数参数变量更新,就得出函数参数是通过引用传递的结论是不正确的。

调用/传递引用:函数参数的值可以/将被相应的函数参数直接更新。如果有帮助,函数参数将成为参数的有效“别名”——它们有效地引用相同内存位置的相同值。如果函数参数是对象变量,则更改对象数据的能力与按值传递的情况没有什么不同,因为函数参数仍将指向与参数相同的对象。但是在对象变量的情况下,如果函数参数被设置为一个完全不同的对象,那么参数同样也会指向不同的对象——这在传值情况下不会发生。

JavaScript 不通过引用传递。如果您仔细阅读,您会发现所有相反的观点都误解了按值传递的含义,并且他们错误地得出结论,通过函数参数更新对象数据的能力与“按值传递”是同义词。

对象克隆/复制:创建一个新对象并复制原始对象的数据。这可以是深拷贝或浅拷贝,但重点是创建了一个新对象。创建对象的副本是与值传递不同的概念。一些语言区分类对象和结构(或类似的),并且对于传递不同类型的变量可能有不同的行为。但是 JavaScript 在传递对象变量时不会自动执行此类操作。但是没有自动对象克隆并不能转化为传递引用。


geo*_*awg 5

JavaScript通过引用按值和对象类型传递基本类型

现在,人们喜欢无休止地争论"通过引用传递"是否是描述Java等人的正确方法.实际上.关键在于:

  1. 传递对象不会复制对象.
  2. 传递给函数的对象可以通过函数修改其成员.
  3. 传递给函数的原始值不能被函数修改.制作副本.

在我的书中,这被称为传递参考.

- Brian Bi - 哪些编程语言通过引用传递?


Ash*_*wat 5

这只是关于按值传递和按引用传递(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
Run Code Online (Sandbox Code Playgroud)
  • 适用于JavaScript中的所有原始类型(字符串,数字,布尔值,未定义和null)。
  • 为a分配了一个内存(例如0x001),而b在内存中创建了该值的副本(例如0x002)。
  • 因此,更改变量的值不会影响其他变量,因为它们都位于两个不同的位置。

通过引用传递(对象)

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" }
Run Code Online (Sandbox Code Playgroud)
  • JavaScript引擎将对象分配给变量c,并且它指向一些内存,例如(0x012)。
  • 当d = c时,在此步骤中d指向相同的位置(0x012)。
  • 更改任何变量的值都会更改两个变量的值。
  • 功能就是对象

特殊情况,通过引用传递(对象)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
Run Code Online (Sandbox Code Playgroud)
  • equal(=)运算符设置新的内存空间或地址


mig*_*elr 5

MDN 文档解释得很清楚,但又不太冗长:

函数调用的参数是函数的参数参数按值传递给函数。如果函数更改参数的值,则此更改不会在全局或调用函数中反映出来。但是,对象引用也是值,而且它们很特殊:如果函数更改引用对象的属性,则该更改在函数外部可见,(...)

来源:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description


Jon*_*lms 5

观察:如果观察者无法检查引擎的底层内存,则无法确定是复制不可变值还是传递引用。

JavaScript 或多或少与底层内存模型无关。没有参考²这样的东西。JavaScript 有。两个变量可以保持相同的(或更准确地说:两个环境记录可以绑定相同的值)。唯一可以改变的值类型是通过抽象 [[Get]] 和 [[Set]] 操作的对象。如果您忘记了计算机和内存,这就是描述 JavaScript 行为所需的全部内容,它使您能够理解规范。

 let a = { prop: 1 };
 let b = a; // a and b hold the same value
 a.prop = "test"; // The object gets mutated, can be observed through both a and b
 b = { prop: 2 }; // b holds now a different value
Run Code Online (Sandbox Code Playgroud)

现在您可能会问自己,两个变量如何在计算机上保持相同的值。然后,您可能会查看 JavaScript 引擎的源代码,您很可能会发现编写该引擎的语言的程序员会称之为参考的东西。

所以实际上你可以说 JavaScript 是“按值传递”,而值是可以共享的,你可以说 JavaScript 是“按引用传递”,这对于来自低级语言的程序员来说可能是一个有用的逻辑抽象,或者您可以将这种行为称为“通过共享调用”。

由于 JavaScript 中没有引用这样的东西,所有这些都没有错,也没有问题。因此,我不认为答案对搜索特别有用。

²说明书中的Reference不是传统意义上的引用。它是一个对象和属性名称的容器,它是一个中间值(例如,a.b计算为Reference { value = a, name = "b" })。术语参考有时也出现在规范的不相关部分。


Jac*_*ght -5

这里有一些关于 JavaScript 中术语“通过引用传递”的使用的讨论,但要回答你的问题:

对象自动通过引用传递,无需特别声明

(摘自上述文章。)

  • 链接的文章不再包含该声明,并完全避免使用“通过引用传递”。 (7认同)