preface

Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

When a function is called, an execution context is created that contains some information about the function call (call stack, incoming parameters, call method). This refers to the execution context.

This is not static and is not bound at write time, but at run time. Its binding has nothing to do with where the function is declared, only how the function is called.

This article is a little long, involving many interview questions, difficult or simple, if you can patiently read through the part, I believe this will not be a problem in the future.

Before learning this, it is recommended to learn the following:

  • JavaScript precompilation
  • JavaScript hand tear new
  • JavaScript hand rip call/apply
  • Static and dynamic scopes of JavaScript
  • JavaScript hand rip array high order function

At the beginning of the article, lay out the content of the article to make the trip worthwhile.

  • The default binding
  • Implicit binding
  • Implicit binding is lost
  • Explicitly bound
  • Explicitly bind the application
  • The new binding
  • Arrow function binding
  • Comprehensive problem
  • conclusion

Where does this point to

To fully understand this in JavaScript, you need to understand the binding rules for this. There are five binding rules for this:

  1. The default binding
  2. Implicit binding
  3. Explicit (hard) binding
  4. newThe binding
  5. ES6New arrow function bindings

Here are the following binding rules for this.

1. Default binding

The default binding usually means that the function is called independently and no other binding rules are involved. In non-strict mode, this refers to window; in strict mode, this refers to undefined.

Topic 1.1: Non-strict mode

var foo = 123;
function print(){
	this.foo = 234;
    console.log(this); // window
	console.log(foo); / / 234
}
print();	
Copy the code

In non-strict mode, print() is the default binding and this refers to window, so print window and 234.

If you’ve learned about precompilation, the foo and print functions are stored in the global GO (window object) during precompilation, so the code looks something like this:

window.foo = 123
function print() {
    this.foo = 234;
    console.log(this); 
	console.log(window.foo);
}
window.print()
Copy the code

Topic 1.2: Strict patterns

Modify topic 1.1 slightly to see the result of execution in strict mode.

“Use strict” enables strict mode

"use strict";
var foo = 123;
function print(){
    console.log('print this is '.this); 
    console.log(window.foo)
    console.log(this.foo);
}
console.log('global this is '.this);
print();
Copy the code

Note: When strict mode is enabled, this points to undefined inside the function, but the global object Window is not affected

The answer

global thisis Window{... } printthis is undefined
123
Uncaught TypeError: Cannot read property 'foo' of undefined
Copy the code

Topic 1.3: Let /const

let a = 1;
const b = 2;
var c = 3;
function print() {
    console.log(this.a);
    console.log(this.b);
    console.log(this.c);
}
print();
console.log(this.a);
Copy the code

Variables defined by let/const have a temporary dead band and are not mounted to the window object, so a and B cannot be retrieved from print.

The answer

undefined
undefined
3
undefined
Copy the code

Topic 1.4: In-object execution

a = 1;
function foo() {
    console.log(this.a); 
}
const obj = {
    a: 10.bar() {
        foo(); / / 1
    }
}
obj.bar(); 
Copy the code

Foo is in obj’s bar function, but it still runs independently, and this in foo still refers to the window object.

Topic 1.5: Execution within functions

var a = 1
function outer () {
  var a = 2
  function inner () { 
    console.log(this.a) / / 2
  }
  inner()
}
outer()
Copy the code

This is similar to problem 1.4, but be careful not to think of it as a closure problem

Topic 1.6: Self-executing functions

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

By default, this of the self-executing function points to the window

Self-executing functions run whenever they are executed, and only once, with this pointing to the window.

The answer

Window{... }1Window{... }2 // b is imply Global, which is mounted to the window
Copy the code

2. Implicit binding

Function calls are triggered on an object, i.e. there is a context object at the location of the call, commonly known as ** xxx.func ()**.

Func’s this points to XXX, but if there is a chain call, such as XXX.yyy.zzz.func, remember one rule: this always points to the object it was last called on.

Topic 2.1: Implicit binding

var a = 1;
function foo() {
    console.log(this.a); 
}
{a:2, foo: foo}
var obj = {a: 2, foo}
foo();
obj.foo();
Copy the code
  • foo(): Default binding, print1
  • obj.foo(): Implicit binding, printing2

The answer

1
2
Copy the code

Obj is defined by var, and obj is mounted to the window. Obj.foo () is equivalent to window.obj.foo(), which confirms the rule that this always refers to the object that last called it.

Topic 2.2: Object chain calls

I feel like I’m always talking about chain calls, so let’s go straight to an example:

var obj1 = {
    a: 1.obj2: {
        a: 2.foo(){
            console.log(this.a)
        }
    }
}
obj1.obj2.foo() / / 2
Copy the code

3. Loss of implicit binding

Implicit binding is a naughty thing that can lose its binding if you’re not careful. There are two common types of loss:

  • Use another variable as the function alias, and then use the alias to execute the function
  • Functions are implicitly assigned when passed as arguments

When the implicit binding is lost, the reference to this enables the default binding.

Look at the title in detail:

Topic 3.1: Fetching function aliases

a = 1
var obj = {
    a: 2.foo() {
        console.log(this.a)
    }
}
var foo = obj.foo;
obj.foo();
foo();
Copy the code

JavaScript addresses for reference types are stored in stack memory, whereas real ontologies are stored in heap memory.

Foo refers to the heap that obj. Foo refers to. Foo refers to the heap that obj points to, and then executes foo. Just to be general, as long as fn has nothing in front of it, it’s definitely not an implicit binding.

The answer

2 
1
Copy the code

Foo does not mount to window if foo is defined as let/const, but it does not affect the final print

Topic 3.2: Fetching function aliases

What if a function alias occurs not globally, but within an object?

var obj = { 
    a: 1.foo() {
        console.log(this.a)
    } 
};
var a = 2;
var foo = obj.foo;
var obj2 = { a: 3.foo: obj.foo }

obj.foo();
foo();
obj2.foo();
Copy the code

Obj2. foo refers to the heap memory of obj.foo, and execution thereafter is independent of obj (unless this is referred to using call/apply).

The answer

1 
2 
3
Copy the code

Topic 3.3: Functions passed as arguments

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

The first two steps of the tetralogy of function precompilation are:

  1. Find parameters and variable declarations, values assignedundefined
  2. To unify a form with an argument, that is, to assign the value of the argument to the parameter.

As an argument to obj.foo, assigning its value to the parameter fn during precompilation assigns the address to which obj.foo points to fn, after which fn execution has nothing to do with obj. Fn is the default binding.

The answer

The Window {... }2
Copy the code

Topic 3.4: Functions passed as arguments

To modify the above slightly, doFoo is not executed on window, but in obj2 instead

function foo() {
  console.log(this.a)
}
function doFoo(fn) {
  console.log(this)
  fn()
}
var obj = { a: 1, foo }
var a = 2
var obj2 = { a: 3, doFoo }
obj2.doFoo(obj.foo)
Copy the code
  • console.log(this): obj2.doFooConform to thexxx.fnFormat,doFooIs implicit binding,thisforobj2To print{a: 3, doFoo: ƒ}
  • fn(): not in theobj2Make a connection, default binding, print 2

The answer

{a: 3.doFoo: ƒ}
2
Copy the code

Topic 3.5: Callback functions

Here’s a problem we often encounter when writing code:

var name='zcxiaobao';
function introduce(){
    console.log('Hello,My name is '.this.name);
}
const Tom = {
    name: 'TOM'.introduce: function(){
        setTimeout(function(){
            console.log(this)
            console.log('Hello, My name is '.this.name); }}})const Mary = {
    name: 'Mary',
    introduce
}
const Lisa = {
    name: 'Lisa',
    introduce
}

Tom.introduce();
setTimeout(Mary.introduce, 100);
setTimeout(function(){
    Lisa.introduce();
},200);
Copy the code

SetTimeout is called asynchronously, and its callback function is executed only when the conditions are met and the synchronous code completes execution.

  • Tom. Introduce (): consoleLocated in thesetTimeoutOf the callback functionthisPoint to thewindow
  • Mary.introduceAs a directsetTimeoutFunction argument ofTopic 3.3), implicit binding loss occurs,thisFor default binding
  • Lisa.introduceExecution although locatedsetTimeoutIn the callback function, but keepxxx.fnMode,thisIs implicitly bound.

The answer

The Window {... } Hello, My name is zcxiaobao Hello,My name is zcxiaobao Hello,My name is LisaCopy the code

So if we want to use external this in setTimeout or setInterval, we need to store it in advance to avoid the loss of this.

const Tom = {
    name: 'TOM'.introduce: function(){
        _self = this
        setTimeout(function(){
            console.log('Hello, My name is ',_self.name);
        })
    }
}
Tom.introduce()
Copy the code

Topic 3.6: Implicit binding loss synthesis

name = 'javascript' ;
let obj = {
    name: 'obj',
    A (){
        this.name += 'this';
        console.log(this.name)
    },
    B(f){
        this.name += 'this';
        f();
    },
    C(){
      setTimeout(function(){
          console.log(this.name);
      },1000); }}let a = obj.A;             
a();                        
obj.B(function(){           
    console.log(this.name); 
});                         
obj.C();                    
console.log(name);   
Copy the code

This topic does not do the analysis, specific can refer to the above topic.

The answer

javascriptthis
javascriptthis
javascriptthis
undefined
Copy the code

4. Explicit binding

Explicit binding is easier to understand, which means forcibly changing the this pointer through call(), apply(), bind(), and so on.

All of the above methods can change the “this” direction, but use them slightly differently:

  • Call () and the apply ()The function is executed immediately
  • bind()The function returns a new function and does not execute the function immediately
  • Call () and the apply ()The difference betweencallTakes a number of parameters,applyAccept an array.

Topic 4.1: Compare the three invocation methods

function foo () {
  console.log(this.a)
}
var obj = { a: 1 }
var a = 2

foo()
foo.call(obj)
foo.apply(obj)
foo.bind(obj)
Copy the code
  • foo(): Default binding.
  • foo.call(obj): Displays binding,foothethisPoint to theobj
  • foo.apply(obj): Explicit binding
  • foo.bind(obj): binds explicitly, but does not execute the function immediately and returns no value

The answer

2
1
1
Copy the code

Topic 4.2: Implicit binding loss

Problem 3.4 has an implicit binding loss: Can we fix this problem by explicit binding?

function foo() {
  console.log(this.a)
}
function doFoo(fn) {
  console.log(this)
  fn()
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo)
Copy the code
  1. First fixdoFoo()Function of thethisPointing to.
doFoo.call(obj, obj.foo)
Copy the code
  1. Then the correctionfnthethis.
function foo() {
  console.log(this.a)
}
function doFoo(fn) {
  console.log(this)
  fn.call(this)}var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo)
Copy the code

And you’re done.

Topic 4.3: Callbacks and Calls

Take up the style of the previous topic, with a slight twist:

var obj1 = {
    a: 1
}
var obj2 = {
    a: 2.bar: function () {
        console.log(this.a)
    },
    foo: function () {
        setTimeout(function () {
            console.log(this)
            console.log(this.a)
        }.call(obj1), 0)}}var a = 3
obj2.bar()
obj2.foo()
Copy the code

At first glance, this problem looks a little confusing, setTimeout what is that?

Before we do this, let’s take a look at the internal mechanism of setTimeout:

setTimeout(fn) {
    if(Callback condition met) (fn)}Copy the code

We corrected the this pointer to fn in the callback function.

The answer

2
{a: 1}
1
Copy the code

Topic 4.4: Pay attention to the Call position

function foo () {
    console.log(this.a)
}
var obj = { a: 1 }
var a = 2

foo()
foo.call(obj)
foo().call(obj)
Copy the code
  • foo(): Default binding
  • foo.call(obj): Explicit binding
  • foo().call(obj):foo()The return value of the execution is executedcall.fooThe return value isundefined, the implementation ofcall()complains

The answer

2
1
2
Uncaught TypeError: Cannot read property 'call' of undefined
Copy the code

Topic 4.5: Pay attention to the Call Position (2)

Foo does not return a call function, so it should return a function.

function foo () {
    console.log(this.a)
    return function() {
        console.log(this.a)
    }
}
var obj = { a: 1 }
var a = 2

foo()
foo.call(obj)
foo().call(obj)
Copy the code
  • foo(): Default binding
  • foo.call(obj): Explicit binding
  • foo().call(obj): foo()Execute, print2, returns the anonymous function throughcallwillthisPoint to theobjTo print1.

Be careful here: the last foo().call(obj) has two functions that print two values.

The answer

2
1
2
1
Copy the code

Topic 4.6: Bind

What happens if you call all of the above calls instead of bind?

Call will execute the function immediately, and bind will return a new function but will not execute the function

function foo () {
    console.log(this.a)
    return function() {
        console.log(this.a)
    }
}
var obj = { a: 1 }
var a = 2

foo()
foo.bind(obj)
foo().bind(obj)
Copy the code

So the first thing we want to do is figure out, how many values are we going to print? Bind does not execute functions, so only two foo() prints a.

  • foo(): Default binding, print2
  • foo.bind(obj): Returns a new function, does not execute the function, no output
  • foo().bind(obj)The first layer:foo(), default binding, print2After,bindwillfoo()Returns an anonymous functionthisPoint to theobjThat does not perform

The answer

2
2
Copy the code

Topic 4.7: Outer this and inner this

If you use call, bind, etc., to modify this of the outer function, will this of the inner function be affected? (Note the difference between arrow functions)

function foo () {
    console.log(this.a)
    return function() {
        console.log(this.a)
    }
}
var obj = { a: 1 }
var a = 2
foo.call(obj)()
Copy the code

Foo. Call (obj): The first layer function foo calls this to obj, printing 1; The second layer of functions is anonymous, bound by default, and prints 2.

The answer

1
2
Copy the code

Topic 4.8: Call in objects

Migrate the above code to objects and see what happens?

var obj = {
    a: 'obj'.foo: function () {
        console.log('foo:'.this.a)
        return function () {
            console.log('inner:'.this.a)
        }
    }
}
var a = 'window'
var obj2 = { a: 'obj2' }

obj.foo()()
obj.foo.call(obj2)()
obj.foo().call(obj2)
Copy the code

Look at all these parentheses, doesn’t it feel a little too big. Okay, let’s go layer by layer:

  • obj.foo()()The first layer:obj.foo()Execute as implicit binding and print outfoo:obj; The second layer of anonymous functions is the default binding, printedinner:window
  • obj.foo.call(obj2)()Similar to:Topic 4.7The first layer,obj.foo.call(obj2)usecallwillobj.foothethisPoint to theobj2To printfoo: obj2; The second layer of anonymous functions is bound by default and printedinner:window
  • obj.foo().call(obj2)Similar to:Topic 4.5, the first layer of implicit binding, printing:foo: obj, the second layer of anonymous functionscallwillthisPoint to theobj2To printinner: obj2

Topic 4.9: Call with Parameters

We talked about the call/apply parameter difference at the beginning of explicit binding, so let’s pass parameters and see how nice this looks after passing parameters.

var obj = {
  a: 1.foo: function (b) {
    b = b || this.a
    return function (c) {
      console.log(this.a + b + c)
    }
  }
}
var a = 2
var obj2 = { a: 3 }

obj.foo(a).call(obj2, 1)
obj.foo.call(obj2)(1)
Copy the code

Note where the call is executed:

  • obj.foo(a).call(obj2, 1):
    • obj.foo(a)Foo returns the anonymous function fn, whose AO b value is passed in as a(the form participates in the argument), with a value of 2
    • Anonymous functionsfn.call(obj2, 1): fn has this pointing to obj2 and c value 1
    • this.a + b + c = obj2.a + FooAO.b + c = 3 + 2 + 1 = 6
  • obj.foo.call(obj2)(1):
    • obj.foo.call(obj2)Foo this refers to obj2 without passing an argument, b = this.a = obj2.a = 3; Returns the anonymous function fn
    • Anonymous functionsfn(1): c = 1, default binding, this refers to window
    • this.a + b + c = window.a + obj2.a + c = 2 + 3 + 1 = 6

The answer

6
6
Copy the code

Are you asleep, guys? We’re almost halfway through. Take a break and trythisEat it all at once.

5. Explicitly bind extensions

There are a lot of call/apply references that can change this, but none of them have much utility. Let’s learn some common calls and apply.

Topic 5.1: Find the maximum array value by apply

JavaScript does not provide functions like Max and min for arrays. Instead, it provides math. Max /min for maximizing multiple numbers, so you can use the Apply method to pass arrays directly to math. Max /min

const arr = [1.10.11.33.4.52.17]
Math.max.apply(Math, arr)
Math.min.apply(Math, arr)
Copy the code

Topic 5.2: Class array to array

ES6 before release, no Array. The from method, class Array can be converted to Array, using Array. Prototype. Slice. The call (the arguments) or []. Slice. The call (the arguments) class Array is converted into an Array.

Topic 5.3: Array higher-order functions

In everyday coding, we often use forEach, map, etc., but these array higher-order methods, they have a second parameter thisArg, each callback function is explicitly bound to thisArg.

Take the following example

const obj = {a: 10}
const arr = [1.2.3.4]
arr.forEach(function (val, key){
    console.log(`${key}: ${val} --- The ${this.a}`)
}, obj)
Copy the code

The answer

0: 1 --- 10
1: 2 --- 10
2: 3 --- 10
3: 4 --- 10
Copy the code

For more information about higher-order array functions, see JavaScript for higher-order array functions

6. New binding

Using new to build a function does the following four things:

  1. Create an empty simpleJavaScriptThe object (i.e.{});
  2. Add attributes to the object created in Step 1__proto__Link this property to the constructor’s prototype object;
  3. Take the object created in Step 1 as the objectthisContext;
  4. Returns if the function does not return an objectthis.

For more information about new, see JavaScript Rip New

Calling the constructor with new generates a new object and binds the new object to the calling function’s this.

Topic 6.1: New binding

function User(name, age) {
    this.name = name;
    this.age = age;
}
var name = 'Tom';
var age = 18;

var zc = new User('zc'.24);
console.log(zc.name)
Copy the code

The answer

zc
Copy the code

Topic 6.2: Properties plus methods

function User (name, age) {
  this.name = name;
  this.age = age;
  this.introduce = function () {
    console.log(this.name)
  }
  this.howOld = function () {
    return function () {
      console.log(this.age)
    }
  }
}
var name = 'Tom';
var age = 18;
var zc = new User('zc'.24)
zc.introduce()
zc.howOld()()
Copy the code

This problem is difficult not to make people think of the following code, are function nesting, specific solution is similar, can compare ah.

const User = {
  name: 'zc';
  age: 18;
  introduce = function () {
    console.log(this.name)
  }
  howOld = function () {
    return function () {
      console.log(this.age)
    }
  }
}
var name = 'Tom';
var age = 18;
User.introduce()
User.howOld()()
Copy the code
  • zc.introduce(): zc is the instance created by new, this points to zc, and printszc
  • zc.howOld()(): zc.howold () returns an anonymous function, which is bound by default, so prints 18(a package forever18)

The answer

zc
18
Copy the code

Title 6.3: Tianwang Mountain in the New World

Tianwang Mountain in the new world, every time after understanding, it will not be long forgotten, but this time to fundamentally understand the problem.

Here’s a taste:

function Foo(){
    getName = function(){ console.log(1); };
    return this;
}
Foo.getName = function(){ console.log(2); };
Foo.prototype.getName = function(){ console.log(3); };
var getName = function(){ console.log(4); };
function getName(){ console.log(5)}; Foo.getName(); getName(); Foo().getName(); getName();new Foo.getName();
new Foo().getName();
new new Foo().getName();
Copy the code
  1. precompiled
GO = {
    Foo: fn(Foo),
    getName: function getName(){ console.log(5)}; }Copy the code
  1. Analyze subsequent execution
  • Foo.getName(): Executes getName on Foo, printing 2
  • getName(): Execute the getName method in GO and print 4
  • Foo().getName()
    • Foo()perform
    // Change global GO getName to function(){console.log(1); }
    getName = function(){ console.log(1)}// Foo is the default binding, this -> window
    // return window
    return this
    Copy the code
    • Foo().getName(): Execute window.getName() and print 1
  • getName(): Execute getName in GO and print 1
  1. Before analyzing the next three prints, let’s add a bit of operator precedence.MDN)

    As you can see from the figure above, the partial priorities are as follows: new(with argument list) = member access = function call > new(without argument list)

  2. new Foo.getName()

First look from left to right: new Foo belongs to new without an argument list (priority 19), foo.getName belongs to member access (priority 20), getName() belongs to function call (priority 20), and the same priority follows execution from left to right.

  • Foo.getNameExecute to get the value ofgetNameattribute
  • Now the original expression becomesnew (Foo.getName)().new (Foo.getName)()Is a list with parameters (priority20),(Foo.getName)()Belongs to the function call (priority20) from left to right
  • new (Foo.getName)()Execute, print2And returns a toFoo.getName()Is an instance of the constructor

There is a misconception here: many people think that new is doing nothing, that it is a function call. So if Foo. GetName () returns undefined, new undefined will be an error, and we can verify that the expression returns the result.

console.log(new Foo.getName())
/ / 2
// Foo.getName {}
Copy the code

You can see that after the member access, the new operation with the argument list format is performed.

  1. new Foo().getName()
    • withStep 4Same analysis, execute firstnew Foo(), returns aFooIs an instance of the constructor
    • FooDoes not exist on the instance objectgetNameMethod found along the prototype chainFoo.prototype.getNameMethod, print2
  2. new new Foo().getName()

Parsing from left to right: the first new takes no argument list (priority 19), new Foo() takes an argument list (priority 20), and the rest of the member accesses and function calls have priority 20

  • new Foo()Execute to return aFooIs an instance of the constructor
  • When performing member access,FooInstance object inFoo.prototypeFind thegetNameattribute
  • performnew (new Foo().getName)(), returns aFoo.prototype.getName()For an instance of the constructor, print2
  1. new Foo.getName()new new Foo().getName()The difference between:
  • new Foo.getName()The constructor ofFoo.getName
  • new new Foo().getName()The constructor of isFoo.prototype.getName

The test results are as follows:

foo1 = new Foo.getName()
foo2 = new new Foo().getName()
console.log(foo1.constructor)
console.log(foo2.constructor)
Copy the code

Output result:

2
3ƒ () {console.log(2); {} ƒ ()console.log(3); }
Copy the code

This step should give you a better understanding of the above order of execution.

The answer

2
4
1
1
2
3
3
Copy the code

Brothers, the revolution is almost successful, try harder, later this will be a small problem.

7. Arrow function

The arrow function does not have its own this; its this points to this in the outer scope and refers to this when the function is defined rather than executed.

  1. This refers to this in the outer scopeThe arrow function does not have onethisBinding, but it can be traced through the scope chain to the outer scopethis
  2. Refer to this at function definition time rather than execution time: JavaScriptStatic scope, meaning that once a function is defined, its scope is fixed, regardless of where it is executed. See you for more detailsStatic and dynamic scopes of JavaScript.

Topic 7.1: Object methods use arrow functions

name = 'tom'
const obj = {
    name: 'zc'.intro: () = > {
        console.log('My name is ' + this.name)
    }
}
obj.intro()
Copy the code

The arrow function’s this is scoped, and the intro function’s upper scope is window.

The answer

My name is tom
Copy the code

Question 7.2: Arrow functions compared with ordinary functions

name = 'tom'
const obj = {
    name: 'zc'.intro:function ()  {
        return () = > {
            console.log('My name is ' + this.name)
        }
    },
    intro2:function ()  {
        return function() {
            console.log('My name is ' + this.name)
        }
    }
}
obj.intro2()()
obj.intro()()
Copy the code
  • obj.intro2()(): Do not repeat, printMy name is tom
  • obj.intro()(): obj.intro()Return arrow function, arrow functionthisDepends on its outer scope, so the arrow functionthisPoint to theobjTo printMy name is zc

Topic 7.3: Nesting of arrow functions with ordinary functions

name = 'window'
const obj1 = {
    name: 'obj1'.intro:function ()  {
        console.log(this.name)
        return () = > {
            console.log(this.name)
        }
    }
}
const obj2 = {
    name: 'obj2'.intro: () = >  {
        console.log(this.name)
        return function() {
            console.log(this.name)
        }
    }
}
const obj3 = {
    name: 'obj3'.intro: () = > {
        console.log(this.name)
        return () = > {
            console.log(this.name)
        }
    }
}

obj1.intro()()
obj2.intro()()
obj3.intro()()
Copy the code
  • obj1.intro()()Similar to:Topic 7.2To printObj1, obj1
  • obj2.intro()(): obj2.intro()Is the arrow function,thisIs the outer scopethisTo point towindow. Returns the anonymous function as the default binding. printThe window, the window
  • obj3.intro()(): obj3.intro()withobj2.intro()Same, return arrow function, outer scopeintrothethisPoint to thewindowTo printThe window, the window

The answer

obj1
obj1
window
window
window
window
Copy the code

Title 7.4: New hits the arrow function

function User(name, age) {
    this.name = name;
    this.age = age;
    this.intro = function(){
        console.log('My name is ' + this.name)
    },
    this.howOld = () = > {
        console.log('My age is ' + this.age)
    }
}

var name = 'Tom', age = 18;
var zc = new User('zc'.24);
zc.intro();
zc.howOld();
Copy the code
  • zcisnew UserInstance, hence the constructorUserthethisPoint to thezc
  • zc.intro(): printingMy name is zc
  • zc.howOld(): howOldIs the arrow function, the arrow functionThis is determined by the outer scope and refers to this when the function is defined, the outer scope isUser.thisPoint to thezcTo printMy age is 24

7.5: Call meets the arrow function

Since the arrow function does not have this, it cannot modify the this pointer through call\apply\bind, but it can modify this indirectly by modifying this in the outer scope

var name = 'window'
var obj1 = {
  name: 'obj1'.intro: function () {
    console.log(this.name)
    return () = > {
      console.log(this.name)
    }
  },
  intro2: () = > {
    console.log(this.name)
    return function () {
      console.log(this.name)
    }
  }
}
var obj2 = {
  name: 'obj2'
}
obj1.intro.call(obj2)()
obj1.intro().call(obj2)
obj1.intro2.call(obj2)()
obj1.intro2().call(obj2)
Copy the code
  • obj1.intro.call(obj2)(): The first-layer functions are ordinary functions and passcallModify thethisforobj2To printobj2. The second function is the arrow function, and itsthisAnd the outerthisSame, same printobj2.
  • obj1.intro().call(obj2): First layer function printobj1, the second function is the arrow function,callInvalid, itsthisAnd the outerthisSame, printobj1
  • obj1.intro2.call(obj2)(): The first layer is the arrow function,callInvalid. The outer scope iswindowTo printwindow; The second is a normal anonymous function, bound by default and printedwindow
  • obj1.intro2().call(obj2): Same as above, printwindow; The second layer is an anonymous function,callModify thethisforobj2To printobj2

The answer

obj2
obj2
obj1
obj1
window
window
window
obj2
Copy the code

8. Arrow function extension

conclusion

  • Arrow function doesn’t have anythis, it’sthisThe outer scope is traced by scope chainthis, and refers to the function definitionthisNot at execution time.
  • Cannot be used as a constructor, cannot be usednewCommand, otherwise an error will be reported
  • Arrow function doesn’t have anyargumentsObject, if you want to use itrestParameters instead of
  • UnusableyieldCommand, so the arrow function cannot be usedGeneratorFunction.
  • Can’t usecall/apply/bindModify thethisPointer to, but can be modified by modifying the outer scopethisTo modify it indirectly.
  • Arrow function doesn’t have anyprototypeProperties.

Avoid use scenarios

  1. Arrow functions define object methods
const zc = {
    name: 'zc'.intro: () = > {
        // this -> window
        console.log(this.name)
    }
}
zc.intro() // undefined
Copy the code
  1. Arrow functions cannot be constructors
const User = (name, age) = > {
    this.name = name;
    this.age = age;
}
// Uncaught TypeError: User is not a constructor
zc = new User('zc'.24);
Copy the code
  1. Event callback function

In the DOM event callback, this is wrapped to the calling element; if the constructor is used, its this refers to the window object

document.getElementById('btn')
        .addEventListener('click'.() = > {
            console.log(this= = =window); // true
        })
Copy the code

9. The comprehensive problem

After learning the above knowledge, is not the feeling that he has tended to the environment, now up the top of Huashan a showdown.

Topic 9.1: Object Synthesis

var name = 'window'
var user1 = {
    name: 'user1'.foo1: function () {
        console.log(this.name)
    },
    foo2: () = > console.log(this.name),
    foo3: function () {
        return function () {
            console.log(this.name)
        }
    },
    foo4: function () {
        return () = > {
            console.log(this.name)
        }
    }
}
var user2 = { name: 'user2' }

user1.foo1()
user1.foo1.call(user2)

user1.foo2()
user1.foo2.call(user2)

user1.foo3()()
user1.foo3.call(user2)()
user1.foo3().call(user2)

user1.foo4()()
user1.foo4.call(user2)()
user1.foo4().call(user2)
Copy the code

This topic is not difficult, is the above many questions to do an integration, if the above have learned, this problem is not a big problem.

  • User1. Foo1 (), user1. Foo1. Call (user2): Implicit and explicit binding
  • User1. Foo2 (), user1. Foo2. Call (user2): arrow function and call
  • User1.foo3 ()(), user1.foo3.call(user2)(), user1.foo3().call(user2): See topic 4.8
  • User1.foo4 ()(), user1.foo4.call(user2)(), user1.foo4().call(user2): See question 7.5

The answer:

var name = 'window'
var user1 = {
    name: 'user1'.foo1: function () {
        console.log(this.name)
    },
    foo2: () = > console.log(this.name),
    foo3: function () {
        return function () {
            console.log(this.name)
        }
    },
    foo4: function () {
        return () = > {
            console.log(this.name)
        }
    }
}
var user2 = { name: 'user2' }

user1.foo1()  // user1
user1.foo1.call(user2) // user2

user1.foo2() // window
user1.foo2.call(user2) // window

user1.foo3()() // window
user1.foo3.call(user2)() // window
user1.foo3().call(user2) // user2

user1.foo4()() // user1
user1.foo4.call(user2)() // user2
user1.foo4().call(user2) // user1
Copy the code

Topic 9.2: Implicit binding loss

var x = 10;
var foo = {
   x : 20.bar : function(){
       var x = 30;
       console.log(this.x)
    
   }
};
foo.bar();
(foo.bar)();
(foo.bar = foo.bar)();
(foo.bar, foo.bar)();
Copy the code

Suddenly there is a very little code of the topic, but also some unfamiliar.

  • foo.bar(): Implicit binding, printing20
  • (foo.bar)()Member access has the same precedence as function calls, default left to right, so parentheses are optional, implicit binding, printing20
  • (foo.bar = foo.bar)(): Implicit binding lost, herefoo.barAlias. Same name, butfoo.barHave been tofooIt doesn’t matter. Default binding, print10
  • (foo.bar, foo.bar)(): Implicit binding missing, function alias, assigning the value of the comma expression (second foo.bar) to the new variable, then executing the function to which the new variable points, default binding, printing10.

An implicit binding must satisfy the xxx.fn () format. If this format is broken, the implicit binding will be lost.

Topic 9.3: Arguments (Recommended)

var length = 10;
function fn() {
    console.log(this.length);
}
 
var obj = {
  length: 5.method: function(fn) {
    fn();
    arguments[0]();
  }
};
 
obj.method(fn, 1);
Copy the code

I want to pay attention to this one, there are holes.

  • Fn (): default binding, print 10

  • Arguments [0](): Arguments [0]()

    1. argumentsIs an array of classes,argumentsTo expand, it should look like this:
    arguments: {
        0: fn,
        1: 1.length: 2
    }
    Copy the code
    1. arguments[0]Is this access object property 0? 0 is hard to understand, let’s change it a little bit to make it easier to understand:
    arguments: {
        fn: fn,
        1: 1.length: 2
    }
    Copy the code
    1. So this should make sense to you, implicit binding,fnfunctionthisPoint to theargumentsTo print 2

Question 9.4: Final question (recommended)

var number = 5;
var obj = {
    number: 3.fn: (function () {
        var number;
        this.number *= 2;
        number = number * 2;
        number = 3;
        return function () {
            var num = this.number;
            this.number *= 2;
            console.log(num);
            number *= 3;
            console.log(number); }}}) ()var myFun = obj.fn;
myFun.call(null);
obj.fn();
console.log(window.number);
Copy the code

Fn. Call (null) or fn. Call (undefined) is equivalent to fn().

  1. Obj.fn is an immediate function: default binding, this refers to window

    Let’s analyze it sentence by sentence:

    • var number: Executes the function immediatelyAOaddnumberProperty with a value ofundefined
    • this.number *= 2: window.number = 10
    • number = number * 2: Executes the function immediatelyAOIn thenumberA value ofundefined, after the assignment, isNaN
    • number = 3: AOIn thenumberValue byNaNModified to3
    • Returns an anonymous function that forms a closure

    Obj can be similarly viewed as the following code (note the presence of closures):

    obj = {
       number: 3.fn: function () {
            var num = this.number;
            this.number *= 2;
            console.log(num);
            number *= 3;
            console.log(number); }}Copy the code
  2. Myfun.call (null): equivalent to myFun(), implicit binding missing, myFun this points to window.

    The same line by line analysis:

    • var num = this.number: thisPoint to thewindow.num = window.num = 10
    • this.number *= 2: window.number = 20
    • console.log(num): printing10
    • number *= 3: the currentAOThere is nonumberProperty along the scope chain that the function can execute at onceAOCheck in to thenumberProperty to change its value to9
    • console.log(number): Prints execute the function immediatelyAOIn thenumberTo print9
  3. Obj.fn (): implicitly bound, fn’s this points to obj

    Continue the step-by-step analysis:

    • var num = this.number: this->obj.num = obj.num = 3
    • this.number *= 2: obj.number *= 2 = 6
    • console.log(num): printingnumValue, print3
    • number *= 3: the currentAODoes not exist in thenumberContinue to modify the execute function immediatelyAOIn thenumber.number *= 3 = 27
    • console.log(number): printing27
  4. The console. The log (window. The number) : 20 printing

When myfun.call (null) is executed, it does not find the number variable. Instead of looking for window.number, it looks for the number in the immediate AO. (A more detailed explanation is given in the recommendation at the beginning of the article)

The answer

10
9
3
27
20
Copy the code

conclusion

  • Default binding: in non-strict modethisPoints to global objects, in strict modethisWill be bound toundefined
  • Implicit binding: meetsXXX.fn()Format,fnthethisPoint to theXXX. If there is a chain call,This always refers to the object that last called it
  • Implicit binding loss: alias the function and run through the alias; Functions as arguments cause implicit binding loss.
  • Show binding: Passcall/apply/bindModify thethisPoint to the
  • newBind: PassnewTo call the constructor, a new object is generated, and this new object is bound to the calling functionthis.
  • Arrow function bindings: Arrow functions do not have anythis, it’sthisThe outer scope is traced by scope chainthis, and refers to the function definitionthisNot at execution time

After the language

It’s a relief that this is almost over. This article has been written for a long time, looking for resources, modifying the blog, all kinds of messy chores, resulting in the slow writing of satisfactory blog. May be born male science reasons, how to write the feeling of the article is very stiff, but fortunately, or smoothly finished.

At the end of the article, thank you for the reference blog and title source

  • Lin Daft big guy
  • Xiao Xi: Hey, do you really understand this?
  • The source of duyi education

Finally, according to the usual practice of Ah Bao, I give an interview question:

var num = 10
var obj = {num: 20}
obj.fn = (function (num) {
  this.num = num * 3
  num++
  return function (n) {
    this.num += n
    num++
    console.log(num)
  }
})(obj.num)
var fn = obj.fn
fn(5)
obj.fn(10)
console.log(num, obj.num)
Copy the code

Finally, I wish everyone can learn the front end, step by step ascend god, become a big man.