将对象分配给"this"

6

假设我有一个类和一些静态帮助方法,例如:

function MyClass (myVar) {
    this.myVar = myVar;

    this.replaceMe = function (value) {
        // this will fail
        this = MyClass.staticHelper( value );

        return this;
    }

    this.revealVar = function () {
        alert( this.myVar );
    }
}

MyClass.staticHelper = function (instance, value) {
    return new MyClass( instance.myVar + value );
}

我想要做的是类似于这样的事情:

var instance = new MyClass( 2 );

instance.revealVar(); // alerts 2
instance.replaceMe( 40 ).revealVar(); // alerts 42

原因是我的类的结构有点复杂,我不想每次手动分配所有内部变量,而是替换整个对象。有简单的方法可以做到吗?


2
看起来你只是想要 return MyClass.staticHelper(this, value );。你不能替换 this,但可以返回一个新的、类似的对象。 - Felix Kling
这将仅在调用链中返回新对象,但实际上不会替换实例。 - Ingo Bürk
是的,这是不可能的。但是你可以为整个类创建一个包装器,并保留对实际实例的内部引用。 - Felix Kling
啊,真遗憾。本来挺好的。我只需更改我的设计,而不是覆盖对象,而是返回它们以启用调用链。对于实际覆盖,我仍然可以重新分配变量或使用我为此目的编写的静态函数。谢谢! - Ingo Bürk
如果您想通过将当前引用更改为不同的对象来“替换”对象,则需要更改其所有属性。您无法分配到超出作用域链的任何内容。 - Bergi
4个回答

2
“instance.replaceMe(40).revealVar();”弹出42。为了实现这个,可以使用“return MyClass.staticHelper(this, value);”。问题在于下一次调用“instance.revealVar()”是否应该弹出2还是42——如果你想要将“instance”更改为42,则会变得更加复杂。
this = MyClass.staticHelper( value ); // this will fail

因为this不是一个普通的变量,而是一个关键字,并且求值为当前执行上下文的ThisBinding的值,它根据函数进入的方式设置 - 你不能对其进行赋值,只能在调用函数时设置它。

我不想每次都手动分配所有内部变量,而是替换整个对象。

很遗憾,你必须这样做,否则你不会改变instancerevealVar() 的属性(以及闭包隐藏变量),revealVar() 将保持为2。

有没有简单的方法?

是的,它可以通过编程完成。最简单的方法是在当前实例上调用构造函数(再次),就像在使用new 关键字时发生的那样:
MyClass.call( instance, instance.myVar + value );

然而,您不能像创建全新实例的静态函数一样使用它。您可以将其放在静态方法中,并从replaceMe中使用this调用该方法,或者直接将其放在replaceMe中。
如果您需要一个首先返回全新实例的静态方法,您也可以通过复制旧实例上的新属性来使用它:
….replaceMe = function(val) {
    var newInst = MyClass.staticHelper(this, val); // new MyClass(this.myVar+val);
    for (var prop in newInst)
        if (newInst.hasOwnProperty(prop))
            this[prop] = newInst[prop];
    return this;
};

这意味着覆盖旧属性,而且旧的闭包现在可以被垃圾回收,因为没有任何引用它们了。
顺便说一下,我建议将您的方法放在原型上而不是在构造函数中分配

0

直接返回新实例怎么样:

function MyClass(myVar) {
    // ...

    this.replaceMe = function (value) {
        return MyClass.staticHelper(this, value);
    }

    // ...
}

MyClass.staticHelper = function (instance, value) {
    return new MyClass( instance.myVar += value );
}

正如问题评论中所提到的,这实际上并不会替换该实例,即在调用链之外,对象将保持不变。 - Ingo Bürk
@IngoBürk 我还添加了 += 而不是 =,这样会改变对象属性。 - David G
哦,我没有看到那个。但是那违背了我的问题的目的:当发生变化时,我的类需要更新多个变量。我正在寻找一种不必手动完成这项工作的方法。 - Ingo Bürk
@IngoBürk 嗯,你不能改变this,因为它是一个常量对象。我认为你可以使用模块模式来解决这个问题。我马上更新。 - David G
是的,我现在明白了。我只是想也许有一种方法,毕竟Javascript是一种具有许多“特性”的语言。 :) - Ingo Bürk

0

这段代码在Javascript中无法运行的原因有两个。

首先,尽管它看起来像一个变量,但实际上this是一个函数调用*,因此不能被赋值。 this=foobar()=baz相同。因此,不可能编写如下代码:

a = 5
a.change(10)
alert(a == 10) // nope

其次,即使this=z是可能的,那种方法也会失败,因为Javascript按值传递,因此不可能有一个改变其参数值的函数:

a = 5
change(a)
alert(a == 10) // nope

* "is" 意味着 "在每个方面完全相同"


3
规范没有说这是一个函数调用。它只提到了 ThisBinding,描述为“与执行上下文相关联的 ECMAScript 代码中 this 关键字关联的值”。(http://es5.github.com/#x10.3, http://es5.github.com/#x11.1.1)。 - Felix Kling
函数内的 this 指针是只读值,而不是函数调用。这就是为什么对 this 的赋值不起作用的原因。 - Aadit M Shah
1
@AaditMShah:“只读值”在某种程度上是个矛盾,因为所有的值都是只读的(5=x也不行)。如果你的意思是“只读变量”,那也不正确,因为变量会保留它们的值,而this不会。 - georg
@thg435 - 哎呀,我确实是指只读变量。话虽如此,内存保留与变量是只读还是可读写有什么关系呢?this指针确实会保留其值,而其值是一个引用(即一个地址),指向内存中的一个对象。 - Aadit M Shah
@AaditMShah:对于一个普通变量,一旦我们将某物分配给它,这个值就会一直保留,直到我们重新分配它。随着我们进入新的函数作用域,“this”的值会自动更改(尽管有另一个具有相同属性的“变量”)。这就是为什么在使用this创建闭包时需要像var me = this这样的技巧的原因。 - georg
@thg435 - 当您进入新函数的作用域时,this 的值不会改变。如果是这样的话,那么语言将会严重出错。每个函数都有自己的 this 指针,它会遮蔽其父函数中的 this 指针。父级的 this 指针保持不变(就像任何其他变量一样),但由于被遮蔽了,因此完全无法访问,并且 this 不能被删除以取消遮蔽。这就是我们将 this 分配给另一个变量的原因。 - Aadit M Shah

0

我曾经想做类似的事情。不幸的是,无法为this分配值 - this指针是只读变量。但是,下一个最好的方法是使用getter和setter对象来更改保存实例本身的变量。

请注意,这仅更新对实例的单个引用。您可以在此处阅读更多信息:JavaScript中模拟指针的更好方法是否存在?

所以它是如何工作的:

function MyClass(pointer, myVar) {
    this.myVar = myVar;

    this.replaceMe = function (value) {
        pointer.value = MyClass.staticHelper(this, pointer, value);
        return pointer.value;
    };

    this.revealVar = function () {
        alert(this.myVar);
    };
}

MyClass.staticHelper = function (instance, pointer, value) {
    return new MyClass(pointer, instance.myVar + value);
};

这是如何创建并使用指针的代码示例:

pointer

var instance = new MyClass({
    get value() { return instance; },
    set value(newValue) { instance = newValue; }
}, 2);

instance.revealVar();               // alerts 2
instance.replaceMe(40).revealVar(); // alerts 42

这不是最优雅的解决方案,但它可以完成工作。您可以在此代码中查看操作:http://jsfiddle.net/fpxXL/1/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接