I have been reading and following several Backbone.js tutorials and when it comes to defaults for the model people seem to do it one of two ways.
First Way - Defaults are an object
The first way is that defaults are declared as an object, for example;
my_model = Backbone.Model.extend({
defaults: {
title: 'Default Title'
}
});
This makes most sense to me, I immediately know that the defaults is an object and it works fine.
Second Way - Defaults are a function
The second way I have seen this is is that defaults are declared as a function, for example;
my_model = Backbone.Model.extend({
defaults: function() {
return {
title: 'Default Title'
}
}
});
This function obviously ends up returning an object, and to me makes little sense (unless you wanted to pass something into the function eventually.
My Question
My question is, is there a benefit to using one over the other assuming that you will not be passing any parameters using the function way. My feeling is that there may be a minuscule overhead from having the anonymous function be called but would love a more informed opinion.
Remember that in JavaScript, objects are passed by reference, so if you include an object as a default value, it will be shared among all instances. Defaults containing objects passed by reference should be defined using a function if you do not wish to share objects between all instances
https://github.com/documentcloud/backbone/issues/1145
Pretty much sums it up. The function method is only recommended when you have object attributes.
I think there are not any performance difference between the 2 techniques you have described. The way the defaults is resolved (= function called or just the object returned) is decided by this line in underscore.js:
return _.isFunction(value) ? value.call(object) : value;
As for the benefits. The regular object offers a static way of declaring the model defaults. You declare them when extending and thats it, they won't change. The function on the other hand provides you with the ability to change the model defaults of the fly without re-creating the whole class, by modifying the object the function is supposed to return.
Another reason to use a function instead of an object is if one of your defaults depends on a method in the model.
e.g.
defaults: function() {
return {
title: this.getTitle()
}
Related
I saw some code in the existing code base:
angular.module("myApp", [])
.service("myService", function() {
return {
getData: function() { } // etc ...
};
});
I think the concept is this: if it is module.factory(), you provide a function to return an object (which is the service object), versus, if it is module.service(), you provide a function, which is in fact just a constructor function, so that later on, a new keyword will be used with this constructor function to obtain the service object.
However, the code above surprisingly actually works. It apparently is due to the fact that, if it is taken to be a constructor function (even though it is not), when the new keyword is used with it, it returns an object, so this object is return instead of the object instantiated with the new keyword. But the fact is, it still works.
So is it true that even when we should use module.factory(), using module.service() actually works. Is there any side effect?
I think it is best to be changed back to module.factory(), but at the same time, I am afraid if everything works now, changing it back to module.factory() may introduce new bugs.
When I use this it works:`
angular.module('app').service('DataService', function() {
return {theme: "amelia"}
});
But when I use this, there is no update? Can you tell me the difference?
angular.module('app').service('DataService', function() {
return {
theme: function() {
return {theme: "amelia"}
}
};
});
Controller
$scope.settings = DataService.theme();
Jade
select.form-control(ng-model="settings.theme", ng-options="theme for theme in themes")
Is it possible to get the second way working? Because I will share more data then one Object!
Thank you!
The first version of the code calls the function once to instantiate the service. After that, because services are singletons in angular the function isn't called again, but rather the return value (a "static" object) is accessed in every controller that uses the service after that.
The second version, each controller you inject the service into calls the theme function, which instantiates a brand new object each time. You have now effectively mitigated the fact that the service is a singleton. This is why data will not be shared with the second set of code.
If you put a break point on the function call in each case and run your code you should see the first version called once while the second version will be called many times.
"Get It Working"...
You can't really make it work with a function call but if you need to share multiple data objects there isn't any reason not to nest them. You could very easily do something like:
angular.module('app').service('DataService', function() {
return {
dataObjects: [
{"type":"theme", "theme":"amelia"},
{"type":"user", "id":123, "name":"ABC"}
]};
});
In the example I added a second object which is a user object to make shared "dataObjects" array. To find a specific object in the "dataObjects" array, you could loop till you find the correct type ("theme", for example). If necessary, you could even nest one level deeper if you needed the objects to be pristine (without the added type attribute).
Hope that helps!
It should be theme: function().... inside your service. Replace "=" with ":".
I recently stumpled over the appProperty within the the Ext.app.Application class and wondered why would I use it. I would require access to App instance anyway to then access a variable that again contains the instance? Maybe I am stupied but for what is this property?
I guess you have a misunderstanding here; The name property just defines a namespace of the Application along with a getter Method for it (getApplication()) but it will not provide you with the current instance of that application unless you call the getter or use the new appProperty.
Lets say you have the following application
Ext.application({
name: 'App',
appProperty: 'instance',
launch: function() {
// some more code
}
});
the you can access this application from any Component by calling either
App.getApplicatio();
or
App.instance
Where the second will be bit faster cause it is no method call and for sure you can define the name of this property. So I guess you see this property is quite useful!
Note that a namespace is always a object in javascript. That is the
reason why you are able to place properties into it.
The plugin model in Backbone.js is really nice, but one thing I'm wondering about is whether it's possible to use multiple plugins without modifying any of the plugin source.
For example, say I've written two plugins for the Collections:
MyBetterCollection = Backbone.Collection.extend({
coolNewFeature: function () {
console.log('This feature is great.');
}
});
MyWayBetterCollection = Backbone.Collection.extend({
wayCoolerNewFeature: function () {
console.log('This feature is even better.');
}
});
I can see some potential issues already, if, for example, both plugins override something like the add method. But having to modify third-party plugins would be a bummer:
MyWayBetterCollection = MyBetterCollection.extend({
...
});
Is there a good approach to handling this situation?
There isn't an easy way to do this. Since there is no traditional inheritance in JavaScript, it's difficult to provide this facility.
You'd have to find plug ins designed to work with each other or fork them and make them compatible.
You could also provide your own extend method that would use the interceptor pattern or some type of monkey patching to provide access to overwritten methods from previous prototypes that were overwritten.
The two collection you've defined above are totally separate from each other. They simply inherit the methods from Backbone.Controller, and if you defined methods with the similar name, then you overwrite those.
Think about Backbone as a class inheritance in other programming languages. Basically, you extend Backbone.Collection as you would do with other languages.
As a result, you can call the superclass, like this
var MyCollection = Backbone.Collection({
toJSON: function() {
var toJSON = this.constructor.__super__.toJSON.call(this);
toJSON.extra = 'my extra value';
return toJSON;
});
Even if you extend your own collection, the logic remains.
Using Backbone.js I know it's highly recommended to set a model's property using the set method, and NOT by directly modifying the attributes internal hash.
However, apart from not firing the "change" event automatically, what other cons or "side-effects" are there in modifying the internal hash directly?
The problem I'm facing is that while the set method takes an object literal, I need to assign the left hand side using a variable determined at run-time. Thanks.
myModel.set({
myProperty : myValue; //myProperty is a variable, so this is invalid syntax
})
//vs
myModel.attributes[myProperty] = myValue; //myProperty is a variable that can be evaluated
Well, if you look at the annotated source code, you'll find that set does a lot.
What if you extended Backbone.Model with a function that does it for you:
Backbone.Model.prototype.setByName = function(key, value, options) {
var setter = {};
setter[key] = value;
this.set(setter, options);
};
Then, you can just do what you want directly on the model:
var model = new Backbone.Model();
model.setByName(myProperty, "bar");
That feels like a better solution to me.
Edit
As #earl3s pointed out, this is no longer necessary in more recent versions of Backbone. Today, you can just call model.set(myProperty, "bar") and it does what you want.
In the annotated source code mentioned by Brian Genisio you can read the following lines:
"Handle both "key", value and {key: value} -style arguments.".
So you can just use model.set(myProperty,"bar",options).
Perhaps they've added this feature after the post of Brian Genisio.. i dunno.