【问题标题】:Should we add methods only to the prototypes of our objects?我们应该只向对象的原型添加方法吗?
【发布时间】:2020-10-22 22:05:26
【问题描述】:

我目前正在学习 Javascript,发现我们可以在对象的原型中而不是在对象的构造函数中定义对象的成员,因为它使对象更轻,因为它们不会在每个实例中都携带方法代码。

如果这个构造函数的所有对象都指向同一个变量字段,我可以看到变量如何工作不好,但是方法不应该改变太多或根本不应该改变,所以共享它们不是问题。

是否有理由不在原型上而不是在对象的构造函数上定义方法?

【问题讨论】:

    标签: javascript


    【解决方案1】:

    人们可能更喜欢将方法放在实例本身上的一个原因是在简洁地添加方法的同时确保正确的this 上下文。让this 正常工作是 JavaScript 中非常常见的问题。

    例如:

    class Foo {
      i = 0;
      clickHandler = () => console.log(this.i++);
    }
    
    const f = new Foo();
    window.onclick = f.clickHandler;

    这是 React 中类组件的常见模式。如果将方法放在原型上,有时会变得更丑:

    class Foo {
      i = 0;
      clickHandler() {
        console.log(this.i++);
      }
    }
    
    const f = new Foo();
    window.onclick = () => f.clickHandler();

    另一种常见的处理方法是将绑定方法从原型移动到构造函数中的实例:

    class Foo {
      i = 0;
      constructor() {
        this.clickHandler = this.clickHandler.bind(this);
      }
      clickHandler() {
        console.log(this.i++);
      }
    }
    
    const f = new Foo();
    window.onclick = () => f.clickHandler();

    这是更喜欢其中一个的原因吗?这取决于您,但要牢记这是一个有争议的优势。

    【讨论】:

    • 为什么这个绑定问题在 React 中是个问题?您的第二个示例在带有标准事件监听器的 Vanilla JS 中运行良好:window.addEventListener("click", () => f.clickHandler())
    • 当然——但是每次需要调用fn 时都需要一个() => obj.fn() (并且当需要传递其他参数时会变得更丑陋)。改用类字段(顺便将方法放在实例本身上)只是众多可用选项之一。
    【解决方案2】:

    只是我的一个想法 - 有一种方法可以处理“正确”的执行上下文,例如单击原型级别的处理程序也是如此。考虑以下示例:

    class AbstractClickHandler {
        
      // the "proper" this is ensured via closure over the actual implementation
      clickHandler() {
        return (...args) => {
          this.handleClick.apply(this, args)
        }
      }
    
      // no-op in base class
      handleClick() {   
      }
    }
    
    class ClickHandler extends AbstractClickHandler {
    
      constructor(data) {
        super()
        this.data = data
      }
    
      // actual implementation of the handler logic
      handleClick(arg1, arg2) {
        console.log(`Click handled with data ${this.data} and arguments ${arg1}, ${arg2}`)    
      }
    }
    
    const instance = new ClickHandler(42)
    const handler = instance.clickHandler()
    handler.call(null, 'one', 'two')
    

    这样,就不需要在每个相关的构造函数中定义绑定处理程序了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-06
      • 1970-01-01
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多