如何在JS类构造函数中调用函数

6
我正在创建一个矩阵类(用于React应用),其定义如下:
class Matrix extends Object {
    constructor(r, c) {
        super();
        this.rows = new Array(r);
        for (var i=0; i<r; i++) {
            this.rows[i] = new Array(c);
        }
        this.assign((v,i,j) => (i===j)? 1:0);   // ERROR HERE
    }
    assign(f) {
        for (var i=0; i<this.rows.length; i++) {
            for (var j=0; j<this.rows[i].length; j++) {
                this.rows[i][j] = f(this.rows[i][j], i, j);
            }
        }
    }
    // and so on

这段代码在使用webpack编译时没有问题,但运行时会在chrome控制台上报错,提示_this.assign不是一个函数

我可以通过以下方式解决:

constructor(r, c) {
    super();
    this.rows = new Array(r);
    for (var i=0; i<r; i++) {
        this.rows[i] = new Array(c);
    }
    this.assign = function(f) {
        for (var i=0; i<r; i++) {
            for (var j=0; j<r; j++) {
                this.rows[i][j] = f(this.rows[i][j], i, j);
            }
        }
    };
    this.assign((v,i,j) => (i===j)? 1:0);
}

但这是错误的,对吗?我不应该在构造函数中定义对象的所有函数,对吗?在同一个文件中,我定义了像这样的React类:

class MatrixComponent extends React.Component { ... }

还有一些人能够在不在构造函数中定义函数的情况下调用它们。我错过了什么吗?


我没有使用webpack,但我无法重现你的问题:https://jsfiddle.net/vxpxmaf1/1。虽然我确实收到了一个错误,即“r”在“assign”中未定义(确实如此),因此我将其更改为“this.rows.length”。 - apsillers
@apsillers - 谢谢。这是我将函数移出构造函数作用域时复制/粘贴错误。我猜你的实验向我展示了这与webpack编译此代码有关? - user1272965
2个回答

5

对我来说,删除extends Objectsuper()调用似乎是可行的。

class Matrix { // extends Object {
    constructor(r, c) {
        // super();
        this.assign();
    }
    assign() {
    }
}

每个类已经在其原型链中有Object,因此通过扩展Object并没有获得任何东西。`extends Object`是隐含的,因此请将其删除。
似乎处理`extends Object`是Babel中的一个错误。Chromium浏览器内置的es6引擎可以很好地处理代码,因为它在这里得到了修复:https://bugs.chromium.org/p/v8/issues/detail?id=3886 但是看起来Babel转译的es5存在问题。

Working es6 in Chrome


为了解释这种行为,让我们来看一下由Babel转译的es5。
给定这个es6:
class OtherClass {}

class OtherClassExtender extends OtherClass {
    constructor(r, c) {
        super();
        this.assign();
    }
    assign() {
    }
}

class ObjectExtender extends Object {
    constructor(r, c) {
        super();
        this.assign();
    }
    assign() {
    }
}

new OtherClassExtender(1, 2);
new ObjectExtender(1, 2);

我们只需要一小部分转译后的ES5代码。我已经删除了很多代码,这不是完整的转译源代码:
function _possibleConstructorReturn(self, call) {
  if (!self) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  }
  if (call && (typeof call === "object" || typeof call === "function")) {
    return call;
  } else {
    return self;
  }
}

var OtherClass = function OtherClass() {};

var OtherClassExtender = function (_OtherClass) {    
    function OtherClassExtender(r, c) {
        var _this = _possibleConstructorReturn(this, _OtherClass.call(this));
        _this.assign();
        return _this;
    }
    OtherClassExtender.prototype.assign = function assign() {};
    return OtherClassExtender;
}(OtherClass);

var ObjectExtender = function (_Object) {
    function ObjectExtender(r, c) {
        var _this2 = _possibleConstructorReturn(this, _Object.call(this));
        _this2.assign();
        return _this2;
    }
    ObjectExtender.prototype.assign = function assign() {};
    return ObjectExtender;
}(Object);

new OtherClassExtender(1, 2);
new ObjectExtender(1, 2);

_possibleConstructorReturn基本上就是super,Babel在其中保留了类的超类引用,以便方法调用可以正确地遍历继承层次结构。它基本上是在说“超类有定义方法,所以让我们在那里查找方法(如assign)”。在这个继承层次结构中添加Object本身是没有意义的,因为在Javascript中的任何对象都会从Object中继承方法。

通过逐步执行转译代码,_possibleConstructorReturn(OtherClassExtender, OtherClass)表示typeof callundefined,因此它返回OtherClassExtender

_possibleConstructorReturn(ObjectExtender, Object)表示typeof callobject,因此它返回Object

对象实例没有assign方法。assignObject类的一个类方法,在例如(new Object).assign上不存在。


我也看到了,谢谢。但我仍然更希望能够得到一个解释,以便在未来的问题中可以理解。 - user1272965
我想我在转译和使用 Babel 方面有点超纲了。也许这就解释了为什么我无法设置断点。非常感谢您的帮助! - user1272965

0

经过尝试和错误,我发现将null扩展为Object可以解决这个问题:

class Matrix extends null { ... }

我猜这是一个答案,但我对为什么以及我尝试做法有什么问题感到困惑。如果有更好的答案,真正解释了这些东西的工作原理,我会很高兴地标记它为正确答案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接