Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the advantage of using `var self = this` in knockout.js view models [duplicate]

Tags:

I see in almost all examples of a knockout.js viewmodels the line var self = this and then all local variables are references as self.variableName. What is the advantage of this over using this.variableName?

like image 895
Neil Avatar asked Jun 18 '13 07:06

Neil


People also ask

Why do we use KnockoutJS?

Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model.

What is knockout ViewModel JS?

KnockoutJS - MVVM Framework. Model-View-ViewModel (MVVM) is an architectural design pattern for developing software applications. MVVM was developed by Microsoft Architect John Gossman in 2005. This pattern is derived from Model-View-Controller (MVC) pattern.

What is two-way binding in KnockoutJS?

KO is able to create a two-way binding if you use value to link a form element to an Observable property, so that the changes between them are exchanged among them. If you refer a simple property on ViewModel, KO will set the form element's initial state to property value.


1 Answers

Normally the main reason for using this approach is to make the current this available to subfunctions or closures. For example:

var myObject = {   param: 123,   method: function(){     alert( this.param );   },   method2: function(){     setTimeout(function(){       alert( this.param );     },100);   } } 

In the above calling myObject.method will give you the correct alert of 123. However calling myObject.method2 will give you undefined. This is because this inside the anonymous function used with setTimeout does not refer to myObject, depending on the JavaScript interpreter it will point to different things. However, if you have:

method2: function(){   var self = this;   setTimeout(function(){     alert( self.param );   },100); } 

This works because the current state of this — at the right point — is captured, and will always reference myObject for every function scope that it is available to.

The problem is not limited to the use of setTimeout. At any point where you have anonymous functions, subfunctions or closures this trick will come in handy. Sometimes people use self, or that or something a bit more descriptive depending on what the current reference represents.


rather than storing as a variable

There is an alternative to using self or any other variable to "remember" the state of this at any particular point, and that is to "bind" your anonymous or sub functions with a particular context. Many modern interpreters now support the Function.prototype.bind method, which can be used thusly:

var method = function(){   console.log(this); }; var methodWithWindow = method.bind(window); var methodWithDocument = method.bind(document); var methodWithObject = method.bind({random:"object"}); 

Calling each of the bound methods in turn would give you the following console output:

Window Document Object {random:"object"} 

If you wish to support older browsers you can use a polyfill, or if you prefer a much simpler implementation, one that doesn't worry about binding arguments as well. The basics of what the bind code does is the following:

!Function.prototype.bind && (Function.prototype.bind = function(context){   var method = this;   return function(){     method.apply(context, arguments);   } }) 

So, how would the initial example look using bind?

method2: function(){   setTimeout((function(){     console.log(this); // `this` will be the same as the `this` passed to bind.   }).bind(this),100); } 

As you can see above, once bound, the returned function (closure) retains that specified context; so it can be passed around where ever you want and still keep a this reference to the object you want. This is useful in the method2 example because we bundle the method up with our current context and pass it to setTimeout which will execute the bound method later (long after we have exited the current block execution).

The same does occur for when using self or any other variable. The variable would be captured within the function's scope chain, and would still be there for access when the function is eventually called again. The benefit of using bind however is that you can override the context easily if you so wish, you would have to code your own specific methods to do so to override a self variable.

WARNING: It is worth noting here that when you bind a function, a new function is returned. This can cause confusing situations if you mix bound functions with event listeners and then attempt to remove the listeners using the original function rather than the bound version.

Also, because binding returns a new function, if you bind a bound function you are in fact wrapping a function in a function, with another function. You should be aware of this because it affects performance and will prove trickier to manage in terms of avoiding memory leaks. My preferred approach to binding is to use closures with their own deconstruction methods (i.e. rely on self, but make sure you have methods to nullify it's content), but this does take more forward thinking and is not so relevant in smaller JS projects; or one off function bindings — especially if the bound method is never caught in any reference.


without self and bind?

It is also worth mentioning that sometimes you can achieve the same result without using bind at all, and instead use apply — which should be natively available in anything you may choose to use. The major difference being that nothing is wrapped up with the function, and calling apply actually executes the function there and then with a different context — the first argument passed to apply.

var externalMethod = function(){   console.log(this); // will output myObject when called below };  var myObject = {   method2: function(){     externalMethod.apply(this);   } }; 


What is this?

Just to elaborate this answer with further detail about this — before the recent comments get deleted. this will refer to one of four things, depending on how the function you are using it within was called:

myObject.method() 

The above will have a this of myObject, unless method has had a .bind(context) operation applied. In which case this will be whatever the last bound context was.

unattachedFunction() 

Will have a this of the global context (usually window in browser environments), unless unattachedFunction has had a .bind(context) operation applied. In which case this will be whatever the last bound context was.

anyFunction.apply(otherObject) 

or

anyFunction.call(otherObject) 

Both will always have a this of otherObject, because calling in this way will override any binding.

new myObject() 

Will have a this that refers to a new instance of myObject, this will override any binding.


Simple thought experiment

Taking all the above into account, what would this be inside referencedMethod?

var referencedMethod = myObject.method; referencedMethod(); 

Correct! it will be the global context. This is why if you want to share methods with other objects or code — but still retain the original owner as context — you really need to either bind, or keep the function bundled with its owner object so you can call or apply.

like image 65
Pebbl Avatar answered Oct 05 '22 03:10

Pebbl