mutated 'this' in javascript super call, how to deal with?
很抱歉,如果很难解释的话。假设类Y扩展类Z,类X扩展类Y
问题是,如果一个类没有一个方法,它称之为超级类,目前为止还不错。
由于类X扩展了类Y,并且类Y中存在getv(),因此调用如下:
1 | Y.prototype.getV = function{return this.parent().getV()+1} |
工作函数parent()返回其超级类的一个实例。假设z还有一个getv方法,它返回一个真正的int值。
1 | Z.prototype.getV = function{return 1} |
所以类y中的getv()函数意味着返回z加1的getv值,并将其发送到最低的类x。
大部分有线部分在这里。只要从x.v()调用getv方法,y.getv()中的"this"就指向x,而不是y!
所以y中的getv()函数变成x.parent().getv()+1,我得到"超过了最大调用堆栈大小"。
解决这个问题的一个愚蠢但非常有效的方法是写
1 | this.parent().parent().getV()+1 |
双亲使发送者Z不是Y,然后在调用x.getv()时返回2
这很愚蠢,因为如果调用者是y本身,就像y.getv(),我认为"this"在这里正确地表示y,那么有太多的parent()调用,导致它未定义。
有些想法,比如我们可以摆脱"这个",用另一种方法获得当前类。不太理想的方法可能是跟踪所有类的所有函数,并设置正确数量的parent()。我相信还有更多。然而,他们都没有经过测试。
从上面的代码片段中选取的最小代码示例可以是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | class Z{ } class Y extends Z{ } class X extends Y{ } X.prototype.v = function{ return this.getV(); } Y.prototype.getV = function{ return this.parent().getV()+1; } Z.prototype.getV = function{ return 1; } var x = new X(); console.log(x.v()); |
如果使用ES6类语法,则应使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Z { getV() { return 1; } } class Y extends Z { getV() { return super.getV() + 1; } } class X extends Y { v() { return super.getV(); // or this.getV(); } } new Z().getV(); // 1 new Y().getV(); // 2 new X().getV(); // 2 new X().v(); // 2 |
在ES5中,我会使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function extend(f1, f2) { f1.prototype = Object.create(f2.prototype); f1.prototype.constructor = f1; f1.super = f2.prototype; } function Z(){} Z.prototype.getV = function() { return 1; }; function Y(){} extend(Y, Z); Y.prototype.getV = function() { return Y.super.getV.call(this) + 1; }; function X(){} extend(X, Y); X.prototype.v = Y.prototype.getV; new Z().getV(); // 1 new Y().getV(); // 2 new X().getV(); // 2 new X().v(); // 2 |
所以最简单的可能是改变这一点:
1 | X.prototype.v = function{return this.getV();} |
到:
1 | X.prototype.v = function{return this.parent().getV();} |
这使得y的
但是,如果您希望保持定义x.prototype.getv的灵活性,那么最好立即定义它,而不是进行上述更改:
1 2 | X.prototype.v = function{return this.getV();} X.prototype.getV = function{return this.parent().getV();} |
备选方案:使用Apply/Call
您可以使用
1 | X.prototype.v = function{return this.getV.apply(this.parent(), arguments);} |
测试原型是否定义了自己的方法:
您可以使用
1 2 3 4 | X.prototype.v = function{ var nextThis = X.prototype.hasOwnProperty('getV') ? this : this.parent(); return this.getV.apply(nextThis, arguments); } |
`