Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/366.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript node.js中类实例之间的共享数组_Javascript_Arrays_Node.js_Oop - Fatal编程技术网

Javascript node.js中类实例之间的共享数组

Javascript node.js中类实例之间的共享数组,javascript,arrays,node.js,oop,Javascript,Arrays,Node.js,Oop,我在node.js中遇到了一个奇怪的问题: person.js factory.js index.js 实际产量 预期产量 为什么向一个实例添加元素会同时为两个实例添加元素?看起来友元数组在实例之间是“共享”的。如何防止这种情况 原型属性在拥有该原型的所有对象之间共享 Person.prototype.friends = []; 表示原型人物有一个friends数组,作为构造函数调用person创建的所有实例共享 相反,您希望为每个人分配一个新数组: function Person(na

我在node.js中遇到了一个奇怪的问题:

person.js
factory.js
index.js
实际产量 预期产量 为什么向一个实例添加元素会同时为两个实例添加元素?看起来
友元数组在实例之间是“共享”的。如何防止这种情况


原型属性在拥有该原型的所有对象之间共享

  Person.prototype.friends = []; 
表示原型人物有一个friends数组,作为构造函数调用
person
创建的所有实例共享

相反,您希望为每个人分配一个新数组:

function Person(name) {
  if (name) {
    this.name = name;
  }
  this.friends = []; // create a new array in the constructor
}
一般来说,原型是关于在JavaScript中共享功能和属性的

删除行

Person.prototype.name = "";
Person.prototype.friends = [];
将它们添加到构造函数:

this.name = name;
this.friends = [];

目前,所有原型都共享同一个对象
朋友

您用于
人的模式对我来说真的很奇怪。您不必将所有内容都封装在Node.js中的匿名函数中

看看这个

person.js

function Person(name) {
  this.name = name || "";
  this.friends = [];
}

Person.prototype.sayHello = function sayHello() {
  console.log("Hello, my name is %s and I have %d friends", this.name, this.friends.length);
};

Person.prototype.addFriend = function addFriend(name) {
  this.friends.push(name);
};

// factory
Person.create = function create(name) {
  return new Person(name);
};

module.exports = Person;
// don't forget your `var` keyword
var factory = require('./person').create;

tyrion = factory("Tyrion");
tyrion.addFriend("Bronn");
tyrion.sayHello();
// Hello, my name is Tyrion and I have 1 friends

daenerys = factory("Daenerys");
daenerys.addFriend("Illyrio");
daenerys.addFriend("Daario");
daenerys.addFriend("Barristan");
daenerys.sayHello();
// Hello, my name is Daenerys and I have 3 friends

tyrion.sayHello();
// Hello, my name is Tyrion and I have 1 friends
注意,我将person.js中的工厂分组为
person.create
。这是一个不会与实例方法冲突的“类”方法。你不需要一个单独的文件

index.js

function Person(name) {
  this.name = name || "";
  this.friends = [];
}

Person.prototype.sayHello = function sayHello() {
  console.log("Hello, my name is %s and I have %d friends", this.name, this.friends.length);
};

Person.prototype.addFriend = function addFriend(name) {
  this.friends.push(name);
};

// factory
Person.create = function create(name) {
  return new Person(name);
};

module.exports = Person;
// don't forget your `var` keyword
var factory = require('./person').create;

tyrion = factory("Tyrion");
tyrion.addFriend("Bronn");
tyrion.sayHello();
// Hello, my name is Tyrion and I have 1 friends

daenerys = factory("Daenerys");
daenerys.addFriend("Illyrio");
daenerys.addFriend("Daario");
daenerys.addFriend("Barristan");
daenerys.sayHello();
// Hello, my name is Daenerys and I have 3 friends

tyrion.sayHello();
// Hello, my name is Tyrion and I have 1 friends
线路

Person.prototype.friends = [];
将friends属性添加到Person原型中,使其与使用Person构造函数创建的所有新对象共享。因此,如果希望每个对象都有自己的朋友,则必须将friends属性添加到单个对象中

您实际想要做的正是您对name所做的:

function Person(name) {
    // friends is a property of this, the new instance object.
    this.friends = [];
    if (name) {
        this.name = name;
    }
}
在Javascript中,原型在某种程度上类似于其他OO语言中的基类(我之所以这样说是出于一些重要原因,稍后我将对此进行解释)。当您向原型中添加某个内容时,该内容将由拥有该原型的所有内容共享。这就是为什么将“sayHello”函数添加到原型中的原因,因为您希望Person的所有实例都能够说hello。通过在原型中添加好友,您是在说‘我希望所有人类型的东西都能共享这一点’

关键是,在Javascript中,实际上有两个步骤可以使对象看起来像类的成员。第一步,创建一个原型并添加将被共享的东西,通常是方法。第2步,创建单个对象后,将属性添加到该对象。如果您在步骤1中添加希望成为“实例变量”的内容,那么您实际要做的是创建共享的变量,就像您的方法一样,这就是您在上面所做的

我之前说过原型有点像基类。我这样说是因为表面上看起来是这样的。这是一个非常重要的细节,了解它的实际工作原理将为您以后节省大量的头痛和困惑

了解Javascript的一个重要方面是它没有类。因此,与其他语言不同的是,Javascript只包含对象,其他语言有一种称为“类”的东西和另一种称为“实例”的东西。即使看起来一个东西是一个类,另一个是该类的实例,它也只是一个外观。如果你不注意,这种外表会欺骗你

Javascript使用了一种称为“原型继承”(Prototypic Inheritance)的东西,这是一种很长的说法,可以说对象继承自其他对象。把它想象成一条链子。如果你有提利昂,并且你访问sayHello,就像这样:

tyrion.sayHello()
Javascript在tyrion对象中查找一个名为sayHello的属性。它没有找到它,所以它会查找tyrion的原型,如果有,它会查看它是否有一个叫做sayHello的属性。这次它找到它,确定它是一个函数,并调用它,告诉它tyrion应该是函数中的“This”。如果它是用javascript编写的,它会像这样:

function find_property(original_obj, property_name) {

    var found_prop = undefined;
    var current_obj = original_obj;

    // we keep searching until we either have a property or we run out of 
    // places to look.
    while(found_prop == undefined && current_obj != undefined) {
       // does the object we are looking at have it's own property with that name?
       if ( obj.hasOwnProperty(property_name) ) {
           // yes, so we can set found_prop
           found_prop = obj[property_name];
       } else {
           // no, we have to look at the next prototype up the chain.
           current_obj = current_obj.__proto__;
       }
    }
    return found_prop;
}

var sayhello = find_property(tyrion, 'sayHello');
if (typeof sayhello == 'function') {
   sayhello.call(tyrion);
}
这是一个需要理解的非常重要的细节,因为每个原型都只是一个对象,可以随时修改。这意味着您可以修改prototype对象,即使在使用它作为原型创建了许多其他对象之后,并且当您这样做时,您实际上是在向在其层次结构中某处使用该原型的每个对象添加内容。对于来自不允许您在“类”创建后更改它的语言的人来说,这是一种非常意外的行为

在上面的例子中,您正在修改原型的好友列表,因为没有一个孩子有自己的“friends”属性,所以当javascript查找“friends”时,它总是在原型上找到那个

  Person.prototype.friends = []; 

希望这对你有所帮助,并为你省去一些后顾之忧。如果你想进一步了解它,道格拉斯·克罗克福德(Douglas Crockford)的《Javascript:The Good Parts》(Javascript:The Good Parts)是一本非常好的书。

事实上,因为
this.name
是一个赋值-赋值
this.name=name
可以很好地工作(不像
push
获取属性然后调用方法)。唯一的区别是,删除它将导致访问它时返回
未定义的
,而不是
,我认为OP不需要它。也许你是对的,保留prototype.name=''是有意义的;避免返回未定义的名称。就个人而言,我更喜欢使用未定义的
名称,而不是未定义的空字符串,但这是一个哲学问题,而不是实际问题。将函数放在原型上是有益的-它不仅在v8中快得多,而且允许以后进行多态调度,挂接现有的方法进行调试,通常是一种很好的模式。这将在ES6(最大最小类)中获得更好的语法,但如果OP的模式不是“非常奇怪”而是惯用的话。@BenjaminGruenbaum很公平,我已经更新了示例以使用
Person.pro
tyrion.sayHello()
function find_property(original_obj, property_name) {

    var found_prop = undefined;
    var current_obj = original_obj;

    // we keep searching until we either have a property or we run out of 
    // places to look.
    while(found_prop == undefined && current_obj != undefined) {
       // does the object we are looking at have it's own property with that name?
       if ( obj.hasOwnProperty(property_name) ) {
           // yes, so we can set found_prop
           found_prop = obj[property_name];
       } else {
           // no, we have to look at the next prototype up the chain.
           current_obj = current_obj.__proto__;
       }
    }
    return found_prop;
}

var sayhello = find_property(tyrion, 'sayHello');
if (typeof sayhello == 'function') {
   sayhello.call(tyrion);
}