原型为数组的Javascript对象成员将被所有类实例共享

Javascript object members that are prototyped as arrays become shared by all class instances


之前有人注意到这种行为吗? 这真的让我失望......我原本希望原型数组对每个类实例都是私有的,而不是在所有类实例之间共享。


有人可以验证这是正确的行为,并可能更详细地解释这种行为?


请注意注释的代码以及它如何影响脚本的行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<html>
<head>

<script type="text/javascript">

function print_r( title, object ) {

    var output = '';
    for( var key in object ) {

        output += key +":" + object[ key ] +"
"
;

    }

    output = title +"

"
+ output;

    alert( output );

}

function Sandwich() {

    // Uncomment this to fix the problem
    //this.ingredients = [];

}

Sandwich.prototype = {

   "ingredients" : [],
   "addIngredients" : function( ingArray ) {

        for( var key in ingArray ) {

            this.addIngredient( ingArray[ key ] );

        }

    },
   "addIngredient" : function( thing ) {

        this.ingredients.push( thing );

    }

}

var cheeseburger = new Sandwich();
cheeseburger.addIngredients( ["burger","cheese" ] );

var blt = new Sandwich();
blt.addIngredients( ["bacon","lettuce","tomato" ] );

var spicy_chicken_sandwich = new Sandwich();
spicy_chicken_sandwich.addIngredients( ["spicy chicken pattie","lettuce","tomato","honey dijon mayo","love" ] );

var onLoad = function() {

    print_r("Cheeseburger contains:", cheeseburger.ingredients );

};



</head>
<body onloadx="onLoad();">
</body>
</html>


非常感谢。



对象的原型只是一个对象。原型属性在从该对象继承的所有对象之间共享。如果您创建"类"的新实例(JS中不存在类),则不会创建属性的副本,即从原型继承的对象。


它只会对您使用这些继承属性的方式产生影响:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Foo() {}

Foo.prototype = {
    array: [],
    func: function() {}
}

a = new Foo();
b = new Foo();

a.array.push('bar');
console.log(b.array); // prints ["bar"]

b.func.bar = 'baz';
console.log(a.func.bar); // prints baz


在所有这些情况下,您始终使用相同的对象。


但是,如果为对象的属性赋值,则将在对象本身而不是其原型上设置/创建属性,因此不会共享:

1
2
3
4
5
6
console.log(a.hasOwnProperty('array')); // prints false
console.log(a.array); // prints ["bar"]
a.array = ['foo'];
console.log(a.hasOwnProperty('array')); // prints true
console.log(a.array); // prints ["foo"]
console.log(b.array); // prints ["bar"]


如果要为每个实例创建自己的数组,则必须在构造函数中定义它:

1
2
3
function Foo() {
    this.array = [];
}


因为在这里,this指的是调用new Foo()时生成的new对象。


经验法则是:应将特定于实例的数据分配给构造函数内的实例,应将共享数据(如方法)分配给原型。


您可能希望阅读对象模型的详细信息,该模型描述了基于类的语言与基于原型的语言之间的差异以及对象实际的工作方式。


更新:


您可以通过Object.getPrototypeOf(obj)访问对象的原型(可能无法在非常旧的浏览器中使用),Object.getPrototypeOf(a) === Object.getPrototypeOf(b)会为您提供true。它是同一个对象,也称为Foo.prototype



行为是正确的。
[]在运行时转换为new Array(),但只创建了一个这样的数组。


换句话说,Obj.prototype = {...}的执行方式与任何其他分配一样。



当你执行var exp1 = new C()时,JavaScript设置exp1.[[Prototype]] = C.prototype。然后,当您访问实例的属性时,JavaScript首先检查它们是否直接存在于该对象上,如果不存在,则查看[[Prototype]]。这意味着您在原型中定义的所有内容都会被所有实例有效共享,您甚至可以在以后更改原型的各个部分,并在所有现有实例中显示更改。