Why are arrays in a Backbone.js Model essentially static variables?
class exports.Content extends Backbone.Model
tags: []
then if i make a few models:
contentA = new Content()
contentB = new Content()
and add one string to each models' array:
contentA.tags.push('hello')
contentB.tags.push('world')
they both end up with the same array:
contentB.tags // ['hello','world']
but if it's a string, then there is no problem:
contentA.name = "c1"
contentB.name = "c2"
contentA.name // "c1"
The short answer
When you call extends to define your object, you are passing the new object's configuration in as an object literal. Objects are passed by reference, and the extends function only passes a reference to the tags array in to the new type definition.
As noted by others, you can correct this by assigning tags to a function. This works because a function delays the evaluation of the tags until the object is instantiated. There's nothing native in JavaScript that does this, but it's Backbone itself that recognizes tags as a function or a value.
The long answer
In spite of your code being in CoffeeScript, this comes down to a combination of a few things in JavaScript:
There are no classes in JavaScript
Object literals are evaluated immediately
JavaScript objects are passed around by reference
In JavaScript, there are no classes. Period. CoffeeScript gives you the notion of a class, but in reality, it gets compiled down to JavaScript which has no classes.
You can have types and type definitions (constructor functions) but not classes. Backbone provides a class-like definition, that looks similar to Java's "extend" class-based inheritance. It's still just JavaScript, though, which has no classes.
What we have, instead, is an object literal being passed in to the extends method. It's as if you write this code:
var config = {
tags: []
}
var MyModel = Backbone.Model.extends(config);
In this code, config is an object literal, or a hash, or a key/value pair, or an associative array. Whatever name you call it, it's the same basic idea. You end up with a config object that has a tags attribute. The value of config.tags is an empty array, [], which is itself an object.
Which brings us back to the short answer:
When you call extends to define your object, you are passing the new object's configuration in as an object literal. Objects are passed by reference, and the extends function only passes a reference to the tags array in to the new type definition.
As noted by others, you can correct this by assigning tags to a function. This works because a function delays the evaluation of the tags until the object is instantiated. There's nothing native in JavaScript that does this, but it's Backbone itself that recognizes tags as a function or a value.
"tags" is being declared on the Content.prototype - which is shared across all instances of Content. You probably need to use defaults instead: http://backbonejs.org/#Model-defaults. However, it should be noted that you must use a function (instead of a hash) when defining defaults, otherwise you still run into the same problem when using types which are passed by reference (e.g. Array).
var Content = Backbone.Model.extend({
defaults: function() {
return {
tags: []
};
}
});
benpickles is correct when it comes to Backbone models, and the way to do this in coffeescript in general is to initialize instance properties in the constructor:
class Foo
constructor: ->
#bar = []
As Derick Bailey mentioned, pass by reference is the problem. I solved the problem by cloning the Array (passing-by-value essentially):
var _events = window.active_model.get('events').slice(0);
_events.push({ key: "xxx", value: "yyy" });
window.active_field.set('events', _events);
Related
How can I get name of object's class? I mean "Process" in this example
I see two ways to get it. First one is to write a getter in this class like
getClassName(){return "Process"}
But I suppose it will be an error if I try to call this method in object which doesn't belong to this class and hasn't got method like this.
And second one is using object instanceof Process. But maybe there is some way to make it better and more correctly?
You can get it from name on constructor:
console.log(object.constructor.name);
When you do ex = new Example, for instance, in the normal course of things that makes Example.prototype the prototype of the object that was created (ex), and the object inherits a constructor property from that object that refers back to the constructor (Example).
I say "in the normal course of things" because there are various ways those normal relationships can be changed. For instance, code could have overridden the constructor property with an own property on the object (ex.constructor = somethingElse;). To rule out that specific scenario, you could use:
console.log(Object.getPrototypeOf(object).constructor.name);
Live example:
class Example1 {
}
const e1 = new Example1();
console.log(e1.constructor.name); // "Example1"
class Example2 {
constructor() {
this.constructor = "I'm special";
}
}
const e2 = new Example2();
console.log(Object.getPrototypeOf(e2).constructor.name); // "Example2"
The TC39 committee members that specify JavaScript were happy enough to use the instance's constructor property in Promises when building the new promise that then and catch return (see Step 3 here which goes here and reads constructor from the instance) (and in some other places), so you wouldn't be out on your own if you also used it. They don't even go to the prototype of the instance.
But yes, just for completeness, even if you go to the prototype for it, it's still possible for that to lead you astray, since the prototype's constructor property can also be mucked with:
class Example {
}
Example.prototype.constructor = Object; // Why would anyone do this? People are weird.
const e = new Example();
console.log(Object.getPrototypeOf(e).constructor.name); // "Object"
It's also possible to redefine the name on a function:
class Example {
}
// Why would someone do this? People are weird.
Object.defineProperty(Example, "name", {
value: "flibberdeegibbit"
});
const e = new Example();
console.log(Object.getPrototypeOf(e).constructor.name); // "flibberdeegibbit"
So...caveat user.
Note that the function name property is new as of ES2015 (as is class syntax). If you're using class syntax via a transpiler, it may or may not set name correctly.
Generally object instanceof Process is desirable if it's known for sure that object originates from this class/function. There may be situations where this won't be so. The appearance of several Process can be caused by iframes, multiple package versions, etc.
There is name property that already exists in regular functions class constructors. A known pitfall is that it will be mangled in minified code, so it is generally useless in browser JS, and its use can be considered an antipattern. name cannot be reassigned (in some browsers), so a separate property is needed to identify the class.
The proper way is to avoid this problem
But I suppose it will be an error if I try to call this method in object which doesn't belong to this class and hasn't got method like this.
is to use a getter:
class Process {
get className() { return 'Process'; }
...
}
Or a property:
class Process {
...
}
Process.prototype.className = 'Process';
As a result, there may be several Process classes that have Process className identifier. This may be desirable or not. While instanceof associates class instance with one particular class.
Use .constructor.name on the object. Each object's constructor by default refers to his creation function, which has a name property. It returns the name of the function.
class SomeClass {
}
const obj = new SomeClass();
console.log(obj.constructor.name);
Use the name property as follows:
class Process {}
console.log(Process.name);
const process = new Process;
console.log(process.constructor.name);
This is the same way it works for normal prototypal inheritance using functions.
I couldn't find any clear answer, so what's the difference between Objecttype and extend type in JavaScript?
In AngularJS, when I consoled $state:
console.log($state)
console.log($state.$current.parent)
I got the output as:
As you can see, the $state is interpreted as an Object where the latter is an extend?
I needed the parent name of current state, so I was planning to take it from the self.name object of $state.$current.parent. Is that okay? Or should I keep out of extend?
They both are objects. Chrome's console additionally tells you their type. The first one it's a generic object whereas the second one is still an object, but named instead.
Here's how you can create named objects very easily:
function NamedObject() { this.aProperty = 'something'; }
var namedObject = new NamedObject();
Generic objects are created like this:
var genericObject = { aProperty: 'something' };
In your specific case, uiRouter uses angular.extend internally. If you were to assign a name to this anonymous function here you'd see how extend will be renamed to the function's name.
In this Udacity video on game development, the instructor mentions that Javascript allows us to create an object by giving a handle to its definition. Then, it says that to allow this "an overloaded object definition will update a hash table with a pointer to its class definition".
I quite know what a hash table, a pointer, an overloaded method, and the factory pattern are, but I cannot make sense of this mysterious statement, or of the rest of the explanation.
"Hash table" is just a fancier way of saying "ordinary Javascript object". What the instructor means by "handle to its definition" is just another way of saying "the function that acts as a constructor for the class".
Ultimately, what he means by the statement you mentioned:
each overloaded entity definition will update a hash table with a pointer to its class definition
is the following:
All "subclasses" of Entity will register their constructor function in a single shared hashmap/object using the key which is the type value.
This allows you to get the constructor function (in other words, the function to call new on, which will return an instance of that entity) for any type by accessing gGameEngine.factory[type].
This is nice, because whenever a programmer adds a new type of entity, so long as they remember to add a new entry to that gGameEngine.factory object with the correct key, then that object will contain everything you need to construct any type of supported object.
So the code that iterates over the JSON structure generated by the level editor can create an instance of any type the same way, using something like:
var typeConstructor = gGameEngine.factory(tileSpec.type),
instance;
if (typeConstructor) {
instance = new(typeConstructor)(tileSpec /* or whatever params */);
}
This is similar to the code visible at around the 1 minute mark of the video you linked to.
Make sense now?
I think all he's saying is that you can map references to functions / objects / variables inside another object. He's using one type of property accessor syntax, but I think he's overcomplicating things by using language like 'handle' and 'hash table'.
var someClass = function () {
// stuff
}
var containingObject = {};
containingObject["someClass"] = someClass;
// same thing as
containingObject.someClass = someClass;
Then you can instantiate the class by calling the containingObject property.
var classInstance = new containingObject["someClass"]()
// or
var classInstance = new containingObject.someClass()
Consider the below code.
I created an empty object. And am adding var1,var2,var3 without declaring them.
var har = new Object();
har.var1 = "Var1";
har.var2 = "Var1";
har.var3 = "Var1";
alert( har.var1 );
If I can do this, then why do I need to create a Class and fix the attributes when I can introduce new attributes anytime?
Why would you even need to use objects in the first place? Non-object-oriented languages are already Turing-complete, so everything you can accomplish with objects you can also do without them.
What you're showing in your example is not really an object, but just a dictionary. In cases like this, where you only need to keep several related properties together, anonymous unprototyped objects like the one you're using are the de-facto standard approach (though it is customary to initialize them with the shorthand syntax, e.g. var har = {}). It is an object, since it uses the object data structure, but it is not object-oriented.
Actual objects, in contrast, not only define data, but also the operations that you can perform on that data. Objects have not only properties, but also methods which work on these properties. These properties are usually defined in the object prototype (which you're calling "class", but Javascript is not a class-based language, but a prototype-based one). All methods defined in the prototype are shared between all instances of that prototype.
function Counter() {
this.counter = 0;
}
Counter.prototype.increment = function() {
this.counter++;
alert(this.counter);
}
var c1 = new Counter();
var c2 = new Counter();
c1.increment(); // alerts 1
c1.increment(); // alerts 2
c2.increment(); // independent from c1: alerts 1 again
Each instance is still a dictionary, as in your example (and you can even still add more properties to them, they are not "fixed" by having a constructor and prototype), but now it can also perform tasks on its properties. This can be done your way as well:
c1 = {
counter: 0,
increment: function() {
this.counter++;
alert(this.counter);
}
}
c2 = {
counter: 0,
increment: function() {
this.counter++;
alert(this.counter);
}
}
You can see, however, that if you need two counters, without using prototypes you will need to duplicate your entire object definition. This will be cumbersome to write and maintain, and each increment function will be defined separately, thus it will also waste memory.
That said, in cases where you need an object that you know you'll only ever need one instance of, it makes no sense to define a constructor and a prototype for it. Such objects are usually regarded as namespaces instead of actual objects.
Appendix: Dictionaries vs Objects
A dictionary is a data structure which holds named elements. Besides "dictionary", they are also called associative arrays or hashmaps. Objects in Javascript are implemented as dictionaries — each property is a named element in the dictionary. In addition to a plain dictionary, objects also have a prototype, which is kind-of like a parent object — when you look up a named element in the dictionary and it is not there, it is automatically searched for in the prototype as well. This way, default properties and methods are defined only once in the prototype, and do not need to be copied into each instance. (The prototype of an object is often visible as the instance.__proto__ property, though this is non-standard and deprecated even in browsers that support it; the actual prototype is defined as an internal, non-accessible property by the standard)
So, Javascript objects are actually dictionaries. When you want to use a plain dictionary to store some related properties together, there is no separate syntax in Javascript to create a dictionary that is not an object, so you create an instance of the base Object type to hold your dictionary (it does not matter if you do var dict = new Object or var dict = {}, the result is the same); thus, dictionaries that you use in your code are also objects.
Relatively new to JavaScript, so wondering what is the best approach to achive the below requirement.
I wanted to create JSON object (RPC request object), and need to enforce some structure. As I learn, JS is dynamic language hence I can add any type to an object at any time. But, I like to define a class, which I can populate to construct the JSON request.
var RecReq = {
ID:{},
HeaderLevel:{},
Content:{} //Content object goes here
},
HeaderLevel = {
HeaderID:{},
Note:{}
},
Content = {
Item: [] //Array of Item goes here
},
Item = {
IDs:[]
};
function ReceiveObj() {
this.RecReq = RecReq,
this.HeaderLevel = HeaderLevel,
this.Content = Content,
this.Item = Item
};
return new ReceiveObj();
I am sure many things wrong with the above code. I don't think the object is created with the arrays initialized.
On the returned object, I cannot do push() operation to insert an iten onto Content.
How would you approach this. Do you create an object on the fly, or better to define an object which enforces some structure.
If, as your code suggests, you want your instances to inherit the outer object, one approach would be to assign the prototype of the 'class' (be careful of thinking in class terms in JS - it's not a class in the Java etc sense) to the object.
var obj = {
prop_1: 'foo',
prop_1: 'bar'
}
function Something() {
alert(this.prop_1); //alerts "foo"
}
Something.prototype = obj;
var something = new Something();
This is just one way. There are many patterns for controlling inheritance (and other patterns that would achieve what you want without even going near the concept of inheritance).
As for push() not working, in your code, Content is an object, whereas push() is a method of the Array prototype. In other words, you can push() into an array only. Perhaps you meant Content.Item, which IS an array.
Lastly, I would avoid capitalising names. In JavaScript, this tends to be done only with functions that are used in class simulation (like your ReceiveObj), to denote that they should be instantiated, not merely invoked.