This is a common JS attribute, but often confusing. This article summarizes some common usage scenarios, hoping to deepen your understanding. This article mainly contains the following contents:

  1. This common scenario
  2. Manually implement call and bind

This global

  • In the global context of the browser host, this points to the window object
  • On the Node command line, this is the global namespace and can be accessed through global. In javascript scripts executed in the Node environment, this is an empty object, unlike global

This in the function method

A function is simply a variable name containing a pointer, and its result depends on the execution context

Function reads and writes variables through the scope chain. So if you tease out the scope chain, you can easily identify this

This in the prototype

All instances created by the same function share a stereotype. If you assign an array to a prototype, all instances will get that array. Unless you overwrite it in an instance.

function Thing() {}

Thing.prototype.things = [];

var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");

console.log(thing2.things); //logs ["foo"]
Copy the code

In general, this isn’t true; changing Thing1 also affects Thing2.

function Thing() {
    this.things = [];
}

var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");

console.log(thing1.things); //logs ["foo"]
console.log(thing2.things); //logs []
Copy the code

In JavaScript, functions can be nested, which means you can continue to define functions inside functions. Instead of inheriting this directly, the inner function gets the value of the variable defined in the outer function through a closure.

function Thing() {}

Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    var info = "attempting to log this.foo:";
    function doIt() {
        console.log(info, this.foo);
    }
    doIt(); // This points to window
}

var thing = new Thing();
thing.logFoo();  //logs "attempting to log this.foo: undefined"
Copy the code

When an instance’s method is passed as a parameter, the instance context is not carried. Because you’re just passing a pointer to a function.

function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {  
    console.log(this.foo);   
}

function doIt(method) {
    method(); // When a method is called, this refers to the context
}

var thing = new Thing();
thing.logFoo(); //logs "bar"
doIt(thing.logFoo); //logs undefined
Copy the code

The solution is to use the bind method to show the specified context when passing parameters

function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () { 
    console.log(this.foo);
}

function doIt(method) {
    method();
}

var thing = new Thing();
doIt(thing.logFoo.bind(thing)); //logs bar
Copy the code

The call to realize

Call is used to redirect the function this, call the function, and return the result

Function.prototype.Call = function (context) {
  context = context || global;
  // This refers to the Call caller, the function bar
  // step1: use the function bar as a context property. This refers to the context
  context.fn = this;
  let args = [];
  // Arguments and this are the two default functions
  // The first argument is context, so I starts at 1
  for (let i = 1, len = arguments.length; i < len; i++) {
    args.push(arguments[i]);
  }
  // step2: Call the function
  constres = context.fn(... args);// What happens if you don't delete it?
  delete context.fn;
  return res;
};

const bar = function (name, age) {
  this.name = name;
  this.age = age;
  console.log(this.name, this.age);
  console.log(this.value);
};

const foo = {
  value: 1
};

bar.Call(foo, "sam".29);
// sam 29
/ / 1
Copy the code

The difference between 🌴 and apply starts with the second argument, call accepts a list of arguments, and apply accepts an array of arguments

The bind to realize

The role of the bind

  • Bind changes the function’s this pointer
  • Return the new function. If the new function is a constructor (called by new), this needs to point to the instance
  • Bridging of prototype chains

js-base-fn

Function.prototype.bindThis = function (context) {
  const self = this; // this is the caller to bindThis, usually a function
  const args = Array.prototype.slice.call(arguments.1);
  / / change this
  const fn = function () {
    return self.apply(
      // this is the instance
      // If the new function is the constructor of new, this will be different
      // New points this to the new instance
      this instanceof fn ? this : context,
      args.concat(Array.prototype.slice.call(arguments))); };// Prototype chains are bridged, and empty functions are used to prevent prototype chains from being common
  const fNOP = function () {};
  fNOP.prototype = this.prototype;
  fn.prototype = new fNOP();

  return fn;
};

function foo(a, b, c) {
  console.log("name: >>".this.name + a + b + c);
}

const bar = {
  name: "balabala"
};

foo.bindThis(bar)("Change"."Beautiful"."Wisdom")
// Name: >> Balabala becomes beautiful and wise
Copy the code

summary

This is a very “magical” attribute, which involves a lot of knowledge, requiring us to have a clear understanding of prototype, scope and other knowledge.

If you still feel confused after reading this article, don’t worry, it is normal, this part of the knowledge is relatively abstract, you need to read more and more practice, slowly will understand 💚