一篇文章詳細講解JavaScript中的this(普通函數(shù)、箭頭函數(shù)、?函數(shù)運用)
前言
this對象:解析器在每次調用函數(shù)時,都會向函數(shù)內部轉遞一個隱含的參數(shù),這個參數(shù)就是this,this指向的是一個對象,這個對象我們稱為函數(shù)執(zhí)行的上下文對象,根據(jù)函數(shù)調用方式的不同,this會指向不同的對象
一、將根據(jù)一下幾個方面介紹this的指向問題
- 全局環(huán)境中的 this
- 函數(shù)中的 this
- 對象方法中的 this
- 構造函數(shù)中的 this
- 事件處理函數(shù)中的 this
- 箭頭函數(shù)中的 this
1、全局環(huán)境中的this
在全局環(huán)境中(不在任何函數(shù)或對象內部),this指向全局對象(在瀏覽器環(huán)境中是window,在node.js中是global)
console.log(this === window); // 在瀏覽器中輸出 true
2、函數(shù)中的this
當一個函數(shù)被直接調用時,this在非嚴格模式下指向全局對象(window),在嚴格模式下指向undefined
JS嚴格模式:JavaScript在語法和行為上存在一些模糊的特性,可能導致一些不易察覺的錯誤,為提高代碼的質量和可維護性,js引入了嚴格模式,通過啟用一些額外的規(guī)則,強制執(zhí)行更嚴格的語法和行為。在嚴格模式下代碼中的潛在問題將被捕獲并拋出錯誤,有助于提前發(fā)現(xiàn)和修復潛在bug。
function regularFunction() { console.log(this); } regularFunction(); // 非嚴格模式下指向 window,嚴格模式下為 undefined // 演示嚴格模式下的情況 (function () { "use strict"; regularFunction(); })();
3、對象方法中的this
當函數(shù)作為對象的方法被調用時,this指向調用該方法的對象
var person = { name: "John", sayName: function () { console.log(this.name); } }; person.sayName(); // 輸出 "John",這里的 this 指向 person 對象
4、構造函數(shù)中的this
使用new關鍵字(實例化)調用函數(shù)時,該函數(shù)被當作構造函數(shù)(類),this會指向新創(chuàng)建的對象實例
function Person(name) { this.name = name; this.sayHello = function () { console.log("Hello, I'm " + this.name); }; } var john = new Person("John"); john.sayHello(); // 輸出 "Hello, I'm John",這里 this 指向 john 實例
構造函數(shù)怎么執(zhí)行創(chuàng)建對象的過程:
- 調用一個構造函數(shù),他會立即創(chuàng)建一個對象
- 將新建的對象設置為函數(shù)中的this,在構造函數(shù)中可以使用this來引用新建的對象
- 逐行執(zhí)行函數(shù)中的代碼
- 將新建的對象作為返回值返回
在構造函數(shù)中,創(chuàng)建對象和返回對象都給我們隱藏了,使用同一個構造函數(shù)創(chuàng)建的對象,我們稱為一類對象,也將一個構造函數(shù)稱為一個類。我們將通過一個構造函數(shù)創(chuàng)建的對象,稱為是該類的實例。
5、事件處理函數(shù)中的this
在DOM事件處理函數(shù)中,this通常指向觸發(fā)事件的元素。
<button id="myButton">Click me</button> <script> var button = document.getElementById("myButton"); button.onclick = function () { console.log(this); // 點擊按鈕時,這里的 this 指向按鈕元素 //打印 :<button id="myButton">Click me</button> }; </script>
7、箭頭函數(shù)中的this
箭頭函數(shù)沒有自己的this,它的this繼承自外層作用域的this
// 普通函數(shù) function outerFunction() { this.name = "Outer"; var innerFunction = function () { console.log(this.name); }; innerFunction(); } // 箭頭函數(shù) function outerFunctionWithArrow() { this.name = "Outer with Arrow"; var innerFunction = () => { console.log(this.name); }; innerFunction(); } new outerFunction(); // 輸出 undefined,因為 innerFunction 中的 this 指向全局對象,全局對象沒有 name 屬性 new outerFunctionWithArrow(); // 輸出 "Outer with Arrow",箭頭函數(shù)的 this 繼承自 outerFunctionWithArrow 的 this
二、改變this指向的方法
由于箭頭函數(shù)的this來自于繼承,箭頭函數(shù)無法使用以下三種方法改變this指向
1、call()方法
call
方法是附加在函數(shù)調用后面使用,可以忽略函數(shù)本身的 this 指向- 語法: 函數(shù)名.call(要改變的 this 指向,要給函數(shù)傳遞的參數(shù)1,要給函數(shù)傳遞的參數(shù)2, ...)
- 使用 call 方法的時候:
1、會立即執(zhí)行函數(shù)
2、第一個參數(shù)是你要改變的函數(shù)內部的 this 指向
3、第二個參數(shù)開始,依次是向函數(shù)傳遞參數(shù)
var obj = { name: 'Jack' } function fn(a, b) { console.log(this) console.log(a) console.log(b) } fn(1, 2) fn.call(obj, 1, 2)
fn(1,2)
的時候,函數(shù)內部的 this 指向 window(函數(shù)被直接調用)fn.call(obj, 1, 2)
的時候,函數(shù)內部的 this 就指向了 obj 這個對象
2、apply()方法
apply
方法是附加在函數(shù)調用后面使用,可以忽略函數(shù)本身的 this 指向- 語法: 函數(shù)名.apply(要改變的 this 指向,[要給函數(shù)傳遞的參數(shù)1, 要給函數(shù)傳遞的參數(shù)2, ...])
- 使用 call 方法的時候:
1、會立即執(zhí)行函數(shù)
2、第一個參數(shù)是你要改變的函數(shù)內部的 this 指向
3、第二個參數(shù)是一個 數(shù)組,數(shù)組里面的每一項依次是向函數(shù)傳遞的參數(shù)(和call方法的主要區(qū)別點)
var obj = { name: 'Jack' } function fn(a, b) { console.log(this) console.log(a) console.log(b) } fn(1, 2) fn.apply(obj, [1, 2])
fn(1,2)
的時候,函數(shù)內部的 this 指向 window(函數(shù)被直接調用)fn.call(obj, 1, 2)
的時候,函數(shù)內部的 this 就指向了 obj 這個對象
3、bind()方法
bind
方法是附加在函數(shù)調用后面使用,可以忽略函數(shù)本身的 this 指向- 和 call / apply 有一些不一樣,就是不會立即執(zhí)行函數(shù),而是返回一個已經改變了 this 指向的函數(shù)
- 語法: var newFn = 函數(shù)名.bind(要改變的 this 指向); newFn(傳遞參數(shù))
var obj = { name: 'Jack' } function fn(a, b) { console.log(this) console.log(a) console.log(b) } fn(1, 2) var newFn = fn.bind(obj) newFn(1, 2)
- bind 調用的時候,不會執(zhí)行 fn 這個函數(shù),而是返回一個新的函數(shù)
- 這個新的函數(shù)就是一個改變了 this 指向以后的 fn 函數(shù)
fn(1, 2)
的時候 this 指向 windownewFn(1, 2)
的時候執(zhí)行的是一個和 fn 一摸一樣的函數(shù),只不過里面的 this 指向改成了 obj
三、回調函數(shù)中this指向
這里我們補充一下在回調函數(shù)運用中this指向(也是容易混淆的知識點)
1、對象方法作為回調函數(shù)
如果回調函數(shù)是一個對象的方法,并且是以對象方法的方式傳遞進去的,那么 this 通常會指向該對象。
var myObject = { value: 10, callbackFunction: function () { console.log(this.value); } }; [1,2,3].forEach(() => { myObject.callbackFunction() }); //輸出三個10
在這個例子中,forEach 是數(shù)組的方法,myObject.callbackFunction 作為回調函數(shù)傳遞給 forEach。當 forEach 調用這個回調函數(shù)時,this 仍然指向 myObject,因為這個函數(shù)本質上還是 myObject 的一個方法。
2、箭頭函數(shù)作為回調函數(shù)
箭頭函數(shù)沒有自己的 this,它會繼承外層作用域的 this(依據(jù)詞法作用域規(guī)則)。
function outerFunction() { this.name = "Outer"; var array = [1, 2, 3]; array.forEach(() => { console.log(this.name); //打印三次Outer }); } new outerFunction();
根據(jù)上面兩個例子,這里我們介紹一下普通函數(shù)和箭頭函數(shù)在確定this指向時的一些區(qū)別:
1、普通函數(shù)
普通函數(shù)在函數(shù)定義時會確定函數(shù)的作用域,但不會明確函數(shù)中this的指向。普通函數(shù)中this的指向是在函數(shù)被調用時被確定(指向調用者或者全局對象)
2、箭頭函數(shù)
箭頭函數(shù)由于其本身不會生成this,其this繼承自外層作用域。箭頭函數(shù)在定義時不僅會確定作用域,而且會捕獲外層作用域的this作為自身的this,箭頭函數(shù)的this在定義時就已經確定,在其后的函數(shù)調用時,無論調用箭頭函數(shù)的是誰它的this指向都不會發(fā)生改變。
以上面這個例子為例:
箭頭函數(shù)定義在 outerFunction 這個函數(shù)內部,注意不是定義在 forEach 方法內。具體可以了解一下函數(shù)傳參的步驟,這里箭頭函數(shù)是先在 outerFunction 這個函數(shù)內部定義,之后才作為參數(shù)傳給 forEach 方法。箭頭函數(shù)繼承了 outerFunction 函數(shù)的this,并在之后被 forEach 方法調用時不會發(fā)生改變。
3、回調函數(shù)指向全局對象的常見情況
當普通函數(shù)作為回調函數(shù),并且這個普通函數(shù)是被一個全局函數(shù)(如 setTimeout、setInterval)或者在全局作用域中獨立調用的函數(shù)(沒有通過對象來調用)調用時,在非嚴格模式下,this 通常會指向全局對象。
//被一個全局函數(shù)調用 setTimeout(function () { console.log(this); }, 1000);
//在全局作用域中獨立調用 function outer(){ inner() } function inner(){ console.log(this); } outer()
四、總結與常見錯誤示例
this指向:1、普通函數(shù):誰調用函數(shù),this就指向誰,沒有調用者就指向全局對象Window
2、箭頭函數(shù):箭頭函數(shù)不會創(chuàng)建this,它的this繼承自上層作用域中的this
1、案例一
//回調函數(shù)中錯誤使用this var person = { name: "Eve", greetLater: function () { setTimeout(function () { console.log(this.name); // 此處的function為普通函數(shù),作為setTimeout的參數(shù)this指向全局對象 }, 1000); } }; person.greetLater();
此處的function為普通函數(shù),被一個全局函數(shù)調用,其this指向全局對象 Window 。
要想輸出 Eve ,將此處的普通函數(shù)改為箭頭函數(shù)即可。
var person = { name: "Eve", greetLater: function () { setTimeout( ()=> { console.log(this.name); // 輸出Eve }, 1000); } }; person.greetLater();
2、案例二
//在嵌套函數(shù)中混淆 this 指向 var outer = { name: "Outer Object", innerFunction: function () { var inner = { name: "Inner Object", nestedFunction: function () { console.log(this.name); // 這里 this 指向 inner,而不是 outer } }; inner.nestedFunction(); } }; outer.innerFunction();
nestedFunction 是作為 inner 對象的方法被調用,this指向 inner 對象(根據(jù)普通函數(shù) this 指向的規(guī)則,當函數(shù)作為對象的方法被調用時,this 會指向調用該函數(shù)的對象。)
4.2.1、使用外層作用域this的方法
想要訪問到 outer 對象的 name 屬性,可以使用以下兩種方法:
1、保存外層 this 的引用
var outer = { name: "Outer Object", innerFunction: function () { var self = this; var inner = { name: "Inner Object", nestedFunction: function () { console.log(self.name); // 現(xiàn)在可以訪問到 outer 的 name 屬性,輸出 "Outer Object" } }; inner.nestedFunction(); } }; outer.innerFunction();
在方法的開頭先把 this 保存到一個變量中(通常命名為 self 或 that 等),然后在 inner 對象中使用這個保存的變量來訪問 outer 對象的屬性。
2、使用箭頭函數(shù)
var outer = { name: "Outer Object", innerFunction: function () { var inner = { name: "Inner Object", nestedFunction: () => { console.log(this.name); // 輸出 "Outer Object" } }; inner.nestedFunction(); } }; outer.innerFunction();
將 nestedFunction 改為箭頭函數(shù),因為箭頭函數(shù)會繼承外層作用域的 this,在這里外層作用域是 outer.innerFunction(),其 this 指向 outer 對象,所以箭頭函數(shù)里的 this 也能指向 outer 對象。
總結
到此這篇關于JavaScript中this(普通函數(shù)、箭頭函數(shù)、 函數(shù)運用)的文章就介紹到這了,更多相關JS中this普通函數(shù)、箭頭函數(shù)、回調函數(shù)運用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用 Jest 和 Supertest 進行接口端點測試實例詳解
這篇文章主要介紹了使用 Jest 和 Supertest 進行接口端點測試,結合實例形式詳細分析了使用 Jest 和 Supertest 進行接口端點測試具體原理、操作技巧與相關注意事項,需要的朋友可以參考下2020-04-04