【JavaScript】聊聊js中關于this的指向
前言
最近在看回JavaScript的面試題,this 指向問題是入坑前端必須了解的知識點,現在迎來了ES6+的時代,因為箭頭函數的出現,所以感覺有必要對 this 問題梳理一下,所以剛好總結一下JavaScript中this指向的問題。
什么是JavaScript
在了解this指向的問題前,首先得了解一下什么是JavaScript。
JavaScript(簡稱“JS”)是一種具有函數優先的輕量級,解釋型或即時編譯型的編程語言。JavaScript基于原型編程、多范式的動態腳本語言,并且支持面向對象、命令式、聲明式、函數式編程范式、支持函數式編程、閉包、基于原型的繼承等高級功能。
什么是this
面向對象語言中 this 表示當前對象的一個引用。
但在 JavaScript 中 this 不是固定不變的,它會隨著執行環境的改變而改變。
在方法中,this 表示該方法所屬的對象。
如果單獨使用,this 表示全局對象。
在函數中,this 表示全局對象。
在函數中,在嚴格模式下,this 是未定義的(undefined)。
在事件中,this 表示接收事件的元素。
類似 call() 和 apply() 方法可以改變 this 的指向 ,引用到任何對象。
所以this的指向完全取決于函數的調用方式。
this的指向
接下來我將在(非嚴格模式下)通過下面例圖與例子來了解this的指向。

1.不使用new關鍵字,使用dot調用。
var obj = {
name: 'bug',
obj2: {
name: 'bug2',
fn: function () {
console.log(this.name); //bug2
}
}
}
//此處通過obj.obj2.fn(),調用了obj中的obj2中的fn函數,此時fn函數中this的指向為dot (.) 前面的對象,即為obj2,obj2中的name即是bug2。
obj.obj2.fn();
2.使用new關鍵字調用。
function fn() {
this.x = 1;
}
//此處通過new關鍵字生成了一個實例對象,此時的this指向了該實例對象fn
var obj = new fn();
//此時的obj的結構為{x:1},所以obj.x=1
obj.x // 1
講到new關鍵字就剛好衍生出看另外一個關鍵點,如果我用new去創建一個實例對象,這個時候實例對象有返回值呢?
通常情況下是不應該有顯式的返回值的。
但是如果當return返回的是一個對象,那么將返回該對象。
但是如果當return返回非對象類型(比如數字、字符串等),那么就不會影響到new關鍵字對對象的創建。
以下就用幾個例子來驗證一下:
①return 空對象
function fn()
{
this.name= 'bug';
//此處return回了一個對象
return {};
}
var obj = new fn();
//此時因為return回的是一個對象,所以此時的obj的結構是返回的空對象{},所以obj.name才會是undefined
console.log(obj.name); //undefined
②return一個非空對象
function fn()
{
this.name= 'bug';
//此處return回了一個對象
return {name:'bug2'};
}
var obj = new fn();
//此時因為return回的是一個非空對象,所以此時的obj的結構是返回的非空對象{name:'bug2'},所以obj.name是bug2
console.log(obj.name); //bug2
③返回數字
function fn()
{
this.name= 'bug';
//此處return回了一個數字
return 11;
}
var obj = new fn();
//此時因為return回的是一個數字,所以此時返回的實例對象不受影響,結構是{name:'bug'},所以obj.name是bug
console.log(obj.name); //bug
④返回字符串
function fn()
{
this.name= 'bug';
//此處return回了一個字符串
return 'xxxxx';
}
var obj = new fn();
//此時因為return回的是一個字符串,所以此時返回的實例對象不受影響,結構是{name:'bug'},所以obj.name是bug
console.log(obj.name); //bug
既然現在進入了Es6+的時代了,就不得不講一講箭頭函數的this指向了
1.什么是箭頭函數
箭頭函數是ECMAScript 6中新增的一種函數定義方式,也被稱為Lambda函數。 它使用箭頭(=>)符號來替代傳統的function關鍵字,從而更簡潔地定義函數,使代碼更加簡潔易讀。
箭頭函數有以下特點:
①語法簡潔:箭頭函數表達式的語法比普通函數更簡潔,使用箭頭(=>)符號來定義函數,可以省略一些不必要的語法元素,如function關鍵字、大括號和參數列表周圍的括號(如果只有一個參數)。
②this綁定:箭頭函數不綁定自己的this,它會捕獲定義時所在上下文的this值,這使得在回調函數或嵌套函數中使用箭頭函數時,this的指向更加明確和可預測。
③沒有arguments對象:箭頭函數沒有自己的arguments對象,這意味著它們無法訪問到傳統函數的特殊屬性arguments。
④不能用作構造器:箭頭函數不能作為構造器使用,即它們不能用作類的實例化。
2.箭頭函數的this指向
因為箭頭函數不綁定自己的this,它會捕獲定義時所在上下文的this值。所以簡單的說就是箭頭函數沒有屬于自己的this。
一下用個例子來簡單了解。
①正常function函數
const obj={
mythis: function(){
console.log(this) //指向了上一級對象obj
}
}
obj.mythis() //返回了obj對象
②箭頭函數
const obj={
mythis: ()=>{
console.log(this) //因為箭頭函數沒有自己的this,所以指向的是window
}
}
obj.mythis() //返回了window
哦這里還有一個坑,就是前面說的,this指向完全取決于函數的調用方式。
你再看看這道題最終返回的是什么?
const obj={
mythis: function(){
console.log(this)
}
}
var a =obj.mythis
a()
點擊查看答案與解析
//是不是有小伙伴認為這里使用的是function,所以返回的還是mythis的上一級對象obj ???
//不不不,這時候返回的是window!因為this指向完全取決于函數的調用方式
//上述例子①為何返回的是obj是因為它是直接obj.mythis()去調用,this指向是mythis的上一級對象
//但是本例子是通過減mythis直接賦值給a,此時,a 成為一個普通的函數引用,它只是 obj.mythis 的一個復制,并沒有 obj 對象的上下文信息
//所以,當 a 作為一個普通函數調用時(不作為對象的方法調用),在非嚴格模式下,JavaScript 中的 this 默認指向全局對象 window
const obj={
mythis: function(){
console.log(this)
}
}
var a =obj.mythis
a() //window
當然,this的指向除了調用的方式不同而不同的同時,也可以通過其它方式強制改變this的指向!那就是使用call、apply、bind。
什么是call、apply、bind,區別是什么?
1.什么是call?
call方法可以接受兩個參數,第一個參數就是this的指向,指向xxx,第二個參數為一個參數列表。當第一個參數為null或者undefined時,this默認指向window。
function fn(...args) {
console.log(this, args);
}
let obj = {
name: "bug"
}
//將fn的this指向obj,并傳入參數列表 1,2
fn.call(obj, 1, 2); //{name:'bug'} , [1,2]
//次數fn中的this指向為window
fn(1, 2) //window , [1,2]
//當第一個參數為null時,this指向為window
fn.call(null,[1,2]);//window , [1,2]
//當第一個參數為undefined時,this指向為window
fn.call(undefined,[1,2]);//window , [1,2]
2.什么是apply?
apply方法可以接受兩個參數,第一個參數就是this的指向,指向xxx,第二個參數為一個參數數組。當第一個參數為null或者undefined時,this默認指向window。
function fn(...args) {
console.log(this, args);
}
let obj = {
name: "bug"
}
//將fn的this指向obj,并傳入參數數組 [[1,2]]
fn.apply(obj, [1,2]); //{name:'bug'} , [[1,2]]
//次數fn中的this指向為window
fn([1,2]) //window , [[1,2]]
//當第一個參數為null時,this指向為window
fn.apply(null,[1,2]);//window , [[1,2]]
//當第一個參數為undefined時,this指向為window
fn.apply(undefined,[1,2]);//window , [[1,2]]
3.什么是bind?
bind方法跟call、apply十分相似,第一個參數也是this的指向,第二個參數傳的也是一個參數列表,但是!這個參數列表可以分多次傳入!并且改變完this的指向并不會立刻執行,而是返回一個已經永久改變this指向的函數
function fn(...args) {
console.log(this, args);
}
let obj = {
name: "bug"
}
const bindFn = fn.bind(obj); //this變為obj,且不會立馬執行
bindFn(1, 2) //得通過調用才會執行,并傳入參數列表1,2,最終this指向obj {name:'bug'}
fn(1, 2) //this執行window
4.call、apply、bind的區別是什么?
①三者都可以改變函數的 this 對象指向
②三者第一個參數都是 this 要指向的對象,如果如果沒有這個參數或參數為 undefined 或 null,則默認指向全局 window
③三者都可以傳參,但是 apply 是數組,而 call 是參數列表,且 apply 和 call 是一次性傳入參數,而 bind 可以分為多次傳入bind 是返回綁定this之后的函數,apply、call 則是立即執行
總結
簡單來說,this的指向不是固定不變的,它會隨著執行環境的改變而改變,具體怎么改變完全取決于函數的調用方式。
箭頭函數沒有屬于自己的this,作為方法的箭頭函數this的指向是當前的上下文。
我是剛畢業一年多的小菜鳥,上述為個人學習整理內容,水平有限,如有錯誤之處,望各位園友不吝賜教!如果覺得不錯,請點擊推薦和關注!謝謝~??????? [鮮花][鮮花][鮮花]

浙公網安備 33010602011771號