Passing method name as a callback VS. wrapping it with annoymous function - javascript

I have a button #test.
var obj = {
name: "John",
test: function() {
console.log('name '+ this.name );
}
};
$("#test").on( "click", obj.test);
This will log an empty string (logging typeof this.name gives a string).
Edit: I understand this context becomes the button, hence this.name returns nothing.
Versus
var obj = {
name: "John",
test: function() {
console.log('name '+ this.name );
}
};
$("#test").on( "click", function() {
obj.test(); //logs John
});
What is the difference?
Edit: How does wrapping obj.test() with an annon function make it behave differently than above?

It's about resolving this. If you use $("#test").on("click", obj.test); then this will be the button but if you pass a closure then this will be obj.
When I call obj.test then this in test will be obj.
JQuery will set this to be the button when it's clicked so passing obj.test without a reference to obj being this will break your obj.test function.
The best way to resolve this problem is to use Function.prototype.bind (you need the polyfil for IE < 9):
var obj = {
name: "John",
test: function () {
console.log('This is:' + this.toString());
},
toString: function () {
return "It's test"
}
};
$("#test").on("click", function () {
// by the way; this here is the button
console.log("what's this here:", this.toString());
obj.test(); //works but creates a closure
});
$("#test").on("click", obj.test);//breaks, this will be button in test
$("#test").on("click", obj.test.bind(obj));//works
// now to show how this is resolved
window.mytest = obj.test;
window.mytest();// this is window
var obj2 = {
toString: function () {
return "this is obj2";
}
};
obj2.test = obj.test;
obj2.test();//this is obj2

The real difference is what object test is operating on. When you call the function in the second example you are calling the function with the obj context. In the first example you are passing a reference to the function but not the context that it was attached to. In the first case when the function is called it is actually operating the the global scope.
To test this out try putting:
var name = "eiriki"; somewhere in global scope then running your function. It will likely print out eiriki unless you have something else assigning to it already.

Related

Scope clarification in javascript [duplicate]

This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Explanation asked about the value of 'this' in Javascript [duplicate]
(2 answers)
Closed 8 years ago.
Simple question. Why do we have set that = this? If we dont, we are in the global scope...but why?
var myObj = {
specialFunction: function () {
},
anotherSpecialFunction: function () {
},
getAsyncData: function (cb) {
cb();
},
render: function () {
var that = this;
this.getAsyncData(function () {
// this now refers to global scope....why?
that.specialFunction();
that.anotherSpecialFunction();
});
}
};
myObj.render();
Writing that = this doesn't change the scope. The way the anonymous function is called will always end up with this being global object,* because that's exactly what the spec says should happen. Using that = this is just a workaround.
You could make this always point to myObj by using Function.call:
var myObj = {
specialFunction: function () {
},
getAsyncData: function (cb) {
cb.apply(this);
},
render: function () {
this.getAsyncData(function () {
this.specialFunction();
});
}
};
and/or using Function.bind:
var myObj = {
specialFunction: function () {
},
getAsyncData: function (cb) {
cb();
},
render: function () {
function callback() {
this.specialFunction();
}
this.getAsyncData(callback.bind(this));
}
};
* Unless you're in strict mode, in which case this is undefined.
take a look at the this keyword in JavaScript and how it works. I’m sure we’ve all come across this issue:
$("myLink").on("click", function() {
console.log(this); //points to myLink (as expected)
$.ajax({
//ajax set up
success: function() {
console.log(this); //points to the global object. Huh?
}
});
});
this is a variable that is automatically set for you when a function is invoked. The value it’s given depends on how a function is invoked. In JavaScript we have a few main ways of invoking functions. I wont talk about them all today, but just the three ways most people use them; either when a function is called as a method, or on it’s own, or as an event handler. Depending on how a function is invoked, this is set differently:
function foo() {
console.log(this); //global object
};
myapp = {};
myapp.foo = function() {
console.log(this); //points to myapp object
}
var link = document.getElementById("myId");
link.addEventListener("click", function() {
console.log(this); //points to link
}, false);
Doing $("myLink").on("click", function() {}) means that when the element is clicked, the function is fired. But this function is bound as an event handler, so this is set to the reference to the DOM element myLink. The success method you define within the Ajax request is just a regular function, and as such when it’s invoked, this is set to the global object, as it is when any function that’s not an event handler or an object method is.
$("myLink").on("click", function() {
console.log(this); //points to myLink (as expected)
var _this = this; //store reference
$.ajax({
//ajax set up
success: function() {
console.log(this); //points to the global object. Huh?
console.log(_this); //better!
}
});
});
Source: http://tinyurl.com/melbl92
EDIT: in JavaScript the "this" context depends on how your function is called, example:
function helloWorld()
{
console.log(this);
}
And here two ways to call this function:
new helloWorld(); note that if you call your function in this
way, the context of this will be the context of the function +
prototype, so your console will show this: helloWorld {}
helloWorld(); if you call your function without of the "new",
the context of "this" will be global(Window), so your console will show
this: Window about:home
Ok, with this little explanation i will try to explain now why you
have sometimes to use self/that...
Imagine that you want to use this.name inside this.hello function. Like I said before, the context of "this" depends on how your function is called, so if you want to ensure that this.name inside of this.hello function refer to this.name outside is recommended that you use self/that to avoid what happens bellow
function helloWorld(){
var self = this;//or that = this
this.name = "YourName"
this.hello = function(){
console.log(this); //the context of "this" here will be: "hello {}"
return this.name; //undefined, because you don't have name attribute inside hello function
}
new this.hello(); //note how hello is called here...
}
var test = new helloWorld();
And here a good explanation about context x scope:
http://ryanmorr.com/understanding-scope-and-context-in-javascript/

javascript: "this" in an inner function

I previously posted a similar question in context to jquery but jquery's inner handling of this confused matters. So to keep it plain and simple consider:
function someCallbackFunction() {
this.name = "Tom";
}
anObject.method(someCallbackFunction);
what is the "this" of someCallbackFunction pointing to when invoked by "anObject"?
That is, when the function "some callback function" is being invoked by an object (within one of its function), what is "this" of (inner) "some callback function" pointing to then? [not the "this" of "outer" (AnObject.function - which is of course pointing to AnObject or another invoking function when called with call or apply)]
I think it should point to the global object (window object) since this inside a closure will point to the global DOM window object (in non-strict mode) or be undefined (in strict mode).
this depends on the way the function is called.
If you call it like mycallback(), this will refer to window object.
If you call it by mycallback.call(object) (or apply), this will refer to object.
http://jsfiddle.net/ydqZ8/1/
function callback()
{
alert(this.toto);
}
window.toto = 0;
var obj = {
toto : "TOTO"
};
callback(); // displays 0
callback.call(obj); // displays TOTO
Try this:
var self = this;
some callback function () {
self.name = "Tom";
}
Object.function (some callback function);
Generally 'this' is pointing to the object hosting the method called. (You may override this by function.call(thisObject, arg1, arg2) or function.apply(thisObject, [argList]).)
A simple example:
var myObject = {
value: 1,
report: function() {
return "value: " + this.value;
}
}
console.log( myObject.report() ); // "value: 1"
Should be quite clear.
Using a constructor and a prototype, it would be:
function Reporter(v) {
this.value = v;
}
Reporter.prototype = {
report: function() { return "value: " + this.value; }
};
var myObject = new Reporter(1);
console.log( myObject.report() ); // "value: 1"
Works just the same. 'this' is the object created by calling "new Reporter(1)" and the 'this' in the prototype is referring to the object which's method "report()" is called.
(The prototype comes only into play, if there's no method "report()" defined in the "myObject" as an own property.)
Now a bit more nested:
function ComplexReporter(v) {
this.value = v;
}
ComplexReporter.prototype = {
report: function() { return "value: " + this.value; },
utils: {
innerReport: function() { return "value: " + this.value; }
}
};
var myObject = new ComplexReporter(1);
console.log( myObject.report() ); // "value: 1"
console.log( myObject.utils.innerReport() ); // "value: undefined"
The first call is just as above and provides the expected result.
In the second call, 'this' is not as might be expected 'myObject', but 'myObject.prototype.utils', which has no property #value of any kind.
This is effectively
ComplexReporter.prototype.utils.innerReport.apply( myObject.prototype.utils, [] );
So as a rule of thumb, 'this' is the entity described by the path to the very last identifier before the last dot when invoking an object's method in dot-notation.
The last example without the prototype (to make it a bit simpler again):
var myComplexObject = {
value: 1,
report: function() {
return "value: " + this.value;
},
utils: {
innerReport: function() {
return "value: " + this.value;
}
}
}
console.log( myComplexObject.report() ); // "value: 1"
console.log( myComplexObject.utils.innerReport() ); // "value: undefined"
// same as myComplexObject.utils.innerReport.apply( myComplexObject.utils, [] );
console.log( myComplexObject.utils.innerReport.apply( myComplexObject, [] ) ); // "value: 1"
And: 'this' is always evaluated the very moment, the function is called (so you can't save the contextual meaning of 'this' when constructing a closure).
I hope, these examples helped a bit in understanding how 'this' works ...
P.S.: If the this-object provided with a call to function.call() or function.apply() is undefined or null, the global object ('self', in a browser identical to 'window') is used as 'this'.

Is it possible to modify a function itself when its property function is called?

Basically I want to do this:
someFunction() // do something
someFunction.somePropertyFunction()
someFunction() // Now someFunction is modified; it should now exhibit a different behaviour
Is this possible?
EDIT:
I'm not looking for what #Kolink was suggesting. Basically I want to augment a function's functionality by calling one of it's property function.
Specifically, I need to: 1. have access to the original function inside my property function (which is entirely doable using this), and 2. bind a new function to the original function's name (which I'm not sure if it's possible).
Just to be clear, I don't have access to the internal definition of the function that I want to augment. I want to attach a function to Function.prototype (so that it will be available as a property of the function that I want to augment), and then I will call func.augmentThis(), and then func should be augmented. But I'm not sure how, hence the question :P
Easily. Here's an example:
var derp = 123;
someFunction = function() {alert(derp);};
someFunction.somePropertyFunction = function() {derp = 456;};
someFunction(); // alerts 123
someFunction.somePropertyFunction();
someFunction(); // alerts 456
Okay, that's an oversimplified example, but yeah, it's entirely possible.
If your question is whether a function attached as a property to another function has a way to access the function to which it is attached, the answer is no. After all, the same function could be attached to any number of functions of objects.
So one alternative is to explicitly refer to the "mother" function within the function that is attached to it and intended to change its behavior:
function f (n) { alert (n + f.offset); }
f.offset = 0;
f.change_offset = function (i) { f.offset = i; };
f (1); //1
f.change_offset (100);
f (1); //101
Here, f is hard-wired into the definition of change_offset. If this bothers you, or you want something slightly more general, write a little routine to set a function as a property on another function, while binding its this to the function being attached to:
function set_func_as_func_prop ( propname, func_to_set, func_to_set_on ) {
func_to_set_on[propname] = func_to_set.bind(func_to_set_on);
}
Now you can write the function more generally
function change_offset (i) {
this.offset = i;
}
and set it on f or any other function.
set_func_as_func_prop ("change_offset", change_offset, f);
set_func_as_func_prop ("change_offset", change_offset, g);
Sort of:
function someFunction() {
return realFunction.apply(this, arguments);
}
function someFunctionA(name) {
return 'Hello, ' + name + '!';
}
function someFunctionB(name) {
return 'Goodbye, ' + name + '...';
}
var realFunction = someFunctionA;
someFunction.somePropertyFunction = function () {
realFunction = someFunctionB;
};
Sure it's possible. It's not recommended, but it's possible. For example:
function a() {
alert("a");
}
function b() {
alert("b");
}
function c() {
return c.f.apply(this, arguments);
}
c.f = a;
c.toggle = function () {
c.f = c.f === a ? b : a;
};
Now let's test it:
c(); // alerts "a"
c.toggle();
c(); // alerts "b"
See the demo: http://jsfiddle.net/LwKM3/
I want to attach a function to Function.prototype. Then I need to bind a new function to the original function's name (which I'm not sure if it's possible).
That indeed is impossible, you don't know what refers to the function. And you cannot change the internal representation of a function, which is immutable.
The only thing you can do is to create a new function and return that, to let the caller of your method use it somehow - specifically assigning it to the original variable:
somefunction = somefunction.augmentSomehow();
Your method for that will look like this:
Function.prototype.augmentSomehow = function() {
var origFn = this;
return function() {
// in here, do something special
// which might include invoking origFn() in a different way
};
};
Not sure if this helps, but I would implement described problem in following way:
// defined by somebody else - unknown to developer
var someFunction = function() {
alert("this is initial behavior");
}
someFunction(); // returns "this is initial behavior"
// defines parent object on which someFunction() is called
var parentObject = this; // returns window object (as called direclty in the
// browser)
// if you are calling someFunction from some object (object.someFunction())
// it would be:
// var parentObject = object;
// augumentThis definition
someFunction.augumentThis = function() {
var newFunction = function() {
alert("this is changed behavior");
};
parentObject.someFunction.somePropertyFunction = function() {
parentObject.someFunction = newFunction;
parentObject.someFunction();
};
};
someFunction.augumentThis(); // change function behavior
someFunction(); // "this is initial behavior"
someFunction.somePropertyFunction(); // "this is changed behavior"
someFunction(); // "this is changed behavior"

JS: Passing a scope as argument, binding it to a function that will be returned

Is it possible to define a function in the scope of a parent function and bind an argument passed through the parent function as its scope before returning it?
Here is an example:
var myObject = {
foo: "bar"
};
var myFunction = (function() {
return function() {
return this.foo;
};
}).call(myObject);
myFunction.call(myObject); // I'd like to bind myObject as this does ...
myFunction(); // ... but I'd like to do it before invoking it, without .call() or .apply()
Or another complex example, that describes what I'm trying to do:
var createMyCopy = function(original, self) {
var copy;
eval("copy=" + original.toString());
console.log(copy()); // returns undefined
};
(function() {
var self = "Hello world",
myFunction = function() {
return self;
};
console.log(myFunction()); // returns "Hello world"
createMyCopy(myFunction);
})();
I'm trying to create a copy of a function, so that I can make changes to it without changing the original one, but I'd like to have the variables that are defined in the original one in the copy as well...
Do you mean like this?
var myObject = {
foo: "bar"
};
var myFunction = (function() {
var self = this;
return function() {
return self.foo;
};
}).call(myObject);
I think you're getting scope and context mixed up in your question.
I think you mean this:
var myFunction = (function(obj) {
return function() {
return obj.foo;
};
})(myObject);
The immediately invoked function expression is passed myObject and returns a new function in which that parameter is bound as the variable obj.
I'm not sure what you mean, but you can do this:
var myObject = {
foo: "bar"
};
function myFunction() {
return this.foo;
}
console.log(myFunction.apply(myObject)); // bar
The first argument of apply is the context (ie. what this refers to). The second argument of apply is an array of arguments (but I've omitted that here as there are no arguments).

Can a method refer to its object in a callback without bind()?

I was making a test case to show how 'bind' is necessary for a method to refer to its function in a callback.
But just when I thought I knew JS - the following code works fine - without requiring bind!
pretendThingConstructor = function (greeting) {
this.greeting = greeting;
this.sayHello = function() {
console.log(this.greeting);
};
}
var pretend_thing = new pretendThingConstructor('hello world');
pretend_thing.sayHello();
setTimeout(function() {
pretend_thing.sayHello()
}, 3000);
When I run it - via node, phantomjs, or another JS environment - it works. 'hello world' is printed twice.
I expected the second 'hello world' - the one ran after the timout - to fail, as 'this' would refer to the event, rather than the object. But it works. Why is this?
The this changes depending on how your call the function. If you specify a base object, it will refer to that instead:
pretend_thing.sayHello()
Here pretend_thing is that base object and therefore this still refers to that object. On the other hand, if you had:
var f = pretend_thing.sayHello;
f();
Here this should refer to window object instead.
You can confirm it by putting:
console.log (this instanceof pretendThingConstructor);
Inside your sayHello function. It will print true in both cases.
pretendThingConstructor = function (greeting) {
this.greeting = greeting;
this.sayHello = function() {
console.log(this.greeting);
console.log(this instanceof pretendThingConstructor);
};
}
var pretend_thing = new pretendThingConstructor('hello world');
////////////////////////////
pretend_thing.sayHello();
setTimeout(function() {
pretend_thing.sayHello();
}, 3000);
will output:
true
true
whereas:
var f = pretend_thing.sayHello;
f();
outputs:
false
In the scope of the function 'pretendThingConstructor', 'this' refers to the function itself. When the constructor is run (when you instantiate an object using the 'new' keyword), the sayHello method (which is an anonymous method) will be assigned to the property 'sayHello' on the instantiated object (in your case, pretend_thing).
Because you're calling the 'sayHello' method FROM an instance of the 'pretendThingConstructor' object (pretend_thing), 'this' refers to the object that you're calling the method from, not the context that you're executing in.
You can change the meaning of the 'this' keyword by using the .apply method:
function myHello(){
this.greeting = 'Hello';
this.method = function(){
this.greeting
}
}
function myGoodbye(){
this.greeting = 'Goodbye';
this.say = function(){
console.log( this.greeting );
}
}
var hello = new myHello();
var goodbye = new myGoodbye();
hello.say(); // Outputs 'Hello'
goodbye.say(); // Outputs 'Goodbye'
hello.say.apply( goodbye ); // Outputs 'Goodbye'
Yes in this case the this object of sayHello is pretend_thing because the function knows on which item it is called. The this only gets lost if you're trying to do this:
<-- language: lang-js -->
var say = pretend_thing.say_hello;
setTimeout(function () {
say(); // this is window or null
}, 15)
// You can do something like
function doThings() {
console.log(this.thing);
}
var test1 = { doThings: doThings, thing: 1 };
var test2 = { doThings: doThings, thing: 2 };
test1.doThings(); // <- 1
test2.doThings(); // <- 2
So the context depends on where the function is attached. But you can override this behavior with the bind-thing.

Categories

Resources