이 키워드는 JavaScript에서 가장 복잡한 메커니즘 중 하나입니다. 모든 기능의 범위에서 자동으로 정의되는 매우 특별한 키워드입니다. 그러나 경험이 풍부한 JavaScript 개발자라도 이것이 정확히 무엇을 가리키는지 파악하는 데 어려움을 겪습니다.
이게 뭐죠?
함수 자체를 가리키시나요?
문자 그대로의 의미를 보면 이것이 함수 자체를 가리키는 것이라고 생각하기 쉽습니다. 우리는 예를 볼 수 있습니다.
function foo() { this.count = this.count ? this.count + 1 : 1; } for (let i = 0; i < 5; i++) { foo(); } console.log(foo.count); // undefined
foo.count의 출력이 우리가 예상한 5
가 아니라 처음에 할당된 0
임을 알 수 있습니다. 즉, 5
,而是一开始赋值的0
。也就是说this其实并没有指向函数本身。
指向作用域?
还有一种比较常见的误解是,this指向了函数的作用域。
function foo() { var a = 2; bar(); } function bar() { console.log(this.a); } foo(); // undefined
这段代码中,bar在foo中运行,输出this.a
得到的却是undefined
。也就是说this也不是指向函数的作用域的。
这也不是,那也不是,this到底是什么呢?在函数执行过程中,会创建一个执行上下文(一个记录),this就是这个上下文中的一个属性,在执行过程中用到。而this的指向则是取决于函数在哪里被调用。
this的绑定规则
this的绑定有四条可以遵循的规则,下面将一一介绍。
1.默认绑定
独立函数调用,非严格模式下,指向window;严格模式下指向undefined。
这里说的独立函数可以理解成除开后面三种情况的一般函数调用。
// 非严格模式 var name = 'Willem'; function foo() { console.log(this.name); } foo(); // Willem // 执行时启用严格模式 (function() { 'use strict'; foo(); // Willem bar(); // Cannot read property 'name' of undefined })(); // 函数体使用严格模式 function bar() { 'use strict'; console.log(this.name); }
上述代码中,分别在普通环境中输出Willem,说明指向的确实是window对象。需要特别注意的一点是:严格模式下指向undefined指的是函数体内启用了严格模式,而不是调用时。
2. 隐式绑定
隐式绑定说的是,在函数执行时,是否被某个对象拥有或包含
。换句话说,在函数运行时,是否是作为某个对象的属性的方式运行的,这样说还是不是很清楚,来个栗子:
function foo() { console.log(this.a); } var a = 1; var obj = { a: 2, foo }; obj.foo(); // 2 var obj2 = { a: 3, obj }; obj2.obj.foo(); // 2
示例中,foo被当做了obj的一个属性进行执行,此时obj作为了函数的上下文,此时this指向了obj,this.a
等价于obj.a
。在对象属性链式的调用中,只有最后一层会对调用位置产生影响,也就是说最后一层会影响this指向。
有很多前端的小伙伴面试时或许还见过这样的题:
function foo() { console.log(this.a); } var a = 1; var obj = { a: 2, foo }; var bar = obj.foo; bar(); // 1
这是隐式绑定最常见的一个问题,隐式丢失:被隐式绑定的函数会丢失绑定对象。虽然bar是对obj.foo的一个引用,但实际上引用的还是foo函数本身,bar函数就是一个独立函数的调用,参考第一条,此时this指向了window|undefined
。
还有一种经典的回调函数的this指向问题也是隐式丢失。
function foo() { console.log(this.a); } function doFoo(fn) { fn(); } var a = 1; var obj = { a: 2, foo }; doFoo(obj.foo); // 1
小结:在隐式绑定中,赋值的情况下(回调是隐式赋值)需要特别注意隐式丢失的问题 。
3. 显示绑定
JavaScript中的Function提供了两个方法call
和apply
,传入的第一个参数是一个对象,会把this绑定到这个对象。如果是传入的是一个原始值(字符串、数字、布尔),会被转换成它的对象形式(new String(), new Boolean(), new Number())。
function foo() { console.log(this.a); } var obj = { a: 1 }; foo.call(obj); // 1
虽然我们可以使用call
和apply
显式指定this的指向,但是还是会存在丢失绑定的问题。可以通过所谓的硬绑定(bind函数)
来解决,这里就不过多赘述了。
4. new
最后要介绍的是使用new
이것은 실제로 함수 자체를 가리키는 것이 아닙니다
.
- 범위를 지정하시겠습니까? 또 다른 일반적인 오해는 이것이 함수의 범위를 가리킨다는 것입니다.
function Foo(a) { this.a = a; } var bar = new Foo(2); bar.a; // 2
로그인 후 복사이 코드에서 bar는 foo에서 실행되고 - 이것은 함수의 범위를 가리키는 것이 아닙니다.
this.a
의 출력은 정의되지 않습니다
입니다. 즉, 함수 실행 중에 실행 컨텍스트(레코드)가 생성됩니다. 이는 이 컨텍스트의 속성이며 실행 중에 사용됩니다. 이것의 요점은 함수가 호출되는 위치에 따라 다릅니다.
이 바인딩 규칙이 바인딩에는 따를 수 있는 4가지 규칙이 있는데, 아래에서 하나씩 소개하겠습니다. 1. 기본 바인딩