我刚刚写了这段代码来代表这个让我失望的错误(Grrr!)
我想知道为什么当我得到错误:方法未定义我已经在Safari中检查过并且parserDidStart()方法中的这个变量不属于EpisodeController类型,它的类型为EpisodeFeedParser,为什么会这样?
<html>
<head>
<script type="text/javascript">
var EpisodeFeedParser = function(url){
this.url = url;
this.didStartCallback = null;
};
EpisodeFeedParser.prototype.parse = function(doc){
this.didStartCallback(this);
};
var EpisodeController = function(){
this.episodes = new Array();
this.parser = null; //lazy
};
EpisodeController.prototype.parserDidStart = function(parser){
console.log("here *this* is not of type EpisodeController but it is EpisodeFeedParser Why?");
this.testEpi(); //**********ERROR HERE!***********
};
EpisodeController.prototype.fetchEpisodes = function(urlString){
if(urlString !== undefined){
if(parser === undefined){
var parser = new EpisodeFeedParser(urlString);
parser.didStartCallback = this.parserDidStart;
this.parser = parser;
}
this.parser.parse();
}
};
EpisodeController.prototype.testEpi = function(){
console.log("it worked!");
};
function testEpisode(){
var controller = new EpisodeController();
controller.fetchEpisodes("myurl");
}
</script>
</head>
<body>
<button type="button" onclick="testEpisode()">press me</button>
</body>
</html>
答案 0 :(得分:9)
这是Javascript经常被误解的方面。 (以及“this”,我的意思是this
)
您可以将this
视为另一个无形地传递给您的函数的参数。所以当你写一个像
function add (a,b) {
return a+b;
}
你真的在写
function add(this, a, b) {
return a+b;
}
这可能是显而易见的,不明显的是传入的,并命名为“this”。其规则如下。有四种方法可以调用一个函数,它们各自绑定一个不同的东西到this
。
add(a,b);
在经典函数调用中,this
绑定到全局对象。该规则现在普遍被视为一个错误,并且可能在将来的版本中设置为null。
new add(a,b);
在构造函数调用中,this
设置为一个新的新对象,其内部(和不可访问的)原型指针设置为add.prototype
someobject.add(a,b);
在方法调用中,this
设置为someobject。无论你最初定义添加的位置,它是在构造函数内部,特定对象的原型的一部分,还是其他什么都没关系。如果以这种方式调用函数,this
将设置为您调用它的任何对象。这是你遇到的规则。
add.call(someobject,a,b);
在调用/应用调用中,this
设置为传入调用方法现在可见的第一个参数的任何内容。
你的代码中会发生什么:
this.parser.didStartCallback = this.parserDidStart;
当你编写parserDidStart时,期望当你的方法调用它时它的this
将是一个EpisodeController ......实际上你正在将它的this
从EpisodeController更改为此。解析器。 特定代码行中没有发生这种情况。直到此处才开始实际切换:
this.didStartCallback(this);
此实例中的this
是EpisodeParser,并且在运行此代码时,您已将parserDidStart命名为didStartCallback。当你在这里调用didStartCallback时,使用这段代码,你实际上是在说......
didStartCallback.call(这一点,这一点);
通过说这个.didStartCallback(),当你调用它时,你将它的this
设置为...... this
。
你应该知道一个名为bind的函数,这里解释一下: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
Bind从现有函数创建一个新函数,其this
被固定(绑定)到您明确传入的任何对象。
答案 1 :(得分:2)
this
传递给
didStartCallback
EpisodeFeedParser.prototype.parse = function(doc){
this.didStartCallback(this);
的类型为EpisodeFeedParser
并在EpisodeController.prototype.fetchEpisodes
中EpisodeController.parserDidStart
影响parser.didStartCallback
:
parser.didStartCallback = this.parserDidStart;
所以this.didStartCallback(this);
实际上是EpisodeController.parserDidStart(this)
我们在开头看到最后一个this
的类型为EpisodeFeedParser
。
Q.E.D
答案 2 :(得分:2)
尝试:
var that = this;
parser.didStartCallback = function(parser) {
that.parserDidStart(parser);
};
这将创建一个在正确范围内传递给parserDidStart
的闭包。目前,当您致电this.parser.parse()
时,它会将EpisodeFeedParser
作为上下文传递,就像调用它一样。这是JavaScript中范围的怪癖之一,可能非常令人沮丧。
答案 3 :(得分:1)
问题是“didStartCallBack”被调用(并且在“this”的上下文中),在执行时“this”指的是EpisodeFeedParser。我已经使用.call()修复了它,虽然我不确定为什么你需要在这个回合中编写代码,但我确信必定有原因。
重要变化:
parse: function(episodeController){
this.didStartCallback.call(episodeController, this);
}//parse
完整代码:
<html>
<head>
<script type="text/javascript">
//An interesting context problem...
//Why is it of type EpisodeFeedParser?
// ---- EpisodeFeedParser
var EpisodeFeedParser = function(url){
this.url = url;
};
EpisodeFeedParser.prototype = {
url:null,
didStartCallback:null,
parse: function(episodeController){
this.didStartCallback.call(episodeController, this);
}//parse
}//prototype
// ---- EpisodeController
var EpisodeController = function(){
this.episodes = new Array();
this.parser = null; //lazy
};
EpisodeController.prototype = {
parserDidStart: function(parser){
console.log("here *this* is not of type EpisodeController but it is EpisodeFeedParser Why?");
debugger;
this.testEpi(); //**********ERROR HERE!***********
},
fetchEpisodes: function(urlString){
if(urlString !== undefined){
if(this.parser === null){
this.parser = new EpisodeFeedParser(urlString);
this.parser.didStartCallback = this.parserDidStart;
}//if
this.parser.parse(this);
}//if
},//fetchEpisodes
testEpi: function(){
console.log("it worked!");
}
}//EpisodeController.prototype
// ---- Global Stuff
function testEpisode(){
var controller = new EpisodeController();
controller.fetchEpisodes("myurl");
}
</script>
</head>
<body>
<button type="button" onclick="testEpisode()">press me</button>
</body>
</html>
答案 4 :(得分:0)
EpisodeFeedParser.prototype.parse = function(doc){
this.didStartCallback(this);
};
我不明白为什么你希望this
不是EpisodeFeedParser
。