Just now I started using the this keyword, and I have been wondering, why doesn't "this" work?
var obj = {
thas: this,
obj2: {
obj3: {
thos: this,
lol: "lol",
pap: function() {
console.log(obj.obj2.obj3.lol);
console.log(this.lol);
console.log(thos.lol);
}
}
}
};
obj.obj2.obj3.pap();
take a look at the third console.log().
I am defining the variable so why am I this error: Uncaught ReferenceError: thos is not defined
I think thos needs to referenced in the same way as lol. You used this.lol to access lol, so to access thos you have to access this.thos, which probably wouldn't work as this.thos.lol. OOP gets weird sometimes. If you want to use thos instead, define it outside of the object and give that a try...
EDIT: Here's the modified code:
// this is the new line:
var thos = this;
var obj = {
thas: this,
obj2: {
obj3: {
lol: "lol",
pap: function() {
console.log(obj.obj2.obj3.lol);
console.log(this.lol);
console.log(thos.lol);
}
}
}
};
obj.obj2.obj3.pap();
Hope this helps!
As MDN tells us:
A function's this keyword behaves a little differently in JavaScript
compared to other languages. It also has some differences between
strict mode and non-strict mode.
In most cases, the value of this is determined by how a function is
called. It can't be set by assignment during execution, and it may be
different each time the function is called. ES5 introduced the bind
method to set the value of a function's this regardless of how it's
called, and ECMAScript 2015 introduced arrow functions whose this is
lexically scoped (it is set to the this value of the enclosing
execution context).
When a function is called as a method of an object, its this is set to the object the method is called on. So, this.lol works fine and equals to just calling lol, since they're both in the scope.
But what happens when thos.lol is called? Since this is set an object property value, the virtual machine thinks this is in global scope:
In the global execution context (outside of any function), this refers to the global object, whether in strict mode or not.
So, you call thos.lol, it sees the global object and gives you undefined error. Try to obj.obj2.obj3.thos.lol; in console and you'll see undefined. Try to obj.obj2.obj3.thos; in console and you'll see the global object.
this refers to the parent object when referred to in an object's property. Inside of the pap() method you can declare:
var thos = this;
Then you can do:
console.log(thos.lol);
you're trying to access thos when it's not in scope. pap: function() { } creates a new function scope that doesn't know anything about thos, so you need to either pass it in or do console.log(obj.obj2.obj3.thos.lol); instead of just console.log(thos.lol);
Related
I wrote a simple object destructuring function that declares variables in given scope through assigning properties to the scope object
Can I use this function in production and if not what are the pitfalls of using such a function?
function destructure(obj, scope) {
scope = scope || this;
Object.keys(obj).forEach(key => {
if (scope[key] === undefined) {
scope[key] = obj[key];
} else {
throw new Error(key + ' variable is already declared.');
}
});
}
var o = {
one: 1,
two: 2,
three: 3,
four: 4
};
destructure(o);
console.log(one); // 1
console.log(two); // 2
console.log(three); // 3
console.log(four); // 4
Are there any pitfalls of declaring variables through this keyword?
Yes: It doesn't really work.
There might be a misunderstanding about how this and scope work.
About scope
You can only declare variables by adding a property to an object is when the environment is backed by an actual object. This is only the case for two situations:
Global scope, which is (partially) backed by the global object (window in browsers)
The with statement
Examples:
// Global scope
window.foo = 42;
console.log(foo);
// with statement
var obj = {};
with (obj) {
obj.bar = 21;
console.log(bar);
}
Since you mention that you are using Node, I want to emphasize that function scope and module (which are just functions in Node anyway atm) scope are not backed by objects, thus there is no way to dynamically declare variables.
About this
The value of this depends on how a function is called. It could basically refer to any value.
Now, calling a(n unbound) function the "normal way", i.e. as f(), has this arbitrary behavior that this will refer to the global object, e.g. window in browsers. Of course being the global scope, any variable added there is accessible every else, but that's not necessarily a good thing.
So, while your solution may seem work, it really only works coincidentally when you run your code in global scope.
About strict mode
Strict mode changes some of the behaviors that were deemed incorrect or dangerous. Most importantly it changes how this inside normal function calls (f()) behaves. Instead of referring to the global object, it will simply be undefined. Thus if your function was declared in strict mode, your code would actually throw an error.
The with statement is also forbidden in strict mode.
With ES2016, new constructs such as classes and modules are strict by default, so you can see that that's the direction where the language is developing and you should not rely on behavior that doesn't work in strict mode.
declares variables in given scope through assigning properties to the scope object
No, that's not how a variable declaration works. Also, the only "scope object" that is available to JS - ignoring the with statement - is the global object (which you happened to use in your proof of concept), and you don't want to mess with that.
Can I use this function in production?
Absolutely not.
If you're sold on the idea, you probably can hack it with eval, but really you just should use real destructuring like everyone else:
var {one, two, three, four} = o;
This is the programmatic approach to:
this["one"] = 1
this["two"] = 2
this["three"] = 3
this["four"] = 4
While this theoretically fine, you may run into trouble by relying on this. It would be better to directly specify global, so that you don't run into trouble with .bind() and friends.
function destructure(obj, scope) {
scope = scope; // Remove the this
Object.assign(scope, obj);
}
destructure(o, global) // Explicitly declare global scope
As for memory leaks, performance, etc. (which I feel this question is more about), you have to remember that everything that you assign to global never gets Garbage Collected. You should really only add functions to global, and that's only if you really have to.
Why not use just Object.assign?
function destructure(obj, scope) {
scope = scope || this;
Object.assign(scope, obj);
}
Consider:
function Thing() {
this.prop = null
}
Thing.prototype.whoIsThis = function() {
console.log(this)
}
a = new Thing()
a.whoIsThis() // logs '> Thing {...}'
f = a.whoIsThis
f() // logs '> Window {...}'
So this is not bound to the Thing in the second call. How does this work in this situation? Isn't a.whoIsThis a "method" of a Thing regardless of any variable its assigned to?
When you say a.whoIsThis, it will refer the function object only. The function object will have no reference to the object on which it is attached. But when you invoke the function, JavaScript dynamically decides the current object and sets that as this inside the function.
This dynamicity allows us to use any object as the current object in the runtime.
But when you simply invoke a function object, without any object reference, by default, JavaScript will set this as the global object (window object in browser) and in Strict mode, this will be set to undefined.
A function's this keyword behaves a little differently in JavaScript compared to other languages. In most cases, the value of 'this' is determined by how a function is called, when 'this' is inside a function.
1) When a function is called as a method of an object, its this is set to the object the method is called on.
2) When a function is called directly, the value of this is not set by the call. Since the code is not in strict mode, the value of this must always be an object so it defaults to the global object. In strict mode, the value of this remains at whatever it's set to when entering the execution context, so 'undefined'.
More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
This binding is decided at run time and not at author time
having f = a.whoIsThis is just another reference to the function WhoIsThis
now Imagine If you have the function declared in the global scope..which is really what f is now..and when calling f JS at run time run the function as if it was declared in the global scope(as I mentioned) so It asks ..what this refers to if I'm in the global scope => default binding rule would answer..it's simply the global(window) object
I've gotten to the point where I am trying to fill out my javascript knowledge with some of the more advaned concepts.
I think I a pretty much understand how scope works. Where objects inherit from a prototype and then global scope, while functions offer a more traditional block scope within themselves.
What I am have trouble understanding is this:
function a(){
console.log(this.z);
}
a.z = 12;
a(); //returns undefined :(
I was expecting to echo out 12, but of course, it doesn't. Where exactly is z being stored? what does "this" refer to in the example?
When you call a function, JavaScript will set the current context (this) as the object on which it is called. If the function is not attached to any object, by default global object (window object in browsers) will be used *.
So, in this case, this refers to the global object, not a. In the global object, z hasn't been defined yet. That is why it returns undefined.
To get the value 12, you need to access it like this
function a() {
console.log(a.z); // Use `a` itself, instead of `this`.
}
a.z = 12;
a();
* In strict mode, this will be set to undefined, if the function is called without any explicit object reference.
I'm trying to understand the following code:
var x = {
editBox: _editBox,
comboBox: _comboBox,
getExchangeRate: function() {
var r = parseFloat(_editBox.val());
return isNaN(r) ? null : r;
}
}
My question is about the use of _editBox in getExchangeRate().
I'm creating a JavaScript object, which has two value properties and a function property. But why can't the function use editBox? It's undefined if I try.
And how can it use _editBox? It could be much later when the function gets called. I understand there is some work being done under the hood to make it available, but how do I know it will still be valid? Wouldn't it make more sense if I could use editBox instead?
Having come from other languages, that's certainly what seems more logical.
But why can't the function use editBox? It's undefined if I try.
It is a property of the object, not an in-scope variable.
this.editBox will probably work (assuming are you calling getExchangeRate in the right context (i.e. x.getExchangeRate()).
And how can it use _editBox?
Because the variable is in scope.
It could be much later when the function gets called.
That doesn't really matter
how do I know it will still be valid?
You control when and if it gets overwritten.
Because there is no variable such as editBox, but there is _editBox. I think you are looking for
this.editBox.val()
If you call the function as x.getExchangeRate(), then this inside the function will refer to x and you can access its property editBox. See the MDN documentation for more information about this.
Having come from other languages, that's certainly what seems more logical.
JavaScript is not like Java where instance members are automatically in scope of the methods. There is no implicit connection between a function and the object it is a property value of. That's because functions are first-class objects, they don't "belong" to anyone. And JS has lexical scope.
Consider this example:
var name = 'bar';
function foo() {
console.log(name);
}
var obj = {
name: 'baz',
prop: foo
};
As you can see, we defined foo "independently" from obj. obj.name doesn't magically become name inside the function if it is called as obj.prop().
I am new to JavaScript. I have the following script working,
var navRef = this.navigator;
function onSearch( templateName) {
navRef.onSearch();
}
but not the one below and I am trying to understand why? Any help is appreciated. (navigator is sent as an argument to this object).
function onSearch( templateName) {
this.navigator.onSearch();
}
'this' points to different objects in the two cases. In the first case 'this' most likely refers to the windows object that has navigator as a property. In the second case 'this', most likely, refers to whatever object invoked the function. It's hard to be exact since you didn't give the context. But this should be enough to understand what's going on.
You need to read up on javascript scopes. In the first example the 'this' is referencing the current scope (that has the navigator property). In your second example, the 'this' is actually referencing the current function scope it is in.
You might want to check out this article on the this keyword. Essentially, this is used within a function to refer to the context in which that function is executed. When the function in question is called as a method on an object, this refers to that object. When the function is called in the global scope and has no instance to refer to, this refers to the window object.
Javascript has lexical scope so variables (like your navRef) can be used inside inner functions
var navRef = this.navigator;
function onSearch(){
navRef.onShow();
}
the this "variable", however, is an evil exception. Each function gets its own this (and the value it has depends on how the function was called) so if you want to access an outer this (or one of its properties) you need to use an intermediate variable:
var that = this;
function onSearch(){
that.navigator.onSearch();
}