2

我有一个令我困惑的问题。我有一个如下定义的 Durandal 视图模型,但是this当我逐步执行代码时,它的值和其他变量和函数会在各处进行更改和更改。

我已经在下面评论了,因为它更容易在代码中移动时显示各种值。

define(['durandal/plugins/router'], function (router) {

    var _outerRouter = router;
    var _outerSelf = this;

    var viewModel = function () {

        var _self = this;
        var _router = router;
        var _searchCriteria = ko.observable('');
        var _searchResults = ko.observableArray([]);
        var _messages = ko.observableArray([]);

        this.searchCriteria = _searchCriteria;
        this.searchResults = _searchResults;
        this.messages = _messages;

        this.search = function () {

            //
            // When called from the view bindings:
            //  > this = viewModel
            //  > _self = _outerSelf = _outerRouter = undefined
            //  > _router = defined
            //
            var that = this;

            //
            // N.B. I set callback context to the viewModel
            //
            var req = $.ajax({
                url: '../api/v1/things',
                dataType: 'json',
                context: this,  
                data: {
                    searchCriteria: this.searchCriteria()
                }
            })
            .done(function (results, status, request) {

                //
                // When called back:
                //  > this = viewModel 
                //  > _self = _outerSelf = _outerRouter = undefined
                //  > _router = defined
                //
                this.searchResults.removeAll();
                this.messages.removeAll();

                //
                // Now when calling:
                //  > doShowResult = showResult = undefined
                //  > this.showResult = defined (have to use 'this')
                //  > displayMultipleResults = defined
                //
                if (results.length == 1) {                    
                    this.showResult(results[0]);
                }
                else {
                    displayMultipleResults(results);
                }
            });

            function displayMultipleResults(results) {

                //
                // When called from ajax.done():
                //  > this = Window ?!?!
                //  > that = viewModel
                //  > _self = _outerSelf = _outerRouter = undefined
                //  > _router = defined
                //
                that.messages.push({ title: 'Found Lots of Things', body: "Please select...blah blah blah", type: 'info' });

                for (var i = 0; i < results.length; i++)
                    that.searchResults.push(results[i]);
            };
        };

        function doShowResult(result) {

            //
            // When called from ajax.done():
            //  > this = viewModel
            //  > _self = _outerSelf = _outerRouter = undefined
            //  > _router = defined
            //
            // and then
            //
            // When called from the view bindings:
            //  > this = the bound searchResult object
            //  > _self = _outerSelf = _outerRouter = undefined
            //  > _router = defined
            //
            _router.navigateTo('show-my-thing');
        }

        this.showResult = doShowResult;
    };

    return viewModel;
});

这是它必然的观点:

<div>
    <div class="container-narrow">    
        <div class="row-fluid">
            <div class="span12">
                <h3>Search</h3>

                <p>Enter search criteria...</p>

                <div class="control-group">
                    <input type="text" class="input-block-level" placeholder="Criteria" data-bind="value: searchCriteria"/>
                </div>

                <div class="pull-right">
                    <button class="btn btn-primary" data-bind="click: search">
                        <i class="icon-search icon-white"></i> Search
                    </button>
                </div>

            </div>
        </div>
        <br />

        <!-- ko foreach: messages -->
        <div class="row-fluid">
            <div class="alert" data-bind="css: { 'alert-error': type == 'error', 'alert-info': type == 'info', 'alert-success': type == 'success' }">
                <strong data-bind="text: title"></strong> <span data-bind="text: body"></span>
            </div>
        </div>
        <!-- /ko -->

        <!-- ko foreach: searchResults -->
        <div class="row-fluid">
            <div class="span12 well well-small">                
                <div class="span10 search-result">
                    <label>Resut:</label> <span data-bind="{text: $data}"></span><br />
                </div>
                <div class="span2">                    
                    <button class="btn btn-mini btn-success pull-right" data-bind="click: $parent.showResult">
                        View
                    </button>
                </div>
            </div>        
        </div>    
        <!-- /ko -->

    </div>
</div>

我的主要问题是:

  1. 为什么我可以在任何地方访问_router但不能访问_self(或任何其他变量(_searchCriteria 等)?

  2. 当执行在内部ajax.done()并且this等于 viewModel 的值但在进入 displaySearchResult 之后this等于 Window 对象时,如何?

  3. 当在里面ajax.done() doShowResult并且showResultundefined但工作正常时this.showResult,肯定是否this定义了?viewModelshowResult

  4. 幸运的是,在这种情况下,我只需要导航doShowResult_router在我从视图绑定和视图绑定调用时定义ajax.done。但是,如果我需要从视图模型中访问一个值——如果从视图绑定中调用,这将不可用——我如何更改绑定或代码以支持这一点(最好以非 hacky 方式)?

预先感谢任何人都可以对此有所了解。

4

1 回答 1

4

'this' 关键字在 JavaScript 中的行为与其他语言非常不同,可见范围的概念也是如此。

在任何给定时间,“this”的值取决于您从中访问它的函数的调用上下文。调用方法有四种不同的方法,每种方法对“this”都有不同的含义。

  1. 作为函数调用(调用命名函数):this = window(即全局对象)。

    function fName() { };
    fName(); // this = window
    
  2. 作为方法调用(调用对象的属性):this = 对象

    var o = {};
    o.whatever = function() {};
    o.whatever(); // this = o
    
  3. 作为构造函数调用(使用 new 关键字):this = 新对象

    var Animal = function() { }; // best practice to capitalise the initial letter of function intended to be called as constructor.
    var cat = new Animal(); // this = (what will become cat)
    
  4. 使用 apply() 和 call() 方法调用:(this = 第一个参数)

    function fName() { };
    var obj1 = {};
    fName.apply(obj1); // this = obj1
    fName.call(obj1); // this = obj1
    
于 2013-04-22T11:54:58.330 回答