I cannot figure out why the following does not work:
var foo = { f1: () => 5, f2: this.f1() }
I get this error:
TypeError: this.f1 is not a function
It seems that this refers to the global scope instead of foo. Everything works fine with the following:
var foo = { f1: () => 5, f2() { return this.f1 } }
Is there a way to refer to f1 without wrapping it in a new function?
The scope where you're executing the assignment is global scope, so this is not foo. One might think that this would then fix it:
var foo = { f1: () => 5, f2: foo.f1() }
but it doesn't - because at the time when the literal is constructing the object, the assignment hasn't happened, and foo is still undefined.
You need to take timing into account, and write one of the following:
var foo = { f1: () => 5 }
foo.f2 = foo.f1
or
var f = () => 5
var foo = { f1: f, f2: f }
Of course, if you want to simply resolve to f1 at runtime, and don't want to explicitly invoke a function with parentheses, you can still sneak in a function as a getter:
var foo = { f1: () => 5, get f2() { return foo.f1 } }
foo.f2
# => [Function: f1]
This answer is probably helpful:
How does the "this" keyword in Javascript act within an object literal?
This answer also has useful comments on the difference between arrow and regular functions, particularly how this is affected in each:
Arrow functions vs Fat arrow functions
var testOne = {
a: 'hello',
b: () => console.log(this.a)
}
var testTwo = {}
testTwo.a = 'hello'
testTwo.b = function() {console.log(this.a)}
console.log(testOne.a)
testOne.b()
console.log(testTwo.a)
testTwo.b()
Related
I would like to learn the proper way to hoisted method in object.
My goal is the put the object definition at the end of the code, and the object usage at the beginning. Let's say:
doing stuff with o
let o = {
bar: 1,
foo: function() {
console.log(this.bar);
},
}
Of course, if I'm writing :
o.foo();
let o = {
bar: 1,
foo: function() {
console.log(this.bar);
},
}
o is undefined because the object is declared after the usage.
I thought about this trick :
o = returnO();
o.foo();
function returnO() {
return o = {
bar: 1,
foo: function() {
console.log(this.bar);
},
}
}
which works but I would like to know if there's not a better way to avoid the statement
o = returnO();
Thanks.
I don't know what's the purpose of what you want to accomplish, maybe you're practicing.
The problem is the post declaration of the variable, the engine at this point of the interpretation of your code o.foo();, the variable o was declared but not yet initialized.
You can use a function declaration (Hoisted function) as follow, and use it as a constructor which is similar to what you want to accomplish:
new A().foo();
function A() {
this.bar = 1;
this.foo = function() {
console.log(this.bar);
};
}
This question already has answers here:
What does "this" refer to in arrow functions in ES6?
(10 answers)
Closed 6 years ago.
Why isn't this inside of the setTimeout equal to the object that invoked the render function when using arrow functions?
class X {
constructor(config) {
this.data = config.data;
this.render_ = config.render;
}
render() {
this.render_(this.data);
}
}
var x = new X({
data: [1, 2, 3],
render: (data) => {
setTimeout(() => {
console.log(this);
}, 200);
}
});
x.render();
Read the part of the arrow function documentation that says "Arrow functions used as methods"
in summary: arrow functions just simply do not bind this or their own version of this, but rather references the global Window object.
Because arrow functions are lexically bound. That means they take on the value of "this" at the time of declaration. They are not affected by other means of modifying the this value, including being called as a method or functions like bind, apply and call.
function F() {
this.type = 'F';
this.logTypeExpression = function() {
console.log(this.type);
};
this.logTypeArrow = () => {
console.log(this.type);
};
}
function G() {
this.type = 'G';
}
var f = new F();
var g = new G();
f.logTypeExpression(); // F
f.logTypeArrow(); // F
// Now lets give these functions to G
g.logTypeExpression = f.logTypeExpression;
g.logTypeArrow = f.logTypeArrow;
g.logTypeExpression(); // G
g.logTypeArrow(); // F(!) (That's because `this` was assigned by the arrow function)
At the time that the arrow function is created, this isn't bound to any object, so it still refers to window. Maybe you want to try console.log(x); if you want to refer to that specific instance?
The code below only holds a reference to the function you created using an object literal syntax.
this.render_ = config.render;
Using bind(this) will tell the function to use the parameter object as the this reference when calling the function in the instance of your X object.
class X {
constructor(config) {
this.data = config.data;
this.render_ = config.render.bind(this);
}
render() {
this.render_(this.data);
}
}
Also, it does not matter if it's an arrow function or a regular function expression in your code snippet.
In the traverse npm package there's this example
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
traverse(obj).forEach(function (x) {
if (x < 0) this.update(x + 128);
});
Inside the callback function you can call this.update. I know in these kinds of cases you should use the regular (not ES6 arrow) style of function definitions like shown above.
But out of curiosity, how would you make that code work with ES6 arrow function syntax? If I try as follows I get TypeError: Cannot read property 'update' of undefined because of course this is not the same as above.
traverse(obj).forEach((x) => {
if (x < 0) this.update(x + 128);
});
I tried to change this by using bind but wasn't successful. How do you change this in ES6 arrow functions?
How do you change this in ES6 arrow functions?
You can't, that's the whole point of using arrow functions (besides concise syntax). If you need to control this, don't use an arrow function.
Arrow functions in ES6 have lexical this. This means you can always tell their this-value by asking the question "What was the value of this when the function was defined?" They are immune to the this-altering effects of call, apply, bind, and method invocation.
Let's take a look at the intended effect by compiling ES6 to ES5 with Babel.
ES6:
let foo = {
bar() {
return [
() => this,
function() {
return this
}
]
},
baz: () => this
}
let fns = foo.bar()
fns.push(...fns.map((fn, i) => fn.bind({i})))
fns.map(fn => fn()).forEach(thisVal => {
console.log(thisVal)
console.log('-------')
})
console.log(foo.baz())
Compiled to ES5:
'use strict';
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var foo = {
bar: function bar() {
var _this = this;
return [function () {
return _this;
}, function () {
return this;
}];
},
baz: function baz() {
return undefined;
}
};
var fns = foo.bar();
fns.push.apply(fns, _toConsumableArray(fns.map(function (fn, i) {
return fn.bind({ i: i });
})));
fns.map(function (fn) {
return fn();
}).forEach(function (thisVal) {
console.log(thisVal);
console.log('-------');
});
console.log(foo.baz());
Check out what happened inside foo.bar. There was an inserted line:
var _this = this;
The arrow function's body was changed so that it returned _this from its lexical outer scope. Therefore, binding the value of this is not supposed to have any effect. By "freezing" the value of "this" just before the function definition, we have prevented the ability to change the function's behavior through binding, using call/apply, or calling the function as a method.
Notice how baz's this value was simply replaced with undefined. That's because if we ask the question "What was the value of this when the function was defined?", we obviously must answer either undefined or some global object context, like Node's global or the browser's window. By asking this question, you can easily deduce the value of this inside arrow functions, and that is a big part of their inherent value.
The output is therefore:
{ bar: [Function: bar], baz: [Function: baz] }
-------
undefined
-------
{ bar: [Function: bar], baz: [Function: baz] }
-------
{ i: 1 }
-------
undefined
The first and third outputs correspond to the arrow function defined within bar(). Notice how they automatically have a this-value that points to the object referred to by foo because they were created with the foo.bar() call. Useful right? Only the behavior of the non-arrow function could be changed by binding. Before binding, it's this-value was undefined, a common cause of many JS bugs and redundant bindings when passing around functions. Arrow functions take away a lot of that hassle.
The code goes like this
var ob = {
a: function() {
b()
},
b: function() {
console.log("hi")
}
};
As you can see, you can't do
ob.a() //returns error
Can someone explain the reason in depth?
Becuase b does not exist in the current scope (which is the global one in this case).
This works, however:
var ob = {
a: function () {
this.b()
},
b: function () {
console.log('hi')
}
};
because this refers to the ob object.
There is no function b defined anywhere, it's a property of object ob, so you can refer it as this.b from inside a:
var ob = {
a: function () {
this.b();
},
b: function () {
console.log("hi");
}
};
ob.a();
You could also access b as ob.b().
The b is a property of the object called ob. That being said, if you use
ob.b
instead of b you will solve your problem.
var ob = {
a:function(){
ob.b()
},
b:function(){
console.log("hi")
}
};
Another way to do achieve this is to use the this operator.
var ob = {
a:function(){
this.b()
},
b:function(){
console.log("hi")
}
};
The this holds a reference to the object you define. Hence using it you can access is properties. It is a better way that the first way, because if you decide later to alter the name of ob to obj, you will not have change it in two places.
I have the following anonymous function:
(function() {
var a = 1;
var b = 2;
function f1() {
}
function f2() {
}
// this => window object!
// externalFunction(this);
})();
function externalFunction(pointer) {
// pointer.f1(); => fail!
}
I need to call external function from this anonymous function and pass it's pointer to call functions f1 & f2.
But I can't do this, as this refer to window object instead of internal scope.
I can set function as:
this.f1 = function() {}
but it's bad idea, as they'll be in global space...
How I can pass anonymous space to external function?
I still wonder why you would make functions to be private, that are needed outside...
But there you go:
(function() {
var a = 1;
var b = 2;
var obj = {
f1: function() {
},
f2: function() {
}
}
externalFunction(obj);
})();
function externalFunction(pointer) {
pointer.f1(); // win
}
Or you can pass f1 and f2 individually, then you don't need to put them into an object.
You can't pass the scope as an object, but you can create an object with whatever you want from the scope:
externalFunction({ f1: f1, f2: f2 });