This question already has answers here:
Can you bind 'this' in an arrow function?
(14 answers)
Closed 4 years ago.
I'm calling .bind(this) on an async function defined in another module inside of a class constructor.
The class is as below
class CannedItem {
constructor (config) {
...
this._fetch = config.fetch.bind(this)
...
}
...
}
The function is something like
module.exports = [
{
...
fetch: async () => {
// Want to refer to 'this' bound to the CannedItem object here
}
}
]
However when the function is called, this is bound to an empty object.
Confusingly Visual Studio Code debugger has the object in scope bound as this in the debugger window, see attached screenshot, however inspecting the variable in the console lists it as undefined. This looks to me like there is a bug. Is this the case or am I misusing .bind()?
The only thing that seems a little unusual is the async function. I tried searching for issues with async and .bind() but no dice.
I am running NodeJs 8.11.1 and the latest VSCode (1.30.2)
You can't rebind arrow functions because this is fixed to the lexically defined this. You need a regular function if you plan on using bind() or any of its relatives:
class CannedItem {
constructor(config) {
this.myname = "Mark"
this._fetch = config.fetch.bind(this)
}
}
let obj = {
fetch: async() => { // won't work
return this.myname
// Want to refer to 'this' bound to the CannedItem object here
}
}
let obj2 = {
async fetch() { // works
return this.myname
// Want to refer to 'this' bound to the CannedItem object here
}
}
// pass arrow function object
let c1 = new CannedItem(obj)
c1._fetch().then(console.log) // undefined
// pass regular function object
let c2 = new CannedItem(obj2)
c2._fetch().then(console.log) // Mark
As a bonus, if you use a regular function, you might not need bind().
this._fetch = config.fetch
will work if you call it from the instance.
Related
I am trying to access this inside my arrow function:
import myObject from '../myObjectPath';
export const myClass = Fluxxor.createStore({
initialize() {
this.list = [];
this.id = null;
},
myOutsideFunction(variable1) {
// here this in NOT undefined
myObject.getMyList(this.id, (myList) => {
// here this in undefined
this.list = myList;
}
});
)};
But inside arrow function which in ma callback function this is undefined!!
I am using babel to transpile the code:
myOutsideFunction: function myOutsideFunction() {
var _this = this;
myObject.getMyList(function (myList) {
_this.list = myList;
});
},
If this is undefined within an arrow function, it's undefined outside of the arrow as well. Arrow function simply capture the this of the surrounding scope.
In this case, you're declaring myOutsideFunction as a method on an object literal and never binding it or doing anything else that would call it with the object as this.
When debugging, bear in mind that transpilers can rename variables (and have to rename this for it to capture correctly). Using the original name in the console without sourcemaps that include renaming will show you undefined even if the original value isn't. Make sure you use the transpiled name in watches or console commands.
I understand that when invoking a method of an object, the this value in that method is assigned to the object itself.
const dog = {
age: 5,
growOneYear: function () {
this.age += 1;
}
};
dog.growOneYear();
dog.age; // 6
That makes sense to me. However, when I try it a different way, it doesn't work:
function invokeTwice(cb) {
cb();
cb();
}
invokeTwice(dog.growOneYear);
dog.age;
// still 6
Why didn't this work?
On the second example there is no object used to invoke growOneYear().
You copy the method growOneYear into a new variable (cb). This looses the link between dog and growOneYear. You then call cb() as a regular function, without using an object to serve as this inside the method.
You said in the question:
... when invoking a method of an object, the this value in that method is assigned to the object itself.
The statement above is correct and it explains why it doesn't work when you invoke growOneYear as cb(): it is "a method of an object" only when it is invoked as dog.growOneYear(). If you copy it into cb and call it as cb() it is just a regular function; no object is used to call it any more.
The situation you describe in the questoin is even listed in the documentation of Function.prototype.bind():
A common mistake for new JavaScript programmers is to extract a method from an object, then to later call that function and expect it to use the original object as its this (e.g. by using that method in callback-based code). Without special care, however, the original object is usually lost.
Function.prototype.bind() is the solution to your problem:
const dog = {
age: 5,
growOneYear: function () {
this.age += 1;
}
};
dog.growOneYear();
console.log(dog.age); // 6
function invokeTwice(cb) {
cb();
cb();
}
// Bind the method to the desired object
// Inside `invokeTwice()`, `cb` is the function `dog.growOneYear` with
// `this` pointing to `dog` ------+
// v
invokeTwice(dog.growOneYear.bind(dog));
console.log(dog.age); // 8
In the second case,your function will be passed the window object as the this parameter since you are not invoking the cb function passed with any specified object.
In case you want to invoke with your dog object use apply like this.
enter code here
const dog = {
age: 5,
growOneYear: function () {
//console.log(this)
this.age += 1;
}
};
dog.growOneYear();
dog.age;
function invokeTwice(cb) {
cb.apply(dog);
cb.apply(dog);
}
invokeTwice(dog.growOneYear);
console.log(dog.age);
I append a services property to this:
function Client(){
this.services = {
'propertyName' : {}
};
and then append a method to this, in which I need to reference the services property of the instance of client:
function Client(){
this.services = {
'propertyName' : {}
};
this.someMethod = function () {
if (this.services['propertyName']) {
//do something
}
}
}
var clientName = new Client();
But this.services - line 6 is undefined. How can I use a property assigned to this in a method assigned to this? It seems like it should be possible because by the time that method is called by the constructor, the services property will exist for the object. Is this a language limitation? Is it possible? Should it be?
But this.services - line 6 is undefined.
That will depend entirely on how you call someMethod. If you call it like this:
clientName.someMethod();
...it'll be fine, because this within the call will be the object created by new Client that you've put the services property on. But in JavaScript, this is not a fixed thing with normal functions, it's set by how you call the function. So:
var f = clientName.someMethod;
f();
...would fail, because this wouldn't be the object you expect. (This isn't true of ES6's new "arrow" functions, which get this from where they're defined, not how they're called.)
You mostly see this when functions are used as callbacks:
doSomething(clientName.someMethod);
...because doSomething doesn't know what object to use as this.
You can fix it by using Function#bind:
doSomething(clientName.someMethod.bind(clientName));
or similarly:
var f = clientName.someMethod.bind(clientName);
f();
Function#bind creates a new function that, when called, will call the original with this set to the argument you give it.
Just to flesh out my ES6 comment above: In ES6, if you had:
function Client(){
this.services = {
'propertyName' : {}
};
this.someMethod = () => { // <== ES6 "arrow" function
if (this.services['propertyName']) {
//do something
}
}
}
...it wouldn't matter how you called someMethod, this would be what it is where that function was created. V. handy. :-)
This question already has answers here:
Methods in ES6 objects: using arrow functions
(6 answers)
How does the "this" keyword in Javascript act within an object literal? [duplicate]
(4 answers)
Closed 5 years ago.
I am trying to understand arrow functions in ECMAScript 6.
This is the definition I came across while reading:
Arrow functions have implicit this binding, which means that the
value of the this value inside of an arrow function is aways the
same as the value of this in the scope in which the arrow function
is defined!
According to the definition, I believe this for an arrow function should contain the same block level values that the arrow function was defined in.
Code:
var test = {
id: "123123",
k: {
laptop: "ramen",
testfunc: () => console.log(this)
}
}
console.log(test.k.testfunc);
However, I am getting this result from the code
function testfunc() {
return console.log(undefined);
}
What I thought I would get would be an output of:
{"laptop": "ramen"}
if I ran this
console.log(test.k.testfunc());
Let's transform into the equivalent ES5 code:
var test = {
id: "123123",
k: {
laptop: "ramen",
testfunc: function(){return console.log(this)}.bind(this)
}
}
Remember that this depends on how you call the function. The outer this isn't inside a function, so it will default to undefined in strict mode.
Simplified scenario below:
console.log(this) // undefined
var test = {
a: this // same `this` as above
}
You are defining the arrow function in the same scope that you defined var test. If you are defining test in the global scope, then the arrow function's context will be the global scope too.
If you are defining test inside of a method, the arrow function will share the method's context.
function method() {
const self = this;
const test = {
foo: () => console.log(self === this);
}
test.foo()
// console: true
}
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
How do I write a named arrow function in ES2015?
(8 answers)
Closed 7 years ago.
For example in the Class constructor:
Socket.on('user:join', onUserJoin);
'onUserJoin' is declared as a method of the class but is being called by socket.io so the 'this' is not my Class. A way to resolve this is to use the '=>' function.
example:
Socket.on('user:join', (data)=>{
this.isOnline = true;
});
Now 'this' is my class, but how do I reference this anonymous function to unsubscribe ?
socket.removeListener('user:join', ????);
I did try this:
let self;
class RoomController {
constructor() {
self = this;
}
...
}
and reference the self in the methods but the self was being shared across sockets...
naming the anonymous function could solve it but I preferred for my case the bind option.
You can use Function.prototype.bind.
Socket.on('user:join', onUserJoin.bind(this));
This ensures that onUserJoin has the correct context, which will be the instance of your class.
You can always bind the arrow functions to the names.
For example,
class RoomController {
constructor() {
this.flag = true;
}
// Assign the arrow function to the name `setFlag`
setFlag = (v) => this.flag = v;
}
let r = new RoomController();
function tester(func) {
func(false);
console.log(r.flag);
// false
func(true);
console.log(r.flag);
// true
}
// Now you can pass the function around, `this` will still refer the object `r`
tester(r.setFlag);