【问题标题】:Typescript overridden class method and this打字稿覆盖的类方法和这个
【发布时间】:2016-04-28 23:15:14
【问题描述】:

我有这两个 Typescript 类:

class Base {
  value: string;

  lambdaExample = () => {
    this.value = 'one';
  }

  methodExample() {
    this.value = 'two';
  }
}

class Child extends Base {
  lambdaExample = () => {
    super.lambdaExample(); // Error, because I've overwritten (instead of overridden) the method
    this.value = 'three'; // ok
  }

  methodExample() => {
    super.methodExample(); // ok
    this.value = 'four'; // Error: this refers to window, not to the actual this
  }
}

如何编写我的方法,使this 引用可靠,并且我可以覆盖方法并从父类调用它们?

【问题讨论】:

标签: typescript


【解决方案1】:

实际上在Microsoft's Git Wiki 上可以很好地了解解决此问题的不同方法。它基本上归结为:

  • 如果您关心继承,则每次从不同的上下文调用方法时都绑定或包装该方法。
  • 如果不这样做,请将方法转换为包含函数的属性。

在实际的 Wiki 中有更多的规定,我真的建议你通读一遍。

编辑

包装示例:

给定班级

class SomeClass {
    public someProp: string = "Hello World";

    public someMethod() {
        console.log(this.someProp);
    }

}

如果您要从(例如)点击处理程序 - someEl.onclick = instanceOfMyClass.someMethod; 调用 someMethod - 将引发异常(假设 window 没有属性 someProp )。

您可以通过将函数绑定到 instanceOfMyClass(不是类型安全,不兼容 ES6)或手动包装它(无论如何,实际上是 bind 所做的)来规避此问题:

someEl.onclick = function() {
    someInstanceOfMyClass.someMethod();
};

这有点冗长和迂腐,但通过调用 someMethod 作为 someInstanceOfMyClass 的属性而不将其传递给事件处理程序(将其转换为 window 的属性),您可以确保 this始终是MyClass 的实例。

【讨论】:

  • 我对包装方法有点不清楚。像这样的东西会起作用吗? class Child { methodExample() { var wrap = () => { super.methodExample() }; wrap(); }
  • 所以包装类似于绑定,因为每次从不同的上下文调用函数时都会这样做。例如,给定类class MyClass { public someMethod() { console.log(this); } } 以使this 成为来自不同上下文的MyClass 的实例,您可以这样称呼它:(() => { instanceOfMyClass.someMethod() })()。重要的是我们将该方法称为instanceOfMyClass 的属性,而不是直接传递它(从而改变它的上下文)。
【解决方案2】:

您对错误原因的假设是错误的,我认为这是您的问题的原因......至少据我了解。

lambdaExample = () => {
  this.value = 'one';
}

例如,这一行定义了一个属性,而不是Base 上的方法,并且您不能覆盖属性。您在Base 中定义的唯一实例方法是methodExample

Child 中,您将一个新变量分配给lambaExample。您对super.lambaExample() 的调用失败,因为它只能通过super() 访问方法;访问属性是通过this 完成的。 methodExample 在您的 Child 类中对我来说显示为语法错误。

请注意,您仍然可以在覆盖的 lambaExample 属性中从 Child 调用 super,但只能在方法上。这有效:

lambdaExample = () => {
  super.methodExample(); // Success on super.<somemethod>()
  this.value = 'three';
}

我只知道在类中声明实例方法的一种方法,如果您与该语法一致,this 将按您的预期工作:

class Base {
  value: string;

  lambdaExample() {
    this.value = 'one';
  }

  methodExample() {
    this.value = 'two';
  }
}

class Child extends Base {
  lambdaExample() {
    super.lambdaExample();
    this.value = 'three';
  }

  methodExample() {
    super.methodExample();
    this.value = 'four';
  }
}

【讨论】:

  • 我可以关注您的整个帖子,直到最后一句:this 在方法示例中无法按预期工作,如问题中所述。 super 调用在这种情况下有效,但 this.value 引用是 undefined
  • @Jorn - 在您将其设置为一个值之前,它将始终未定义,该值仅在调用 lambdaExamplemethodExample 时执行。
【解决方案3】:

this 的作用域问题可以通过一个非常简单的class 装饰器来解决,并且您不再需要对方法使用丑陋的*箭头函数语法——或者再次考虑作用域问题:

function BindMethods(target: any): any {
    var originalCtor = target;
    var newCtor: any = function (...args) {
        var c: any = function () {
            // Methods are defined on prototype.
            var prototype = Object.getPrototypeOf(this);

            // Bind methods.
            Object.keys(prototype).forEach(propertyKey => {
                if (typeof this[propertyKey] === "function") {
                    prototype[propertyKey] = this[propertyKey].bind(this);
                }
            });

            // Invoke original constructor.
            return originalCtor.apply(this, args);
        }
        c.prototype = originalCtor.prototype;
        return new c();
    }

    // Copy prototype so 'intanceof' still works.
    newCtor.prototype = originalCtor.prototype;

    // Overrides original constructor.
    return newCtor;
}

使用它就像在一个类上捕捉它一样简单(methodExample 方法被修改仅用于演示目的):

@BindMethods
class Base {
    value: string;

    methodExample() {
        console.log(this.value);
    }
}

这将确保对this 的引用始终正确(即使是继承):

var base = new Base();
base.value = "two";
base.methodExample(); // "two"
setTimeout(base.methodExample, 20); // "two"

不幸的是,没有办法使用方法装饰器一个一个地绑定方法,因为需要实例引用。如果这是一个要求,您可以使用 decorator factory 来传递方法的属性键以进行绑定。


*用于方法时很丑

【讨论】:

    【解决方案4】:

    我找到了一种解决方法,但它很难看:

    class Base {
      someFunction = () => {
        this.otherFunction();
      }
      protected otherFunction = () => {
        // actual implementation here
      }
    }
    class Child {
      someFunction = () => {
        this.otherFunction();
        // additional implementation here
      }
    }
    

    这样,您可以在任何实例上调用someFunction,并且仍然使用otherFunction 访问原始实现。

    【讨论】:

    • 为什么投反对票?我已经说过它很丑,但到目前为止它也是唯一可行的解​​决方案......
    • 有些堆垛机对回答您自己的问题非常挑剔。我认为这是一个很好的自我回答,但有人可能会反对。
    • 好吧,不是我,尽管我相信我找到了一个更好的alternative 可以产生干净的代码。
    猜你喜欢
    • 2020-12-22
    • 2020-05-26
    • 1970-01-01
    • 2019-06-20
    • 2013-06-07
    • 1970-01-01
    • 2021-06-09
    • 2019-01-15
    • 1970-01-01
    相关资源
    最近更新 更多