Вопрос: Является ли JavaScript перекрестной ссылкой или языком с переходом?


Примитивные типы (Number, String и т. Д.) Передаются по значению, но объекты неизвестны, поскольку они могут быть как переданными по значению (в случае, если мы считаем, что переменная, содержащая объект, на самом деле является ссылкой на объект ) и передается по ссылке (когда мы считаем, что переменная к объекту содержит сам объект).

Хотя в конце концов это не имеет большого значения, я хочу знать, каков правильный способ представления условных условных аргументов. Есть ли выдержка из спецификации JavaScript, которая определяет, какова должна быть семантика в этом отношении?


1120


источник


Ответы:


Это интересно в 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.itemне повлияет на obj1вне функции.
  • Если бы это был чистый проход по ссылке, тогда все изменилось бы. numбыло бы 100, а также obj2.itemбудет читать "changed",

Вместо этого ситуация заключается в том, что переданный элемент передается по значению. Но элемент, который передается по значению, сам ссылка. Технически это называется вызов по обмену ,

В практическом плане это означает, что если вы измените сам параметр (как в случае с numа также obj2), что не повлияет на элемент, который был подан в параметр. Но если вы измените ВНУТРЕННИЕ параметра, который будет распространяться обратно (как в случае с obj1).


1346



Он всегда проходит по значению, но для объектов значение переменной является ссылкой. Из-за этого, когда вы передаете объект и меняете его члены , эти изменения сохраняются вне функции. Это делает это смотреть как пройти по ссылке. Но если вы действительно измените значение переменной объекта, вы увидите, что это изменение не сохраняется, доказывая, что оно действительно передается по значению.

Пример:

function changeObject(x) {
  x = {member:"bar"};
  alert("in changeObject: " + x.member);
}

function changeMember(x) {
  x.member = "bar";
  alert("in changeMember: " + x.member);
}

var x = {member:"foo"};

alert("before changeObject: " + x.member);
changeObject(x);
alert("after changeObject: " + x.member); /* change did not persist */

alert("before changeMember: " + x.member);
changeMember(x);
alert("after changeMember: " + x.member); /* change persists */

Вывод:

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

before changeMember: foo
in changeMember: bar
after changeMember: bar

369



Переменная не «удерживает» объект, он содержит ссылку. Вы можете назначить эту ссылку другой переменной, теперь оба ссылаются на один и тот же объект. Он всегда проходит по значению (даже если это значение является ссылкой ...).

Невозможно изменить значение, удерживаемое переменной, переданной как параметр, что было бы возможно, если бы JS поддерживала передачу по ссылке.


131



My 2 Cents.... This is the way I understand it. (Feel free to correct me if I'm wrong)

It's time to throw out everything you know about pass by value / reference.

Because in JavaScript, it doesn't matter whether it's passed by value or by reference or whatever. What matters is mutation vs assignment of the parameters passed into a function.

OK, let me do my best to explain what I mean. Let's say you have a few objects.

var object1 = {};
var object2 = {};

What we have done is "assignment"... We've assigned 2 separate empty objects to the variables "object1" and "object2".

Now, let's say that we like object1 better... So, we "assign" a new variable.

var favoriteObject = object1;

Next, for whatever reason, we decide that we like object 2 better. So, we simply do a little re-assignment.

favoriteObject = object2;

Nothing happened to object1 or to object2. We haven't changed any data at all. All we did was re-assign what our favorite object is. It is important to know that object2 and favoriteObject are both assigned to the same object. We can change that object via either of those variables.

object2.name = 'Fred';
console.log(favoriteObject.name) // logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // logs Joe 

OK, now let's look at primitives like strings for example

var string1 = 'Hello world';
var string2 = 'Goodbye world';

Again, we pick a favorite.

var favoriteString = string1;

Both our favoriteString and string1 variables are assigned to 'Hello world'. Now, what if we want to change our favoriteString??? What will happen???

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'

Uh oh.... What has happened. We couldn't change string1 by changing favoriteString... Why?? because strings are immutable and we didn't mutate it. All we did was "RE ASSIGN" favoriteString to a new string. This essentially disconnected it from string1. In the previous example, when we renamed our object, we didn't assign anything. (Well, actually... we did, we assigned the name property to a new string.) Instead, we simply mutated the object which keeps the connections between the 2 variables and the underlying objects.

Now, on to functions and passing parameters.... When you call a function, and pass a parameter, what you are essentially doing is "assignment" to a new variable, and it works exactly the same as if you simply assigned using the equal (=) sign.

Take these examples.

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'

Now, the same thing, but with a function

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'

OK, now lets give a few examples using objects instead... first, without the function.

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
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object no longer mutates the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

Now, the same thing, but with a function call

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 no longer mutates 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

OK, if you read through this entire post, perhaps you now have a better understanding of how function calls work in javascript. It doesn't matter whether something is passed by reference or by value... What matters is assignment vs mutation.

Every time you pass a variable to a function, you are "Assigning" to whatever the name of the parameter variable is, just like if you used the equal (=) sign.

Always remember that the equals sign (=) means assignment. Always remember that passing a parameter to a function also means assignment. They are the same and the 2 variables are connected in exactly the same way.

The only time that modifying a variable affects a different variable is when the underlying object is mutated.

There is no point in making a distinction between objects and primitives, because it works the same exact way as if you didn't have a function and just used the equal sign to assign to a new variable.

The only gotcha is when the name of the variable you pass into the function is the same as the name of the function parameter. When this happens, you have to treat the parameter inside the function as if it was a whole new variable private to the function (because it is)

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'

80



Consider the following:

  1. Variables are pointers to values in memory.
  2. Reassigning a variable merely points that pointer at a new value.
  3. Reassigning a variable will never affect other variables that were pointing at that same object

So, forget about "pass by reference/value" don't get hung up on "pass by reference/value" because:

  1. The terms are only used to describe the behavior of a language, not necessarily the actual underlying implementation. As a result of this abstraction, critical details that are essential for a decent explanation are lost, which inevitably leads to the current situation where a single term doesn't adequately describe the actual behavior and supplementary info has to be provided
  2. These concepts were not originally defined with the intent of describing javascript in particular and so I don't feel compelled to use them when they only add to the confusion.

To answer your question: pointers are passed.


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

// illustration
               'Fred'
              /
             /
(obj) ---- {}
             \
              \
               1


// code
obj.name = 'George';


// illustration
                 'Fred'


(obj) ---- {} ----- 'George'
             \
              \
               1


// code
obj = {};

// illustration
                 'Fred'


(obj)      {} ----- 'George'
  |          \
  |           \
 { }            1


// 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'

Some final comments:

  • It's tempting to think that primitives are enforced by special rules while objects are not, but primitives are simply the end of the pointer chain.
  • As a final example, consider why a common attempt to clear an array doesn't work as expected.


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

54



Object outside a function is passed into a function by giving a reference to the outside obejct. When you use that reference to manipulate its object, the object outside is thus affected. However, if inside the function you decided to point the reference to something else, you did not affect the object outside at all, because all you did was re-direct the reference to something else.


23



Javascript is always pass-by-value, everything is of value type. Objects are values, member functions of objects are values themselves (remember that functions are first-class objects in Javascript). Also, regarding the concept that everything in Javascript is an object, this is wrong. Strings, symbols, numbers, booleans, nulls and undefineds are primitives. On occasion they can leverage some member functions and properties inherited from their base prototypes, but this is only for convenience, it does not mean that they are objects themselves. Try the following for reference

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

In both alerts you will find the value to be undefined.


14



Think of it like this: It's always pass by value. However, the value of an object is not the object itself, but a reference to that object.

Here is an example, passing a number (a primitive type)

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

Repeating this with an object yields different results:

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 }

One more example:

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}

13



In JavaScript, the type of the value solely controls whether that value will be assigned by value-copy or by reference-copy.

Primitive values are always assigned/passed by value-copy:

  • null
  • undefined
  • string
  • number
  • boolean
  • symbol in ES6

Compound values are always assigned/passed by reference-copy

  • objects
  • arrays
  • function

For example

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]

In the above snippet, because 2 is a scalar primitive, a holds one initial copy of that value, and b is assigned another copy of the value. When changing b, you are in no way changing the value in a.

But both c and d are separate references to the same shared value [1,2,3], which is a compound value. It's important to note that neither c nor d more "owns" the [1,2,3] value -- both are just equal peer references to the value. So, when using either reference to modify (.push(4)) the actual shared array value itself, it's affecting just the one shared value, and both references will reference the newly modified value [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]

When we make the assignment b = [4,5,6], we are doing absolutely nothing to affect where a is still referencing ([1,2,3]). To do that, b would have to be a pointer to a rather than a reference to the array -- but no such capability exists in 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]

When we pass in the argument a, it assigns a copy of the a reference to x. x and a are separate references pointing at the same [1,2,3] value. Now, inside the function, we can use that reference to mutate the value itself (push(4)). But when we make the assignment x = [4,5,6], this is in no way affecting where the initial reference a is pointing -- still points at the (now modified) [1,2,3,4] value.

To effectively pass a compound value (like an array) by value-copy, you need to manually make a copy of it, so that the reference passed doesn't still point to the original. For example:

foo( a.slice() );

Compound value (object, array, etc) that can be passed by reference-copy

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

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Here, obj acts as a wrapper for the scalar primitive property a. When passed to foo(..), a copy of the obj reference is passed in and set to the wrapperparameter. We now can use the wrapper reference to access the shared object, and update its property. After the function finishes, obj.a will see the updated value 42.

Source


12



A very detailed explanation about copying, passing and comparing by value and by reference is in this chapter of "JavaScript: The Definitive Guide" book.

Before we leave the topic of manipulating objects and arrays by reference, we need to clear up a point of nomenclature. The phrase "pass by reference" can have several meanings. To some readers, the phrase refers to a function invocation technique that allows a function to assign new values to its arguments and to have those modified values visible outside the function. This is not the way the term is used in this book. Here, we mean simply that a reference to an object or array -- not the object itself -- is passed to a function. A function can use the reference to modify properties of the object or elements of the array. But if the function overwrites the reference with a reference to a new object or array, that modification is not visible outside of the function. Readers familiar with the other meaning of this term may prefer to say that objects and arrays are passed by value, but the value that is passed is actually a reference rather than the object itself.

One thing I still cannot figure out. Check code below. Any thoughts?

function A() {}
A.prototype.foo = function() {
    return 'initial value';
}


function B() {}
B.prototype.bar = A.prototype.foo;

console.log(A.prototype.foo()); //initial value
console.log(B.prototype.bar()); //initial value

A.prototype.foo = function() {
    return 'changed now';
}

console.log(A.prototype.foo()); //changed now
console.log(B.prototype.bar()); //Why still 'initial value'???

10