<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
箭頭函數作為ES6新增的語法,在使用時不僅能使得程式碼更加簡潔,而且在某些場景避免this指向問題。但是箭頭函數不是萬能的,也有自己的缺點以及不適用的場景,雖然可以解決this只想問題,但是也可能會帶來this指向問題。具體場景具體分析,本文就深入探討箭頭函數。
箭頭函數沒有自己的this,其this取決於上下文中定義的this。
學懂 JavaScript 語言,一個標誌就是理解下面兩種寫法,可能有不一樣的結果。
var obj = { foo: function () {} }; var foo = obj.foo; // 寫法一 obj.foo() // 寫法二 foo()
上面程式碼中,雖然obj.foo
和foo
指向同一個函數,但是執行結果可能不一樣。請看下面的例子。
var obj = { foo: function () { console.log(this.bar) }, bar: 1 }; var foo = obj.foo; var bar = 2; obj.foo() // 1 foo() // 2
這種差異的原因,就在於函數體內部使用了this
關鍵字。很多教科書會告訴你,this
指的是函數執行時所在的環境。對於obj.foo()
來說,foo
執行在obj
環境,所以this
指向obj
;對於foo()
來說,foo
執行在全域性環境,所以this
指向全域性環境。所以,兩者的執行結果不一樣。
這種解釋沒錯,但是教科書往往不告訴你,為什麼會這樣?也就是說,函數的執行環境到底是怎麼決定的?舉例來說,為什麼obj.foo()
就是在obj
環境執行,而一旦var foo = obj.foo
,foo()
就變成在全域性環境執行?
本文就來解釋 JavaScript 這樣處理的原理。理解了這一點,你就會徹底理解this
的作用。
JavaScript 語言之所以有this
的設計,跟記憶體裡面的資料結構有關係。
var obj = { foo: 5 };
上面的程式碼將一個物件賦值給變數obj
。JavaScript 引擎會先在記憶體裡面,生成一個物件{ foo: 5 }
,然後把這個物件的記憶體地址賦值給變數obj
。
也就是說,變數obj
是一個地址(reference)。後面如果要讀取obj.foo
,引擎先從obj
拿到記憶體地址,然後再從該地址讀出原始的物件,返回它的foo
屬性。
原始的物件以字典結構儲存,每一個屬性名都對應一個屬性描述物件。舉例來說,上面例子的foo
屬性,實際上是以下面的形式儲存的。
{ foo: { [[value]]: 5 [[writable]]: true [[enumerable]]: true [[configurable]]: true } }
注意,foo
屬性的值儲存在屬性描述物件的value
屬性裡面。
這樣的結構是很清晰的,問題在於屬性的值可能是一個函數。
var obj = { foo: function () {} };
這時,引擎會將函數單獨儲存在記憶體中,然後再將函數的地址賦值給foo
屬性的value
屬性。
{ foo: { [[value]]: 函數的地址 ... } }
由於函數是一個單獨的值,所以它可以在不同的環境(上下文)執行。
var f = function () {}; var obj = { f: f }; // 單獨執行 f() // obj 環境執行 obj.f()
JavaScript 允許在函數體內部,參照當前環境的其他變數。
var f = function () { console.log(x); };
上面程式碼中,函數體裡面使用了變數x
。該變數由執行環境提供。
現在問題就來了,由於函數可以在不同的執行環境執行,所以需要有一種機制,能夠在函數體內部獲得當前的執行環境(context)。所以,this
就出現了,它的設計目的就是在函數體內部,指代函數當前的執行環境。
var f = function () { console.log(this.x); }
上面程式碼中,函數體裡面的this.x
就是指當前執行環境的x
。
var f = function () { console.log(this.x); } var x = 1; var obj = { f: f, x: 2, }; // 單獨執行 f() // 1 // obj 環境執行 obj.f() // 2
上面程式碼中,函數f
在全域性環境執行,this.x
指向全域性環境的x
。
在obj
環境執行,this.x
指向obj.x
。
回到本文開頭提出的問題,obj.foo()
是通過obj
找到foo
,所以就是在obj
環境執行。一旦var foo = obj.foo
,變數foo
就直接指向函數本身,所以foo()
就變成在全域性環境執行。
1.箭頭函數沒有arguments
參數列,普通函數可以直接獲取到
arguments
是呼叫函數時,傳遞給函數的一個類似陣列的物件,幾乎所有的函數都有此區域性變數,可直接存取並使用傳遞給函數的參數列,箭頭函數除外。該變數不是陣列物件,只是類似於陣列,沒有陣列的常用方法。
let fn1 = () => { console.log('arguments', arguments); } fn1(1, 2); // arguments is not defined let fn2 = function() { console.log('arguments', arguments); } fn2(1, 2); // Arguments物件,可檢視具體的引數
2.無法通過apply、call、bind改變this的指向。箭頭函數的this預設指向父作用域或者當前呼叫物件,無法通過call等修改,但是function申明的函數可以修改this
this指向是js中經常容易出錯的地方。箭頭函數的this指向是固定的,一般都是指向父作用域,預設指向window,不能在apply、call、bind中改變this的指向。普通函數的this指向不是固定的,有可能根據傳入的物件改變。
console.log('this1', this); // 指向window let fn3 = () => { console.log('this2', this); // 指向window } fn3.call({x: 'y'}); // 傳入新的物件 // fn3.apply({x: 'y'}); let fn4 = function() { console.log('this3', this); // 指向{x: 'y'} } fn4.call({x: 'y'});
1.物件的方法,不建議使用箭頭函數
let obj = { key: 'key', getKey: () => { return this.key; }, getKey2() { return this.key; } }; obj.getKey(); // this指向window,返回值取決於window中是否有對應的屬性 obj.getKey2(); // this指向obj,返回 'key'
2.物件的原型的方法,不建議使用箭頭函數
每個物件都有原型,原型也是一個物件,因此也不能新增箭頭函數的方法
let obj = { key: 'key' }; obj.__proto__.getKey = () => { console.log('this', this); // this指向window return this.key; } obj.getKey();
3.箭頭函數不能用作建構函式
定義一個建構函式可通過函數定義或者使用class定義一個類。箭頭函數不能用作建構函式,可使用普通函數
let fn5 = (userName, passwd) => { this.userName = userName; this.passwd = passwd; } let f1 = new fn5('張三', '123'); // fn5 is not a constructor console.log(f1.userName); let fn6 = function (userName, passwd) { this.userName = userName; this.passwd = passwd; } let f2 = new fn6('張三', '123'); console.log(f2.userName); // 張三
4.監聽事件中需要使用this時不建議使用箭頭函數
比如在addEventListener
中,如果要在回撥函數中使用this,那麼就不建議使用箭頭函數,而是應該普通函數,更好的是使用已定義的函數名,便於回收事件監聽,避免可能的記憶體漏失。
dom.addEventListener('click', () => { console.log('this', this); // this指向window })
5.Vue的生命週期以及methods中的方法不建議使用箭頭函數
頁面中建立的Vue範例,本質上來說也就是一個物件,其生命週期就是對應的屬性,methods也是一個物件。在Vue的生命週期或者methods中使用箭頭函數,則this的指向將不是當前Vue範例,而是window物件,如果在方法中使用了this,則可能會丟擲錯誤。
export default { mounted() {}, // mounted: () => {} methods: { getKey() {}, // getKey: () => {} } }
到此這篇關於詳解JavaScript什麼情況下不建議使用箭頭函數的文章就介紹到這了,更多相關JavaScript箭頭函數內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45