为什么这个不是这个

Why *this* is not *this*?

本文关键字:为什么      更新时间:2023-09-26

我刚刚写了这段代码来表示这个正在杀死我的错误(Grrr!(

我想知道为什么当我得到错误:方法未定义时,我在Safari中检查了parserIdStart((方法中的这个变量不是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> 

这是Javascript中一个经常被误解的方面。(我所说的"这个"是指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。最初在哪里定义add并不重要,无论它是在构造函数内部,还是特定对象原型的一部分,或者其他什么。如果你用这种方式调用一个函数,this就会被设置为你调用它的任何对象。这就是你正在运行的规则。

调用/应用调用

 add.call(someobject,a,b);

在call/apply调用中,this被设置为传递给调用方法的现在可见的第一个参数的任何值。

代码中发生的情况是:

 this.parser.didStartCallback = this.parserDidStart;

当你编写parserIdStart时,期望它的this在方法调用它时会是一个EpicodeController…实际上,你现在正在将其this从EpicodeController更改为this.parser。这在特定的代码行中不会发生。直到这里,转换才真正发生:

this.didStartCallback(this);

其中,本例中的this是EpicodeParser,在运行此代码时,您已经将parserIdStart命名为didStartCallback。当你在这里用这个代码调用didStartCallback时,你本质上是在说。。。

didStartCallback.call(this,this(;

通过这样说.ddStartCallback((,您将其this设置为..嗯。。当你叫它this时。

您应该知道一个名为bind的函数,这里对此进行了解释:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

Bind从现有函数创建一个新函数,该函数的this被固定(绑定(到您显式传递的任何对象。

中传递给didStartCallbackthis

EpisodeFeedParser.prototype.parse = function(doc){
    this.didStartCallback(this);

类型为EpisodeFeedParser

EpisodeController.prototype.fetchEpisodes中影响EpisodeController.parserDidStartparser.didStartCallback:

parser.didStartCallback = this.parserDidStart;

所以this.didStartCallback(this);实际上是EpisodeController.parserDidStart(this)

并且我们在开始时看到最后一个CCD_ 23是CCD_。

Q.E.D.

尝试:

var that = this;
parser.didStartCallback = function(parser) {
  that.parserDidStart(parser);
};

这将创建一个闭包,该闭包在正确的范围内传递给parserDidStart。目前,当您调用this.parser.parse()时,它会将EpisodeFeedParser作为上下文传递,因为这是它的调用来源。这是JavaScript中作用域的一个怪癖,可能非常令人沮丧。

问题是"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> 
EpisodeFeedParser.prototype.parse = function(doc){
    this.didStartCallback(this);
};

我不明白你为什么期望thisEpisodeFeedParser以外的任何东西。