In depth JavaScript learning notes: this point of function

In depth JavaScript learning notes: this point of function

  • Reference: Teacher Wang Hongyuan's in-depth JavaScript course

Why do you need this?

  • In common programming languages, almost all have this keyword (self is used in Objective-C), but this in JavaScript is different from this in common object-oriented languages:
    • In common object-oriented programming languages, such as Java, C + +, Swift, Dart and so on, this usually only appears in the methods of classes.
    • That is, you need to have a class, class method (especially instance method), this represents the current calling object.
    • But this in JavaScript is more flexible, no matter where it appears or what it represents.
  • Let's take a look at the difference between writing an obj object with this and without this

What does this point to?

  • What does this point to under the global action?
// In most cases, this appears in the function
// Under global scope
// Browser: window(globalObject)
// Node environment: {}
console.log(this)
// console.log(window)
  • However, this is rarely used directly under the global action in development, and it is usually used in functions.
    • When all functions are called, an execution context will be created:
    • In this context, the call stack and AO object of the function are recorded;
    • this is also one of the records;

What does this point to?

  • Example:
// What this points to has nothing to do with the position of the function
// It has something to do with the way the function is called

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

// 1. Call this function directly
foo()

// 2. Create an object in which the function points to foo
var obj = {
  name: 'why',
  foo: foo
}

obj.foo()

// 3.apply call
foo.apply("abc")

  • Let's start with a confusing question:
    • Defining a function, we call it in three different ways, and it produces three different results
  • What enlightenment can this case give us?
    • 1. When the function is called, JavaScript will bind a value to this by default;
    • 2. The binding of this has nothing to do with the defined location (the location of writing);
    • 3. The binding of this is related to the calling method and the calling location;
    • 4.this is bound at runtime;
  • So what kind of binding rules is this?  
    • Binding 1: default binding;
    • Binding 2: implicit binding;
    • Binding 3: display binding;
    • Binding: new

Rule 1: default binding

  • When do I use the default binding? Independent function call.
    • Independent function call can be understood as that the function is not bound to an object for call;
  • Let's take a look at several cases. For the common default binding, the following this is all window
// Default binding: independent function call
// 1. Case 1:
function foo() {
  console.log(this)
}

foo()

// 2. Case 2:
function foo1() {
  console.log(this)
}

function foo2() {
  console.log(this)
  foo1()
}

function foo3() {
  console.log(this)
  foo2()
}

foo3()


// 3. Case 3:
var obj = {
  name: "why",
  foo: function() {
    console.log(this)
  }
}

var bar = obj.foo
bar() // window


// 4. Case 4:
function foo() {
  console.log(this)
}
var obj = {
  name: "why",
  foo: foo
}

var bar = obj.foo
bar() // window

// 5. Case 5:
function foo() {
  function bar() {
    console.log(this)
  }
  return bar
}

Rule 2: implicit binding

  • Another common calling method is to call through an object:
    • That is, in its calling position, it is a function call initiated by an object.
  • Let's take a look at the common default bindings through several cases
    • Implicit binding: object fn()
    • The object object will be bound to this in the fn function by the js engine
function foo() {
  console.log(this)
}

// Independent function call
// foo()

// 1. Case 1:
var obj = {
  name: "why",
  foo: foo
}

obj.foo() // obj object

// 2. Case 2:
var obj = {
  name: "why",
  eating: function() {
    console.log(this.name + "I'm eating")
  },
  running: function() {
    console.log(obj.name + "Running")
  }
}

obj.eating()
obj.running()



// 3. Case 3:
var obj1 = {
  name: "obj1",
  foo: function() {
    console.log(this)
  }
}

var obj2 = {
  name: "obj2",
  bar: obj1.foo
}

obj2.bar()

Rule 3: display binding

  • Implicit binding has one prerequisite:
    • There must be a reference to the function (such as an attribute) inside the called object;
    • If there is no such reference, an error that the function cannot be found will be reported when calling;
    • It is through this reference that this is indirectly bound to this object;
  • What should we do if we don't want to include a reference to this function inside the object and want to make a forced call on this object?
    • All JavaScript functions can use call and apply methods (this is related to Prototype).
      • The difference between them will not be expanded here;
      • In fact, it is very simple. The first parameter is the same. For the following parameters, apply is the array and call is the parameter list;
    • The first parameter of these two functions requires an object. What is the function of this object? It's for this.
    • When this function is called, this will be bound to the incoming object.
  • Because of the above process, we explicitly bind the object pointed to by this, so it is called display binding
  • Example:
function foo() {
  console.log("The function was called", this)
}
//1. The difference between foo direct call and call/apply call lies in the difference of this binding
foo The direct call points to the global object(window)
foo()
var obj = {
  name: "obj"
}
//call/apply is a binding object that can specify this
foo.call(obj)
foo.apply(obj)
foo.apply("aaaa")
  • Output:

  • Example:
// 2. What's the difference between call and apply?
function sum(num1, num2, num3) {
  console.log(num1 + num2 + num3, this)
}

sum.call("call", 20, 30, 40)
sum.apply("apply", [20, 30, 40])

//call and apply can explicitly bind this when executing functions. This binding rule is called display binding
  • Output:

  • What can we do if we want a function to always be bound to an object?
function foo() {
  console.log(this)
}

// foo.call("aaa")
// foo.call("aaa")
// foo.call("aaa")
// foo.call("aaa")

// Default binding and display binding conflict: priority (display binding)

var newFoo = foo.bind("aaa")

newFoo()
newFoo()
newFoo()

new binding

  • Functions in JavaScript can be used as constructors of a class, that is, using the new keyword.
  • If you use the new keyword to call a function, the following operations will be performed:
    • 1. Create a new object;
    • 2. The new object will be prototype d;
    • 3. The new object will be bound to this of the function call (this binding is completed in this step);
    • 4. If the function does not return other objects, the expression will return this new object;
  • Example:
// When we call a function (constructor) through a new keyword, this is the object created when calling the constructor
// this = created object
// This binding process is called new binding

function Person(name, age) {
  this.name = name
  this.age = age
}

var p1 = new Person("why", 18)
console.log(p1.name, p1.age)

var p2 = new Person("kobe", 30)
console.log(p2.name, p2.age)

Rule Priority

  • 1. The default rule has the lowest priority
    • There is no doubt that the default rule has the lowest priority, because when there are other rules, this will be bound by other rules
  • 2. The priority of display binding is higher than that of implicit binding

var obj = {
  name: "obj",
  foo: function() {
    console.log(this)
  }
}

obj.foo()

// 1. The explicit binding of call / apply is higher than the implicit binding
obj.foo.apply('abc')
obj.foo.call('abc')

// 2.bind takes precedence over implicit binding
function foo() {
  console.log(this)
}

var obj = {
  name: "obj",
  foo: foo.bind("aaa")
}

obj.foo()
  • 3.new binding takes precedence over implicit binding

var obj = {
  name: "obj",
  foo: function() {
    console.log(this)
  }
}

// new takes precedence over implicit binding
var f = new obj.foo()  // foo {}
  • 4. The binding priority of new is higher than that of bind
    • new binding, call and apply are not allowed to be used at the same time, so there is no higher priority
    • New binding can be used together with bind, and new binding has higher priority
// new takes precedence over bind
function foo() {
  console.log(this)
}

var bar = foo.bind("aaa")

var obj = new bar()  // foo {}
  • new binding > apply / call / bind > implicit binding (obj. Foo()) > default binding (independent function call)

Outside this rule – ignore display bindings

  • If we pass in null or undefined in the display binding, the display binding will be ignored and the default rule will be used:

Outside this rule - indirect function reference

  • In another case, create an indirect reference to a function, which uses the default binding rules.
    • The result of assignment (obj2.foo = obj1.foo) is foo function;
    • If foo function is called directly, it is the default binding;

Arrow functionarrow function

  • Arrow function is a method of writing function added after ES6, and it is more concise than function expression:
    • The arrow function will not bind this and arguments attributes;
    • The arrow function cannot be used as a constructor (it cannot be used with new, and an error will be thrown);
  • How to write arrow function?
    • (): parameters of function
    • {}: executor of function

Programming optimization of arrow function

  • Example:
// 1. Write arrow function
// 1> (): parameter
// 2> = >: arrow
// 3> {}: executor of function
var foo = (num1, num2, num3) => {
  console.log(num1, num2, num3)
  var result = num1 + num2 + num3
  console.log(result)
}

function bar(num1, num2, num3) {
}

// When using higher-order functions, you can also pass in arrow functions
var nums = [10, 20, 45, 78]
nums.forEach((item, index, arr) => {})

// There are some common abbreviations for arrow functions:
// Abbreviation 1: if there is only one parameter, () can be omitted
nums.forEach(item => {
  console.log(item)
})

// Abbreviation 2: if the function executor has only one line of code, {} can also be omitted
// Emphasis: and it will take the execution result of this line of code as the return value by default
nums.forEach(item => console.log(item))
var newNums = nums.filter(item => item % 2 === 0)
console.log(newNums)

// filter/map/reduce
var result = nums.filter(item => item % 2 === 0)
                 .map(item => item * 100)
                 .reduce((preValue, item) => preValue + item)
console.log(result)

// Abbreviation 3: if an arrow function has only one line of code and returns an object, how to write the abbreviation at this time
// var bar = () => {
//   return { name: "why", age: 18 }
// }

var bar = () => ({ name: "why", age: 18 })

Outside this rule – ES6 arrow function

  • The arrow function does not use the four standard rules of this (that is, it does not bind this), but determines this according to the outer scope.
var name = "why"

var foo = () => {
  console.log(this)
}

foo()
var obj = {foo: foo}
obj.foo()
foo.call("abc")
  • Output:

  • Take a look at a case that simulates a network request:
    • Here, I use setTimeout to simulate the network request. How can I store the requested data in the data?
    • We need to get the obj object and set data;
    • However, the directly obtained this is window (setTimeout is called directly and bound by default). We need to define VaR in the outer layer_ this = this
    • Used in the callback function of setTimeout_ this represents the obj object

  • Use the arrow function:
var obj = {
  data: [],
  getData: function() {
    // Send the network request and put the result in the data attribute above
    // Solution before arrow function
    // var _this = this
    // setTimeout(function() {
    //   var result = ["abc", "cba", "nba"]
    //   _this.data = result
    // }, 2000);
    // After arrow function
    setTimeout(() => {
      var result = ["abc", "cba", "nba"]
      this.data = result
    }, 2000);
  }
}

obj.getData()

Exercises

  • Exercise 1:
var name = "window";

var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  }
};

function sayName() {
  var sss = person.sayName;
  sss(); // window: independent function call
  person.sayName(); // person: implicit call
  (person.sayName)(); // person: implicit call
  (b = person.sayName)(); // window: assignment expression (independent function call)
}

sayName();
  • Exercise 2:
var name = 'window'

var person1 = {
  name: 'person1',
  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 person2 = { name: 'person2' }

person1.foo1(); // Person1 (implicit binding)
person1.foo1.call(person2); // Person2 (display binding priority is higher than implicit binding)

person1.foo2(); // Window (no scope is bound, and the upper scope is global)
person1.foo2.call(person2); // window

person1.foo3()(); // Window (independent function call)
person1.foo3.call(person2)(); // Window (independent function call)
person1.foo3().call(person2); // Person2 (the final call returns the function expression, using the display binding)

person1.foo4()(); // Person1 (the arrow function is not bound to this, and the upper scope this is person1)
person1.foo4.call(person2)(); // Person2 (a person2 is bound to the displayed upper scope)
person1.foo4().call(person2); // Person1 (the upper layer finds person1)
  • Exercise 3:
var name = 'window'

function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  },
  this.foo2 = () => console.log(this.name),
  this.foo3 = function () {
    return function () {
      console.log(this.name)
    }
  },
  this.foo4 = function () {
    return () => {
      console.log(this.name)
    }
  }
}

var person1 = new Person('person1')
var person2 = new Person('person2')

person1.foo1() // person1
person1.foo1.call(person2) // Person2 (display higher than implicit binding)

person1.foo2() // Person1 (this in the upper scope is person1)
person1.foo2.call(person2) // Person1 (this in the upper scope is person1)

person1.foo3()() // Window (independent function call)
person1.foo3.call(person2)() // window
person1.foo3().call(person2) // person2

person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1


var obj = {
  name: "obj",
  foo: function() {

  }
} 
  • Exercise 4:
var name = 'window'

function Person (name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo2: function () {
      return () => {
        console.log(this.name)
      }
    }
  }
}

var person1 = new Person('person1')
var person2 = new Person('person2')

person1.obj.foo1()() // window
person1.obj.foo1.call(person2)() // window
person1.obj.foo1().call(person2) // person2

person1.obj.foo2()() // obj
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj

Implement apply, call and bind

  • Implementation of call function
// Usage of apply/call/bind
// js simulate their implementation? difficulty

// Add a hycall method to all functions
Function.prototype.hycall = function(thisArg, ...args) {
  // Here you can execute the called function (foo)
  // Question: you can get which function executes hycall
  // 1. Get the function to be executed
  var fn = this

  // 2. Convert thisArg to object type (prevent it from passing in non object type)
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg): window

  // 3. Call the function to be executed
  thisArg.fn = fn
  var result = thisArg.fn(...args)
  delete thisArg.fn

  // 4. Return the final result
  return result
}


function foo() {
  console.log("foo The function is executed", this)
}

function sum(num1, num2) {
  console.log("sum The function is executed", this, num1, num2)
  return num1 + num2
}


// call method of system function
foo.call(undefined)
var result = sum.call({}, 20, 30)
// console.log("result of system call:", result)

// hycall method of self implemented function
// Implicit binding by default
// foo.hycall({name: "why"})
foo.hycall(undefined)
var result = sum.hycall("abc", 20, 30)
console.log("hycall Call of:", result)

// var num = {name: "why"}
// console.log(Object(num))
  • Implementation of apply function
// Implement hyapply by yourself
Function.prototype.hyapply = function(thisArg, argArray) {
  // 1. Get the function to be executed
  var fn = this

  // 2. Handle the bound thisArg
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg): window

  // 3. Execute function
  thisArg.fn = fn
  var result
  // If (! Argarray) {/ / argarray has no value (no parameter passed)
  //   result = thisArg.fn()
  // }else {/ / parameters passed
  //   result = thisArg.fn(...argArray)
  // }

  // argArray = argArray ? argArray: []
  argArray = argArray || []
  result = thisArg.fn(...argArray)

  delete thisArg.fn

  // 4. Return results
  return result
}

function sum(num1, num2) {
  console.log("sum Called", this, num1, num2)
  return num1 + num2
}

function foo(num) {
  return num
}

function bar() {
  console.log("bar The function is executed", this)
}

// system call
// var result = sum.apply("abc", 20)
// console.log(result)

// Self implemented call
// var result = sum.hyapply("abc", [20, 30])
// console.log(result)

// var result2 = foo.hyapply("abc", [20])
// console.log(result2)

// edge case
bar.hyapply(0)
  • Implementation of bind function
Function.prototype.hybind = function(thisArg, ...argArray) {
  // 1. Get the function that needs to be called
  var fn = this

  // 2. Bind this
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg): window

  function proxyFn(...args) {
    // 3. Put the function into thisArg and call it
    thisArg.fn = fn
    // Special: merge two passed in parameters
    var finalArgs = [...argArray, ...args]
    var result = thisArg.fn(...finalArgs)
    delete thisArg.fn

    // 4. Return results
    return result
  }

  return proxyFn
}

function foo() {
  console.log("foo Be executed", this)
  return 20
}

function sum(num1, num2, num3, num4) {
  console.log(num1, num2, num3, num4)
}

// bind usage of the system
var bar = foo.bind("abc")
bar()

// var newSum = sum.bind("aaa", 10, 20, 30, 40)
// newSum()

// var newSum = sum.bind("aaa")
// newSum(10, 20, 30, 40)

// var newSum = sum.bind("aaa", 10)
// newSum(20, 30, 40)


// Use your own defined bind
// var bar = foo.hybind("abc")
// var result = bar()
// console.log(result)

var newSum = sum.hybind("abc", 10, 20)
var result = newSum(30, 40)

Keywords: linq p2p GNU

Added by mlewis on Fri, 04 Feb 2022 08:41:13 +0200