preface

The generation of this article was based on hu Yuba’s JavaScript in-depth interpretation of this from the ECMAScript specification, this is corresponding to the digging gold link, the article discussed in detail the causes and consequences, I suggest you can go to understand, very helpful, and this article when writing, also had great help of Hu. Thanks again ~

The ES5 specification in this paper is based on yan Haijing’s large translation, which is also appreciated here.

So why this article? Since many of my classmates didn’t understand his comments on his blog, I was one of them, so I decided to understand why and now I will share some of my comments. I hope it will help you too.

Again, for those of you who know how to judge this in various situations, this article will not tell you how to judge this, but more about why to judge this. You are not satisfied with knowing what it is, but why it is.

From a.Reference Type(Reference type) Start:

Reference Type: indicates the Reference Type. In the ES5 documentation standard, Reference is described as a Resolved Name Binding

In the ES5 translation of Yan Da, it is translated as the solved named binding.

  1. Resolved

  2. The name binding is translated as a named binding without any problems and may be better understood if you have experience with back-end languages.

Let’s explain named binding again: a binding has two sides, so the name, the name we give it, has to be bound to something, in other words, the name describes something.

1. Why does it need a name? Doesn’t it have a name of its own?

For example: Now we have an object: time, and it has three properties:

 time {
   second: 32.minute: 12.hour: 10
 }
Copy the code

2. Where does this object exist?

Once we have defined it, it must exist somewhere before we can retrieve it in later code.

Its existence depends on the nature of time itself. Because it is an object, its internal properties can be added or subtracted; in other words, its size is not fixed. So we put it in the heap.

What if it’s a fixed size? For example, there are six basic types of values in JavaScript: null, undefined, Boolean, Number, String, and Symbol. Since the sizes are fixed, we can put them on the stack.


What is a stack? Why separate types of data?

  • The stack: A small block of memory allocated by the system while a program is running, the size of which is determined by compiler parameters at compile time.
  • The heap: refers to the amount of free memory that is currently available and needs to be allocated and released by the programmer himself. (In JS, V8 has automatic garbage collection mechanism, which does not need to be operated by us.)


4. Reference types exist in the heap. How does this relate to the example?

Okay. If you already understand that our time is stored in the heap, then this is easy to understand. Time. Second = time[‘ second ‘] = time. Second = time[‘ second ‘] = time.

This seems like a silly question, isn’t it? Haha, but think about it: the value is in a small piece of memory, so we need to find this piece of memory in order to get the value.

Now it’s easy to understand. Second or time[‘ second ‘] are bound to the memory location that actually holds the value of second. As long as you use time.second or time[‘ second ‘], the compiler will find, oh, this is the value that exists in the XXXXX address, which is 32.

twoReference TypethisWhat does it matter?

This has always been a difficult point for beginners to understand in Javascript, and some even after 2 years of writing projects have not figured out why this is different.

Here we do not look at the direction of this from the use of the scene, or back to the origin.

From the compiler’s point of view, how does this point to? Since this is often referred to in Function Calls, let’s look at 11.2.3 Function Calls in the ES5 documentation standard.

There are 8 steps:


1. Let ref be the result of evaluating MemberExpression.

2. Let func be GetValue(ref).

3. Let argList be the result of evaluating Arguments, producing an internal list of argument values (see 11.24.).

4. If Type(func) is not Object.throw a TypeError exception.

5. If IsCallable(func) is false.throw a TypeError exception.

6. If Type(ref) is Reference, then

    a.If IsPropertyReference(ref) is true, then
    i.Let thisValue be GetBase(ref).

    b. Else, the base of ref is an Environment Record
    i.Let thisValue be the result of calling the Implicit This Value concrete method of GetBase(ref).

7. Else, Type(ref) is not Reference.

    a.Let thisValue be undefined.

8. Return the result of calling the [[Call]] internal method on func, providing thisValue as the this value and providing the list argList as the argument values.
Copy the code

I’m not going to translate it, because you’re probably tired of reading it, so let’s visualize the process a little bit more.

I’ve highlighted the most critical steps in red, so if the func returned in step 3 doesn’t pass 5, there’s no point in talking about this at all.

So let’s focus on: the most critical point in this, steps 2, 6, 7:

  • Step 2: Evaluate MemberExpression and assign it to Ref: that is, evaluate the content to the left of () and assign it to Ref. In other words: a Ref is a reference calculated to the left of ().

  • Step 6: Determine if ref is of Reference type: there is nothing to say about this.

  • By executing IsPropertyReference(V), return true if the base value is an object or HasPrimitiveBase(V) is true; Otherwise return false. HasPrimitiveBase(V) : Returns true if the base value is Boolean, String, Number. In plain English, depending on who the Ref is based on, right? Return true if it’s based on an object or Boolean String Number otherwise return false.

OK, look at this, I guess you are a little tired, but the most important part is below.

Three. Look backthisN cases of theta

1. Direct call

let a = 'm';
function test() {
  console.log(this.a);
}
test(); // m
Copy the code

Let’s use the three steps we just saw to determine this:

  1. test()RefistestReference, which is associated with storage in memorytest()A fragment of.
  2. judgetest()Reference type =>true
  3. judgeRefAttribute reference type =>falseIt is not defined inside a reference type.
  4. Enter the ninth step in the diagram:this = ImplicitThisValue(Ref)In theEnvironment RecordsThe returnundefinedIn non-strict mode, the browser willthisPoint to thewindow

It’s a lot of trouble to say, but it’s really easy to understand.

2. Call inside the object

function test() {
  console.log(this.a);
}
let parent = {
  a: 's'.test: test
};
parent.test(); // s
Copy the code
  1. parent.test()Refisparent.testReference, which is associated with storage in memorytest()A fragment of.
  2. judgeparent.test()Reference type =>true
  3. judgeRefAttribute reference type =>true
  4. Enter the eighth step in the diagram:this = GetBase(Ref)Then thetest()Who is the method based on? Obviously it isparent, sothisPoint to theparent

3. The new keyword

let a = 'k';
function Foo() {
  console.log(this);
}
let c = new Foo();
c.a = 's'
Copy the code

New keyword call, different from the general function call, you can see the explanation on the MDN, clearly pointed out

  1. A new object inherited from foo. prototype is created.
  2. Call the constructor Foo with the specified arguments and bind this to the newly created object

If you still want to explain from a specification perspective, I suggest you read The ES5 specification :11.2.2 The New Operator and The associated ES5 specification :8.7.1 GetValue (V). I read it many times, But I didn’t find how to explain the pointing problem of this from the perspective of the specification. Finally I consulted Hu Dada and I knew that New might have a process of specifying this at the bottom of the code and it was not suitable to interpret this in such a way. However, if you have a better answer, I am very welcome to discuss with you

{a: ‘s’} {a: ‘s’}

Arrow function

function Foo() {
  return (a)= > {
    return (a)= > {
      console.log(this);
    };
  };
}
console.log(Foo()()());
Copy the code

Like new, the arrow function is a special case, but the arrow function can also find the answer to this from the corresponding specification:

Please refer to the following passage in the ES6 specification – Arrow functions – Evaluation:

“An ArrowFunction does not define local bindings for arguments, super, this, or new.target. Any reference to arguments, Super, this, or new.target within an ArrowFunction must resolve to a binding in a lexically enclosing environment.”

ArrowFunction does not define local bindings for arguments, super, this, or new.target. Any references to arguments, super, this, or new.target in ArrowFunction must be resolved as bindings in lexical scope.

In other words, the arrow function does not define this inside, but only the lexical scope outside it. In other words, the arrow function does not define this inside the arrow function.

Going back to this example, we know that all the time, no matter how many layers of arrow functions you have, this is going to point to this in Foo, and that this in Foo is going to point to the window from our previous example.

4. The last

Welcome to pay attention to my nuggets column, and I will update more quality content in the future. Any questions, welcome rational and friendly discussion

The picture is from Unsplash