在 JavaScript 原型函数中保留对“this”的引用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2025789/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Preserving a reference to "this" in JavaScript prototype functions
提问by Jimmy Cuadra
I'm just getting into using prototypal JavaScript and I'm having trouble figuring out how to preserve a thisreference to the main object from inside a prototype function when the scope changes. Let me illustrate what I mean (I'm using jQuery here):
我刚刚开始使用原型 JavaScript,我无法弄清楚如何this在范围更改时从原型函数内部保留对主对象的引用。让我来说明我的意思(我在这里使用 jQuery):
MyClass = function() {
this.element = $('#element');
this.myValue = 'something';
// some more code
}
MyClass.prototype.myfunc = function() {
// at this point, "this" refers to the instance of MyClass
this.element.click(function() {
// at this point, "this" refers to the DOM element
// but what if I want to access the original "this.myValue"?
});
}
new MyClass();
I know that I can preserve a reference to the main object by doing this at the beginning of myfunc:
我知道我可以通过在以下开头执行此操作来保留对主对象的引用myfunc:
var myThis = this;
and then using myThis.myValueto access the main object's property. But what happens when I have a whole bunch of prototype functions on MyClass? Do I have to save the reference to thisat the beginning of each one? Seems like there should be a cleaner way. And what about a situation like this:
然后myThis.myValue用于访问主对象的属性。但是当我有一大堆原型函数时会发生什么MyClass?我是否必须this在每个开头保存引用?似乎应该有一种更清洁的方法。像这样的情况怎么办:
MyClass = function() {
this.elements $('.elements');
this.myValue = 'something';
this.elements.each(this.doSomething);
}
MyClass.prototype.doSomething = function() {
// operate on the element
}
new MyClass();
In that case, I can't create a reference to the main object with var myThis = this;because even the original value of thiswithin the context of doSomethingis a jQueryobject and not a MyClassobject.
在这种情况下,我无法创建对主对象的引用,var myThis = this;因为即使this在上下文中的原始值doSomething也是jQuery对象而不是MyClass对象。
It's been suggested to me to use a global variable to hold the reference to the original this, but that seems like a really bad idea to me. I don't want to pollute the global namespace and that seems like it would prevent me from instantiating two different MyClassobjects without them interfering with each other.
有人建议我使用全局变量来保存对原始 的引用this,但这对我来说似乎是一个非常糟糕的主意。我不想污染全局命名空间,这似乎会阻止我在MyClass不相互干扰的情况下实例化两个不同的对象。
Any suggestions? Is there a clean way to do what I'm after? Or is my entire design pattern flawed?
有什么建议?有没有一种干净的方法来做我所追求的?还是我的整个设计模式有缺陷?
采纳答案by CMS
For preserving the context, the bindmethod is really useful, it's now part of the recently released ECMAScript 5th EditionSpecification, the implementation of this function is simple (only 8 lines long):
为了保留上下文,这个bind方法真的很有用,它现在是最近发布的ECMAScript 5th EditionSpecification 的一部分,这个函数的实现很简单(只有 8 行):
// The .bind method from Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments),
object = args.shift();
return function(){
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
}
And you could use it, in your example like this:
你可以在你的例子中使用它,如下所示:
MyClass.prototype.myfunc = function() {
this.element.click((function() {
// ...
}).bind(this));
};
Another example:
另一个例子:
var obj = {
test: 'obj test',
fx: function() {
alert(this.test + '\n' + Array.prototype.slice.call(arguments).join());
}
};
var test = "Global test";
var fx1 = obj.fx;
var fx2 = obj.fx.bind(obj, 1, 2, 3);
fx1(1,2);
fx2(4, 5);
In this second example we can observe more about the behavior of bind.
在第二个例子中,我们可以观察到更多关于 的行为bind。
It basically generates a new function, that will be the responsible of calling our function, preserving the function context (thisvalue), that is defined as the first argument of bind.
它基本上生成了一个新函数,它将负责调用我们的函数,保留函数上下文(this值),它被定义为 的第一个参数bind。
The rest of the arguments are simply passed to our function.
其余的参数只是简单地传递给我们的函数。
Note in this example that the function fx1, is invoked without any object context(obj.method()), just as a simple function call, in this type of invokation, the thiskeyword inside will refer to the Global object, it will alert "global test".
注意在这个例子中,函数fx1, 是在没有任何对象上下文( obj.method()) 的情况下调用的,就像一个简单的函数调用一样,在这种类型的调用中,this里面的关键字将引用全局对象,它会警告“全局测试”。
Now, the fx2is the new function that the bindmethod generated, it will call our function preserving the context and correctly passing the arguments, it will alert "obj test 1, 2, 3, 4, 5" because we invoked it adding the two additionally arguments, it already had bindedthe first three.
现在,这fx2是该bind方法生成的新函数,它将调用我们的函数,保留上下文并正确传递参数,它会警告“obj test 1, 2, 3, 4, 5”,因为我们调用它另外添加了两个争论,它已经绑定了前三个。
回答by icktoofay
For your last MyClassexample, you could do this:
对于你的最后一个MyClass例子,你可以这样做:
var myThis=this;
this.elements.each(function() { myThis.doSomething.apply(myThis, arguments); });
In the function that is passed to each, thisrefers to a jQuery object, as you already know. If inside that function you get the doSomethingfunction from myThis, and then call the apply method on that function with the arguments array (see the applyfunctionand the argumentsvariable), then thiswill be set to myThisin doSomething.
如您所知,在传递给 的函数中each,this指的是一个 jQuery 对象。如果在该函数中您doSomething从 中获取函数myThis,然后使用参数数组调用该函数上的 apply 方法(请参阅apply函数和arguments变量),this则将设置为myThisin doSomething。
回答by Nemesarial
I realize this is an old thread, but I have a solution that is much more elegant, and has few drawbacks apart from the fact that it is not generally done, as I have noticed.
我意识到这是一个旧线程,但我有一个更优雅的解决方案,除了我注意到的通常不会完成的事实之外,几乎没有缺点。
Consider the following:
考虑以下:
var f=function(){
var context=this;
}
f.prototype.test=function(){
return context;
}
var fn=new f();
fn.test();
// should return undefined because the prototype definition
// took place outside the scope where 'context' is available
In the function above we defined a local variable (context). We then added a prototypical function (test) that returns the local variable. As you have probably predicted, when we create an instance of this function and then execute the test method, it does not return the local variable because when we defined the prototypical function as a member to our main function, it was outside the scope where the local variable is defined. This is a general problem with creating functions and then adding prototypes to it - you cannot access anything that was created in the scope of the main function.
在上面的函数中,我们定义了一个局部变量(上下文)。然后我们添加了一个返回局部变量的原型函数(test)。正如你可能已经预料到的,当我们创建这个函数的一个实例然后执行测试方法时,它不会返回局部变量,因为当我们将原型函数定义为我们的主函数的成员时,它超出了定义了局部变量。这是创建函数然后向其添加原型的普遍问题 - 您无法访问在主函数范围内创建的任何内容。
To create methods that are within the scope of the local variable, we need to directly define them as members of the function and get rid of the prototypical reference:
要创建在局部变量范围内的方法,我们需要直接将它们定义为函数的成员并摆脱原型引用:
var f=function(){
var context=this;
this.test=function(){
console.log(context);
return context;
};
}
var fn=new(f);
fn.test();
//should return an object that correctly references 'this'
//in the context of that function;
fn.test().test().test();
//proving that 'this' is the correct reference;
You may be worried that because the methods are not being created prototypically, different instances may not really be data-separated. To demonstrate that they are, consider this:
您可能会担心,因为这些方法不是以原型方式创建的,所以不同的实例可能不会真正进行数据分离。为了证明它们是,请考虑:
var f=function(val){
var self=this;
this.chain=function(){
return self;
};
this.checkval=function(){
return val;
};
}
var fn1=new f('first value');
var fn2=new f('second value');
fn1.checkval();
fn1.chain().chain().checkval();
// returns 'first value' indicating that not only does the initiated value remain untouched,
// one can use the internally stored context reference rigorously without losing sight of local variables.
fn2.checkval();
fn2.chain().chain().checkval();
// the fact that this set of tests returns 'second value'
// proves that they are really referencing separate instances
Another way to use this method is to create singletons. More often than not, our javascript functions are not being instantiated more than once. If you know that you will never need a second instance of the same function, then there is a shorthand way to create them. Be warned, however: lint will complain that it is a weird construction, and question your use of the keyword 'new':
使用此方法的另一种方法是创建单例。通常情况下,我们的 javascript 函数不会被多次实例化。如果您知道永远不需要相同函数的第二个实例,那么可以使用一种速记方法来创建它们。但是请注意:lint 会抱怨它是一个奇怪的结构,并质疑您对关键字“new”的使用:
fn=new function(val){
var self=this;
this.chain=function(){
return self;
};
this.checkval=function(){
return val;
};
}
fn.checkval();
fn.chain().chain().checkval();
Pro's:The benefits to using this method to create function objects are plentiful.
优点:使用此方法创建函数对象的好处很多。
- It makes your code easier to read, since it indents the methods of a function object in a way that makes it visually easier to follow.
- It allows access to the locally defined variables only in methods originally defined in this mannereven if you later add prototypical functions or even member functions to the function-object, it cannot access the local variables and whatever functionality or data you store on that level remains safe and inaccessible from anywhere else.
- It allows a simple and straight-forward way to define singletons.
- It allows you to store a reference to 'this' and maintain that reference indefinitely.
- 它使您的代码更易于阅读,因为它缩进了函数对象的方法,使其在视觉上更易于理解。
- 它只允许在最初以这种方式定义的方法中访问本地定义的变量,即使您稍后向函数对象添加原型函数甚至成员函数,它也无法访问局部变量,并且您在该级别存储的任何功能或数据仍然存在安全且无法从其他任何地方访问。
- 它允许一种简单直接的方式来定义单例。
- 它允许您存储对“this”的引用并无限期地维护该引用。
Con's:There are some drawbacks to using this method. I don't pretend to be comprehensive :)
缺点:使用这种方法有一些缺点。我不假装很全面:)
Because the methods are defined as members to the object and not prototypes - inheritance can be achieved using member definition but not prototypical definitions.This is actually incorrect. The same prototypical inheritance can be achieved by acting onf.constructor.prototype.
因为方法被定义为对象的成员而不是原型 - 可以使用成员定义而不是原型定义来实现继承。这实际上是不正确的。相同的原型继承可以通过作用于f.constructor.prototype.
回答by Gabriel McAdams
You can set the scope by using the call() and apply() functions
您可以使用call() 和 apply() 函数设置范围
回答by Ali C
You can create a reference to the this object or you can use the with (this)method. The later is extremely useful when your using event handlers and you have no way of passing in a reference.
您可以创建对 this 对象的引用,也可以使用该with (this)方法。当您使用事件处理程序并且您无法传递引用时,后者非常有用。
MyClass = function() {
// More code here ...
}
MyClass.prototype.myfunc = function() {
// Create a reference
var obj = this;
this.element.click(function() {
// "obj" refers to the original class instance
with (this){
// "this" now also refers to the original class instance
}
});
}
回答by Sampson
Since you're using jQuery, it's worth noting that thisis already maintained by jQuery itself:
由于您使用的是 jQuery,值得注意的是this它已经由 jQuery 本身维护:
$("li").each(function(j,o){
$("span", o).each(function(x,y){
alert(o + " " + y);
});
});
In this example, orepresents the li, whereas yrepresents the child span. And with $.click(), you can get the scope from the eventobject:
在本例中,o代表li,而y代表孩子span。使用$.click(),您可以从event对象中获取范围:
$("li").click(function(e){
$("span", this).each(function(i,o){
alert(e.target + " " + o);
});
});
Where e.targetrepresents the li, and orepresents the child span.
哪里e.target代表了li,又o代表了孩子span。
回答by SimplGy
Another solution (and my favorite way in jQuery) is to use the jQuery provided 'e.data' to pass 'this'. Then you can do this:
另一个解决方案(也是我在 jQuery 中最喜欢的方式)是使用 jQuery 提供的“e.data”来传递“this”。然后你可以这样做:
this.element.bind('click', this, function(e) {
e.data.myValue; //e.data now references the 'this' that you want
});

