admin管理员组文章数量:1333381
I have a a weird problem in node.js:
person.js
var Person;
Person = (function() {
Person.prototype.name = "";
Person.prototype.friends = [];
function Person(name) {
if (name) {
this.name = name;
}
}
Person.prototype.sayHello = function() {
return console.log("Hello, my name is " + this.name + " and I have " + this.friends.length + " friends");
};
Person.prototype.addFriend = function(name) {
this.friends.push(name);
};
return Person;
})();
module.exports = Person;
factory.js
var Person = require('./Person.js');
module.exports = function(name) {
return new Person(name);
};
index.js
factory = require('./factory');
tyrion = factory("Tyrion");
tyrion.addFriend("Bronn");
tyrion.sayHello();
daenerys = factory("Daenerys");
daenerys.addFriend("Illyrio");
daenerys.addFriend("Daario");
daenerys.addFriend("Barristan");
daenerys.sayHello();
tyrion.sayHello();
Actual output
Hello, my name is Tyrion and I have 1 friends
Hello, my name is Daenerys and I have 4 friends
Hello, my name is Tyrion and I have 4 friends
Expected output
Hello, my name is Tyrion and I have 1 friends
Hello, my name is Daenerys and I have 3 friends
Hello, my name is Tyrion and I have 1 friends
How e that adding element to one instance does add it for both? It looks like the friend
array is "shared" between instances. How to prevent such?
Demo here
I have a a weird problem in node.js:
person.js
var Person;
Person = (function() {
Person.prototype.name = "";
Person.prototype.friends = [];
function Person(name) {
if (name) {
this.name = name;
}
}
Person.prototype.sayHello = function() {
return console.log("Hello, my name is " + this.name + " and I have " + this.friends.length + " friends");
};
Person.prototype.addFriend = function(name) {
this.friends.push(name);
};
return Person;
})();
module.exports = Person;
factory.js
var Person = require('./Person.js');
module.exports = function(name) {
return new Person(name);
};
index.js
factory = require('./factory');
tyrion = factory("Tyrion");
tyrion.addFriend("Bronn");
tyrion.sayHello();
daenerys = factory("Daenerys");
daenerys.addFriend("Illyrio");
daenerys.addFriend("Daario");
daenerys.addFriend("Barristan");
daenerys.sayHello();
tyrion.sayHello();
Actual output
Hello, my name is Tyrion and I have 1 friends
Hello, my name is Daenerys and I have 4 friends
Hello, my name is Tyrion and I have 4 friends
Expected output
Hello, my name is Tyrion and I have 1 friends
Hello, my name is Daenerys and I have 3 friends
Hello, my name is Tyrion and I have 1 friends
How e that adding element to one instance does add it for both? It looks like the friend
array is "shared" between instances. How to prevent such?
Demo here
Share Improve this question asked Sep 12, 2014 at 18:08 Vinz243Vinz243 9,95611 gold badges45 silver badges95 bronze badges4 Answers
Reset to default 3Remove lines
Person.prototype.name = "";
Person.prototype.friends = [];
add them to constructor instead:
this.name = name;
this.friends = [];
At the moment all prototypes share same object friends
.
The line
Person.prototype.friends = [];
Adds the friends property to the Person prototype, which makes it shared to all the new objects created with the Person constructor. So, if you want each object to have its own friends, you have to add the friends property to the individual object.
What you actually want to do is exactly what you did with name:
function Person(name) {
// friends is a property of this, the new instance object.
this.friends = [];
if (name) {
this.name = name;
}
}
In Javascript, a prototype is somewhat like a base class in other OO languages (I say somewhat for important reasons which I will explain in a moment). When you add something to a prototype, it is shared by all the things that have that prototype. This is why your 'sayHello' function is added to your prototype, because you want all your instances of Person to be able to sayHello. By adding friends to the prototype, you are saying 'I want this to be shared by all of the things of the Person type.'
The point is that in Javascript there are actually two steps to making an object that looks like a member of a class. Step 1, create a prototype and add things that will be shared, usually the methods. Step 2, after you create an individual object, add the properties to that object. If you add what you want to be 'instance variables' in Step 1, what you will actually do is create variables that are shared, just like your methods, which is what you did above.
I said before that a prototype is somewhat like a base class. I say somewhat because it only appears that way on the surface. This is an extremely important detail and understanding how it really works will save you LOTS of headaches and confusion later.
An important thing to understand about Javascript is that it does not have classes. So unlike other languages where there is type of thing called a 'class', and another thing called an 'instance', Javascript only has objects. Even if it appears that one thing is a class, and another is an instance of that class, it is only appearance. If you are not paying attention, that appearance can fool you.
Javascript uses something called 'prototypical inheritence,' which is a long way of saying that objects inherit from other objects. Think of it like a chain. If you have tyrion and you access sayHello, like so:
tyrion.sayHello()
Javascript looks at the tyrion object for a property called sayHello. It does not find it, so it then looks up tyrion's prototype and if there is one, it looks at that to see if it has a property called sayHello. This time it finds it, determines that it is a function, and calls it, telling it that tyrion should be 'this' within the function. If it was written in javascript, it would look something like this:
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);
}
This is a very important detail to understand, because each prototype is just an object and can be modified at any time. What that means is that you can modify the prototype object even after many other objects have been created using it as their prototype and when you do that you are essentially adding things to every object that uses that prototype somewhere in it's hierarchy. This is a very unexpected behavior for folks ing from languages that don't allow you to change the 'Class' after it's created.
In your case, above, you were modifying the prototype's friends list, and since none of the children had their own 'friends' property, when javascript went to find 'friends' it always found the one on the prototype.
Hope that helps, and saves you some headaches down the road. If you want to learn more about it, Douglas Crockford's 'Javascript: The Good Parts' is an excellent book to start with.
This pattern you're using for Person
seems really strange to me. You don't have to wrap everything in anonymous functions in Node.js.
Have a look at this
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;
Notice I grouped the factory within person.js as Person.create
. This is a "class" method that won't conflict with your instance methods. You don't need a separate file for it.
index.js
// 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
The prototype properties are shared between all objects who haave that prototype.
Person.prototype.friends = [];
Means the prototypical person has a friends array, shared across all instances created by calling Person
as a constructor.
Instead, you want to assign a new array to each person:
function Person(name) {
if (name) {
this.name = name;
}
this.friends = []; // create a new array in the constructor
}
Generally speaking - the prototype is about sharing functionality and properties in JavaScript.
本文标签: javascriptShared array between class instances in nodejsStack Overflow
版权声明:本文标题:javascript - Shared array between class instances in node.js - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742324438a2453441.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论