I want to decorate a function of a JavaScript "class" (prototype):
SomeClass = function() { this.someVar = 5; };
SomeClass.prototype = {
foo: function(arg) { /* do something with this.someVar */ }
}
However, I cannot change the source of SomeClass nor am I able to influence the instance creation of SomeClass instances.
Therefore I thought about doing the following:
var oldFoo = SomeClass.prototype.foo;
SomeClass.prototype.foo = function(arg) {
console.log("Yey, decorating!");
oldFoo(arg);
};
This seems to work fine, however, due to function scoping oldFoo cannot access someVar anymore (the this object is now window). How to overcome this issue?
You need to delegate it correctly. What's happening is that since you're calling oldFoo like a bare function the this value is set to undefined (or the global object in non-strict mode).
Apply the method with the arguments and set the this value explicitly:
oldFoo.apply(this, arguments); // use the same `this` and same arguments as called.
Note that in order to be really correct - you also need to return the result. So in total your code should look something like:
SomeClass.prototype.foo = function(arg) {
console.log("Yey, decorating!");
return oldFoo.apply(this, arguments); // alternatively capture the return value
}; // and process it and then return it
Related
I can't seem to find an example of what I'm trying to achieve, although I'm sure it has been done many times before...
I want to create an object which will have a set of properties and member functions but that I can also call directly. In the same way the jQuery object allows you to call $("selector") or $.method(...)
Here's a slimmed down version of what I'm trying to achieve :
var foobar = function(param) {
return "FOO : " + this.lookup(param);
}
foobar.vals = {
1: "one",
2: "two"
};
foobar.lookup = function (param) {
return "BAR : " + this.vals[param];
}
foobar.lookup("1")
// returns "BAR : one"
foobar("1")
// returns error since 'this' points to global scope
// I'd want it to return "FOO : BAR : one"
I've also tried various approaches with function prototype but can't seem to find a method which gives me everything I want...
var foobar = function(param) {
return "FOO : " + foobar.lookup(param);
}
will return you what you want
To understand this, maybe you should take a look at the basics of JavaScript. What are functions how to instanciate an object and what are objects...
To get something like JQuery this is not very difficult, the JQuery main object is simply a function which also has "static" functions.
to declare a variable as function you do
var myFunc = function(){};
to use the variable and extend it with static stuff you simply assign it via
myFunc.staticFunc = function(){};
this doesn't mean that myFunc.staticFunc can be accessed with this in any instance of myFucn because you didn't add the function to the prototype...
To define a class like object which can be instanciated you also define it as function and then extend the prototype. Prototype is your class definition which is used to construct the object's instance:
myFunc = function(){
// ctor
this.lala = "blub";
} ;
myFunc.prototype.objectFunc = function() {
return this.lala;
}
now the object myFunc has a function objectFunc. I have to initialize it with new...
alert(new myFunc().objectFunc());
instances can access itself with this...
To do something like jquery you'll have to do some tricks. Your global variable must be a function which returns an instance of your "real" object, which can implement whatever...
Then you can call your variable as if it is a function, e.g. myFunc()...
Hope the following example makes it more clear how this works: (can be found on jsfiddle)
(function ($) {
var myRealObj = function (outId, printText) {
this.text = printText;
$("#" + outId).append("<li>" + this.text + "</li>");
};
myRealObj.prototype.objectFunc = function () {
return this.lala
};
var myFunc = function (out, txt) {
return new myRealObj(out, txt);
};
myFunc.someFunc = function () {
myFunc("out", "myFunc.someFunc got called");
};
myFunc.static = {};
myFunc.static.someFunc = function () {
myFunc("out", "myFunc.static.someFunc got called");
};
window.$$ = myFunc;
})($);
$$("out", "test obj function");
$$.someFunc();
$$.static.someFunc();
You could add:
foobar = foobar.bind(foobar);
to make the variable refer to a bound version of the function. Any call to the (updated) "foobar" would have this bound to the original function object. You'd also have to mirror the properties if you wanted to get at them directly, which is sort-of a mess.
In the jQuery implementation, there's a separate internal function that handles the basic routing of the master $() function.
Note also that the "global" $.whatever() functions don't really have much to do with the set of methods supported by jQuery instances (objects returned from the $() function). The $ object just serves as a namespace so that those global utilities don't pollute the global (window) namespace.
you declare var foobar = function(param) {... in the global scope so this will always be a window
How do I call class methods from functions within the class? My code is:
var myExtension = {
init: function() {
// Call onPageLoad
},
onPageLoad: function() {
// Do something
},
}
I've tried ...
onPageLoad();
... from within the init method but it's saying it's not defined.
I'm not having much luck with google because I don't understand the syntax used. All the JS OOP examples I find online are in a different format. I'm using the format Mozilla use for extension development.
The object the current method was invoked on is available via the special variable this. Any time you call a method on an object, this will refer, within the method, to the object.
var myExtension = {
init: function() {
this.onPageLoad();
},
onPageLoad: function() {
// Do something
},
};
this always refers to the calling object rather than the object the function is defined on or is a property of.
value = 'global';
var ob0 = {
value: 'foo',
val: function() {
return this.value;
},
},
ob1 = {value: 'bar'},
ob2 = {value: 'baz'};
ob0.val(); // 'foo'
ob1.val = ob0.foo;
ob1.val(); // 'bar'
ob0.val.call(ob2); // 'baz'
var val = ob0.val;
val(); // 'global'
In the last case, val is executed as a free function (a function that isn't bound to an object, i.e. not a method), in which case this takes on the value of the global object (which is window in web browsers) within the execution of val. Global variables are actually properties of the global object, hence val() returns 'global' (the value of the global variable named value). Since global variables are actually properties of the global object, you can view free functions as actually being methods of the global object. From this viewpoint, the last two lines (when executed in global scope) are equivalent to:
window.val = ob0.val;
window.val();
This viewpoint doesn't exactly match the reality of scoping, though you'll only notice the difference within functions. In a function, window.val = ... will create a global while var val won't.
value = 'global';
var ob0 = {
value: 'foo',
val: function() {
return this.value;
},
};
function lcl() {
var val = ob0.val; // doesn't set a global named `val`
return val(); // 'global'
}
lcl(); // 'global'
val(); // error; nothing named 'val'
function glbl() {
window.val = ob0.val; // sets a global named `val`
return window.val(); // 'global'
}
glbl(); // 'global'
val(); // 'global'
See MDN's reference page for more on the call method used above. For more on the this variable, see "JavaScript “this” keyword" and "How does “this” keyword work within a JavaScript object literal?"
Assuming that you have called init like this:
myExtension.init();
then it should be:
init: function() {
// before
this.onPageLoad();
// after
}
But in Javascript functions are not actually bound to objects and you can call any function on any other object, like this:
myExtension.init.call(anotherObject); // or
myExtension.init.apply(anotherObject);
In this example this within init would be anotherObject, which doesn't have onPageLoad defined. If you want to support this kind of usage you'll have to manually reference the initial object:
init: function() {
// before
myExtension.onPageLoad();
// after
}
In Javascript you need to explicitly mention the this
this.onPageLoad()
The same is also true for other member variables (remember that in Javascript methods are just member variables that happen to be functions)
this.memberVariable
Have you considered a closure, instead?
For example:
var myExtension = ( function() {
var me = {};
var private_thingy = "abcDEFG123";
var onPageLoad = function() {
// do page loading business
alert( private_thingy );
}
me.onPageLoad = onPageLoad;
var init = function() {
onPageLoad();
}
me.init = init;
return me;
})();
///////////////
myExtension.private_thingy = "DIDDLED!";
// will NOT modify the private_thingy declared within the closure
myExtension.init(); // will work fine.
Anything you define within the closure is available within the closure at all times, and when implemented carefully will yield private members not accessible to users of the object. Only members that you explicitly export - e.g., the me.xxx = xxx lines - are publicly available.
Thus, when onPageLoad executes, "abcDEFG123" will be displayed in the alert, not "DIDDLED!". Users of the object can modify properties using the dot operator until the cows come home; what's not made public by the closure, however, can never be modified: even if the user reassigns a function on the public interface, calls to the private function from within the closure will always point to the function defined within the closure.
The important part: it unties you from the constant use of this (unless you really want to use it; save your fingers for more important typing!).
Give it a shot. And have a look at Javascript: The Good Parts by Crockford.
I have a question on 'call' in javascript.
var humanWithHand = function(){
this.raiseHand = function(){
alert("raise hand");
}
}
var humanWithFoot = function(){
this.raiseFoot = function(){
alert("raise foot");
}
}
var human = function(){
humanWithHand.call( this );
humanWithFoot.call( this );
}
var test = new human();
so..when I use 'call' as humanWithHand.call(this), what happens internally?
does humanWithHand variable copies (or points?) its properties and members to human variable's prototype?
Yehuda Katz has a good writeup of JavaScript's Function#call method. His writeup should answer your question, and many followup questions besides.
When you call a function directly, using the general syntax:
var foo = function() {
console.log("foo");
return this;
};
foo(); // evaluates to `window`
Then this inside the function call is whatever this is outside the function call. By default, in the browser, this outside any function calls is window. So inside the function call as above, this is also by default window.
When you call a function using the method-call syntax:
var bar = {
foo: function() {
console.log("foo");
return this;
}
};
bar.foo(); // evaluates to `bar`
Then this inside the function call is the object to the left of the rightmost period: in this case, bar.
We can simulate this situation using call.
When you set up a function outside an object and want to call it with this inside the function call set to an object, you can:
var foo = function() {
console.log("foo");
return this;
}
var bar = { };
foo.call(bar); // evaluates to `bar`
You can use this technique to pass arguments as well:
var foo = function(arg1, arg2) {
console.log("foo");
return arg1 + arg2;
}
var bar = { };
foo.call(bar, "abc", "xyz"); // evaluates to `"abcxyz"`
.call() sets the this value and then calls the function with the arguments you passed to .call(). You use .call() instead of just calling the function directly when you want to set the this value inside the called function rather than let it be set to whatever javascript would normally set it to.
.apply() is a sister function. It can also set the this value and it can take arguments in an array so it can be used when you are trying to pass a variable argument list from some other function call or when you're constructing an argument list programmatically which may have different numbers of arguments depending upon the situation.
i have JavaScript components, that has following architecture:
var MyComponent = function(params)
{
setup(params);
this.doSomething()
{
// doing something
};
function setup(params)
{
// Setup
// Interaction logic
var _this = this; // "this" points to DOMWindow, not to created object
$(".some-element").click(function(){
_this.doSomething(); // it craches here, because of above
});
}
};
When something, being controlled by interaction logic, happens, sometimes i must forward execution to "public" methods of component.
In this situation, i have a problem with "this" pointer.
Sample code demonstrates it:
var Item = function()
{
this.say = function()
{
alert("hello");
};
this.sayInternal = function()
{
_sayInternal();
};
function _sayInternal()
{
this.say();
};
};
To test it,
Create an object:
var o = new Item();
This works fine:
o.say(); // alerts "hello"
This crashes:
o.sayInternal();
I get an error:
TypeError: Result of expression 'this.say' [undefined] is not a function.
I think, such a behaviour takes place, because _sayInternal() function is declared (and not assigned to object, like "this.say = function()"). This way, it is shared across all created objects and acts like a static function in C++.
Is this true ?
No, sayInternal is not shared between created objects. But you are right, the created objects don't have access to sayInternal as it is not assigned to them. This function is only local to the constructor function.
this always refers to the context a function is invoked in. If you call it like func(), then this refers to the global object (which is window in browser). If you set the function as property of an object and call it with obj.func(), then this will refer to obj.
If you assign a "bound" function to a variable and call it:
var method = obj.func;
method();
then this will again refer to the global object. In JavaScript, functions are like any other value, they don't have a special relationship to the object they are assigned to.
You can explicitly set the context with call or apply:
var MyComponent = function(params)
{
setup.call(this, params); // <- using `call`
this.doSomething()
{
// doing something
};
function setup(params)
{
// Setup
// Interaction logic
var _this = this; // "this" to new created object
$(".some-element").click(function(){
_this.doSomething();
});
}
};
or in you other example:
var Item = function()
{
this.say = function()
{
alert("hello");
};
this.sayInternal = function()
{
_sayInternal.call(this);
};
function _sayInternal()
{
this.say();
};
};
That said, this approach to assign functions to objects is not good, because every instance will have its own this.sayInternal function. So for the Item code above, every creation of an instance involves creating three functions too, which is a waste of memory.
Making use of prototype inheritance would be a better way:
var Item = function() {
};
Item.prototype = (function() {
function _sayInternal() {
this.say();
};
return {
say: function() {
alert("hello");
},
sayInternal: function(){
_sayInternal.call(this);
}
}
}());
This way, _sayInternal is only created once and all instances inherit (refer to) the prototype, so say and sayInternal also exist only once. The "trick" with the immediate function makes _sayInternal only accessible by say and sayInternal.
var Dog = function() {
var _instance = 'hello world';
return function() {
console.log(this._instance);
}
} (); //note that it is self invoking function
var l = new Dog(); //#> undefined
In the above case I was expecting an output of:
'hello world'
Why is this._instance not accessing the the variable which should be accessible by virtue of closure? I tested this in FF and am getting undefined.
You don't assign _instance to the object, it's just a closure variable, and should be accessed without using this:
var Dog = function() {
var _instance = 'hello world';
return function() {
console.log(_instance);
}
} (); //note that it is self invoking function
var l = new Dog();
I'd probably write it like so instead:
var Dog = (function() {
var defaults = {
name: 'Rags'
};
var Dog = function (options) {
// Take options as a constructor argument, this
// allows you to merge it with defaults to override
// options on specific instances
this.setOptions(options);
};
Dog.prototype = {
// Common methods for Dogs here
setOptions: function (options) {
// Declare all variables in the beginning of the method because
// JavaScript hoists variable declarations
var key = null;
// First assign all the defaults to this object
for ( key in defaults) {
this[key] = defaults[key];
}
// Now override with the values in options:
if (options && options.hasOwnProperty) {
for ( key in options ) {
this[key] = options[key];
}
}
}
};
return Dog; // Return the constructor method
} ()); // wrap the self-invoked function in paranthesis to visualize that
// it creates a closure
var buster = new Dog({name: 'Buster'}),
unnamed = new Dog();
alert(buster.name); // Alerts 'Buster'
alert(unnamed.name); // Alerts 'Rags'
Note that I have not tried to compile the above code, so it might contain a few mistakes. Nothing JsLint can't handle though!
You might want to consider adding filtering to the setOptions method so that it doesn't assign properties you don't want, or filter out methods etc declared in the options-parameter.
Additionally, if you use JQuery, or similar library, there are (often) utility functions for merging objects, making it trivial to write the setOptions-method:
function setOptions (options) {
// I assume JQuery here
// true as the first argument gives us a recursive merge
var mergedOptions = $.extend(true, defaults, options);
for (var key in mergedOptions ) {
if(this.checkAllowedProperty(key, typeof(mergedOptions[key])) {
this[key] = mergedOptions[key];
}
}
}
/**
* This method checks if propertyName is an allowed property on this object.
* If dataType is supplied it also checks if propertyName is allowed for
* dataType
* #return true if propertyName, with type dataType, is allowed on this object,
* else false
*/
function checkAllowedProperty (propertyName, dataType);
Your problem is this.
Change this._instance to _instance. You may also want to wrap your self-invoking function in parentheses like (function() { ... })(); for maximum browser compatibility.
As the others have said, you need to remove "this." from your function.
The reason for the problem is down to the binding of the "this" keyword in the two function contexts. Inside the closure, "this" refers to the function that is being returned, and not to the outer function. You could resolve this by doing the following:
var Dog = function() {
var _instance = 'hello world';
var that = this; //Assign "this" to "that"
return function() {
console.log(that._instance); //Use reference to "that"
}
} ();
var l = new Dog();
You could also probably do something closer with the function.apply() method, but I'll leave that to you.
I hope that helps.
Perhaps you are satisfied by removing "this.", but you may be interested to learn that "this" doesn't refer to what you wanted it to anyway. What it refers to really depends on how the function is called. It does not necessarily refer to an instance of an object constructed by the function you returned, or its container function, or to any other object. By default, if you merely call the function as a normal function, "this" will refer to the global window context.
What you must do to have "this" be bound to any specific object is to call the function as a method of that object, or of its prototype. e.g. foo.myMethod(). Another way is that you can use the apply or call method, passing in the object you want it to apply to. e.g. anyFunction.apply(foo).