이를 위치에 따라 이해하자면 상황은 크게 세 가지로 나눌 수 있습니다.
1. 함수에서: 이는 일반적으로 암시적 매개변수입니다.
2. 함수 외부(최상위 범위): 브라우저에서는 전역 개체를 나타내며 Node.js에서는 모듈 내보내기를 나타냅니다.
3. eval()에 전달되는 문자열: eval()이 직접 호출되면 현재 객체를 참조하고, eval()이 간접적으로 호출되면 전역 객체를 참조합니다.
다음 카테고리에 대해 해당 테스트를 수행했습니다.
1. 함수
함수는 기본적으로 JS에서 호출 가능한 모든 구조를 나타낼 수 있으므로 이를 사용하는 가장 일반적인 시나리오이며 함수는 다음 세 가지 역할로 나눌 수 있습니다.
실제 함수
생성자
메서드
1.1이
실제 기능에서는
실제 함수에서 this의 값은 그것이 발견되는 맥락에 따라 달라지는 패턴입니다.
엉성한 모드: 전역 개체(브라우저의 창)를 나타냅니다.
function sloppyFunc() {
console .log( this === window); // true
}
sloppyFunc();
엄격 모드: 이 값은 정의되지 않습니다.
function strictFunc() {
' use strict' ;
console.log(this === 정의되지 않음); // true
}
strictFunc();
이것은 함수의 암시적 매개변수이므로 해당 값은 항상 동일합니다. 그러나 call() 또는 apply() 메서드를 사용하여 이 값을 명시적으로 정의할 수 있습니다.
function func(arg1, arg2) {
console .log(this); // 1
console.log(arg1); // 2
console.log(arg2) // 3
}
func.call(1) , 2, 3); // (this, arg1, arg2)
func.apply(1, [2, 3]) // (this, arrayWithArgs)
1.2 this
생성자
new를 사용하여 함수를 생성자로 사용할 수 있습니다. new 작업은 새 객체를 생성하고 이를 통해 이 객체를 생성자에 전달합니다.
var saveThis;
function Constr( ) {
saveThis = this;
}
var inst = new Constr();
console.log(savedThis === inst); // true
JS의 새로운 작업 구현 원리는 대략 다음 코드와 같습니다(더 정확한 구현을 보려면 여기를 참조하세요. 이 구현도 더 복잡합니다).
function newOperator(Constr, arrayWithArgs) {
var thisValue = Object.create(Constr.prototype);
Constr.apply(thisValue, arrayWithArgs);
return thisValue;
}
1.3이
메소드에서
메소드에서 this를 사용하는 것은 전통적인 객체 지향 언어에 더 가깝습니다. 이것이 가리키는 수신자는 이 메소드를 포함하는 객체입니다.
var obj = {
메서드: function () {
console.log(this === obj); // true
}
}
obj.method();
2. 범위 내에서는
브라우저에서 범위는 전역 범위이며 이는 전역 개체를 나타냅니다(창과 마찬가지로).
<script><br> 콘솔. log(this === 창); // true<br></script>
Node.js에서는 일반적으로 모듈 단위로 함수를 실행합니다. 따라서 최상위 범위는 매우 특별한 모듈 범위입니다.
// `global`(`window 아님) `)는 전역 객체를 참조합니다:
console.log(Math === global.Math); // true
// `this`는 전역 객체를 참조하지 않습니다:
console .log( this !== global); // true
// `this`는 모듈의 내보내기를 나타냅니다.
console.log(this === module.exports) // true
3. eval()에 있습니다
eval()은 직접(함수 이름 'eval' 호출) 또는 간접적(call()과 같은 다른 방법으로 호출)으로 호출할 수 있습니다. 자세한 내용은 여기를 참조하세요.
// 실제 함수
함수 sloppyFunc () {
console.log(eval('this') === window); // true
}
sloppyFunc();
function strictFunc() {
'use strict';
console.log(eval('this') === 정의되지 않음); // true
}
strictFunc();
// 생성자
var saveThis;
function Constr() {
saveThis = eval('this');
}
var inst = new Constr();
console.log(savedThis === inst ); / / true
// 메소드
var obj = {
메소드: function () {
console.log(eval('this') === obj) / / 참
}
}
obj.method();
4. 이에 관련된 함정
아래에서 소개할 이와 관련된 3가지 함정을 조심하셔야 합니다. 다음 예제에서는 엄격 모드를 사용하면 코드 보안이 향상될 수 있습니다. 실제 함수에서는 this 값이 정의되지 않으므로 문제가 발생하면 경고가 표시됩니다.
4.1 new를 사용하는 것을 잊어버렸습니다
생성자를 호출하기 위해 new를 사용하지 않는 경우 실제로는 실제 함수를 사용하고 있는 것입니다. 따라서 이는 예상한 값이 아닙니다. Sloppy 모드에서 이는 window를 가리키며 전역 변수를 생성합니다.
function Point(x, y) {
this .x = x;
this.y = y;
}
var p = Point(7, 5) // new를 잊어버렸습니다!
console.log(p == = 정의되지 않음) ; // true
// 전역 변수가 생성되었습니다:
console.log(x); // 7
console.log(y);
그러나 엄격 모드를 사용하는 경우에는 여전히 경고가 표시됩니다(이===정의되지 않음).
function Point(x, y) {
' 엄격한 사용';
this.x = x;
this.y = y;
}
var p = Point(7, 5);
// TypeError: Cannot 정의되지 않은 'x' 속성 설정
4.2 부적절한 메소드 사용
메소드의 값을 직접 가져오면(호출하지 않고) 메소드를 함수로 사용하는 것입니다. 메소드를 함수나 호출 메소드에 매개변수로 전달하려는 경우 이 작업을 수행할 수 있습니다. 이는 setTimeout() 및 등록된 이벤트 핸들러의 경우입니다. 이 시나리오를 시뮬레이션하기 위해 callIt() 메서드를 사용하겠습니다.
/**setTimeout() 및 setImmediate()와 유사합니다.*/
function callIt(func) {
func();
}
Sloppy 모드에서 메소드를 함수로 호출하면 *this*는 전역 객체를 가리키므로 이후 생성되는 모든 항목은 전역 변수가 됩니다.
코드 복사 코드는 다음과 같습니다.
var counter = {
count: 0,
// 엉성한 모드 방법
inc: function () {
this.count ;
}
}
callIt(counter.inc);
// 작동하지 않음:
console.log(counter.count); // 0
// 대신 전역 변수가 생성되었습니다
// (NaN은 undefine에 적용한 결과입니다):
console.log(count) // NaN
엄격 모드에서 이 작업을 수행하면 정의되지 않으며 여전히 원하는 결과를 얻지 못하지만 최소한 다음 경고가 표시됩니다.
var counter = {
count: 0,
// 엄격 모드 메서드
inc: function () {
'use strict';
this.count ;
}
}
callIt(counter. inc);
// TypeError: 정의되지 않은 'count' 속성을 읽을 수 없습니다
console.log(counter.count);
예상된 결과를 얻으려면 바인딩()을 사용할 수 있습니다.
var counter = {
count: 0,
inc: function () {
this.count ;
}
}
callIt(counter.inc.bind(counter));
// 작동했습니다!
콘솔 .log(counter.count) // 1
bind()는 항상 이 값을 counter로 설정하는 또 다른 함수를 만듭니다.
4.3 숨기기
메소드에서 함수를 사용할 때 함수에 this가 있다는 사실을 종종 잊어버립니다. 이것은 방법과 다르기 때문에 두 가지를 함께 혼합할 수는 없습니다. 자세한 내용은 다음 코드를 참조하세요.
var obj = {
이름: 'Jane' ,
친구: [ 'Tarzan', 'Cheeta' ],
루프: function () {
'use strict';
this.friends.forEach(
function ( 친구) {
console.log(this.name '은 ' 친구를 알고 있음);
정의되지 않은 '이름' 속성을 읽을 수 없습니다
위의 예에서 함수의 this.name은 함수의 this 값이 루프() 메서드의 this와 다르기 때문에 사용할 수 없습니다. 이 문제를 해결하기 위해 아래에 세 가지 아이디어가 제공됩니다.
1. that=this, 이것을 변수에 할당하여 이것이 명시적으로 표시되도록 하고(또한 self는 이것을 저장하는 데 사용되는 매우 일반적인 변수 이름이기도 함) 해당 변수를 사용합니다:
코드 복사
this.friends.forEach(function (friend) {
console.log(that.name ' Knows ' friend);
});
}
2. 바인드(). 바인딩()을 사용하여 전달하려는 값이 항상 포함된 함수를 만듭니다(아래 예에서는 메서드의 this).
코드 복사
console.log(this.name '알고 있는 친구);
}.bind(this));
}
3. forEach의 두 번째 매개변수를 사용합니다. forEach의 두 번째 매개변수는 콜백 함수에 전달되어 콜백 함수의 this로 사용됩니다.
loop: function () {
'엄격한 사용';
this.friends.forEach(함수(친구) {
console.log(this.name '알고 있는 '친구);
}, this);
}
5. 모범 사례
이론적으로 실제 함수에는 자체 this가 없다고 생각하며, 위의 솔루션도 이 아이디어를 기반으로 합니다. ECMAScript 6에서는 이 효과를 얻기 위해 화살표 함수를 사용합니다. 화살표 함수는 자체 this가 없는 함수입니다. 그런 함수에서는 이것을 부담없이 사용할 수 있고, 암묵적으로 존재하는지 걱정할 필요가 없습니다.
loop: function () {
'use strict';
// forEach()의 매개변수는 화살표 함수입니다
this.friends.forEach(friend => {
// `this`는 루프의 `this`입니다
console.log (this.name '알고 있는 친구);
});
}
저는 일부 API가 이것을 실제 함수의 추가 매개변수로 취급하는 것을 좋아하지 않습니다.
beforeEach(function () {
this.addMatchers({
toBeInRange: 함수(시작, 끝) {
...
} });
암시적 매개변수를 명시적으로 작성하고 전달하면 코드를 더 쉽게 이해할 수 있으며 이는 화살표 함수의 요구 사항과 일치합니다.
beforeEach(api => {
api.addMatchers({
toBeInRange(시작, 종료) {