자바 스크립트 프로토 타입 함수에서 "this"에 대한 참조 유지
이 질문에 이미 답변이 있습니다.
프로토 타입 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();
나는 시작 부분에 이것을 수행함으로써 주 객체에 대한 참조를 유지할 수 있다는 것을 알고 있습니다 myfunc
.
var myThis = 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();
이 경우, I가 갖는 메인 오브젝트에 대한 참조를 생성 할 수 var myThis = this;
조차 때문에 원래 값 this
의 맥락은 doSomething
A는 jQuery
오브젝트가 아닌 MyClass
오브젝트.
원본에 대한 참조를 유지하기 위해 전역 변수를 사용하는 것이 나에게 제안되었지만 그것은 나에게 this
정말 나쁜 생각처럼 보입니다. 나는 전역 이름 공간을 오염시키고 싶지 않으며 서로 MyClass
간섭하지 않고 두 개의 다른 객체 를 인스턴스화하는 것을 막을 것 같습니다 .
어떤 제안? 내가 추구하는 일을 할 수있는 깨끗한 방법이 있습니까? 아니면 전체 디자인 패턴에 결함이 있습니까?
컨텍스트를 보존하기 위해이 bind
메서드는 정말 유용합니다. 이제 최근에 출시 된 ECMAScript 5th Edition 사양의 일부입니다. 이 함수의 구현은 간단합니다 (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)));
};
};
}
그리고 다음과 같이 예제에서 사용할 수 있습니다.
MyClass.prototype.myfunc = function() {
this.element.click((function() {
// ...
}).bind(this));
};
다른 예시:
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);
이 두 번째 예에서는의 동작에 대해 더 많이 관찰 할 수 있습니다 bind
.
It basically generates a new function, that will be the responsible of calling our function, preserving the function context (this
value), that is defined as the first argument of 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 this
keyword inside will refer to the Global object, it will alert "global test".
Now, the fx2
is the new function that the bind
method 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 binded the first three.
For your last MyClass
example, you could do this:
var myThis=this;
this.elements.each(function() { myThis.doSomething.apply(myThis, arguments); });
In the function that is passed to each
, this
refers to a jQuery object, as you already know. If inside that function you get the doSomething
function from myThis
, and then call the apply method on that function with the arguments array (see the apply
function and the arguments
variable), then this
will be set to myThis
in doSomething
.
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.
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':
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 manner even 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.
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
.
You can set the scope by using the call() and apply() functions
Since you're using jQuery, it's worth noting that this
is already maintained by jQuery itself:
$("li").each(function(j,o){
$("span", o).each(function(x,y){
alert(o + " " + y);
});
});
In this example, o
represents the li
, whereas y
represents the child span
. And with $.click()
, you can get the scope from the event
object:
$("li").click(function(e){
$("span", this).each(function(i,o){
alert(e.target + " " + o);
});
});
Where e.target
represents the li
, and o
represents the child span
.
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.
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
}
});
}
Another solution (and my favorite way in jQuery) is to use the jQuery provided 'e.data' to pass 'this'. Then you can do this:
this.element.bind('click', this, function(e) {
e.data.myValue; //e.data now references the 'this' that you want
});
'code' 카테고리의 다른 글
Java JTable 설정 열 너비 (0) | 2020.09.08 |
---|---|
github의 이슈를 다른 저장소로 어떻게 옮기나요? (0) | 2020.09.08 |
AngularJS를 명시 적으로 지원하는 IDE가 있습니까? (0) | 2020.09.08 |
C #에서 식 트리 직렬화 및 역 직렬화 (0) | 2020.09.08 |
서로 다른 서버의 두 데이터베이스에있는 두 테이블을 결합하여 데이터 쿼리 (0) | 2020.09.08 |