I was developing using module pattern, and was wondering why I can't acces to the module scope using this. Maybe I'm wrong with the understanding of the revealing module pattern.
Here is the code I use :
var BLOG = window.BLOG || {};
BLOG.Module = (function(){
var
_this = this,
_hasLoaded = false;
function init(){
console.log(_this); // Logs the Window object
}
function _privateMethod(){
$.ajax({
url: 'lazyload/lazyload.html',
success : function( data ){
// _hasLoaded = true; // I want to access the variable in my module, How do I refer to it?
}
});
}
return {
init : init
};
})();
this is determined by how a function is called. If it's called directly, not through an object property (as your outer scoping function is), within that call this will be the global object in loose mode (undefined in strict mode). On browsers, that's the window object.
You wouldn't normally use this to try to refer to things within that outermost scoping function (for this reason).
If something did this:
BLOG.Module.init();
...then within the call to init, this (not _this) would refer to Module and you could refer to other properties on the object you create at the end of your outermost scoping function (there aren't any others currently, just init).
Re your edit:
var
_this = this,
_hasLoaded = false;
// ...
function _privateMethod(){
$.ajax({
url: 'lazyload/lazyload.html',
success : function( data ){
// _hasLoaded = true; // I want to access the variable in my module, How do I refer to it?
}
});
}
Just uncomment that line:
_hasLoaded = true;
This is because both _privateMethod and any ajax success handlers created as a result of calling _privateMethod are closures over the variables defined within your outermost scoping function. So you just refer to them directly.
If this use of the word "closure" is unfamiliar, don't worry, closures are not complicated.
Side note: This is an odd construct:
var BLOG = window.BLOG || {};
...as it mixes code requiring that it be at global scope with code that doesn't require that it be at global scope. It's entirely functional, it's just a bit odd. I'd probably go one way or the other:
// Requires that it's at global scope (and yes, this does work)
var BLOG = BLOG || {};
or
// Creates a global whether it's at global scope or not
window.BLOG = window.BLOG || {};
Related
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>
When I type this in node.js, I get undefined.
var testContext = 15;
function testFunction() {
console.log(this.testContext);
}
testFunction();
=>undefined
Without var keyword, it passes (=>15). It's working in the Chrome console (with and without var keyword).
It doesn't work in Node when using var because testContext is a local of the current module. You should reference it directly: console.log(testContext);.
When you don't type var, what happens is that testContext is now a global var in the entire Node process.
In Chrome (or any other browser - well, I'm unsure about oldIE...), it doesn't matter if you use var or not in your example, testContext will go to the global context, which is window.
By the way, the "global context" is the default this of function calls in JS.
The key difference is that all modules (script files) in Node.js are executed in their own closure while Chrome and other browsers execute all script files directly within the global scope.
This is mentioned in the Globals documentation:
Some of these objects aren't actually in the global scope but in the module scope - this will be noted.
The vars you declare in a Node module will be isolated to one of these closures, which is why you have to export members for other modules to reach them.
Though, when calling a function without a specific context, it will normally be defaulted to the global object -- which is conveniently called global in Node.
function testFunction() {
return this;
}
console.log(testFunction() === global); // true
And, without the var to declare it, testContext will default to being defined as a global.
testContext = 15;
console.log(global.testContext); // 15
As mentioned in the document
var something inside an Node.js module will be local to that module.
So, it is going to be different as the var testContext is in module context and the context of this is global.
You can alternatively, use:
global.testContext = 15;
function testFunction() {
console.log(this.testContext);
}
testFunction();
I believe the problem has to do with the this key word. If you do console.log(this) you will see that testContext is not defined. You may want to try:
this.testContext = 15;
function testFunction() {
console.log(this.testContext);
}
testFunction();
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
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.
If I want to give a JavaScript variable global scope I can easily do this:
var myVar;
function functionA() {
myVar = something;
}
Is there a similarly simple and clean way -- without creating an object -- to separate the "declaring" and the "defining" of a nested function? Something like:
function functionB; // declared with global scope
function functionA() {
functionB() { // nested, would have local scope if declared here
CODE;
}
}
I should clarify that I'm referring to the scope of the function name itself -- so that if it is in an iframe it can be called from a script in the parent document. (Nothing to do with scope of variables inside nested functions.)
You can create global variables and functions by creating instances on the window object:
function _A()
{
// scoped function
function localFunctionInTheScopeOf_A()
{
}
// global function
window.globalFunctionOutsideTheScopeOf_A = function ()
{
};
}
In your case, though, all you need to do is this:
var myFn; // global scope function declaration
function randomFn()
{
myFn = function () // global scope function definition
{
};
}
Note: It is never a good idea to clog up the global scope. If you can; I'd recommend that you re-think how your code works, and try to encapsulate your code.
Perhaps I'm misunderstanding the question, but it sounds like you want something like this:
var innerFunc;
function outerFunc() {
var foo = "bar";
innerFunc = function() {
alert(foo);
};
}
You cannot globalize variables/functions cross windows/iframes that way. Each window/iframe has it's own global scope and to target variables/functions in another window/iframe, you need explicit accessor code and conform to the same origin policy. Only variables/functions inside the windows/iframes global scope are accessible.
code in top window.
var iframe = document.getElementById('iframeId');
var iframeContext = iframe.contentWindow || iframe;
// this will only work if your iframe has completed loading
iframeContext.yourFunction();
You could also possibly define functions/variables in the top window instead and simply work in one scope by binding the stuff you need from the iframe through a closure. Again, assuming you meet the same origin policy. This will not work cross domain.
code in iframe.
var doc = document;
var context = this;
top.myFunction = function(){
// do stuff with doc and context.
}
It is also important to note, that you need to check if your iframe content and it's scripts are fully loaded. Your top page/window will inadvertidly be done and running before your iframe content is done, ergo variables/functions might not be declared yet.
As for exposing a private function, others have awnsered this, but copy/pasting for completeness.
var fnB;
var fnA = function(){
var msg = "hello nurse!";
fnB = function(){
alert(msg);
}
}
I have the habbit of declaring stand alone functions as variables (function expression) and only use function statements to signify constructors/pseudo-classes. It also avoids a few possible embarrasing mistakes.. In any case, fnB resides in the global scope of the iframe and is available to the top window.
Why exactly you want this beats me, seems it makes matters more complicated to debug or update a few months later.
You can kind of do what you want.
You can create a function that acts like a namespace for properties and methods, and then you could essentially call either...
functionB();
or
functionA.functionB();
There is an article on how to do it here:
http://www.stevefenton.co.uk/Content/Blog/Date/201002/Blog/JavaScript-Name-Spacing/
In response to the update...
Is the iframe on the same domain as the parent site? You can't call JavaScript across the domain boundary, which may explain the problem.