Context in javascript - javascript

function katana(){
this.isSharp = true;
}
katana();
assert( isSharp === true, "A global object now exists with that name and value." );
var shuriken = {
toss: function(){
this.isSharp = true;
}
};
shuriken.toss();
assert( shuriken.isSharp === true, "When it's an object property, the value is set within the object." );
I am actually not getting what is this code trying to tell?. Can anyone explain me what is Context in JavaScript and what exactly does context represent here in the above code?

Well, the reason this === window in the first example and this === shuriken in the second example all has to do with where those functions are created. Notice that when you define shuirken.toss outside of the object, this points to the window object. And when you call katana with new, this points to the newly created object.

The first call is equivalent to:
katana.call(window); // window is now referenced as this inside the function
In a similar fashion, you could change the call to katana() like this to change the context:
var shuriken = {}
katana.call(shuriken); // shuriken.isSharp === true
The second invocation implicitly has this equivalent function call:
shuriken.toss.call(shuriken);

Related

Why "this" is undefined in function called via logical operator?

The following code (codepen) doesn't work.
"use strict"; //without strict "this" works, but it refers to window object
let person = {
name : "Shimon",
logName : function(){
//console.log("test") //this works
console.log(this.name); //doesn't work
}
};
//person.logName(); //works
(false || person.logName)(); //doesn't work. Uncaught TypeError: Cannot read property 'name' of undefined
I want to understand why.
When I call (false || person.logName)(); I suppose to call person.logName(), and it indeed called.
So why I cannot use this inside this method?
The are 4 rules for the JavaScript engine to determine the this reference.
New Bound: Was the current function called using the new keyword
Explicit Binding: Was the function called using Function#call or Function#apply.
Implicit Binding: Was the function directly on its owning object?
Default: If in strict mode undefined otherwise global
In your case person.logName() falls under the 3rd category so the this reference is set to the value of person.
But in the other case, the line (false || person.logName)() is equivalent to var x = false || person.logName; x();. That is because you are using an expression to get the function and thus the context is lost.
So you either need to use person.logName() or (false || person.logName).call(person)
Here is a more complex example:
var actionMap = {
0: function() {
console.log(this === actionMap ? "actionMap" : "global");
},
1: function() {
console.log(this === actionMap ? "actionMap" : "global");
},
};
function other() {
console.log(this === actionMap ? "actionMap" : "global");
}
actionMap[0](); // works
(actionMap[0])(); // works, brackets get ignored
(actionMap[0] || actionMap[1])(); // context is lost in the expression
(actionMap[2] || actionMap[1])(); // context is lost in the expression
(actionMap[2] || actionMap[1]).call(actionMap); // works, context is called explicitly
var boundOther = other.bind(actionMap);
(boundOther || actionMap[1])(); // function is bound
As you can see in this example, the context is lost even though both functions are owned by the same object. Since the function that gets called is derived by an expression and is not immediately called on its owning object.
This is simply how Javascript semantics behave. Unless you call the method from the object it is attached to this cannot be resolved in the function scope.
Your example of:
(false || person.logName)();
Could be rewritten as:
var func = person.logName;
func();
Here you can visually see that there is an intermediate step where logName() is detached from person. This has a side effect of removing the this context.
There are ways to enforce the this context on "detached" functions. One of them being Function.prototype.bind() which enables the binding of this to any function.
(false || person.logName.bind(person))();
Here a new function is created with person bound as this to achieve the desired behavior. Alternatively you could use Function.prototype.call() to avoid creating a new function.
(false || person.logName).call(person);
This works in your contrived use-case, but it may not fit into your real use-case as you will need to pass different objects as the this parameter when the functions you "detach" are not from the same object.
this works on instantiated classes/functions. In your case you have to treat the object as static like:
let person = {
name : "Shimon",
logName : function(){
console.log(person.name);
}
};
If you want this behavior you can either use classes or prototypal inherintance
class Person {
constructor(name) {
this.name = name;
}
logName() {
console.log(this.name);
}
}
or
function person(name) {
this.name = name;
}
person.prototype.logName = function() {
console.log(this.name);
}
let shimon = new Person("Shimon");
shimon.logName() // >> Shimon

Why when invoking a function is the first this the parent object, but on a subsequent function call this refers to the window object?

When initially invoking a function, the first this within the first function that is called refers to the parent object foo but on a subsequent function called by that first function this refers to the window object?
var foo = (function Obj(){
var t = this;
return {
getThis: getThis,
getSecondThis: getSecondThis
};
function getThis(){
console.log(this);
console.log(t);
getSecondThis()
return false;
}
function getSecondThis(){
console.log(this);
console.log(t);
return false;
}
})();
foo.getThis();
If I change the call from getSecondThis() to this.getSecondThis() then the this within getSecondThis() refers to the parent object foo see the code below
var foo = (function Obj(){
var t = this;
return {
getThis: getThis,
getSecondThis: getSecondThis
};
function getThis(){
console.log(this);
console.log(t);
this.getSecondThis() //line edited
return false;
}
function getSecondThis(){
console.log(this);
console.log(t);
return false;
}
})();
foo.getThis();
The getSecondThis() is within the scope of the parent object foo but window is returned when this is not specified on the second call.
It's just the way JS binds the calling context: JS binds the context (the this reference) ad-hoc. Meaning: depending on how, where and by what means it is invoked, this will reference a different object.
I've explained this in some detail before here, and in the linked answers found on the bottom
Basically, functions are first class objects, meaning that, like any value, you can assign a function to a multitude of variables/properties. Of course, if you assign a function to an object (as a property), that function is often referred to as a method, and you'd expect this to point to the object that owns that method.
However, as Pointy noted in the comments: An object cannot own another object. Objects can be referenced by one or more properties of another object.
JS will kindly set this to refer to the object that owns the function. But if you then assign the object to a variable, it would make no sense to have this point to that same object. Think of situations where you're passing functions as function arguments (callbacks in jQuery and so on). You probably want this to reference the new context (certainly the case in jQ event handlers!).
If no context is provided, JS sadly defaults the this reference to the global object.
You can explicitly bind a function to a given context using the Function.prototype.bind call.
If you want to specify the context for a single call, you can use Function.prototype.call(context, arg1, arg2); or Function.prototype.apply(context, [args]);
Most larger projects (toolkits like jQ for example) solve this issue by taking advantage of closure scoping. Using the module pattern, for example, is a common, and easy way to control the context. I've explained this, too, complete with graphs to illustrate what is going on :)
Some examples/puzzles to make this easier to follow or more fun:
var obj = (function()
{//function scope
'use strict';//this will be null, instead of global, omit this and this can be window
var obj = {},
funcOne = function()
{
console.log(this, obj, this === obj);
funcTwo(this);
obj.funcTwo();
obj.funcTwo(this);
obj.funcTwo(obj);
},
funcTwo = function(that)
{
console.log(this, obj, this === obj, this === that, that === obj);
};
obj.funcOne = funcOne;
obj.funcTwo = funcTwo;
return obj;//is assigned to the outer var
}());
obj.funcOne();
//output:
//- first logs itself twice, then true
//then calls made in funcOne:
funcTwo()
console.log(this, obj, this === obj, this === that, that === obj);
//- this: undefined (omit 'use strict' and it'll be window), obj,
// this === obj => false, this === that => false, that === obj => true
obj.funcTwo();
console.log(this, obj, this === obj, this === that, that === obj);
//logs obj twice, because obj.funcTwo means the context === obj
//this === obj is true (of course)
//that is undefined, so this === that and that === obj are false
//lastly
obj.funcTwo(this);
obj.funcTwo(obj);
You should be able to work that out. You know the context in which funcOne is being executed, and you know what the effects are of invoking funcTwo as a method of obj
Rule of thumb:
I hesitated to write this, because it's far from accurate, but 8/10 cases. Assuming no code has been meddling with contexts through bind, call, and apply, you can work out the context using this trick:
someObject.someMethod();
/\ ||
|===this===|
//another object:
var obj2 = {
borrowed: someObject.someMethod,
myOwn: function()
{
this.borrowed();
}
};
obj2.myOwn();//this === obj2 (as explained above),
\\
\==> this.borrowed === obj2.borrowed
\\
\==> ~= someObject.someMethod.call(obj2);//function is executed in context of obj2
//simple vars
var alias = someObject.someMethod;//assign to var
alias();//no owner to be seen?
||
?<==|
//non-strict mode:
[window.]alias();
/\ implied ||
|| ||
|==<this>===|
//strict mode
alias.call(undefined);//context is undefined

Binding function to another function in object

I try to bind a function but I dont really know how its work
For example
q={};
q.e=function(){return 1;}
q.e.i=function(){alert(this);}
q.e().i(); //Nothing happend I excepted that it will alert 1
So how does it work?
Thank you.
A function does also inherit from Object in Javascript. Hence you can assign properties to a function object, which you're just doing by calling
q.e.i = function() {};
but thats it. If you want to call it, you need to the same semantics
q.e.i();
in your current snippet, you're trying to execute .i() on the return value of e(), which happens to be the number 1.
You should be getting an error when calling q.e().i(); q.e() == 1 so (1).i() is an error since the Number object doesn't have an i method.
Hard to help since the code doesn't make any sense. I can only say that what you expected doesn't make sense in my head :)
Here's some code that would do what you expect
var q = {};
q.e = function() { return 1; };
q.e.i = function() { alert(this); }
// Call q.e.i, specifying what to use as this
q.e.i.call(q.e());
The trick is that in JS, this changes depending on how you call the function.
function a() {
console.log(this);
}
var obj = {
method: a
};
// Outputs the window object, calling a function without a `.` passes
// The window object as `this`
a();
// Outputs the obj, when you say obj.method(), method is called with obj as `this`
obj.method();
// You can also force the this parameter (to the number 1 in this case)
// outputs 1
obj.method.call(1);

Is it possible to toggle a global Booleans from one single function?

So far I am trying with:
var myBoolean = false; // global
function toggleBoolean(vr) {
vr = !vr;
}
alert(myBoolean); // false
toggleBoolean(myBoolean);
alert(myBoolean); // false
But obviously, It failed.
Edit: sorry, I forgot to point out that I want the function to work with many Booleans and not just one
Yes, you can toggle a global boolean from a function. You can't do it as in your attempt because JavaScript is strictly call-by-value.
function toggleBoolean() {
myBoolean = ! myBoolean;
}
Now, if you want to toggle a global by name, you could do this (though it's a little icky):
function toggleBoolean(name) {
window[ name ] = ! window[ name ];
}
Global variables (in JavaScript on a browser) are properties of the global object, which is known as "window". In other contexts there are ways of associating a name with the global context. You'd call that function, therefore, like this:
toggleBoolean( "myBoolean" );
Note that I pass a string there instead of a reference to the actual global variable.
you are passing the value to the function by value. Instead, to achieve what you want, you can do something like:
var myBoolean=false;// Global
function toggleBoolean(){
myBoolean = !myBoolean;
}
You can do
var myState = { state : false };
function toggleBoolean( s )
{
s.state = ! s.state;
}
alert( myState.state );
toggleBoolean( myState );
alert( myState.state );
primitives are passed by value by default in javascript. You can have a wrapper object and pass the object to the toggle function.

How to change the context of a function in javascript

I'm trying to understand why in javascript, you might want to change the context of a function. I'm looking for a real world example or something which will help me understand how / why this technique is used and what its significance is.
The technique is illustrated using this example (from http://ejohn.org/apps/learn/#25)
var object = {};
function fn(){
return this;
}
assert( fn() == this, "The context is the global object." );
assert( fn.call(object) == object, "The context is changed to a specific object." );
jQuery makes use of it to good effect:
$('a').each(function() {
// "this" is an a element - very useful
});
The actual jQuery code looks like this:
for ( name in object ) {
if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
break;
}
}
If it just did callback( name, object[ name ] ) then this wouldn't be set to the current object in your iterator and you'd have to use the parameter instead. Basically it just makes things easier.
Please have a look at this example:
<script>
var el = document.getElementById('button');
el.onclick = function(){
this.value = "Press Me Again"; //this --> now refers to the the element button not on the window
}
//Another Example:
var Person = function(name,location){
this.name = name;
this.location = location;
alert(this.location);
}
var p2 = new Person("Samantha","California"); //this refers to the instance of the function Person(Person now acts as a class)
var p1 = Person(); // this refers to the window(Person simply acts as a simple function)
</script>
<button id="button1">Press Me</button>
The new keyword changes the context.
It's very useful when doing callbacks from AJAX requests:
function Person(_id, _name) {
this.id = _id;
this.name = _name;
};
Person.prototype.sayHi = function(greeting) {
alert(greeting + " from " + this.name);
};
Person.prototype.loadFromAJAX = function(callback) {
// in this example, it's jQuery, but could be anything
var t = this;
$.get("myurl.php", function(data) {
callback.call(t, data.greeting);
});
};
Actually, that's a pretty crappy example.
There are tons of uses of it in jQuery. For example, the jQuery().get() function:
get: function( num ) {
return num === undefined ?
// Return a 'clean' array
Array.prototype.slice.call( this ) :
// Return just the object
this[ num ];
}
It's using the functions of the Array prototype but in the context of the jQuery object.
A real world example that i've encountered:
If you add a function as an event handler to a DOM element and if you use "this" inside that function, "this" will refer to the DOM element that you added the event handler to.
But that function might be a method of an object and you want the "this" keyword used inside it to refer to the owner object...so you need to change the context so that "this" will not refer to the DOM element but will refer to the owner object.
You can easily change the context of a function in jquery using the proxy() function.
See this question:
jquery "this" binding issue on event handler (equivalent of bindAsEventListener in prototype)
and the first answer
bind function might be what you're looking for, bind function returns a new function with in the context that you passed in , a real world scenario could be when you are using jquery delegates to attach some behavior to a dom element, and you want the callback being execute in a different context. 'cause the default context in a jquery delgate is the dom object that is bound to the handler , which means you can't access any property besides the properties that belongs to the dom object
I always find myself in the need of having different context when using setTimeout and jQuery has a handy function $.proxy which does the trick:
function iAmCalledAfterTimeout()
{
alert(this.myProperty); //it will alert "hello world"
}
setTimeout($.proxy(iAmCalledAfterTimeout, {myProperty:"hello world"}), 1000);

Categories