阿里云主机折上折
  • 微信号
Current Site:Index > The binding rule of 'this' translates this sentence into English.

The binding rule of 'this' translates this sentence into English.

Author:Chuan Chen 阅读数:60850人阅读 分类: JavaScript

In JavaScript, the binding rules of this determine the object to which this points when a function is executed. Understanding these rules is crucial for writing maintainable code, especially when dealing with object methods, event callbacks, or higher-order functions. Below are the core rules of this binding and their application scenarios.

Default Binding

When a function is called independently, this defaults to pointing to the global object (window in browsers, global in Node.js). In strict mode, this is undefined.

function showThis() {
  console.log(this);
}

showThis(); // Output in browsers: Window {...}

Behavior in strict mode:

"use strict";
function strictShowThis() {
  console.log(this);
}

strictShowThis(); // Output: undefined

Implicit Binding

When a function is called as a method of an object, this points to the object that calls the method.

const user = {
  name: "Alice",
  greet() {
    console.log(`Hello, ${this.name}!`);
  }
};

user.greet(); // Output: Hello, Alice!

Note the issue of implicit loss:

const greetFunc = user.greet;
greetFunc(); // Output: Hello, undefined! (default binding takes effect)

Explicit Binding

Use call, apply, or bind to forcibly specify the object to which this points.

function introduce(lang) {
  console.log(`I code in ${lang}. My name is ${this.name}`);
}

const dev = { name: "Bob" };
introduce.call(dev, "JavaScript"); // Output: I code in JavaScript. My name is Bob
introduce.apply(dev, ["Python"]); // Output: I code in Python. My name is Bob

const boundFunc = introduce.bind(dev, "Rust");
boundFunc(); // Output: I code in Rust. My name is Bob

new Binding

When a constructor is called with new, this points to the newly created instance object.

function Person(name) {
  this.name = name;
  this.sayHi = function() {
    console.log(`Hi, I'm ${this.name}`);
  };
}

const charlie = new Person("Charlie");
charlie.sayHi(); // Output: Hi, I'm Charlie

Arrow Functions

The this in arrow functions inherits from the outer scope and cannot be modified using the above rules.

const timer = {
  seconds: 0,
  start() {
    setInterval(() => {
      this.seconds++;
      console.log(this.seconds);
    }, 1000);
  }
};

timer.start(); // Outputs incrementing numbers every second

Comparison with the pitfall of regular functions:

const brokenTimer = {
  seconds: 0,
  start() {
    setInterval(function() {
      // this points to the global object!
      this.seconds++; // TypeError
    }, 1000);
  }
};

Priority Rules

When multiple rules apply simultaneously, the following priority determines the outcome:

  1. new binding > explicit binding > implicit binding > default binding
function Demo() {
  console.log(this);
}

const obj = {};
const boundDemo = Demo.bind(obj);

new boundDemo(); // Output: Demo instance, not obj

Special Scenarios

DOM Event Handling

In event handler functions, this points to the DOM element that triggered the event:

button.addEventListener("click", function() {
  console.log(this); // Output: <button> element
});

Timer Callbacks

In non-arrow function callbacks, this points to the global object:

setTimeout(function() {
  console.log(this); // Output in browsers: Window
}, 100);

Higher-Order Functions

Array methods can accept a second parameter to specify this:

[1, 2, 3].forEach(function(item) {
  console.log(item, this); // this points to {x:10}
}, { x: 10 });

Common Pitfalls and Solutions

Nested Functions and this

const problem = {
  data: 42,
  getData() {
    function inner() {
      return this.data; // this points to the global object
    }
    return inner();
  }
};

console.log(problem.getData()); // undefined

Solution:

const solution = {
  data: 42,
  getData() {
    const inner = () => this.data; // Arrow function inherits this
    return inner();
  }
};

Callback Function Binding

class Loader {
  constructor() {
    this.data = null;
    // Explicit binding is needed
    document.addEventListener("load", this.handleLoad.bind(this));
  }

  handleLoad() {
    this.data = "loaded";
  }
}

Advanced Patterns

Soft Binding

Implementing a default binding that can be overridden:

Function.prototype.softBind = function(obj) {
  const fn = this;
  return function() {
    fn.apply(!this || this === global ? obj : this, arguments);
  };
};

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

const obj1 = { name: "obj1" },
      obj2 = { name: "obj2" };

const bar = foo.softBind(obj1);
bar(); // obj1

obj2.bar = bar;
obj2.bar(); // obj2 (implicit binding takes precedence)

this and Prototype Chain

When methods are called via the prototype chain, they still follow implicit binding rules:

const parent = {
  name: "Parent",
  getName() {
    return this.name;
  }
};

const child = Object.create(parent);
child.name = "Child";

console.log(child.getName()); // Output: Child

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.