The this keyword is a very important part of JS and is the foundation of JS programming fundamentals. When many people study it, they tend to independently explore what this actually points to. But I think that understanding is isolated and faulted, and we don’t use this just because we use this. This is a convenient way to solve the problem. At the same time, its emergence can also extend some “special applications” in other aspects.

My study of this is also a process of exploration, some places may not be accurate, if the big guys find there is wrong, welcome to actively put forward in the comments section ~

What’s the significance of this?

As you can see from the code below, this helps us reference the appropriate context object.

let person = { name: "meng", }; function read(... params) { console.log(`${this.name} is reading`, params); / / meng is reading (2) [parameters of "1", "parameter 2]}. Read the call (person, parameters of" 1 ", "parameter 2");Copy the code

Without this, the style of this piece of code changes. Look at the following code:

let person = { name: "meng", }; function read(person, ... params) { console.log(`${person.name} is reading`, params); / / meng is reading (2) [parameters of "1", "parameter 2"]} read (person, parameters of "1", "parameter 2");Copy the code

1. Context objects and parameters are mixed up

We don’t see person at first sight. Maybe it’s a context object for a function, or maybe it’s just an ordinary parameter, and the code is not very readable

2. Poor maintainability

If we change the name of our object, and instead of calling it Person we call it Student, we change a lot of things all at once. This is just a few lines of code. If there were more than 500 lines of code today, it would be a blur.

So this is very important in our development process, but in the process of using this, I believe that everyone in the small white period will have confused times.

  • Why would I write a variable in an objectthis.xx = undefined
  • I see my colleagues use it a lotlet that = this; that.xxx();Why do I write it like this?

Now, what does this point to?

Four binding rules for this

The first thing to learn about this is that this is the binding that happens when the function is called, and what it points to depends entirely on where the function is called. So it doesn’t matter what scope this is in.

In short, it has to do with where you call it, not where you define it.

Four binding rules

  1. The default binding
  2. Implicit binding
  3. Explicitly bound
  4. The new binding

The priority is new binding > Explicit binding > Implicit binding > Default binding

Note: Arrow functions are special and are not limited to these four rules, depending on the scope of this

The default binding

A direct call to a function without any decorations is the default binding. The default binding of this is bound to window

var a = 2; function foo() { console.log(this.a); } // Call foo() directly (default binding); / / 2Copy the code

In strict mode this is undefined

The binding rule for this depends on where the call is made. The premise of this is that foo() runs in non-strict mode, which means that the default binding can be bound to global objects. You cannot bind this in strict mode. This is undefined

"use strict"; var a = 2; function foo() { console.log(this.a); } foo(); var a = 2; function foo() { "use strict"; console.log(this.a); } foo();Copy the code

Note, however, that strict mode refers to whether the function body is in strict mode, not whether the call location is in strict mode. If the function body is in strict mode, this is bound to undefined; otherwise this is bound to the global object.

var a = 2; function foo() { console.log(this.a); // 2 } (function () { "use strict"; foo(); }) ();Copy the code

Implicit binding

Implicit binding is when a function is called through an object property, and it is bound to the last level of the call, as shown in the following example.

obj1.obj2.foo(); // This in foo is bound to obj2Copy the code

Implicit loss

When this is bound, the implicitly bound function will sometimes lose its binding object. In this case, the default binding is applied, binding this to the global object or undefined (depending on whether it is in strict mode).

Implicit loss can occur in the following cases:

  • Function is an alias
  • Pass in the callback function

Function is an alias

A function alias accepts a reference with a variable name, as shown below.

  function foo() {
    console.log(this.a); 
  }

  var obj = {
    a: 2,
    foo,
  };
  var bar = obj.foo; 
  bar(); // undefined
Copy the code

In the above code, bar is a reference to obj.foo, but it refers to Foo itself, so bar is the default binding.

Passing in a callback function (custom function)

With a callback as an argument, the callback itself is implicitly lost, as follows

  function foo() {
    console.log(this.a); 
  }
  function doFoo(fn) {
    fn();
  }
  var obj = {
    a: 2,
    foo,
  };
  var a = "global";
  doFoo(obj.foo);  // global
Copy the code

Passing a callback function (built-in)

This is the case where you pass parameters to your own defined functions. If you pass functions to built-in functions, you will also implicitly lose them, which is a very common form of everyday development.

function foo() { console.log(this.a); } var obj = { a: 2, foo, }; var a = "global"; setTimeout(obj.foo); // global setTimeout(function () { console.log(this); // window }); Function setTimeout(fn) {fn(); }Copy the code

Explicit binding (hard binding)

Apply, call, bind

Explicit binding uses call, apply, and this to enforce the function. Call and apply differ only in the form of the parameters passed.

function foo(... params) { console.log(this, params); // obj [" parameter 1", "parameter 2"]} let obj = {a: 2,}; Foo.call (obj, "argument 1"," argument 2"); Foo. Apply (obj, [" parameter 1", "parameter 2"]); let a = foo.bind(obj); A (" 1", "2");Copy the code

This is null

A special case where this is null will apply the default binding.

function foo() { console.log(this.a); } var a = 2; foo.apply(null); / / 2Copy the code

Xx. Apply (null) is used to receive parameters when implementing function curtailment. The specific implementation process of function curtailment is described at the end of this article.

Notes:

  • This operation mounts an a property on the window, but this binding modifies the global object, causing some bugs that are hard to parse.
  • You can use object.create to create an empty Object that does not affect the application and does not contaminate global variables
      let _null = Object.create(null);

      function foo() {
        console.log(this.a);
      }
      var a = 2;
      foo.apply(_null); // undefined
Copy the code

Optional parameters are used as context

Many built-in functions also give us an optional argument as context, such as forEach, which is also a form of explicit binding.

let obj = { a: 2, }; ForEach (function (item) {console.log(item, this.a); }, obj); [" parameter 1", "parameter 2"].foreach ((item) => {console.log(item, this.a); }, obj);Copy the code

Hard binding

An explicit binding is also called a hard binding because once it is explicitly bound, it cannot be changed a second time by another binding (the new binding can be changed, but this is a special case). The soft binding that follows solves this problem.

var a = 3; function foo() { console.log(this.a); return this.a; } let obj = { a: 2, }; let bar = function () { foo.call(obj); }; bar(); // 2 bar.call(window); //2, this is not changed to windowCopy the code

The new binding

A new binding is to construct a new object and bind it to this. Any function can be called with new, and this form of new is called a constructor call.

function foo(a) { this.a = a; } // Bind the new object to this. Let bar = new foo(2); console.log(bar.a); / / 2Copy the code

Let’s do a more complicated example

function foo(something) { this.a = something; } let obj1 = { foo, }; let obj2 = {}; obj1.foo(2); let bar = new obj1.foo(4); console.log(obj1.a); // 2 console.log(bar.a); / / 4Copy the code

To summarize: When a function is called with new, the following is done

  1. Create an entirely new object
  2. The new object performs the Prototype connection
  3. This new object is bound to the this function call
  4. If the function returns no other object, then the function in the new expression automatically returns the new object

In the fourth flow, where the function returns a new object, we can test this

Function foo(a) {this.a = a; } let bar = new foo(2); console.log(bar); // foo {a: 2} // foo {a: 2} // foo {a: 2} // foo {a: 2} return { b: this.a, c: 1, }; } let bar = new foo(2); console.log(bar); Function foo(a) {this.a = a; // {b: 2, c: 1} function foo(a) {this.a = a; Return "test "; } let bar = new foo(2); console.log(bar); // foo {a: 2}Copy the code

The new binding modifies the hard-bound this

In the following code, we point this to obj1, obj1.a = 3, with an explicit binding. A = 3, so new is the only way to change the hard binding.

function foo(something) { this.a = something; } var obj1 = {}; let bar = foo.bind(obj1); bar(2); console.log(obj1.a); // 2 var baz = new bar(3); console.log(obj1.a); // 2 console.log(baz.a); / / 3Copy the code

The use of hard binding in the new binding facilitates parameter passing and is a form of curation.

      function foo(p1, p2) {
        this.val = p1 + p2;
      }

      let bar = foo.bind(null, "p1");
      var baz = new bar("p2");

      console.log(baz.val); // p1p2
Copy the code

Special arrow function

The four binding rules apply to all functions except arrow functions. The arrow does not apply to the above rule. The point of this in the arrow function is determined by its scope.

function foo() { return (a) => { console.log(this.a); }; } let obj1 = { a: 2 }; let obj2 = { a: 3 }; let bar = foo.call(obj1); bar.call(obj2); / / 2Copy the code

An explicit binding to bar.call does not change the direction of the arrow function. This in the arrow function inherits from its scope, and this in foo refers to obj1.

Arrow functions are often used in callback functions, such as timers. Because of the implicit loss in this case, you can use the arrow function to bind the outer this.

Function foo() {setTimeout() => {// foo console.log(this.a); // foo console.log(this.a); }); } let obj = { a: 2 }; foo.call(obj); / / 2Copy the code

exercises

Now that we’ve seen the various forms, let’s do a couple of problems to check them out.

Question 1

  function foo() {
    var a = 2;
    this.bar();
  }
  function bar() {
    console.log(this.a);
  }
  foo();
Copy the code

The answer is undefined, and foo is a default binding.

Question 2

  function foo(num) {
    this.count++;
  }
  foo.count = 0;
  for (let i = 0; i < 5; ++i) {
    foo(i);
  }
  console.log(foo.count);
Copy the code

The answer is 0. The default binding for calling foo is this, which binds to window, creating a count globally. But window.count is undefined, undefined + number = NaN

window.count; // NaN
Copy the code

Question 3

  let count = 0;
  window.count =0;
  function foo(num) {
    this.count++;
  }
  foo.count = 0;
  for (let i = 0; i < 5; ++i) {
    foo(i);
  }
  console.log(count);
  console.log(window.count); 
Copy the code

The answer is 0 and 5.

other

In non-strict mode, global variables declared by var become properties of window, while variables declared by let, const are not bound to window objects.

I usually brush some topics to consolidate their own understanding, here recommend an article, he summed up the topic is quite comprehensive.

Juejin. Cn/post / 684490…

Hand code

This article covers function curation, as well as apply, call, and bind, but let’s take a look at their internal implementation

Function curcurization

Function curation is changing a function that takes multiple parameters to a function that can take a single parameter.

add(1, 2, 3, 4); Add (1)(2)(3)(4)Copy the code

Implementation ideas:

  • Compare the number of arguments passed in to the number of arguments to the function
  • If they are equal, the result of the calculation is returned. If they are not equal, the new function is returned
let _null = Object.create(null); const curry = (fn, ... args) => fn.length > args.length ? (... params) => curry(fn, ... args, ... params) : fn.apply(_null, args); let addSum = (a, b, c, d) => a + b + c + d; let add = curry(addSum); console.log(add(1)(2)(3)(4)); console.log(add(1, 2, 3, 4)); console.log(add(1, 2, 3, 4, 5));Copy the code

At the end

Above is all things, this article is read “you do not know javascript” write the article, after understanding to do their own summary, recommend you can go to see this book. Some things are understood relatively shallow, or not comprehensive enough. We welcome your valuable comments in the comments section.

See you next time.