Here is a working example from "Javascript - The Good Parts".
function add(x, y){ return x + y};
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
};
myObject.increment(2);
document.writeln(myObject.value);
myObject.double = function ( ) {
var that = this; // Workaround.
var helper = function ( ) {
that.value = add(that.value, that.value)
};
helper( ); // Invoke helper as a function.
};
myObject.double( );
document.writeln(myObject.value); // 4
For function invocation pattern, 'this' object will have global reference. But I cannot fully understand under-the-hood of mentioned workaround:-
var that = this; // Workaround.
if we do this, aren't we just copying the reference to 'this' to 'that' ? i.e 'that' will hold to global scope same as 'this' ? how does this work internally ?
It's not the same here, this refers to myObject so you're getting the right value property it has, this would refer to window...which is why you want to keep the reference like it's doing.
You can test it out here, a few alerts inside the helper function show what's happening pretty well.
An alternative would be to .call() or .apply() the function with the right context, so this continues to refer to the myObject instance you want...like this:
myObject.double = function () {
var helper = function () {
this.value = add(this.value, this.value)
};
helper.call(this); // Invoke helper as a function.
};
You can test that version here.
There are two functions being involved here: one is myObject.double and the other is helper. When you call myObject.double() this refers to myObject. So that === myObject. Later, inside that function, you also call helper(), and inside that scope you have this === the global object.
Related
I would like to redefine a prototype, but still use the original prototype function. The purpose is to add test bench debug code to an existing production prototype definition.
// production definition
function tester(n) { this.name = n ; }
tester.prototype.getName = function() { return this.name; }
// debug override
if (typeof tester.prototype.getName === 'function') {
var f = tester.prototype.getName;
tester.prototype.getName = function () {
return f() + " (plus some more)";
};
}
// test
var tt = new tester('testing');
console.log(tt.getName()); // logs ' (plus some more)' but I want 'testing (plus some more)'
Naturally, my production prototypes are much more complex, and I would rather not embed my test bench code in the production code.
Many thanks in advance! - John :)
You have to call f with the correct this value and the correct arguments:
f.apply(this, arguments);
Learn more about this and apply.
This seems to be a very fragile approach though because it also requires you to know what kind of value the original method returns and what calling code is doing with it.
But I guess you are only doing this for very specific methods.
In your redefinition, you need to set the proper context (this) for the f function using Function.prototype.apply(). In your code, the context of the f function is set to the global scope, even when it is called in the redefined getName property of tt. Instead, it should be the context of the redefined getName function.
// production definition
function tester(n) { this.name = n ; }
tester.prototype.getName = function() { return this.name; }
// debug override
if (typeof tester.prototype.getName === 'function') {
var f = tester.prototype.getName;
// Here, 'this' is the global scope (window).
console.log('Global scope:');
console.log(this);
console.log('f called with context of global scope:');
console.log(f()); // prints 'name' prop of global scope
tester.prototype.getName = function () {
console.log("Instance:");
console.log(this);
// Here, 'this' is tt when tt.getName() is evaluated.
// If we call f() here, the context for f
// will still be the global scope, instead of tt.
// So, we use apply to set the context of f to
// an instance of tester.
return f.apply(this) + " (plus some more)";
};
}
// test
var tt = new tester('testing');
console.log(tt.getName()); // logs testing (plus some more)'
http://jsfiddle.net/a27xzqc9/3/
Extending Felix and Patrick's answer with encapsulation to avoid using the global variable "f", since I will be reusing this structure several times.
;(function(){
if (typeof tester.prototype.getName === 'function') {
var f = tester.prototype.getName;
tester.prototype.getName = function () {
return f.apply(this) + " (plus some more)";
};
}
})();
I just learned OOP and there is one little thing that I am not able to solve. The problem is a scope issue of some sort.
If I create a new object then how will I be able to give it access to my constructor function if the constructor function is inside another function? Right now I get undefined. Storing the function in a global variable wont do the job.
var example = new something x(parameter);
example.i();
var getFunction;
var onResize = function() {
getFunction = function something(parameter) {
this.i= (function parameter() {
// Does something
});
};
};
window.addEventListener('resize', onResize);
onResize();
For OOP javascript, the pattern should be like this.
//defining your 'class', class in quotes since everything is just functions, objects, primitives, and the special null values in JS
var Foo = function (options) {
// code in here will be ran when you call 'new', think of this as the constructor.
//private function
var doSomething = function () {
}
//public function
this.doSomethingElse = function () {
}
};
//actual instantiation of your object, the 'new' keyword runs the function and essentially returns 'this' within the function at the end
var foo = new Foo(
{
//options here
}
)
If I understand you, you want to know how to access variables inside another function. Your attempt is reasonable, but note that getFunction is not bound until after onResize is called. Here's a bit of a cleaner demo:
var x;
function y() {
// The value is not bound until y is called.
x = function(z) {
console.log(z);
}
}
y();
x('hello');
A common JavaScript pattern is to return an object that represents the API of a function. For example:
function Y() {
var x = function(z) {
console.log(z);
}
return {
x: x
};
}
var y = Y();
y.x('hello');
You should definitely read up on the basic concepts of JavaScript. Your code and terminology are both sloppy. I'd recommend Secrets of the JavaScript Ninja. It does a good job explaining scope and functions, two tricky topics in JavaScript.
Is there a way to get the Function object, while the function is executing?
I am assigning properties to my function, and want to access them. "this" doesn't help. Something like:
a.b=function(){...code...};
a.b.c=100;
I want to access a.b.c from the code in the function, without knowing its own name. "this" refers to a. How can get b?
I tried binding the function to his own object, but I couldn't.
Thank you.
I'm adding this example, I have to repeat after several different "theString" and "someSpecificValues":
Object.defineProperty(theObject, theString, {get: function(...){...}.bind(theObject, someSpecificValues), configurable: true});
You can use a named function expression for this:
var a = {};
a.b = function myFunc() {
console.log(myFunc.c);
};
a.b.c = 100;
a.b();
It allows code inside the function to access the function itself, but does not add the identifier to the enclosing scope.
Edit: Here is a more elaborate example of how the name myFunc only exists within the function:
var a = {};
a.b = function myFunc() {
console.log(myFunc.c);
};
a.b.c = 100;
a.d = function myFunc() {
console.log(myFunc.c);
};
a.d.c = 300;
a.b(); // logs 100
a.d(); // logs 300
console.log(typeof myFunc); // logs "undefined"
// create a myFunc variable
var myFunc = function() {
console.log("nooooooo!!!!");
};
a.b(); // STILL logs 100. the myFunc variable in this scope
// has no effect on the myFunc name that a.b uses
function callFunc(theFunc) {
theFunc();
}
callFunc(a.d); // STILL logs 300
// ===========================
function returnNamedFunction () {
return function myFunc() {
console.log(myFunc.c);
};
}
var iGotAFunction = returnNamedFunction();
iGotAFunction.c = 700;
iGotAFunction(); // logs 700
In the case when you cannot use a named function expression, e.g. when you are using .bind() on it, then an IIFE will suffice most of the time:
var myObj = {};
myObj.theFunc = (function () {
var f = function (arg1, arg2) {
console.log(this.theProp);
console.log(arg1);
console.log(arg2);
console.log(f.lista);
}.bind(myObj, "A!");
return f;
})();
myObj.theProp = "B!";
myObj.theFunc.lista = [1, 2, 3];
myObj.theFunc("C!");
There are two ways to get current function.
One is "almost deprecated" usage of arguments.callee. In function body it always refers to this function.
var a = {};
a.b = function () {
console.log(arguments.callee.c);
};
a.b.c = 100;
a.b();
arguments.callee is forbidden in strict mode. Reference.
The second one is using named function expression as JLRishe pointed.
arguments.callee pros and cons
Advantages:
it can be safely used with bound functions (arguments.callee refers to bound function)
it can be used with functions created using new Function
Disadvantages:
it can slow your program due to disabling certain optimisations
it's considered as almost deprecated
it can't be used in strict mode
Named function expression pros and cons
Advantages:
it's faster than arguments.callee
it's easier to understand how it works
Disadvantages:
it won't work as expected with bound functions (functionName will refer to original function, not bound one)
it can't be used in functions created with new Function
I was going through this article http://dev.opera.com/articles/view/objects-in-javascript/
where I read "'this' keyword doesn’t always refer to the object on which a method is defined, but instead can change based on specific contexts."
I couldn't find any example in which 'this' does not refer to the object on a method is defined....please gimme an example if possible
One example that you could realistically run into and not expect this result:
var obj = {
testFunc : function(){
alert("testFunc: " + this);
}
}
obj.testFunc(); // this is the object
setTimeout(obj.testFunc, 1000); // this is window
http://jsfiddle.net/t7ycd/
There are many way to change context. jsfiddle
using bind: (not supported by older IE browsers (IE < 9))
var obj = {};
function fun1(){};
obj2 = {};
obj2.fun1 = fun1.bind(obj);
obj2.fun1(); // context inside fun1 would be obj.
Using apply or call.
var obj = {};
function fun1(){};
obj2 = {};
obj2.fun1 = function(){
fun1.apply(obj, arguments);
//or fun1.call(obj, ar1, arg2,...);
};
obj2.fun1(); // context inside fun1 would be obj.
Here is one way
var a = {};
a.foo = function(){ console.log(this); }
a.foo(); // object a
a.foo.call(window); // window object
If you call a method in class B from class A, the 'this' will refer to the class that is calling the method - A, not the class that it is in.
Here is an example taken directly from the Mozilla documentation
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this
var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37
So the function independent is defined in the global context, but is then attached to an object. When it is invoked on that object the context becomes the object on which the function is invoked rather than the object on which the function is defined.
You can achieve similar results using the javascript call and bind methods, and using anonymous functions.
like this
var MyObject = function()
{
this.initialize();
}
MyObject.prototype.SomeMethod = function()
{
//common fix is to use
//var self = this;
(function(){
//this has lost scope
//self will retain the this scope that was desired
})();
}
I have 2 blocks of code, one that does not work, and one that works because I assign that = this and use that in my function instead of this. Can someone help me understand why this is so. It would help to know how one should think of accessing variables in functions in objects in JavaScript, and the nature of "this", if I am saying that right (if not, please enlighten me). Thank you!
var add = function (x, y) {
return x + y;
}
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
};
myObject.double2 = function () {
// var that = this;
var helper = function () {
this.value = add(this.value, this.value)
};
helper();
};
myObject.increment(100);
document.writeln(myObject.value); // Prints 100
myObject.double2();
document.writeln('<BR/>'); // Prints <BR/>
document.writeln(myObject.value); // Prints 100, **FAILS**
And the modified code:
var add = function (x, y) {
return x + y;
}
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
};
myObject.double2 = function () {
var that = this;
var helper = function () {
that.value = add(that.value, that.value)
};
helper();
};
myObject.increment(100);
document.writeln(myObject.value); // Prints 100
myObject.double2();
document.writeln('<BR/>'); // Prints <BR/>
document.writeln(myObject.value); // Prints 200 - **NOW IT WORKS**
The first one doesn't work because when each function's this depends on how it was called.
First you do myObject.double2() and so this = myObject. But inside double2, you call helper() by itself and there is no object you're calling it under (it's not myObject.helper()). So this defaults to the global object (or window object in a browser).
In the second example, you "capture" a reference to myObject (that=this=myObject) and so that.value=myObject.value.
I think this link would greatly help you in understanding the differences of object and private members in Javascript to tackle your problem, please take a look at the Private section. Hope it helps!
Mozilla has some good reading on this. If you want this to work without assigning this to that, you can always use call.
Example: jsfiddle.net/5azde/
And you can always remember this:
When a function of an object is called, the object will be passed as this value(as the case 'add' and 'increment' function which belong to 'window' and 'myObject' separately). And if the function doesn't belong to any objects, window(or global) will be passed as this value.(as the case with function helper in your sample code).
And I'm glad to see a pure js question. No jQuery, No css, No dom selector. haha.
May it helps. :-)