【问题标题】:JavaScript: Binding the context of object's methods to parentJavaScript:将对象方法的上下文绑定到父级
【发布时间】:2020-01-23 18:05:27
【问题描述】:

此示例按预期工作:

class A {
  constructor(name) {
     this.name = name
  }
}

A.prototype.someMethod1 = function() { console.log("1: " + this.name) }
A.prototype.someMethod2 = function() { console.log("2: " + this.name) }

let a = new A("John")
a.someMethod1()
a.someMethod2()

有时我们需要将方法按其含义分组到子对象中,将其称为:a.some.method1()、a.some.method2()。让我们试着写:

class A {
  constructor(name) {
     this.name = name
  }
}

A.prototype.some = {
  method1: function() { console.log("1: " + this.name) },
  method2: function() { console.log("2: " + this.name) }
}

现在我们可以调用 a.some.method1() 和 a.some.method2(),但是其中的“this”指向“a.some”,而不是根据需要指向“a”。因此,这些方法中的“this.name”现在将是“未定义的”。

那么我可以将方法的上下文绑定到主对象吗?

目前我只知道一种方法可以做到这一点。在构造函数中是这样的:

class A {
  constructor(name) {
    this._name = name

    // bind context to this
    Object.keys(this.name).forEach(n => {
      this.name[n] = this.name[n].bind(this)
    })
  }
}

但是还有更优雅的方法吗?

注意:应在申报时指定。不接受修改 a.some.method1().call(a) 之类的方法调用。

【问题讨论】:

  • “有时我们需要将方法按其含义分组到子对象中” - 这背后的原因是什么?它们不都只是实例的方法吗?为什么需要这种分组?
  • 例如,如果我有一种扩展某个类的插件,我不想让它们乱扔对象范围。我更喜欢一种方法,当每个插件都将其方法分开以插件名称命名的范围键时。我认为这不是唯一需要这样做的情况。

标签: javascript object methods binding this


【解决方案1】:

您可以将some 设为一个函数,该函数返回一个对象,您可以在其中指定要拥有的方法,然后您可以从该对象调用这些方法。

class A {
  constructor(name) {
    this.name = name
  }
}


A.prototype.someMethod1 = function() {
  console.log("1: " + this.name)
}
A.prototype.someMethod2 = function() {
  console.log("2: " + this.name)
}

A.prototype.some = function() {
  return {
    someMethod1: this.someMethod1.bind(this),
    someMethod2: this.someMethod2.bind(this)
  }
}

let a = new A("John")
a.someMethod1()
a.someMethod2()
a.some().someMethod1()
a.some().someMethod2()

同样的逻辑可以用没有原型的类方法来实现。您也可以省略使用箭头函数作为类属性的绑定,但在这种情况下您应该关心支持。

class A {
  constructor(name) {
    this.name = name
  }

  some1 = () => `1. ${this.name}`
  some2 = () => `2. ${this.name}`

  some = () => {
    return {
      some1: this.some1,
      some2: this.some2
    }
  }
}

let a = new A("John")
console.log(a.some1())
console.log(a.some2())
console.log(a.some().some1())
console.log(a.some().some2())

您还可以对some 使用getter,这样您就可以将其称为属性而不是方法。

class A {
  constructor(name) {
    this.name = name
  }

  some1 = () => `1. ${this.name}`
  some2 = () => `2. ${this.name}`

  get some() {
    return {
      some1: this.some1,
      some2: this.some2
    }
  }
}

let a = new A("John")
console.log(a.some1())
console.log(a.some2())
console.log(a.some.some1())
console.log(a.some.some2())

【讨论】:

  • 在这种情况下,我们没有解决主要目标 - 保持主要对象的范围干净。
  • @WindBridges 您可以为此专门创建另一个类,该类继承自第一个类jsfiddle.net/s3zy47mh/1
【解决方案2】:

是的,您可以定义类的实例,然后通过将其绑定到 a 在原型上添加方法

class A {
  constructor(name) {
     this.name = name
  }
}

let a = new A("Hello")

A.prototype.some = {
  method1: function() { console.log("1: " + this.name) }.bind(a),
  method2: function() { console.log("2: " + this.name) }.bind(a)
}

a.some.method1()

【讨论】:

  • 不,这是不可接受的。我写道,它必须在声明中。客户端代码不应该知道实现细节。
【解决方案3】:

这可能根本不是您想要的,但您可以考虑这样做:

class A {
    constructor(name) {
        this.name = name;
        this.some = new Some(this);        
    }
}

class Some {
    constructor(a) {
        this.a = a;
    }

    method1() { console.log("1: " + this.a.name) }
    method2() { console.log("2: " + this.a.name) }
}

var x = new A('x name');
x.some.method1();
x.some.method2();

【讨论】:

  • 是的,这不是我要找的。​​span>
猜你喜欢
  • 2013-05-03
  • 2013-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-19
  • 1970-01-01
  • 2014-08-04
  • 2017-03-15
相关资源
最近更新 更多