JavaScript 中的 this !
全域執行
首先,我們在全域環境中看看它的this 是什麼:
first. 瀏覽器:
console.log(this);
// Window {speechSynthesis: Speechthesis, Saches, cators, Saches,asis: Speechthesis,cators, S localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}
可以看到印出了window 物件;
second. node:
cons;到列印出了global 物件;
總結:在全域作用域中它的this 執行目前的全域物件(瀏覽器端是Window,node 中是global)。
函數中執行
純粹的函數調用
這是最普通的函數使用方法了:
function test() { console.log(this); }; test(); // Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}
我們可以看到,一個函數被直接調用的時候,屬於全局調用,這時候它的this 指向全局物件;
嚴格模式'use strict';
如果在嚴格模式的情況下執行純粹的函數調用,那麼這裡的this 並不會指向全局,而是undefined,這樣的做法是為了消除js 中一些不嚴謹的行為:
'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'
這時候,this 指向當前的這個對象;
當然,我們還可以這麼做:
function test() { console.log(this.name); } var obj = { name: 'qiutc', foo: test } obj.foo(); // 'qiutc'
同樣不變,因為在js 中一切都是對象,函數也是一個對象,對於test ,它只是一個函數名,函數的引用,它指向這個函數,當foo = test,foo 同樣也指向了這個函數。
如果把物件的方法賦值給一個變量,然後直接呼叫這個變數呢:
var obj = { name: 'qiutc', foo: function() { console.log(this); } } var test = obj.foo; test(); // Window
可以看到,這時候this 執行了全域,當我們把test = obj.foo ,test 直接指向了一個函數的引用,這時候,其實和obj 這個物件沒有關係了,所以,它是被當作一個普通函數來直接調用,因此,this 指向全域物件。
一些坑
我們經常在回調函數裡面會遇到一些坑:
var obj = { name: 'qiutc', foo: function() { console.log(this); }, foo2: function() { console.log(this); setTimeout(this.foo, 1000); } } obj.foo2();
執行這段程式碼我們會發現兩次列印出來的this 是不一樣的:
第一次是foo2 中直接列印this ,這裡指向obj 這個對象,我們毋庸置疑;
但是在setTimeout 中執行的this.foo,卻指向了全局對象,這裡不是把它當作函數的方法使用嗎?這點常常讓許多初學者疑惑;
其實,setTimeout 也只是一個函數而已,函數必然有可能需要參數,我們把this.foo 當作一個參數傳給setTimeout 這個函數,就像它需要一個fun 參數,在傳入參數的時候,其實做了個這樣的操作fun = this.foo,看到沒有,這裡我們直接把fun 指向this.foo 的引用;執行的時候其實是執行了fun() 所以已經和obj 無關了,它是被當作普通函數直接呼叫的,因此this 指向全域物件。 這個問題是很多非同步回呼函數中普遍會碰到的;解決
為了解決這個問題,我們可以利用閉包的特性來處理:
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();
可以看到直接用this 仍然是Window;因為foo2 中的this 是指向obj,我們可以先用一個變數_this 來儲存,然後在回調函數中使用_this,就可以指向當前的這個物件了;
setTimeout 的另一個坑之前啊說過,如果直接執行回呼函數而沒有綁定作用域,那麼它的this 是指向全域物件(window),在嚴格模式下會指向undefined,然而在setTimeout 中的回呼函數在嚴格模式下卻表現出不同:'use strict'; function foo() { console.log(this); } setTimeout(foo, 1); // window
按理說我們加了嚴格模式,foo 呼叫也沒有指定this,應該是出來undefined,但是這裡仍然出現了全局對象,難道是嚴格模式失效了嗎?
並不,即使在嚴格模式下,setTimeout 方法在調用傳入函數的時候,如果這個函數沒有指定了的this,那麼它會做一個隱式的操作—-自動地註入全局上下文,等同於調用foo.apply(window) 而非foo();
當然,如果我們在傳入函數的時候已經指定this,那麼就不會被注入全域對象,例如: setTimeout(foo.bind(obj), 1) ;;
http://stackoverflow.com/questions/21957030/why-is-window-still-defined-in-this-strict-mode-code
作為一個構造函數使用
在js 中,為了實現類,我們需要定義一些建構函數,在呼叫一個建構函數的時候需要加上new 這個關鍵字:
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中文网!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python和JavaScript開發者的薪資沒有絕對的高低,具體取決於技能和行業需求。 1.Python在數據科學和機器學習領域可能薪資更高。 2.JavaScript在前端和全棧開發中需求大,薪資也可觀。 3.影響因素包括經驗、地理位置、公司規模和特定技能。

實現視差滾動和元素動畫效果的探討本文將探討如何實現類似資生堂官網(https://www.shiseido.co.jp/sb/wonderland/)中�...

學習JavaScript不難,但有挑戰。 1)理解基礎概念如變量、數據類型、函數等。 2)掌握異步編程,通過事件循環實現。 3)使用DOM操作和Promise處理異步請求。 4)避免常見錯誤,使用調試技巧。 5)優化性能,遵循最佳實踐。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

如何在JavaScript中將具有相同ID的數組元素合併到一個對像中?在處理數據時,我們常常會遇到需要將具有相同ID�...

zustand異步操作中的數據更新問題在使用zustand狀態管理庫時,經常會遇到異步操作導致數據更新不及時的問題。 �...
