简体   繁体   中英

What is the weird behavior of native objects caused by?

Recently I started learning about object-oriented programming in JavaScript. What I understood, is that when referencing to variables, we in fact reference not to their actual values, but locations in the memory. That's why all those " return this " methods that are supposed to copy instances don't work.

So, example code:

//An example object with a simple property and
//failing "copy" function.
function MyObject()
{
    this.myProperty = 123;
    this.copy = function() { return this; };
}

var iOne = new MyObject();
var iTwo = iOne.copy();
iTwo.myProperty = 321;

Now "myProperty" property of both iOne and iTwo equals 321, because "copy" method returned a reference, instead of a value. This behavior is expected, and everything is okay.

Now, I tried doing the same with a native object type, Number. Let's create an instance of it, in a more object-oriented programmer-friendly way:

var iOne = new Number(123);
var iTwo = iOne; //Equals "iTwo = iOne.copy()", except there isn't a copy method

iOne = 321;

And now, something terrible happened. iOne equals 321, but iTwo kept its value and is still equal to 123.

I have no idea what is this behavior caused by. Maybe Number is some kind of "special"? Maybe the decimal number associated with it is something more than a property? Or maybe it's just supposed to make life of inexperienced programmers easier? The last option is related to operators. If anyone know something about it, please don't let my way of understanding JavaScript fall apart.

Objects, Arrays and Strings are assigned by reference (not by copy). All other types are effectively copies when assigned (eg they make a new variable that has nothing to do with the old one).

Strings are a special case because they are immutable so when you change a string, it always creates a new string so it behaves more like it makes a copy even though the previous assignment was a reference.

Assigning:

iOne = 321;

Is replacing the value of iOne with a simple primitive numeric type so it will have no effect on any other variable.

var iOne = new Number(123);
var iTwo = iOne; //Equals "iTwo = iOne.copy()", except there isn't a copy method

iOne = 321;

You're overwriting the object reference held by the iOne variable, with a distinct primitive number.

The objects are held as references, but they are not pointers that can be directly dereferenced, so you can't replace the data held in that memory location. You can only mutate it (if the object is mutable) .

Specifically, the Number object wrapper is not mutable, or at least the primitive value it holds can't be replaced. You can only replace the entire object.

iOne = 321;

此代码执行了预期的操作,您为变量iOne分配了321 ,覆盖了它最初引用的内容。

There's no real difference in behaviour between "native types" and objects in Javascript (except that native types are immutable).

In your second example you're simply changing what variable iOne is pointing to, why should it change what another independent iTwo variable is pointing to?

In the first case instead you have two variables pointing to the same object and if you use one variable to mutate the object and you can observe the change also using the other variable (obvious... it's pointing to the same object).

In Javascript you can imagine that everything is always by reference and never by value (copy). If you want to copy something you need to do it explicitly... for arrays you can use x.slice() to make a shallow copy of x ; for objects there's no primitive function for doing the same so you must call the constructor.

A common OOP pattern is to have a member function .clone() that returns a copy so who needs the copy doesn't need to know how to make a copy of every class.

function P2d(x, y) {
    this.x = x;
    this.y = y;
}

P2d.prototype.clone = function() {
    return new P2d(this.x, this.y);
}

Another possibility specific to the protoype model of Javascript and that can be useful in some cases is to create a separate object that will appear like a shallow copy that can be mutated without affecting the original but that is instead referencing the original object when reading:

function fakeCopy(x) {
    function f() { }
    f.prototype = x;
    return new f;
}

p = new P2d(10, 20);
q = fakeCopy(p);
console.log(q.x); // Displays 10
q.y = 30;
console.log(q.y); // Displays 30
console.log(p.y); // Displays 20 -- original not changed
p.x = 99;
console.log(q.x); // Displays 99 (!)

This happens because Javascript objects have a "prototype chain" that is searched when accessing a member for reading. q is created as an empty object that with p as its prototype so when looking for an attribute (for reading) it will go searching inside p if something is not found inside q . When writing however an attribute will be set inside q , not affecting p and from that point on the value present in q will be returned instead of having to go up in the prototype chain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM