摘要:調用在中,通過的形式調用一個構造函數,會創建這個構造函數實例,而這個實例的指向創建的這個實例。如下例所示,在構造函數內部使用并沒有改變全局變量的值。顯然,箭頭函數是不能用來做構造函數。
關于javascript中this指向的問題,現總結如下,如有不正確,歡迎指正。
javascript中,this的指向并不是在函數定義的時候確定的,而是在其被調用的時候確定的。也就是說,函數的調用方式決定了this指向。記住:this 就是一個指針,指向我們調用函數的對象。
在此將javascript中this的調用方式分為以下幾種:
1、直接調用:直接調用是指通過 funName(..) 這種方式調用。此時,函數內部的this指向全局變量。
function foo() { console.log(this === global); } foo(); //true
注意:直接調用并不是僅僅指在全局作用域下進行調用,而是說在任何作用域下通過funName(..) 這種方式調用。如下兩個例子亦是屬于直接調用方式:
a、使用bind函數改變外層函數的作用域,然后在內層直接調用,其this指向依然是全局變量:
function foo() { console.log(this === global); } function foo1() { foo(); }; var foo2 = foo1.bind({}); //改變foo1函數的作用域 foo2(); //true 依然指向全局變量
b、函數表達式,將一個函數賦值給一個變量,然后通過直接調用的調用方式調用此函數,其this指向依然是全局變量:
var obj = { foo: function(){ console.log(this === obj); //false console.log(this === global);//true } } var f = obj.foo; f();2、方法調用:
方法調用是指通過對象來調用其方法函數,類似于obj.foo(..)的調用方式。此時,this指向調用該方法的對象,注意,是最終調用該方法的對象。
var obj = { foo1: function(){ console.log(this === obj); } } obj.foo2 = function() { console.log(this === obj); } function foo3() { console.log(this === obj); } obj.foo3 = foo3; obj.foo1(); // true obj.foo2(); // true obj.foo3(); // true3、new 調用:
在es5中,通過 new Constructor() 的形式調用一個構造函數,會創建這個構造函數實例,而這個實例的this指向創建的這個實例。如下例所示,在構造函數內部使用this.name并沒有改變全局變量name的值。
var name = "全局"; function Person(name){ this.name = name; } var p = new Person("局部"); console.log(p.name); console.log(name);
上述三種情況是常見的調用方式,以下還有一些特殊的調用方式:如bind、call、apply以及es6中箭頭函數。
4、bind函數對this的影響bind函數用于綁定this的指向,并且返回一個綁定函數,此函數綁定了this指向,注意:綁定函數的this指向將不可再次更改。
var obj = {}; function foo1() { console.log(this === obj); } foo1(); //false 此時屬于上述直接調用的方式,因此其this指向global var foo2 = foo1.bind(obj); foo2(); //true 綁定函數的this指向其綁定的對象 /** * 注意:綁定函數的this指向不可更改 */ var foo3 = foo2.bind({"a": 1}); foo3(); //true foo2.apply({"a": 1}); //true foo2.call({"a": 1}); //true5、apply和call對this指向的影響
apply和call亦可以用于改變this指向,并且返回執行結果。關于bind、apply和call的區別以及實現可以參考我的博客:《bind、call、apply的區別與實現》,在此不做重復說明。需要注意的一點是:apply和call不可以改變綁定函數(使用bind返回的函數)的this指向。
var obj = {}; function foo1() { console.log(this === obj); } var foo2 = foo1.bind({"a": 1}); /** * 注意:此處并不是綁定函數,因此其返回值依然是ture,即apply改變其this指向。 */ foo1.apply(obj); //true /** * 注意:此處是綁定函數,因此不可再通過apply和call的形式改變其this指向。 */ foo2.apply(obj); //false6、es6箭頭函數中的this
箭頭函數沒有自己的this綁定,其使用的this是其直接父級函數的this。也就是說,箭頭函數內部的this是由其直接外層函數(方法)決定的,而外層函數中的this是由其調用方式決定的。
const obj = { foo: function() { const inner = () => { console.log(this === obj); }; inner(); }, far: function() { return () => { console.log(this === obj); } } } /** * inner()內的this是foo的this,其指向取決于foo的調用方式 */ obj.foo(); //true var foo1 = obj.foo; foo1(); //false 此時應該指向global const far1 = obj.far(); far1(); //true const far2 = obj.far; far2()(); //false 此時應該指向global6.1 箭頭函數常見錯誤及其解決方案:
在對象上定義函數:
const test = { array: [1, 2, 3], sum: () => { console.log(this === window); // => true return this.array.reduce((result, item) => result + item); } }; test.sum(); // TypeError: Cannot read property "reduce" of undefined
原因就是,箭頭函數沒有它自己的this值,箭頭函數內的this值繼承自外圍作用域。
對象方法內的this指向調用這個方法的對象,如果使用箭頭函數,this和對象方法在調用的時候所處環境的this值一致。因為 test.sum()是在全局環境下進行調用,此時this指向全局。
解決方法也很簡單,使用函數表達式或者方法簡寫(ES6 中已經支持)來定義方法,這樣能確保 this 是在運行時是由包含它的上下文決定的。
const test = { array: [1, 2, 3], sum() { console.log(this === test); // => true return this.array.reduce((result, item) => result + item); } }; test.sum(); // 6
定義原型方法
在對象原型上定義函數也是遵循著一樣的規則
function Person(name) { this.name = name; } Person.prototype.sayName = () => { console.log(this === window); // => true return this.name; }; const cat = new Person("Mew"); cat.sayName(); // => undefined
使用傳統的函數表達式就能解決問題
function Person(name) { this.name = name; } Person.prototype.sayName = function() { console.log(this === Person); // => true return this.name; }; const cat = new Person("Mew"); cat.sayName(); // => Mew
定義事件回調函數
this是JS中非常強大的特點,他讓函數可以根據其調用方式動態的改變上下文,然后箭頭函數直接在聲明時就綁定了this對象,所以不再是動態的。
在客戶端,在DOM元素上綁定事件監聽函數是非常普遍的行為,在DOM事件被觸發時,回調函數中的this指向該DOM,但是,箭頭函數在聲明的時候就綁定了執行上下文,要動態改變上下文是不可能的,在需要動態上下文的時候它的弊端就凸顯出來:
const button = document.getElementById("myButton"); button.addEventListener("click", () => { console.log(this === window); // => true this.innerHTML = "Clicked button"; });
因為這個回調的箭頭函數是在全局上下文中被定義的,所以他的this是window。換句話說就是,箭頭函數預定義的上下文是不能被修改的,這樣 this.innerHTML 就等價于 window.innerHTML,而后者是沒有任何意義的。
使用函數表達式就可以在運行時動態的改變 this:
const button = document.getElementById("myButton"); button.addEventListener("click", function() { console.log(this === button); // => true this.innerHTML = "Clicked button"; });
定義構造函數
如果使用箭頭函數會報錯。
顯然,箭頭函數是不能用來做構造函數。
const Message = (text) => { this.text = text; }; const helloMessage = new Message("Hello World!"); // Throws "TypeError: Message is not a constructor"
理論上來說也是不能這么做的,因為箭頭函數在創建時this對象就綁定了,更不會指向對象實例。
箭頭函數帶來了很多便利。恰當的使用箭頭函數可以讓我們避免使用早期的.bind()函數或者需要固定上下文的地方并且讓代碼更加簡潔。
箭頭函數帶來了很多便利。恰當的使用箭頭函數可以讓我們避免使用早期的.bind()函數或者需要固定上下文的地方并且讓代碼更加簡潔。
以上是個人對JavaScript中this的理解,歡迎您的留言。
歡迎訪問我的個人博客了解更多信息。
參考文獻:
1、《JavaScript 的 this 指向問題深度解析》
2、《ES6使用箭頭函數注意點》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98170.html
摘要:他的組成如下對的就是你關注的那個變量對象作用域鏈跟閉包相關由于是單線程的,一次只能發生一件事情,其他事情會放在指定上下文棧中排隊。 閉包和this,是兩個相當高頻的考點,然而你有沒有想過,實際上他們兩個都跟同一個知識點相關? 有請我們的這篇文章的主角,執行上下文 執行上下文 執行上下文是什么 可以簡單理解執行上下文是js代碼執行的環境,當js執行一段可執行代碼時,會創建對應的執行上下文...
摘要:而在構造函數中,返回了的實例對象。在中直接返回過的實例,這里的是的真正構造函數最后對外暴露入口時,將字符與對等起來。因此當我們直接使用創建一個對象時,實際上是創建了一個的實例,這里的正真構造函數是原型中的方法。 showImg(https://segmentfault.com/img/remote/1460000008749398); 早幾年學習前端,大家都非常熱衷于研究jQuery源...
摘要:但是有一個總的原則,那就是指的是,調用函數的那個對象。純粹的函數調用這是函數的最通常用法,屬于全局性調用,因此就代表全局對象此時的是作為對象方法的調用函數還可以作為某個對象的方法調用,這時就指這個上級對象。 函數this的用法 his是Javascript語言的一個關鍵字。 它代表函數運行時,自動生成的一個內部對象,只能在函數內部使用。比如 function test(){ thi...
摘要:本文節選自設計模式就該這樣學使用備忘錄模式實現草稿箱功能大家都用過網頁中的富文本編輯器,編輯器通常都會附帶草稿箱撤銷等操作。首先創建發起人角色編輯器類。 本文節選自《設計模式就該這樣學》1 使用備忘錄模式實現草稿箱功能大家都用過網頁中的富文本編輯器,編輯器通常都會附帶草稿箱、撤銷等操作。下面用一段代碼來實現一個...
閱讀 971·2021-11-24 10:42
閱讀 3516·2021-11-19 11:34
閱讀 2652·2021-09-29 09:35
閱讀 2535·2021-09-09 09:33
閱讀 683·2021-07-26 23:38
閱讀 2522·2019-08-30 10:48
閱讀 1392·2019-08-28 18:07
閱讀 426·2019-08-26 13:44