First of all I'm not sure is this the correct way to use this library. I would like to define multiple functions/static methods in a module and use them inside the module's scope. For example:
define({
foo: function() {
return "Hello";
},
bar: function() {
alert( this.foo() );
}
});
Main.js
require( ['foobar'], function( foobar) {
foobar.bar(); // works
$('body').click( foobar.bar ); // crash - Object #<HTMLBodyElement> has no method 'foo'
});
This code won't work if bar() is triggered by an event because obviously this will mean something different in that scope. Is there any global variable which refers to the defined object and will allow me to can access methods and attributes from inside the define() code?
EDIT: For information about this see below.
You can have an inner scope by using a function. You may want to declare your module like this:
define((function () { var self = { // this line is changed
foo: function() {
return "Hello";
},
bar: function() {
alert( self.foo() );
}
}; return self; }())); // and this
This looks awful, I acknowledge. Point is, it stores a reference to the object you return, and uses this to call foo. I'm not sure if you want it to be like this... But it works.
NOTE: This part is about the handling of this in JS. See the comment why this is not that relevant anymore for this question.
The problem is actually in main.js, not in your module. The problem is how JS handles this. This is not the same as in most other languages! this in JS is the context in which the function is called, not in which it is defined. You can apply functions to other objects, even without them having this function.
In your case you want to bind bar to your foobar object, so the this in bar is the correct object. You use the function bind for that. It creates a new function with the context set to the object you specified. Example:
function doubleMe () { return this * 2; }
You can call this function by doing this:
doubleMe.apply(5) // returns 10
You can also do this:
var giveMeTen = doubleMe.bind(5);
Now giveMeTen is a function, that returns 10 if executed. I encourage you to read more on this topic, the handling of this is strange at first, but beautiful if understood ;-)
Regarding your code, I would write:
require( ['foobar'], function( foobar) {
foobar.bar();
$('body').click( foobar.bar.bind(foobar));
});
Note that jQuery sets this to the element that has been clicked in the click() handler.
Not sure what you are trying to accomplish here, perhaps with more context as what you are trying to do, we could give you more accurate counselling.
Nevertheless, here's how I typically make modules with requireJS (classes, mostly)
if ( typeof define === "function" && define.amd ) {
define( "module", function () {
var MyModule = function(){
this.foo = function(){
return "Hello!";
};
this.bar = function(){
return this.foo();
};
}
return MyModule;
});
}
And now to use:
require("module", function(MyMod){
var a = new MyMod();
console.log(a.bar());
}
Hope this helps.
(You can probably guess how to make singletons from this using a closed scope)
This can be done by passing a function to define (this function is called a definition function). Inside the definition function:
Define the functions you'd like to use inside the module.
Make the definition function return an object with the functionality you'd like the module to expose. Here, in the object you are returning, you can call the functions defined in the previous step.
define(function() {
function foo() {
return "Hello";
};
return {
bar: function() {
alert(foo());
}
}
});
Related
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've got some code that looks like this:
var x = x || (function() {
// Some private variables
// ...
return {
init:function(options) {
// Do stuff
// ...
},
// Some other public methods
// ...
};
})();
If I'm correct then this is a singleton class. Now I wanted to write some unit tests for this class using Jasmine. I started with something like this:
describe("x", function() {
var myX;
beforeEach(function(){
myX = x;
});
it("has been instatiated correctly", function() {
expect(myX.init).toBeDefined();
});
});
I've got the understanding that, since there are parentheses around the outermost function(){}, this singleton gets instantiated immediately after it has been parsed. The lack of a call to this singleton's init method in the rest of the code strengthens my assumption (what is the sense of the init method in this case anyway?).
Concluding from this I know that
var myX;
beforeEach(function(){
myX = x;
});
can't be right. I've tried many other permutations to get something to work with (starting from leaving it all out, since I figured that the file containing the code has already been parsed at the point when the browser reaches the test spec, so the singleton class should be available, right?). But everything results in exactly the same error message:
ReferenceError: x is not defined
So how do I fix this?
Thanks in advance!
– Chris
If you get x is not defined, you seem not to have included the module code. Please show us how you embed it in your tests.
Use two type assertions, one before instantiation, and one after instantiation, to test a singleton:
function foo()
{
foo = {};
return 1;
}
console.assert(foo.constructor === Object, "foo.constructor === Function", 'foo should be a type of Function before it is instantiated')
foo();
console.assert(foo.constructor === Object, "foo.constructor === Object", 'foo should be a type of Object after it is instantiated')
References
MOP: Metamodel Oriented Programming
I understand that it is good practice to assign methods directly to a class' prototype so that they are not redefined every time the function is called.
var A = function () {
this.prop = "A property";
}
A.prototype.method = function () {
return "A method";
}
Say I want to call a method defined in this way in the constructor. Is this possible?
var A = function (options) {
initialize(options); // initialize is undefined.
}
A.prototype.initialize = function (options) {
// do something with options.
}
Creating a stub method inside the constructor doesn't work because it gets called instead of the prototype's version of the function. The only way I have been able to get this to work is to reference the function via bracket syntax this["initialize"]() which seems pretty inelegant.
var A = function (options) {
this["initialize"](options); // function isn't defined yet but will be soon!
}
A.prototype.initialize = function (options) {
// do something with options.
}
This seems pretty janky and would probably not be the most optimal way to call this function. Is there another way or am I missing something?
Your last way is correct, but you don't need the quotes.
var A = function (options) {
this.initialize(options);
};
A.prototype.initialize = function (options) {
// do something with options.
};
Assuming A is invoked as a constructor, this is a reference to the new object being constructed, and its prototype chain is already established, so it already has access to the initialize method.
Of course there's no requirement that initialize be prototyped. If it's not intended to be called more than once, then it may make more sense to define it as a stand alone function.
var A = function (options) {
initialize(this, options);
};
function initialize (obj, options) {
// do something with options.
};
or you could just put the initialization code right in the constructor function.
I'm having a confusing problem using 'this' in javascript. I have a method 'get_data' which returns me some member variable of an object. Sometimes it returns to me the object itself... I have no idea why. Can someone explain what is happening here?
function Feed_Item(data) {
this.data = data;
this.get_data = function() {
return this.data;
}
this.foo = function() {
return this.foo2();
}
this.foo2 = function() {
//here type of this.data() == Feed_Item!!! It should be of type Data
}
this.bar = function() {
//here type of this.data() == Data, as I'd expect
}
}
What 'this' is in JavaScript depends on how you call the function. If 'this' is not bound to an object, this will be the window object.
If you call
item = new Feed_Item()
item.foo() //foo will be called with correct 'this'
But if you do Feed_Item(some_data), you will add a couple of functions to the global window object.
There are a lot of articles explaining this, e.g. http://www.digital-web.com/articles/scope_in_javascript/
A good blog post that explains "this" is available here: http://www.scottlogic.co.uk/2010/05/what-is-this/
Essentially the definition of this is:
The value of this is determined at the point at which the function is invoked, and is set to the object on which the function is invoked
However sometimes it's not easy to figure out exactly what that object is. This is because it depends on how the function is invoked. You can even dynamically set the value of this by invoking the function via its call method e.g.
window.str = "hello";
var fn = function(){
alert(this.str);
};
fn();
Running this code in the browser console gives hello which is the value of str on the global window object, however if you run:
fn.call({
str: 'goodbye'
}, []);
You get 'goodbye', as the context has been changed to the object passed in. Some libraries e.g. JQuery, ExtJS, ... make use of this feature to make then easier to use.
Consider this:
window.onload = function () {
myObj.init();
};
var myObj = {
init: function () {
console.log("init: Let's call the callMe method...");
//callMe is not defined...
callMe();
//Works fine!
this.callMe();
},
callMe: function () {
console.log('callMe');
}
};
Since the init function gets called this way (myObj.init), I expect this to be myObj in the init function. And if that is the case, why the callMe function fails? How am I supposed to call the callMe function without using the this context in the init body? (Actually, it's too annoying to call the object methods using this over and over again through the functions. So what's the point of having a single object?)
I would like to know how can I fix this so that the callMe method gets called using the first invocation in the code above?
this is never implicit in JavaScript as it is in some other languages. Although there are ways to do it, like this using the with statement:
init: function () {
console.log("init: Let's call the callMe method...");
// Make `this` implicit (SEE BELOW, not recommended)
with (this) {
// Works
callMe();
}
},
...it's generally a bad idea. Douglas Crockford probably wrote one of the better descriptions of why it's a bad idea, which you can find here. Basically, using with makes it nearly impossible to tell what the code's going to do (and slows the code down, if you do anything else in that with statement that doesn't come from the this object).
This isn't the only way that JavaScript's this is not the same as it is in some other languages. In JavaScript, this is defined entirely by how a function is called, not where the function is defined. When you do this.callMe() (or the equivalent this["callMe"](), or of course foo.callMe(), etc.), two things happen: The function reference is retrieved from the property, and the function is called in a special way to set this to be the object that property came from. If you don't call a function through a property that way, the call doesn't set any particular this value and you get the default (which is the global object; window on browsers). It's the act of making the call that sets what this is. I've explored this in depth in a couple of articles on my blog, here and here.
This (no pun) can be made even clearer if you look at JavaScript's call and apply functions, which are available on all function objects. If I do this:
callMe.call({});
...it'll call the callMe function with a blank object ({}) as this.
So basically, just get used to typing this. :-) It's still useful to have properties and methods associated with an object, even without the syntactic convenience (and confusion!) of an implicit this.
You can also use the module pattern, which captures all private variables inside a closure, so you are free to use them without this, as they're in the same scope. You then pick and choose which methods/variables you want to make public:
var myObj = (function () {
var init = function () {
callMe(); // This now works
};
var callMe = function () {
...
};
// Now choose your public methods (they can even be renamed):
return {
init: init, // Same name
callMyName: callMe // Different name
};
}) ();
Now:
myObj.init(); // Works
myObj.callMyName(); // Works
myObj.callMe(); // Error