简体   繁体   English

由本机对象引起的奇怪行为是什么?

[英]What is the weird behavior of native objects caused by?

Recently I started learning about object-oriented programming in JavaScript. 最近我开始学习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. 现在iOne和iTwo的“myProperty”属性等于321,因为“copy”方法返回了一个引用,而不是一个值。 This behavior is expected, and everything is okay. 这种行为是预期的,一切都很好。

Now, I tried doing the same with a native object type, Number. 现在,我尝试使用本机对象类型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. iOne等于321,但iTwo保持其值,仍然等于123。

I have no idea what is this behavior caused by. 我不知道这是由什么造成的。 Maybe Number is some kind of "special"? 也许Number是某种“特殊”? 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. 如果有人对此有所了解,请不要让我理解JavaScript的方式崩溃。

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. 用简单的原始数字类型替换iOne的值,因此它对任何其他变量都没有影响。

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. 您将覆盖iOne变量持有的对象引用,并使用不同的原始数字。

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. 具体来说, Number对象包装器不可变,或者至少它所拥有的原始值不能被替换。 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). Javascript中的“本机类型”和对象之间的行为没有真正的区别(除了本机类型是不可变的)。

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? 在你的第二个例子中,你只是简单地改变iOne指向的变量,为什么要改变另一个独立的iTwo变量所指向的?

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). 在Javascript中,您可以想象一切都始终通过引用而从不通过值(复制)。 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 ; 如果你想要复制一些东西你需要明确地...对于数组你可以使用x.slice()来制作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. 一个常见的OOP模式是拥有一个成员函数.clone() ,它返回一个副本,因此需要副本的人不需要知道如何制作每个类的副本。

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: 另一种特定于Javascript的原型模型的可能性在某些情况下可能很有用,就是创建一个单独的对象,它看起来像一个浅拷贝,可以在不影响原始的情况下进行变异,而是在读取时引用原始对象:

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. 发生这种情况是因为Javascript对象具有在访问成员进行读取时搜索的“原型链”。 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 . q被创建为一个空对象,以p为原型,因此在查找属性(用于读取)时,如果在q内找不到某些内容,它将在p内搜索。 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. 然而,在写入时,将在q内部设置属性,不影响p并且将返回q存在的值的那一点,而不是必须在原型链中上升。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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