阿里云主机折上折
  • 微信号
Current Site:Index > Function Binding mode's handling of this

Function Binding mode's handling of this

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

Function Binding Patterns and this Handling

In JavaScript, the this keyword points to different objects depending on the execution context. Function binding patterns explicitly control the value of this to address issues of lost execution context. Common binding techniques include call(), apply(), bind(), and arrow functions, each with its own advantages in different scenarios.

Default Binding and Implicit Loss

When a function is called independently, the default binding rule causes this to point to the global object (in non-strict mode) or undefined (in strict mode). Implicit binding occurs during method calls:

const obj = {
  name: 'Object',
  showThis() {
    console.log(this.name)
  }
}

const outerShow = obj.showThis
outerShow() // Outputs undefined (window.name in non-strict mode)

Here, when the method is assigned to a variable and called, the original implicit binding is lost. Function binding patterns are designed to solve such problems.

Explicit Binding Methods

Immediate Invocation with call and apply

call() and apply() execute the function immediately and temporarily bind this, differing only in how arguments are passed:

function introduce(lang, tool) {
  console.log(`${this.name} uses ${lang} with ${tool}`)
}

const dev = { name: 'Alice' }
introduce.call(dev, 'JavaScript', 'VS Code')  // Arguments passed individually
introduce.apply(dev, ['TypeScript', 'WebStorm']) // Arguments passed as an array

Permanent Binding with bind

bind() creates a new function with this permanently bound and supports argument currying:

const boundFn = introduce.bind(dev, 'Python')
boundFn('PyCharm')  // Outputs "Alice uses Python with PyCharm"

A typical application is binding event handlers:

class ToggleButton {
  constructor() {
    this.state = false
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick() {
    this.state = !this.state
    console.log(this.state)
  }
}

const btn = new ToggleButton()
document.querySelector('button').addEventListener('click', btn.handleClick)

Lexical Binding with Arrow Functions

Arrow functions determine this through lexical scope, and the binding cannot be modified:

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

Compare this with regular functions:

start() {
  setInterval(function() {
    // Here, `this` points to window/undefined
    console.log(this) 
  }, 1000)
}

Binding Priority Rules

When multiple binding methods coexist, the priority from highest to lowest is:

  1. new binding (constructor)
  2. Explicit binding (call/apply/bind)
  3. Implicit binding (method call)
  4. Default binding
function showThis() {
  console.log(this.name)
}

const obj1 = { name: 'obj1', showThis }
const obj2 = { name: 'obj2', showThis }

obj1.showThis.call(obj2)  // Outputs "obj2" (explicit > implicit)
new obj1.showThis()       // Outputs undefined (new binding)

Binding Techniques in Higher-Order Functions

In functional programming, binding is often used to preserve context:

const utils = {
  prefix: 'Result:',
  process(items, mapper) {
    return items.map(mapper.bind(this))
  }
}

const output = utils.process([1, 2, 3], function(item) {
  return `${this.prefix} ${item * 2}`
})
// Outputs ["Result: 2", "Result: 4", "Result: 6"]

Performance Considerations with Binding

Frequent use of bind() creates multiple function instances, so alternative approaches should be considered in hot code paths. For example, in React components, it is recommended to choose between class property arrow functions or constructor binding:

// Option 1: One-time binding in constructor
class Component1 {
  constructor() {
    this.handleEvent = this.handleEvent.bind(this)
  }
}

// Option 2: Class property arrow function
class Component2 {
  handleEvent = () => {
    // Automatically binds to the instance
  }
}

Handling Special Edge Cases

When both preserving this and accessing the original function are needed, a wrapper can be created:

function softBind(fn, context) {
  return function() {
    return fn.apply((this === global ? context : this), arguments)
  }
}

const bound = softBind(obj.method, obj)
bound.call(otherObj) // Uses otherObj if it exists

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

如果侵犯了你的权益请来信告知我们删除。邮箱: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 ☕.