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

Javascript 本机对象的奇怪行为是由什么引起的?,javascript,oop,Javascript,Oop,最近我开始学习JavaScript中的面向对象编程。我的理解是,当引用变量时,我们实际上引用的不是它们的实际值,而是内存中的位置。这就是为什么所有那些应该复制实例的“返回此”方法都不起作用的原因 因此,示例代码: //An example object with a simple property and //failing "copy" function. function MyObject() { this.myProperty = 123; this.copy = func

最近我开始学习JavaScript中的面向对象编程。我的理解是,当引用变量时,我们实际上引用的不是它们的实际值,而是内存中的位置。这就是为什么所有那些应该复制实例的“返回此”方法都不起作用的原因

因此,示例代码:

//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;
现在iOne和iTwo的“myProperty”属性都等于321,因为“copy”方法返回的是引用,而不是值。这种行为是意料之中的,一切正常

现在,我尝试对本机对象类型Number执行相同的操作。让我们以更面向对象的程序员友好方式创建它的一个实例:

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

iOne = 321;
现在,可怕的事情发生了。iOne等于321,但它保持了它的值,仍然等于123


我不知道这种行为是由什么引起的。也许数字是一种“特殊”?也许与之相关联的十进制数不仅仅是一个属性?或者它只是为了让没有经验的程序员的生活变得更轻松?最后一个选项与运算符相关。如果有人对此有所了解,请不要让我理解JavaScript的方式分崩离析。

对象、数组和字符串是通过引用(而不是通过副本)分配的。所有其他类型在赋值时都是有效的副本(例如,它们生成一个与旧变量无关的新变量)

字符串是一种特殊情况,因为它们是不可变的,所以当您更改字符串时,它总是创建一个新字符串,因此它的行为更像是创建一个副本,即使之前的赋值是一个引用

分配:

iOne = 321;

正在用简单的基元数字类型替换iOne的值,因此它对任何其他变量都没有影响。

对象、数组和字符串是通过引用(而不是通过副本)分配的。所有其他类型在赋值时都是有效的副本(例如,它们生成一个与旧变量无关的新变量)

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

iOne = 321;
字符串是一种特殊情况,因为它们是不可变的,所以当您更改字符串时,它总是创建一个新字符串,因此它的行为更像是创建一个副本,即使之前的赋值是一个引用

分配:

iOne = 321;
正在用简单的基元数字类型替换iOne的值,因此它对任何其他变量都没有影响

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

iOne = 321;
您正在覆盖由
iOne
变量持有的对象引用,该变量带有一个不同的原语编号

对象作为引用保存,但它们不是可以直接取消引用的指针,因此不能替换该内存位置中保存的数据。您只能对其进行变异(如果对象是可变的)

具体来说,
Number
对象包装器是不可变的,或者至少它所持有的原语值不能被替换。只能替换整个对象

iOne = 321;
您正在覆盖由
iOne
变量持有的对象引用,该变量带有一个不同的原语编号

对象作为引用保存,但它们不是可以直接取消引用的指针,因此不能替换该内存位置中保存的数据。您只能对其进行变异(如果对象是可变的)

具体来说,
Number
对象包装器是不可变的,或者至少它所持有的原语值不能被替换。只能替换整个对象

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


这段代码做了预期的事情,您将
321
分配给变量
iOne
,覆盖了它最初引用的内容。

Javascript中的“本机类型”和对象之间的行为没有真正的区别(本机类型是不可变的除外)

在第二个示例中,您只是在更改变量
iOne
指向的对象,为什么它要更改另一个独立
iTwo
变量指向的对象

在第一种情况下,你有两个变量指向同一个对象,如果你用一个变量来改变对象,你也可以用另一个变量来观察变化(很明显,它指向同一个对象)

在Javascript中,您可以想象一切都是通过引用,而不是通过值(复制)。如果你想复制一些东西,你需要明确地做它。。。对于阵列,您可以使用
x.slice()
制作
x
的浅拷贝;对于对象,没有用于执行相同操作的基元函数,因此必须调用构造函数

一种常见的OOP模式是有一个返回副本的成员函数
.clone()
,这样需要副本的人就不需要知道如何创建每个类的副本

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

P2d.prototype.clone = function() {
    return new P2d(this.x, this.y);
}
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 (!)

这是因为Javascript对象有一个“原型链”,在访问成员进行读取时会对其进行搜索
q
被创建为一个空对象,其原型为
p
,因此在查找属性(用于读取)时,如果在
q
中找不到任何内容,它将在
p
中搜索。然而,在编写时,属性将被设置在
q
内部,不会影响
p
,从那时起,
q
中的值将被返回,而不必在原型链中上升。

Javascript中的“本机类型”和对象之间的行为没有真正的区别(本机类型是不可变的除外)

在第二个示例中,您只是在更改变量
iOne
指向的对象,为什么它要更改另一个独立
iTwo
变量指向的对象

在第一个案例中