为什么在Javascript中这个上下文会根据调用方式而改变?

时间:2013-12-14 14:10:30

标签: javascript

虽然我特意使用KnockoutJS来解决这个问题,但我的问题更像是一般的javascript问题。

然而,很好理解ko.observable()和ko.observableArray()返回一个方法,因此在为它们赋值时,需要将目标作为方法调用,而不是简单地为它们赋值。我正在使用的代码也应该支持普通对象和数组,这就是为什么我需要解析一个方法来调用为目标赋值。

想想这两个例子:

非工作方式(此方法在调用方法中更改):

// Assigning value to the target object
var target;

// target can be of any of thr following types
target = ko.observableArray(); // knockout observable array (function)
// target = ko.observable(); // knockout observable (function)
// target = {}; // generic object
// target = []; // generic array

//#region resolve method to call
var method;

if (isObservable(target)) {
    // if it is a knockout observable array, we need to call the target's push method
    // if it is a konckout observable, we need to call the target as a method
    method = target.push || target;
} else {
    // if target is a generic array, we need to use the array's push prototype
    // if target is a generic object, we need to wrap a function to assign the value
    method = target.push || function(item){ target = item; };
}
//#endregion

// call resolved method
method(entity);

工作一个(这个背景很好):

if (isObservable(target)) {
    if (target.push) {
        target.push(entity);
    } else {
        target(entity);
    };
} else {
    if (target.push) {
        target.push(entity);
    } else {
        target = entity;
    };
}

现在,对于实际问题:

在第一种方法中,稍后在执行链中,当使用knockout observable knockout时,引用其自身内的this上下文,尝试访问observable本身(即如果有人想知道,则为this.t())。在这种特殊情况下,由于callin的方式,this已更改为window对象,而不是指向原始的可观察对象。

在后一种情况下,淘汰赛的this背景正常。

你们中的任何一个javascript大师都可以告诉我,我的调用方式是如何改变被调用函数的“this”上下文的?

好的,我知道有人想要小提琴,所以这里有:)

Method 1 (Uncaught TypeError: Object [object global] has no method 'peek')

Method 2 (Works fine)

P.S。我不是要修复代码,我试图理解为什么我的代码会更改this上下文。


更新

感谢您的快速解答!当我不知道为什么(尤其是如何)发生了什么事情时,我必须说我讨厌它。从你的答案I fiddled up this quick fiddle来重新调整情况,我想我现在得到了它:)

// So having an object like Foo
function Foo() {
    this.dirThis = function () {
        console.dir(this);
    };
};

// Instantiating a new Foo
var foo = new Foo();

// Foo.dirThis() has it's original context
foo.dirThis(); // First log in console (Foo)

// The calling code is in Window context
console.dir(this); // Second log in console (Window)

// Passing a reference to the target function from another context
// changes the target function's context
var anotherFoo = foo.dirThis;

// So, when being called through anotherFoo, 
// Window object gets logged
// instead of Foo's original context
anotherFoo(); // 3rd log


// So, to elaborate, if I create a type AnotherFoo
function AnotherFoo(dirThis){
    this.dirThis = dirThis;
}

// And and instantiate it
var newFoo = new AnotherFoo(foo.dirThis);

newFoo.dirThis(); // Should dir AnotherFoo (4th in log)

2 个答案:

答案 0 :(得分:3)

如果您正在选择将在通话时使用的“此”, 你应该使用bind,这就是为此完成的。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

因此,如果SomeObject有一个push方法,那么像这样存储它将不起作用:

var thePushMethod = someObject.push;

因为在写这个时你松开了函数的上下文。

现在,如果你这样做:

 var thePushMethod = someObject.push.bind(someObject);

上下文现在存储在thePushMethod中,您只需使用

调用
  thePushMethod();

请注意,您也可以绑定参数,例如,您可以编写:

var pushOneLater = someObject.push.bind(someObject, 1 );

// then, later :

pushOneLater(); // will push one into someObject

答案 1 :(得分:1)

考虑这个例子,

function Person () {
    this.fname = "Welcome";
    this.myFunc = function() {
        return this.fname;
    }
};

var a = new Person();
console.log(a.myFunc());
var b = a.myFunc;
console.log(b());

<强>输出

Welcome
undefined

当您拨打a.myFunc()时,当前对象(this)设置为a。所以,第一个例子工作正常。

但在第二种情况下,var b = a.myFunc;只获得对函数的引用,当您调用它时,您不会调用任何特定对象,因此会分配窗口对象。这就是它打印undefined的原因。

要解决此问题,您可以使用this函数显式传递call参数,如下所示

console.log(b.call(a));

因此,对于您的情况,您可能必须这样做

method.call(target, entity);

检查fixed fiddle