JavaScript "me" = "this", why? - javascript

I saw in many source codes:
var me = this;
specially in Ext-JS 4 (JS framework). Why doing such thing? Is there any other reason or you just want for a variable to be called like "me" instead of "this"?
Thank you.

Usually so you can keep a reference to this inside a scope in which this refers to something else (like a callback function, for example).
Consider this example, in which the click event handler function has a different context to what you may expect (this doesn't refer to an instance of MyClass):
var MyClass = function (elem) {
this.elem = elem;
this.name = "James";
elem.addEventListener("click", function () {
alert(this.name); //oops
}, false);
};
Now consider this example, in which we store a reference to the value of this inside the constructor function, and use that inside the callback function:
var MyClass = function (elem) {
var me = this;
this.elem = elem;
this.name = "James";
elem.addEventListener("click", function () {
alert(me.name); //works!
}, false);
};
The callback function can refer to a variable that was declared in the outer function, even after that function has returned (the MyClass constructor returns as soon as it's executed the addEventListener). This is a demonstration of a closure.

Though of course closures are the more obvious reason for doing this, I just wanted to add that another reason can be to reduce the size of the minified version of a javascript file.
this as a keyword cannot be renamed in the process of minifying the file, while a local variable can. In other words, whenever you would use this (4 characters), instead a 1 character local variable can be used.
Consider the following example function of ExtJS's Ext.data.Store:
filterBy: function(fn, scope) {
var me = this;
me.snapshot = me.snapshot || me.data.clone();
me.data = me.queryBy(fn, scope || me);
me.fireEvent('datachanged', me);
me.fireEvent('refresh', me);
}
(note there's no closure involved here)
and its minified version:
filterBy:function(b,a){var c=this;c.snapshot=c.snapshot||c.data.clone();c.data=c.queryBy(b,a||c);c.fireEvent("datachanged",c);c.fireEvent("refresh",c)}
(151 characters/bytes)
Now, let's compare it to the minified version if we did not assign this to a local variable:
filterBy:function(b,a){this.snapshot=this.snapshot||this.data.clone();this.data=this.queryBy(b,a||this);this.fireEvent("datachanged",this);this.fireEvent("refresh",this)}
(170 characters/bytes)
As you can see the version with a local variable only takes 88% of the size of the function which uses this each time instead.
Especially in big libraries this can reduce the file size quite a bit.

Setting me=this allows you to use the this variable from an outer scope in an inner scope.
var Outer= function () {
var me = this;
me.x = "outerx";
me.inner = {
x: "innerx",
displayValues: function () {
console.log(me.x); //outerx
console.log(this.x); //innerx
}
};
};
new Outer().inner.displayValues();

Basically this utilizes closure in javascript. Read this about closure.
It is used to carry the particular instance of this to function calls where this has a different meaning.

Related

Function property goes in global variables for no reason

I'm using JQuery, latest version.
$(document).ready(function(){
function ViewModel(){
this.Impossible = "impossible!";
}
let vm = ViewModel();
});
$(window).on("beforeunload", function (e) {
console.log("this: " + this.Impossible);
console.log("window: " + window.Impossible);
// return "Test";
});
https://jsfiddle.net/5wkdxvhq/1/
How is it possible that "impossible!" gets logged? Impossible is wrapped by at least 2 functions, it definitely shouldn't be a global variable, yet there it is.
Is this a behavior of JQuery or JavaScript?
Is this standard behavior that I can rely on? Or is it some quirk that won't work in other browsers/future Knockout updates or a bad design practice by me?
Impossible isn't wrapped up by your functions declarative/lexical environments. It isn't local to your innermost function. If it was declared like this:
let Impossible = "impossible!"
//or
//var Impossible = "impossible!"
Then Impossible would have been local to that function and there wouldn't be any way to access it unless you use a closure.
What this does:
this.Impossible = "impossible!"
Is create a property definition on the this object. The value of this of course will change depending on the invocation of the surrounding function (ViewModel). In this case, you have called ViewModel() which means there's no object context and on scripts (non-strict mode) it will default to the global object (window).
Had you call it like this: new ViewModel() it would have created a new object and that would have been your this object which would have been assigned to your vm variable (and only accessible through it).
Firstly, $(document).ready(function (){}) apply global scope to the callback function function (){}. Try this, you will see console.log(this) is a Window object.
$(document).ready(function(){
console.log(this);// this is window (global) object
});
Secondly, this is different when calling function and creating an object instance of a function, i.e let vm = ViewModel(); vs let vm = new ViewModel();.
function f1 () {
function f2 () {
console.log(this === window);
}
f2()
}
f1()
In your case, by creating instance of ViewModel, you will see the different values of window.Impossible
$(document).ready(function(){
function ViewModel(){
this.Impossible = "impossible!";
}
let vm = new ViewModel(); /// create instance instead of calling
console.log('vm:' + vm.Impossible);
console.log("this: " + this.Impossible);
console.log("window: " + window.Impossible);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Accessing variables from within jQuery Document Ready

I have a simple thing like this:
function init() {
var $something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
I thought this would work, but it doesn't, console says that $something is not defined. What's the issue?
Many thanks!
$something is defined within the scope of the function 'init' so you will only ever be able to access it from within that function as it is. If you wanted to get a value back, you could return it, like so:
function init() {
var $something = 'something';
return $something;
}
jQuery(document).ready(function($) {
var $something = init();
alert($something);
}
Notice that both those variables have the same name (normally a bad idea). They are each defined within their own scope, and thus they are totally different variables.
An alternate pattern might be to wrap the entire thing and use that scope, like so:
(function () {
var $something;
function init() {
$something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
})();
That way you have a single variable, but you avoid polluting the global namespace.
Edit:
In response to your comment, the above could be written like:
var newscope = function () {
var $something;
function init() {
$something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
}
newscope();
But I have defined the function AND called it at basically the same time without having to give it a name.
This is a scoping issue.
$something is defined within the scope of the init() function, and therefore, it will be disposed of when the init() function completes.
Vars declared with var will be local to the closure in which they are declared. As you've found, this means, therefore, that outside of that closure they are not reachable.
There's many ways round this and each means a different design pattern. Here's one pattern you could use:
({
init: function() {
this.something = 'hello';
jQuery(function() { this.dom_ready(); }.bind(this));
},
dom_ready: function() {
//DOM code here
alert(this.something); //hello
}
}).init();
Here I declare several methods of an object, or namespace. Since they belong to, and are called in the context of, this one object, they communicate between one another with properties rather than variables.
Variables are thus demoted to being useful only within (but never outside of) the closure in which they are declared.
One advantage of this pattern is that you can separate any code that needs to wait for the DOM to be ready from any code that doesn't. This is achieved by having a dedicated dom_ready method.

"this" keyword in a javascript module

I have defined the following module globally:
var module = (function () {
console.log(this);
this.fn = function () {
console.log(this);
}
return this;
})();
http://www.quirksmode.org/js/this.html :
In JavaScript |this| always refers to the “owner” of the function we're executing, or rather, to the object that a function is a method of.
The first call to console.log logs Window as the value of this, and that I understand. But, so does also the second call to console.log.
Since this refers to the owner of the function, why does module.fn log Window and not module?
When I call fn I still have to write module.fn, I can't write Window.fn. Since this refers to Window I find this confusing.
EDIT: I forgot to return this in my example.
Since this refers to the owner of the function, why does module.fn log Window and not module?
The return value of the outer function is window because it doesn't get called in any particular context, so module ends up being window as well.
It seems that the way you have applied the module pattern is wrong. It should be returning the public interface that gets used in the rest of your code:
var module = (function () {
console.log(this);
// public interface
return {
fn: function () {
console.log(this);
}
}
})();
module.fn(); // "Object {fn: function}"
In your example, the global object receives the fn. It is the window object in case of browsers. That is because you are calling the function in place (effectively constructing a new scope) without specific context.
On the end, your module object is just a reference to the window (because of return this;).
What is this?
In JavaScript, this is the current context, the object on which the function was called that particular time. It is not the "holder" of the function. You can always "steal" the method from other objects and apply (literally) it to your own.
Assume you want to slice the arguments object for some reason. It looks just like an array, but it is NOT an array. arguments.slice(2,4) does not work (assuming ECMAScript < 5). What to do?
Array.prototype.slice.apply(arguments, [2,4]);
You need to steal the slice function from the Array prototype, and use if on your arguments. Inside the slice call, the "this" is the arguments object that particular time.
How to construct a valid module?
Your job is to return the module object. You do not want do mess with the context. It is not relevant, as long as you are not applying the function directly on module object.
The easiest solution is the simplest.
var module = (function() {
// do something internally, great for "private" stuff
// then return the "public" interface
return {
doSomething: function() {
// do something
},
introduce: function() {
console.log(this);
}
};
})();
module.introduce(); // Object {doSomething: function, introduce: function}
module.doSomething();
The other way.
Alternatively, you could use the this to do your job, using the apply, if you really want to.
var module = {};
(function(){
this.doSomething = function() {
// do something
};
this.introduce = function() {
console.log(this);
};
}).apply(module);
module.introduce(); // Object {doSomething: function, introduce: function}
module.doSomething();
Note this is almost equal to the "new" call.
There are more equally valid ways to do it, but the first presented one is frequently used and very clear. Anyway, everything really depends on your code conventions.
Your pattern is wrong what you are doing to make a closed scope and setting module to the return from that scope:
// This is the design pattern:
var module = (function () {
var module = {};
var localVar = 1;
module.moduleVar = 2;
module.fn = function () {
console.log(this);
}
return module;
})();
console.log(module.fn); // function() { console.log(this) ;}
console.log(module.moduleVar); // 2
console.log(module.localVar); // undefined

JS complicated massaging of scope

For a particular listener in my application, I'm using the following code for scope-busting purposes:
// this is all in a prototype of MyClass
var self = this;
myElement.addEventListener("stuff", function(e){self.doStuff(e)});
That will get doStuff to have the desired this binding.
The problem shows up when I try to removeEventListener. I suppose it's because the native function signatures must be different?
// in a different prototype of MyClass
var self = this;
myElement.removeEventListener("stuff", function(e){self.doStuff(e)}); // doesn't work
If I make a separate function that contains all of my scope-busting code, then the this binding in that code will be to the unwanted object of myElement. So the question is: How can I force listener scope and still be able to remove an added event listener?
*note using global / static variables in any way is prohibited due to the nature of the project (otherwise this would be simple!)
This has nothing to do with scope or the way in which you're storing a reference to this. The problem is that removeEventListener expects a reference to a function that's previously been registered as a listener, but you're giving it a brand new function it's never seen before.
You need to do something like this:
var self = this;
var listener = function(e){self.doStuff(e)}
myElement.addEventListener("stuff", listener);
// later
myElement.removeEventListener("stuff", listener);
It doesn't matter that the bodies of your two functions are the same; they're still different functions.
See:
https://developer.mozilla.org/en/DOM/element.removeEventListener
Inline anonymous functions are a very bad practice anyway, so I will suggest the obvious:
function MyClass() {
this.onStuff = this.onStuff.bind(this); //Each instance steals the prototyped function and adds a bound version as their ownProperty
}
MyClass.prototype = {
onStuff: function (e) { //Prototyped, no instance actually uses this very function
this.dostuff()
},
bind: function () {
myElement.addEventListener("stuff", this.onStuff);
},
unbind: function () {
myElement.removeEventListener("stuff", this.onStuff);
}
}
see removeEventListener on anonymous functions in JavaScript
You can't removeEventListener as your using an anonymous function.

Static variables in an anonymous function

I'm trying to mimic static variables on a JavaScript function, with the following purpose:
$.fn.collapsible = function() {
triggers = $(this).children('.collapse-trigger');
jQuery.each(triggers, function() {
$(this).click(function() {
collapse = $(this).parent().find('.collapse');
})
})
}
How do I save the "collapse" object so it doesn't have to be "found" on each call? I know that with named functions I could do something like "someFunction.myvar = collapse", but how about anonymous functions like this one?
Thanks!
You can save your variable in the function, using either functioName.myVar = value or arguments.callee.myVar = value if you don't have the current function name.
arguments.callee is the current function you are in.
For anonymous function you could use a function that returns a function.
For instance:
var myAnonymousFunction = (function(){
var myFirstStatic = $("#anElement");
var anotherStatic = true;
return function(param1,param2) {
// myFirstStatic is in scope
// anotherStatic also
}
})();
Should work like a charm and you're assured initialisation code for statics is only executed once.
It seems that a better answer to this question is found elsewhere on Stack Overflow.
In short, you can actually give anonymous functions names without polluting the namespace, yet still allow self-referencing.
mything.prototype.mymethod = function myKindOfFakeName() {
myKindOfFakeName.called = true;
}
As long as you're assigning the function to a variable like that, you should be able to access it as $.fn.collapsible, and thus assign variables as $.fn.collapsible.myvar.

Categories