Object literal jquery event scoping - javascript

What is the best way to solve this scoping problem?
NAMESPACE.myObject = {
foo: 'foo',
init: function() {
$('.myBtn').on('click', this.myMethod);
},
myMethod: function() {
console.log($(this), foo);
}
};
NAMESPACE.myObject.init();
The result of the console.log should be the jQuery object that was clicked and the propertie foo of myObject. How would I achieve this?

Basically you can't have more than one this, so need to work around it.
As a general rule, create a scoped variable (THIS in the example below) to hold the scope you want to retain/access from inside any other scope.
You need to retain the this on the call to the myMethod though, inside the click handler, so you can't just pass myMethod as it loses the myObject instance.
NAMESPACE.myObject = {
this.foo: 'foo',
init: function() {
var THIS = this;
$('.myBtn').on('click', function(){
// "this" here is the button clicked
// "THIS" is still the myObject instance
THIS.myMethod(this);
});
},
myMethod: function(element) {
// "this" here is myObject
// The clicked element was passed as parameter "element" instead
console.log($(element), this.foo);
}
};
NAMESPACE.myObject.init();
I hope I explained this clearly enough :)
As jfriend00 points out, you can also use bind to basically create a function call with this scope on-the-fly (very cute), but that does not work on IE8 or older.

You can use .bind() like this:
NAMESPACE.myObject = {
foo: 'foo',
init: function() {
$('.myBtn').on('click', this.myMethod.bind(this));
},
myMethod: function() {
console.log($(this), foo);
}
};
NAMESPACE.myObject.init();
Or, for older versions of IE, since you already have jQuery you can use jQuery's $.proxy():
NAMESPACE.myObject = {
foo: 'foo',
init: function() {
$('.myBtn').on('click', $.proxy(this.myMethod, this));
},
myMethod: function() {
console.log($(this), foo);
}
};
NAMESPACE.myObject.init();
When you pass this.myMethod to the event listener, it loses its binding to this (as you've noticed) because the event listener doesn't save that reference or call the method with it. One way to keep that binding is to use .bind() (requires IE9 or a polyfill for earlier versions of IE).

Since I see you tagged jQuery, you can also use this approach, I know it is different from what you posted in the question, but I still an option.
working example
var NAMESPACE = NAMESPACE || {};
$(function() {
"use strict"
$.extend(NAMESPACE, true, {
getMyObject: function() {
function myObject() {
var self = this;
self.foo = 'foo';
self.init = function() {
$('.myBtn').click(self.myMethod);
};
self.myMethod = function() {
console.log($(this), self.foo);
};
}
return new myObject();
}
});
var myObject = NAMESPACE.getMyObject();
myObject.init();
})

Related

Difference between nameFunction() {} and nameFunction () => {} in ECMA6

I'm start learning Vue.js and ECMA6 syntax, I saw this in the tutorial:
methods: {
someMethod: function() {
console.log(this) // this works
}
}
Then I thought the syntax could be:
methods: {
someMethod: () => {
console.log(this) // this undefined
}
}
but this works:
methods: {
someMethod () {
console.log(this) // this works
}
}
Can explain the difference and the ECMA5 syntax?
Of your three options, only the first one is supported in ES5. The other two are additions in ES6.
The third option is an ES6 shortcut for the first option and thus they work identically in ES6.
When you use the arrow syntax as in the second one, this is NOT set to be the host object as it is in your first and third. That's one of the features of the arrow syntax and thus it should not be used when you expect this to be set to the host object. Instead, this will be set to the lexical context from where the code was defined - often referred to as "the value of this in the enclosing context" or the "lexical value of this" which in your case would be whatever this was when the host object was initially declared which apparently was undefined.
Here's a good reference article on arrow functions: ES6 In Depth: Arrow functions
Object methods that has method someMethod. In this case this is a link to object methods.
Object methods that has property someMethod that stores some anonymous function. In this function this is undefined because function is anonymous.
Object methods has internal function someMethod. In this function this is link to methods, because it's internal named function (not anonymous or external) of this object.
Good luck!
+ Try this way
var methods1 = function() {
var self = {
someMethod: function() {
console.log(self);
}
};
return self;
}();
var methods2 = function() {
var self = {
someMethod: () => {
console.log(self);
}
};
return self;
}();
var methods3 = function() {
function someOtherMethod() {
console.log(self);
}
var self = {
someMethod: function() {
someOtherMethod();
}
}
return self;
}();
methods1.someMethod();
methods2.someMethod();
methods3.someMethod();

Access properties of object literal within jQuery method

I have an object literal as follows. In the Init method I set a handler for a click event. Later, when the handler is called, I want to access the Bar property using this keyword. At that point, this has the jQuery meaning.
Also, to make things clear, I don't want to implement functions inline with the selectors.
var StackOver = {
Bar: "MyBarValue",
Init: function(){
$("#postId").click(this.Foo);
},
Foo: function(eventObject){
// here **this** is jQuery keyword
// how do I access StackOver.Bar?
}
}
How do I access properties of this object literal inside Foo?
This could have been easy if I was using a constructor literal, which is not a go for me:
var StackOver = function (){
var self = this;
function bar()
{
// I can use self here
}
}
edit I forgot to mention that I use the Revealing Module Pattern in this object literal, that hides private properties from the object.
Everyone else is suggesting .bind, which makes sense, but you also may just be able to reference the object itself in the closure:
Foo: function(eventObject) {
console.log(StackOver.Bar);
}
one option:
Init: function(){
$("#postId").click(this.Foo.bind(this));
}
another option: (from http://api.jquery.com/jquery.proxy/)
Init: function(){
$("#postId").click($.proxy(this.Foo, this));
}
both of there take the this variable so you can't use this for other purposes
if, however, you can't use this:
Init: function(){
$("#postId").click(function (self) {
return function (event) {
return self.Foo(self, event);
}
}(this));
}
and in Foo just add the self parameter.
Foo: function (self, event...) {
...
}
All that said, why can't you use (function () {var self = this; ... }()) ?
It is the revealing module pattern, after all
var StackOver = {
/*...*/
Init: function(){
$("#postId").click(this.Foo.bind(this));
},
/*...*/
Foo: function(eventObject){
// here **this** was actually the html element
// now it's the old this.
alert(this.Bar);
}
}
I'm not sure why this has to be an object literal. If you can use other structures, you could gain access through a revealing module like this:
var StackOver = (function() {
var bar = "MyBarValue",
init = function(){
$("#postId").click(foo);
},
foo = function(eventObject) {
// here `this` might be a jQuery wrapper object
// but you can access `bar` directly.
};
return {
Bar: bar, // Or not. Do you really want this public?
Init: init,
Foo: foo
}
}())

How to reference object instance from event handler

In the code below, is there a better way to reference the object instance from handleClick() than pulling it in as a global?
var Widget = function() {
this.property = 'value';
this.bindEvents();
}
Widget.prototype = {
bindEvents: function() {
$('button').on('click', this.handleClick);
},
handleClick: function() {
var self = window.Widget;
console.log(self.property);
}
}
window.Widget = new Widget();
This question asks the same thing, and the (non-accepted) answer is to pass a callback to $.on() and call the handler from there, passing in the instance as a parameter like this:
bindEvents: function() {
var self = this;
$('button').on('click', function() {
self.handleClick.apply(self);
});
}
Is this technique of passing the instance around really the right way to do it, or is there still a preferred way besides the two I've shown?
The object can be passed as eventData to on(), like so:
var Widget = function () {
this.property = 'value';
this.bindEvents();
}
Widget.prototype = {
bindEvents: function () {
$('button').on('click', {o: this}, this.handleClick);
},
handleClick: function (e) {
var widgt = e.data.o;
console.log(widgt.property);
}
}
window.Widget = new Widget();
FIDDLE
This keeps the scope of the event handler as it should be.
You could use bind():
bindEvents: function() {
$('button').on('click', function() {
this.handleClick();
}.bind(this));
}
But when it comes to IE, that would work only in IE >= 9.
Edit: in legacy IE, you could use, as suggested by #Kevin B in the comment, jQuery's $.proxy():
bindEvents: function() {
$('button').on('click', $.proxy(function() {
this.handleClick();
}, this));
}
As #kamituel says, bind can be used:
Widget.prototype = {
bindEvents: function() {
$('button').on('click', this.handleClick.bind(this));
},
handleClick: function() {
console.log(this.property);
}
}
The specified technique is called closure, and it is in common use.
However, sometimes it is important to pay attention not to cause memory leaks.
You can read more about closure here:
How do JavaScript closures work?
and about memory leaks related to closures, here:
Memory leak risk in JavaScript closures
The best way to preserve this are lambda functions:
$('button').on('click', () => this.handleClick());
or if you would like to keep the reference to the event:
$('button').on('click', (event) => this.handleClick(event));
The special thing about lambda functions is that you don't define another function in a (potentially different) context. The lambda term is just an instruction and therefore part of the object it was defined in - in your case the instance created by window.Widget = new Widget().

Error returning an object attribute in JS

function Objecte(name){
this.name=name;
}
Objecte.prototype.look=function(){
return function(){
alert(this.name);
};
}
I'm trying to access the Object's attribute but when I call the function, it alerts undefined.
extintor = new Objecte("extintor");
$(document).on('dblclick',"#extintor",extintor.look());
this is not lexically defined. You need to capture its value in order to ensure that the returned function can use it.
Objecte.prototype.look=function(){
var self = this;
return function() {
alert(self.name);
};
}
You can also use $.proxy
Objecte.prototype.look=function(){
return $.proxy(function() {
alert(this.name);
}, this);
}
or .bind() in modern browsers
Objecte.prototype.look=function(){
return function() {
alert(this.name);
}.bind(this);
}
The anonymous function that you return has another context of this.
Therefore you have two options:
1. Create a reference to this outside and use it inside your anonymous function
Objecte.prototype.look = function() {
var objecteInstance = this;
return function() {
alert(objecteInstance.name);
};
}
2. Use Function.prototype.bind which is supported in all major browsers including IE9+
Objecte.prototype.look = function() {
return function() {
alert(this.name);
}.bind(this);
}
If you don't need to support IE8 and lower, then the second option it is - as for me it looks more elegant.
Does look need to return a function? I think what you are trying to do is this:
Objecte.prototype.look=function(){
alert(this.name);
}
However, this in this context will be resolved when the function is executed and could be bound to another object. I think it is clearer in any case to use the closure declaration of objects instead of using the prototype. You could declare your object this way:
function Objecte(name) {
var that = this;
this.name = name;
this.look = function() {
alert(that.name);
}
}
The disadvantage is that every object of the type Objecte will have its own copy of the function look, but when was the last time you ran out of memory?
Another disadvantage is that you can't inherit from another object, rewrite a method and later call the original method from this object, as it will be lost. However disadvantageous that is..

Prototype Pattern and "this"

I'm trying to create a client-side api for a web control using the Prototype pattern. However I want to make life easier by not having to manage "this".
This is some sample code (i have commented the problematic line):
MyObject = function ()
{
MyObject.initializeBase(this);
this._someProperty = null;
};
MyObject.prototype = {
initialize: function()
{
// Init
},
get_someProperty: function()
{
return this._someProperty;
},
set_someProperty: function(value)
{
this._someProperty = value;
},
doSomething: function ()
{
$('.some-class').each(function ()
{
$(this).click(this.doClick); // this.doClick is wrong
});
},
doClick: function ()
{
alert('Hello World');
}
};
Normally, using the revealing module pattern I would declare a private variable:
var that = this;
Can I do something similar with the Prototype pattern?
You can do the exact same thing you are used to, just do it within the doSomething method:
doSomething: function ()
{
var instance = this;
$('.some-class').each(function ()
{
$(this).click(instance.doClick);
});
},
This approach has nothing to with prototype or not, it's just how to manage context with nested functions. So when a function on a prototype (method) has nested functions within in, you may have to preserve the context this at any of those level if you want to access it in a nested scope.
ES5's Function.prototype.bind() might be an option for you. You could go like
doSomething: function ()
{
$('.some-class').each(function(_, node)
{
$(node).click(this.doClick); // this.doClick is right
}.bind(this));
},
Now, we proxied each event handler by invoking .bind() and as a result, we call it in the context of the prototype object. The caveat here is, you no longer have this referencing the actuall DOM node, so we need to use the passed in arguments from jQuery instead.

Categories