在javascript超级调用中突变了'this',如何处理?

mutated 'this' in javascript super call, how to deal with?

抱歉,如果难以解释。 假设 class Y 扩展了 class Z,并且 class X 扩展了 class Y

问题是,如果 class 没有方法,它会调用它的超级 class,到目前为止还不错。

X.prototype.v = function{return this.getV();} //getV() doesn't exist in class X

因为 class X extends class Y 并且 getV() 存在于 class Y 中,调用在这里:

Y.prototype.getV = function{return this.parent().getV()+1} 

工作函数 parent() return 是其超 class 的一个实例。假设 z 也有一个方法 getV,它 return 是一个真正的 int 值。

Z.prototype.getV = function{return 1}

所以classY中的函数getV()是指return从Z中得到的getV值加1,送往最低的classX。

最有线的部分在这里。只要从 X.v() 调用方法 getV,y.getV() 中的 'this' 指的是 X,而不是 Y!

所以 Y 中的函数 getV() 变为 X.parent().getV()+1,我得到 'maximum call stack size exceeded'

解决这个问题的一个愚蠢但非常有效的方法是写

this.parent().parent().getV()+1

双亲使发送者Z不是y,那么在调用X.getV()

时returns 2

这很愚蠢,因为如果调用者是 Y 本身,比如 Y.getV(),我认为 'this' 在这里正确地表示 Y,然后有太多的 parent() 调用,导致它未定义。

一些想法,比如我们可以摆脱 'this',并使用另一种方式获得电流 class。不太理想的方法可能是跟踪所有 classes 的所有函数,并设置正确数量的 parent()。我相信还有更多。但是,其中 none 个已经过测试。

取自上述代码片段的最小代码示例可以是:

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());

this指的是调用函数的上下文,或者更实际的说法:它指的是调用中出现在点之前的对象。当您调用 abc.def() 时,def 中的 this 将与 abc.

相同

所以最简单的可能是改变这个:

X.prototype.v = function{return this.getV();}

至:

X.prototype.v = function{return this.parent().getV();}

这使得 Y 的 this 成为上面的 this.parent()。这样您就不必更改原始调用。

但是如果你想保持定义X.prototype.getV的灵活性,那么最好立即定义它,而不是上面的改变,这样做:

X.prototype.v = function{return this.getV();}
X.prototype.getV = function{return this.parent().getV();}

备选方案:使用 apply/call

您可以使用 apply 方法(或 call 的另一种形式)传递对目标函数应使用的内容的显式引用 this

X.prototype.v = function{return this.getV.apply(this.parent(), arguments);}

测试原型是否定义了自己的方法:

你可以使用 hasOwnProperty:

X.prototype.v = function{
    var nextThis = X.prototype.hasOwnProperty('getV') ? this : this.parent();
    return this.getV.apply(nextThis, arguments);
}

`

如果你使用 ES6 class 语法,你应该使用 super:

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 中,我会使用类似

的东西
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