I read "Why and how to bind methods in your React component classes?" and grabbed the basic idea of how different is this
by the scopes it gets called.
I made a simple class to test this.
class Something {
constructor(some) {
this.some = some;
}
print() {
console.log(
"print from ",
this,
this === undefined ? "!!this is undefined!!" : this.some
);
}
}
From what I understood, the scope of this
can be different although it is from the same object (instance).
const some = new Something("is");
some.print(); // 'this' is defined. 'this.some' is 'is'.
const going = some.print;
going(); // 'this' is undefined.
going.call(some); // 'this' is defined. 'this.some' is 'is'.
But then, why is this
(which should be the object itself) undefined
in the latter case? Shouldn't it be the top-level this
, which is an empty object {}
?
there's a function print
when you call it like this
some.print();
that effectively gets translated to this
print.call(some, ...args)
When you just pull it out
const going = some.print
You've just gotten a reference to the standalone function so calling it with
going()
Is the same as calling
print();
It's the .
between some
and print
that is magically passing some
as this
to print
. going()
has no period so no this is passed.
Note that you could assign going to an object and use the period operator to do the magic "pass the thing on the left as this
" operation
function print() {
console.log(this);
}
const someObj = {
foo: print,
};
someObj.foo();
all the class
keyword does is help assign print to Something
's prototype
class Something {
print() { console.log(this); }
}
is the same as
function Something() {} // the constructor
Something.prototype.print = function() { console.log(this} );
which is also effectively the same as this
function print() { console.log(this); }
function Something() {} // the constructor
Something.prototype.print = print;
Saikat showed using bind
. Bind effectively makes a new function that wraps your old function. Example
const going = print.bind(foo);
Is nearly the same as
function createAFuncitonThatPassesAFixedThis(fn, objectToUseAsThis) {
return function(...args) {
return fn.call(objectToUseAsThis, ...args);
}
}
const going = createAFunctionThatPassesAFixedThis(print, foo);
going(); // calls print with 'foo' as this
Some of the other answers have appear to have a fundamental mis-understanding of this
. this
is not the "owner" nor is this
the current object. this
is just effectively another variable inside a function. It gets set automatically if you use the .
operator with a function call. so a
.
b()
sets this
to a
. if the dot operator did not exist you could still set this
by using call
or apply
as in somefuntion.call(valueForThis, ...args)
or somefunction.apply(valueForThis, [...args])
;
function print() {
console.log(this.name);
}
print.call({name: "test"}); // prints test
const foo = {
name: "foo",
bar: print,
};
foo.bar(); // prints foo
function Something(name) {
this.name = name;
}
Something.prototype.someFunc = print;
const s = new Something("something");
s.someFunc(); // prints something
s.foobar = print;
s.foobar(); // prints something
Also note that ES6 added the =>
arrow operator which binds this
to whatever it was when the function is created. In other words
const foo = () => { console.log(this); }
Is the same as
const foo = function() { console.log(this); }.bind(this);
it should also be clear that both functions made with bind
and arrow functions you can not change this
using call
or apply
since effectively they made a wrapper that always sets this
to what it was when the wrapper was made just like createAFuncitonThatPassesAFixedThis
did above.
Let's add some comments to show what I mean
function createAFuncitonThatPassesAFixedThis(fn, objectToUseAsThis) {
// return a function that calls fn with objectToUsAsThis as this
return function(...args) {
// when we arrive here from the "going.call" line below
// `this` will be "someObject"
// but on the next line we're passing "objectToUseAsThis" to fn as this
// so "someObject" is effectively ignored.
return fn.call(objectToUseAsThis, ...args);
}
}
const going = createAFunctionThatPassesAFixedThis(print, foo);
going.call(someObject); // pass some object as `this`
One more thing. It's very common to use bind
to make a function for an event listener or callback. It's very common in React. Example
class Component extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// 'this' will be the current component since we bound 'this' above
console.log("was clicked");
}
render() {
return (<button onClick={this.handleClick}>click me</button>);
}
}
This would be handled in at least 3 common ways.
Using bind
in the constructor (see above)
Using bind
in render
render() {
return (<button onClick={this.handleClick.bind(this)}>click me</button>);
}
Using arrow functions in render
render() {
return (<button onClick={() => { this.handleClick(); }}>click me</button>);
}
Above I showed what bind
and =>
actually do with createAFuncitonThatPassesAFixedThis
. They create a new function. So that should make it clear that if use style 2 or 3 above when every single time render
is called a new function is created. If you use style 1 then a new function is only created in the constructor. It's a style issue as to whether or not that's important. Creating more functions means more garbage which means a possibly slower webpage but in most cases unless you have a crazy complicated webpage that is rendering constantly it probably doesn't matter which of the 3 ways you do it.
you should correct this line to
const going = some.print.bind(some);
Otherwise going is getting a different scope and its not an member of some object so this
is undefined
NOTE
this
refers to the current class object. And going
is not a member of any class so this
is undefined
const going = some.print;
going(); // 'this' is undefined.
Here, in the first line, you're storing a reference to the print
function (property) of the some
object in the variable going
.
Note, you're just storing the reference to that particular property (print
), not along with the owner (some
).
this
refers to the 'owner' always.
So when you execute going
, the owner (this
) is unknown (or call it undefined
).