badigard
badigard

Reputation: 850

Right way of referencing Javascript Objects inside Objects

I'm a newbie to JavaScript, and I'm not very sure about the following legacy knockout and JS code which is being used in my app:

file1.js:

var FbPicturesObj = {
    fbPicturesVM: new FbPicturesVM(),
    initialize: function () {
        ko.applyBindings(FbPicturesObj.fbPicturesVM, $("#fb-albums")[0]);
        ko.applyBindings(FbPicturesObj.fbPicturesVM, $("#fb-album-photos")[0]);
    },
    Reset: function Reset() {
        FbPicturesObj.fbPicturesVM.albums([]);
        FbPicturesObj.fbPicturesVM.photos([]);
    }
}

file2.js:

function FbPicturesVM() { 

...... some code ....
 }

My question are:

  1. Does each call to FbPicturesObj.fbPicturesVM will create a new instance of fbPicturesVM in memory?
  2. Are the ko.applyBindings calls are written correctly? (in terms of code optimization)

Thanks a lot.

Upvotes: 5

Views: 202

Answers (3)

JotaBe
JotaBe

Reputation: 39065

File1.js contains a definition of a JavaScript object, more exactly a "literal object creation".

Inside it, each propertyName: value pair declares an initializes a new property, so that code is run only once, when creating the object. For example fbPicturesVM: new FbPicturesVM()

Properties of JavaScript objects can be functions, like this: initialize: function () {...},. In this case, whenever you run FbPicturesObj .initialize this function will be run.

The calls to ko.applyBindings are correct. This method expects the viewmodel object and an optional DOM element as the second parameter. A jQuery expression is (not exactly) an array of selected DOM elements, so this $("#fb-album-photos")[0] extracts the first DOM element of the jQuery expression, as needed by ko.applyBindings.

NOTE: as you suspect, the way of defining the model is not the best possible, to say the least. You can use the Revealing Module Pattern, which makes things much easier.

Revealing Module Pattern

In few words:

var vm = (function() { // declare a function
   // create variables and functions
   var name = ko.observable();
   var age = ko.observable();
   var _privateVar = 'Yuhu'; // won't be revealed
   var incAge = function() {
      age = age + 1;
   };
   // reveal only what you want, by returning it in an object
   return {
     name: name,
     age: age,
     incAge: incAge
     // note that _privateVar is not exposed, but could be
   }
})(); // This invokes the function, so that it return the model

NOTE the pattern Immediately-Invoked Function Expression (IIFE).

var value = (function() {/*return something*/})(); 

This pattern defines an anonymous function, that returns something. The () at the end runs it, so that the value is returned, and stored in value;

I recommend this patter because it's extremely easy to use and little error prone. Classical prototypal inheritance, constructor, and things like that are much harder to work with.

You can easyly convert this in a view model factory (something similar to a constructor, but not eactly a js constructor) by removing the invocation - () at the end - and storing the funcion definition, so that you can call it repeatedly.

var factory = function() {/*return something*/}; 

var vm1 = factory();
var vm2 = factory();

Upvotes: 3

CarbonDry
CarbonDry

Reputation: 850

This question should be split up, because the first question is unrelated to Knockout, and more to do with how object literals are associated with instantiated objects.

So for question 1, the answer is "no" because FbPicturesObj saves the instantiated property, FbPicturesObj.fbPicturesVM

Consider the following:

var Person = function(name) {
	this.name = name
}

Person.prototype.sayName = function() {
	return this.name
}

var people = {
	fred : new Person('fred')
}

console.log(people.fred.sayName()); // returns 'Fred'

var people = {
	fred : new Person('jane')
}

console.log(people.fred.sayName()) // returns 'jane' and not 'fred'

var people = {
	fred : new Person('fred'),
  jane : new Person('jane')
}

console.log(people.fred.sayName()) // returns 'fred'
console.log(people.jane.sayName()) // returns 'jane'

In this case each call to 'person.fred' will refer to the same instance always, because its saved to the people object.

Upvotes: 0

Matt Styles
Matt Styles

Reputation: 2452

The constructor for FbPicturesVM only invokes when the FbPicturesObj object is instantiated (in your example, this will just be when the script is included in the page). So, in terms of memory, you have just the one instance and you're then passing references to it (JS passes objects by reference mostly).

I dont know knockout very well but theres nothing syntactically wrong and passing an object and an element sounds about right for binding code.

Upvotes: 0

Related Questions