在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
抱歉,如果难以解释。 假设 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