글로벌 실행
먼저 글로벌 환경에서 이것이 무엇인지 살펴보겠습니다:
먼저 브라우저:
console.log(this)
// Window {speechSynesis: SpeechSynesis, 캐시: CacheStorage, localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}
// global
요약: 전역 범위에서 이는 현재 전역 개체(브라우저 측의 창, 노드의 전역)를 실행합니다. 함수에서의 실행순수 함수 호출함수를 사용하는 가장 일반적인 방법은 다음과 같습니다.
function test() { console.log(this); }; test(); // Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}
'use strict'; function test() { console.log(this); }; test(); // undefined
(function (){ "use strict"; console.log(this); })(); // undefined
var obj = { name: 'qiutc', foo: function() { console.log(this.name); } } obj.foo(); // 'qiutc'
function test() { console.log(this.name); } var obj = { name: 'qiutc', foo: test } obj.foo(); // 'qiutc'
var obj = { name: 'qiutc', foo: function() { console.log(this); } } var test = obj.foo; test(); // Window
이것이 객체 obj를 가리키는 foo2에서 처음으로 직접 인쇄될 때 우리는 의심할 여지가 없습니다.
그러나 setTimeout에서 실행된 this.foo는 전역 객체를 가리키므로 여기서는 안됩니다. 함수 메소드로 사용하시나요? 이는 종종 많은 초보자를 혼란스럽게 합니다.
사실 setTimeout은 함수일 뿐이며 함수에는 매개변수가 필요합니다. 전달할 때 fun 매개변수가 필요한 것처럼 this.foo를 setTimeout 함수에 매개변수로 전달합니다. 실제로 fun = this.foo 작업을 수행합니다. 여기서는 fun이 실행 시 this.foo의 참조를 직접 가리키므로 fun()이 obj와 결합됩니다. 관련이 없지만 일반 함수로 직접 호출되므로 전역 객체를 가리킵니다. 이 문제는 많은 비동기 콜백 함수에서 흔히 발생합니다. 해결책
이 문제를 해결하기 위해 클로저의 특성을 사용하여 처리할 수 있습니다. 🎜 >
var obj = { name: 'qiutc', foo: function() { console.log(this); }, foo2: function() { console.log(this); setTimeout(this.foo, 1000); } } obj.foo2();
setTimeout의 또 다른 함정
앞서 말했듯이 콜백 함수가 바인딩된 범위 없이 직접 실행되면 this는 전역 객체(window)를 가리킵니다. 그러나 setTimeout의 콜백 함수는 엄격 모드에서 다르게 작동합니다.
var obj = { name: 'qiutc', foo: function() { console.log(this); }, foo2: function() { console.log(this); var _this = this; setTimeout(function() { console.log(this); // Window console.log(_this); // Object {name: "qiutc"} }, 1000); } } obj.foo2();
논리적으로 말하면 엄격 모드를 추가했으며 foo 호출에서는 이를 지정하지 않습니다. 정의되지 않은 상태로 나오지만 여기에는 전역 개체가 계속 나타납니다. 엄격 모드가 유효하지 않은 것인가요?
로 사용됩니다. constructorjs에서는 클래스를 구현하려면 생성자를 정의해야 합니다. 생성자를 호출할 때 새 키워드를 추가해야 합니다.
function Person(name) { this.name = name; console.log(this); } var p = new Person('qiutc'); // Person {name: "qiutc"}
我们可以看到当作构造函数调用时,this 指向了这个构造函数调用时候实例化出来的对象;
当然,构造函数其实也是一个函数,如果我们把它当作一个普通函数执行,这个 this 仍然执行全局:
function Person(name) { this.name = name; console.log(this); } var p = Person('qiutc'); // Window
其区别在于,如何调用函数(new)。
箭头函数
在 ES6 的新规范中,加入了箭头函数,它和普通函数最不一样的一点就是 this 的指向了,还记得在上文中(作为对象的方法调用-一些坑-解决)我们使用闭包来解决 this 的指向问题吗,如果用上了箭头函数就可以更完美的解决了:
var obj = { name: 'qiutc', foo: function() { console.log(this); }, foo2: function() { console.log(this); setTimeout(() => { console.log(this); // Object {name: "qiutc"} }, 1000); } } obj.foo2();
可以看到,在 setTimeout 执行的函数中,本应该打印出在 Window,但是在这里 this 却指向了 obj,原因就在于,给 setTimeout 传入的函数(参数)是一个箭头函数:
函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
根据例子我们理解一下这句话:
在 obj.foo2() 执行的时候,当前的 this 指向 obj;在执行 setTimeout 时候,我们先是定义了一个匿名的箭头函数,关键点就在这,箭头函数内的 this 执行定义时所在的对象,就是指向定义这个箭头函数时作用域内的 this,也就是 obj.foo2 中的 this,即 obj;所以在执行箭头函数的时候,它的 this -> obj.foo2 中的 this -> obj;
简单来说, 箭头函数中的 this 只和定义它时候的作用域的 this 有关,而与在哪里以及如何调用它无关,同时它的 this 指向是不可改变的。
call, apply, bind
在 js 中,函数也是对象,同样也有一些方法,这里我们介绍三个方法,他们可以更改函数中的 this 指向:
call
fun.call(thisArg[, arg1[, arg2[, ...]]])
它会立即执行函数,第一个参数是指定执行函数中 this 的上下文,后面的参数是执行函数需要传入的参数;
apply
fun.apply(thisArg[, [arg1, arg2, ...]])
它会立即执行函数,第一个参数是指定执行函数中 this 的上下文,第二个参数是一个数组,是传给执行函数的参数(与 call 的区别);
bind
var foo = fun.bind(thisArg[, arg1[, arg2[, ...]]]);
它不会执行函数,而是返回一个新的函数,这个新的函数被指定了 this 的上下文,后面的参数是执行函数需要传入的参数;
这三个函数其实大同小异,总的目的就是去指定一个函数的上下文(this),我们以 call 函数为例;
为一个普通函数指定 this
var obj = { name: 'qiutc' }; function foo() { console.log(this); } foo.call(obj); // Object {name: "qiutc"}
可以看到,在执行 foo.call(obj) 的时候,函数内的 this 指向了 obj 这个对象,成功;
为对象中的方法指定一个 this
var obj = { name: 'qiutc', foo: function () { console.log(this); } } var obj2 = { name: 'tcqiu222222' }; obj.foo.call(obj2); // Object {name: "tcqiu222222"}
可以看到,执行函数的时候这里的 this 指向了 obj2,成功;
为构造函数指定 this
function Person(name) { this.name = name; console.log(this); } var obj = { name: 'qiutc2222222' }; var p = new Person.call(obj, 'qiutc'); // Uncaught TypeError: Person.call is not a constructor(…)
这里报了个错,原因是我们去 new 了 Person.call 函数,而非 Person ,这里的函数不是一个构造函数;
换成 bind 试试:
function Person(name) { this.name = name; console.log(this); } var obj = { name: 'qiutc2222222' }; var Person2 = Person.bind(obj); var p = new Person2('qiutc'); // Person {name: "qiutc"} console.log(obj); // Object {name: "qiutc2222222"}
打印出来的是 Person 实例化出来的对象,而和 obj 没有关系,而 obj 也没有发生变化,说明,我们给 Person 指定 this 上下文并没有生效;
因此可以得出: 使用 bind 给一个构造函数指定 this,在 new 这个构造函数的时候,bind 函数所指定的 this 并不会生效;
当然 bind 不仅可以指定 this ,还能传入参数,我们来试试这个操作:
function Person(name) { this.name = name; console.log(this); } var obj = { name: 'qiutc2222222' }; var Person2 = Person.bind(obj, 'qiutc111111'); var p = new Person2('qiutc'); // Person {name: "qiutc111111"}
可以看到,虽然指定 this 不起作用,但是传入参数还是起作用了;
为箭头函数指定 this
我们来定义一个全局下的箭头函数,因此这个箭头函数中的 this 必然会指向全局对象,如果用 call 方法改变 this 呢:
var afoo = (a) => { console.log(a); console.log(this); } afoo(1); // 1 // Window var obj = { name: 'qiutc' }; afoo.call(obj, 2); // 2 // Window
可以看到,这里的 call 指向 this 的操作并没有成功,所以可以得出: 箭头函数中的 this 在定义它的时候已经决定了(执行定义它的作用域中的 this),与如何调用以及在哪里调用它无关,包括 (call, apply, bind) 等操作都无法改变它的 this。
只要记住箭头函数大法好,不变的 this。
更多JavaScript 中的 this !相关文章请关注PHP中文网!