You should know about `this`
source link: https://dannypsnl.github.io/blog/2018/02/09/cs/you-should-know-about-this/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
You should know about `this`
this
scope rule is one of the hardest problem in JS. Let's start it.
function print() {
console.log(this.data)
}
let obj = {
data: "This is obj",
print,
}
obj.print() // Output: This is obj
// It's good. But if we send print to other place?
let printClone = obj.print
printClone() // Which `this` at here? You have to see `this` of this place then you will know
// Ok, let me tell you true, you don't know where does the function be send, so you also can't know which this at there.
// The behavior is totally can not be prediction.
Ok! Now we know the truth: this
is dynamic. However, we still need to send us function to other place, right? We have to understanding print get this at obj.print()
this expression, not at let obj = { ... }
part. Why this is important? Because, a lots of novice of JS will write something like this.
// ignore, we are in obj definition
print: print.bind(this),
// ignore
bind
is the next thing we will discuss, the point is this of = { ... }
, whatever is type in, there is only {}
, a null object at there! So you will get a null object out of your expected. What does bind do? Let me show it for you.
let printClone = print.bind(obj) // Or obj.print, understanding a truth: function has no relation with any object before we bind(and some other operation) it.
printClone() // `this` at here is `obj`
// Output: This is obj
bind
return a new function, combine by a function with a object, use object to be new function's this
. However, we of course want to define function with its user. How to do that?
function NewCar() {
let self = {
name: "Tasla",
price: 100000,
}
return {
print() {
console.log(self)
},
}
}
let car = NewCar()
car.print() // Output: { name: "Tasla", price: 100000 }
let printClone = car.print
printClone() // Output: { name: "Tasla", price: 100000 }
Very interesting, right? This is because we use closure at here. We reference the self
in NewCar
. The most important part came, every time you call NewCar
for a new object. The self
is different. This is the one of the greatest trick in ES3/5, seems this rule is so complex, why don't use closure to avoid it. In ES3/5, this trick is good enough.
In ES6, we have class
, but what does it real mean?
function Car() {
this.name = "Tasla"
this.price = 100000
this.dump = function () {
console.log(this)
}
}
var car = new Car()
car.dump()
var clone = car.dump
clone() // Of course, error
Car
call constructor in ES5, because we can use new
ask JS this
reference to the object we create. However, as you see, the function inside still have dynamic this
. Don't worry about that, because what I want to say is keyword class
work as same as constructor! How we fix it in ES5?
function Car() {
// ... ignore
this.dump = function () {
console.log(this)
}.bind(this)
}
Which trick we use at here? First, if we use keyword new
, this
is the object we created. So we bind
target really is that one we expected & wanted! However, this way has some problem, the most important part is hiding ability. In the previous solution, we can hide the attributes of object
in closure
, the ability we lose at constructor and class
. How to implement the pattern by class
? We have to understand what is class
first.
class Car {
constructor() {
this.name = "Tasla"
this.price = 100000
this.dump = this.dump.bind(this)
}
dump() {
console.log(this)
}
}
These code as same as previous solution. So now we know, class
is just a syntax sugar, and cannot bind correct this
for you. You must write done bind
at constructor. That is too ridicules, if someone forgive to bind the function(point! I still call it function, JS class still not behavior as method as other language), the program will fall into dark. So, how to avoid the problem, the answer is arrow function, arrow function still is a function. However, traditional function have a this
by execute environment or bind
one. Then arrow function have a this
at it's declared place! And we go back to class
, class
provide this
at body part!
class Xxx {
// this at here is instance of Xxx!
}
So we can write these:
class Car {
constuctor() {
this.name = "Tasla"
this.price = 100000
}
dump = () => {
console.log(this)
}
}
As your expected, dump always bind
with instance of Car. !!! dump = ...
in a class definition is esNext(stage3) standard, you can always use babel plugin babel-preset-stage-0
to follow the latest standard.
References:
You don't know JS
- Author: Kyle Simpson
- ISBN: 978-986-476-049-7
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK