源:Loong Panda
一、不同環(huán)境下的this的指向:
1、this的指向是在函數(shù)運(yùn)行時確定下來的,而不是創(chuàng)建的時候,誰調(diào)用就指向誰。
2、ES5中普通函數(shù)this指向window,ES6中普通函數(shù)this指向?yàn)閡ndefined;
3、事件的this指向,為事件源本身
4、定時器的this指向,為window.
5、構(gòu)造函數(shù)內(nèi)部的this指向new出來的這個對象
注: 嚴(yán)格模式下,普通函數(shù)和全局環(huán)境的this為undefined
consolog.log(this) // window
function test() { console.log(111, this) // window } test()
注: 'use strict' 嚴(yán)格模式下
consolog.log(this) // undefined
function test() { console.log(111, this) // undefined } test()
let obj3={
say() {
console.log(6666, this) // obj3
return function () {
console.log(7777, this) // window
}
}
}
obj3.say()()
function Person(e) {
this.name=e console.log(5555, this)
}
let a=new Person('李四1') // Person { name: '李四1'}
let b=new Person('李四2') // Person { name: '李四2'}
let obj2={
name: '張三',
say: function () {
console.log(333, this) // obj2
setTimeout(function () {
console.log(444, this) // window
}, 1000)
}
}
obj2.say()
let obj3={
say() {
console.log(6666, this) // obj3
return ()=> {
console.log(7777, this) // obj3
}
}
}
obj3.say()()
二、改變this指向的方法:
1、箭頭函數(shù) -----> 只適合現(xiàn)代瀏覽器;
2、bind() -----> 第一個是參數(shù)是指向誰,第二個以后的參數(shù)是自定義的參數(shù),需要單獨(dú)調(diào)用才執(zhí)行,如: bind(obj)()
3、call(xx, arg1, arg2) -----> 第一個是參數(shù)是指向誰,第二個以后的參數(shù)是自定義的參數(shù)
4、apply(xx, [arg1, arg2]) -----> 第一個是參數(shù)是指向誰,第二個的參數(shù)是自定義的參數(shù),數(shù)據(jù)類型必須是數(shù)組
在 Java 等面向?qū)ο蟮恼Z言中,this 關(guān)鍵字的含義是明確且具體的,即指代當(dāng)前對象。一般在編譯期確定下來,或稱為編譯期綁定。而在 JavaScript 中,this 是動態(tài)綁定,或稱為運(yùn)行期綁定的,這就導(dǎo)致 JavaScript 中的 this 關(guān)鍵字有能力具備多重含義,帶來靈活性的同時,也為初學(xué)者帶來不少困惑。本文僅就這一問題展開討論,閱罷本文,讀者若能正確回答 JavaScript 中的 What 's this 問題,那就會覺得花費(fèi)這么多功夫,撰寫這樣一篇文章是值得的。
在 Java 中定義類經(jīng)常會使用 this 關(guān)鍵字,多數(shù)情況下是為了避免命名沖突,比如在下面例子的中,定義一個 Point 類,很自然的,大家會使用 x,y 為其屬性或成員變量命名,在構(gòu)造函數(shù)中,使用 x,y 為參數(shù)命名,相比其他的名字,比如 a,b,也更有意義。這時候就需要使用 this 來避免命名上的沖突。另一種情況是為了方便的調(diào)用其他構(gòu)造函數(shù),比如定義在 x 軸上的點(diǎn),其 x 值默認(rèn)為 0,使用時只要提供 y 值就可以了,我們可以為此定義一個只需傳入一個參數(shù)的構(gòu)造函數(shù)。無論哪種情況,this 的含義是一樣的,均指當(dāng)前對象。
清單 1. Point.java
由于其運(yùn)行期綁定的特性,JavaScript 中的 this 含義要豐富得多,它可以是全局對象、當(dāng)前對象或者任意對象,這完全取決于函數(shù)的調(diào)用方式。JavaScript 中函數(shù)的調(diào)用有以下幾種方式:作為對象方法調(diào)用,作為函數(shù)調(diào)用,作為構(gòu)造函數(shù)調(diào)用,和使用 apply 或 call 調(diào)用。下面我們將按照調(diào)用方式的不同,分別討論 this 的含義。
在 JavaScript 中,函數(shù)也是對象,因此函數(shù)可以作為一個對象的屬性,此時該函數(shù)被稱為該對象的方法,在使用這種調(diào)用方式時,this 被自然綁定到該對象。
清單 2. point.js
函數(shù)也可以直接被調(diào)用,此時 this 綁定到全局對象。在瀏覽器中,window 就是該全局對象。比如下面的例子:函數(shù)被調(diào)用時,this 被綁定到全局對象,接下來執(zhí)行賦值語句,相當(dāng)于隱式的聲明了一個全局變量,這顯然不是調(diào)用者希望的。
清單 3. nonsense.js
對于內(nèi)部函數(shù),即聲明在另外一個函數(shù)體內(nèi)的函數(shù),這種綁定到全局對象的方式會產(chǎn)生另外一個問題。我們?nèi)匀灰郧懊嫣岬降?point 對象為例,這次我們希望在 moveTo 方法內(nèi)定義兩個函數(shù),分別將 x,y 坐標(biāo)進(jìn)行平移。結(jié)果可能出乎大家意料,不僅 point 對象沒有移動,反而多出兩個全局變量 x,y。
清單 4. point.js
這屬于 JavaScript 的設(shè)計(jì)缺陷,正確的設(shè)計(jì)方式是內(nèi)部函數(shù)的 this 應(yīng)該綁定到其外層函數(shù)對應(yīng)的對象上,為了規(guī)避這一設(shè)計(jì)缺陷,聰明的 JavaScript 程序員想出了變量替代的方法,約定俗成,該變量一般被命名為 that。
清單 5. point2.js
JavaScript 支持面向?qū)ο笫骄幊蹋c主流的面向?qū)ο笫骄幊陶Z言不同,JavaScript 并沒有類(class)的概念,而是使用基于原型(prototype)的繼承方式。相應(yīng)的,JavaScript 中的構(gòu)造函數(shù)也很特殊,如果不使用 new 調(diào)用,則和普通函數(shù)一樣。作為又一項(xiàng)約定俗成的準(zhǔn)則,構(gòu)造函數(shù)以大寫字母開頭,提醒調(diào)用者使用正確的方式調(diào)用。如果調(diào)用正確,this 綁定到新創(chuàng)建的對象上。
清單 6. Point.js
讓我們再一次重申,在 JavaScript 中函數(shù)也是對象,對象則有方法,apply 和 call 就是函數(shù)對象的方法。這兩個方法異常強(qiáng)大,他們允許切換函數(shù)執(zhí)行的上下文環(huán)境(context),即 this 綁定的對象。很多 JavaScript 中的技巧以及類庫都用到了該方法。讓我們看一個具體的例子:
清單 7. Point2.js
在上面的例子中,我們使用構(gòu)造函數(shù)生成了一個對象 p1,該對象同時具有 moveTo 方法;使用對象字面量創(chuàng)建了另一個對象 p2,我們看到使用 apply 可以將 p1 的方法應(yīng)用到 p2 上,這時候 this 也被綁定到對象 p2 上。另一個方法 call 也具備同樣功能,不同的是最后的參數(shù)不是作為一個數(shù)組統(tǒng)一傳入,而是分開傳入的。
如果像作者一樣,大家也覺得上述四種方式不方便記憶,過一段時間后,又搞不明白 this 究竟指什么。那么我向大家推薦 Yehuda Katz 的這篇文章:( http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/)。在這篇文章里,Yehuda Katz 將 apply 或 call 方式作為函數(shù)調(diào)用的基本方式,其他幾種方式都是在這一基礎(chǔ)上的演變,或稱之為語法糖。Yehuda Katz 強(qiáng)調(diào)了函數(shù)調(diào)用時 this 綁定的過程,不管函數(shù)以何種方式調(diào)用,均需完成這一綁定過程,不同的是,作為函數(shù)調(diào)用時,this 綁定到全局對象;作為方法調(diào)用時,this 綁定到該方法所屬的對象。
通過上面的描述,如果大家已經(jīng)能明確區(qū)分各種情況下 this 的含義,這篇文章的目標(biāo)就已經(jīng)完成了。如果大家的好奇心再強(qiáng)一點(diǎn),想知道為什么 this 在 JavaScript 中的含義如此豐富,那就得繼續(xù)閱讀下面的內(nèi)容了。作者需要提前告知大家,下面的內(nèi)容會比前面稍顯枯燥,如果只想明白 this 的含義,閱讀到此已經(jīng)足夠了。如果大家不嫌枯燥,非要探尋其中究竟,那就一起邁入下一節(jié)吧。
JavaScript 中的函數(shù)既可以被當(dāng)作普通函數(shù)執(zhí)行,也可以作為對象的方法執(zhí)行,這是導(dǎo)致 this 含義如此豐富的主要原因。一個函數(shù)被執(zhí)行時,會創(chuàng)建一個執(zhí)行環(huán)境(ExecutionContext),函數(shù)的所有的行為均發(fā)生在此執(zhí)行環(huán)境中,構(gòu)建該執(zhí)行環(huán)境時,JavaScript 首先會創(chuàng)建 arguments變量,其中包含調(diào)用函數(shù)時傳入的參數(shù)。接下來創(chuàng)建作用域鏈。然后初始化變量,首先初始化函數(shù)的形參表,值為 arguments變量中對應(yīng)的值,如果 arguments變量中沒有對應(yīng)值,則該形參初始化為 undefined。如果該函數(shù)中含有內(nèi)部函數(shù),則初始化這些內(nèi)部函數(shù)。如果沒有,繼續(xù)初始化該函數(shù)內(nèi)定義的局部變量,需要注意的是此時這些變量初始化為 undefined,其賦值操作在執(zhí)行環(huán)境(ExecutionContext)創(chuàng)建成功后,函數(shù)執(zhí)行時才會執(zhí)行,這點(diǎn)對于我們理解 JavaScript 中的變量作用域非常重要,鑒于篇幅,我們先不在這里討論這個話題。最后為 this變量賦值,如前所述,會根據(jù)函數(shù)調(diào)用方式的不同,賦給 this全局對象,當(dāng)前對象等。至此函數(shù)的執(zhí)行環(huán)境(ExecutionContext)創(chuàng)建成功,函數(shù)開始逐行執(zhí)行,所需變量均從之前構(gòu)建好的執(zhí)行環(huán)境(ExecutionContext)中讀取。
有了前面對于函數(shù)執(zhí)行環(huán)境的描述,我們來看看 this 在 JavaScript 中經(jīng)常被誤用的一種情況:回調(diào)函數(shù)。JavaScript 支持函數(shù)式編程,函數(shù)屬于一級對象,可以作為參數(shù)被傳遞。請看下面的例子 myObject.handler 作為回調(diào)函數(shù),會在 onclick 事件被觸發(fā)時調(diào)用,但此時,該函數(shù)已經(jīng)在另外一個執(zhí)行環(huán)境(ExecutionContext)中執(zhí)行了,this 自然也不會綁定到 myObject 對象上。
清單 8. callback.js
button.onclick=myObject.handler;
這是 JavaScript 新手們經(jīng)常犯的一個錯誤,為了避免這種錯誤,許多 JavaScript 框架都提供了手動綁定 this 的方法。比如 Dojo 就提供了 lang.hitch,該方法接受一個對象和函數(shù)作為參數(shù),返回一個新函數(shù),執(zhí)行時 this 綁定到傳入的對象上。使用 Dojo,可以將上面的例子改為:
清單 9. Callback2.js
button.onclick=lang.hitch(myObject, myObject.handler);
在新版的 JavaScript 中,已經(jīng)提供了內(nèi)置的 bind 方法供大家使用。
JavaScript 中的 eval 方法可以將字符串轉(zhuǎn)換為 JavaScript 代碼,使用 eval 方法時,this 指向哪里呢?答案很簡單,看誰在調(diào)用 eval 方法,調(diào)用者的執(zhí)行環(huán)境(ExecutionContext)中的 this 就被 eval 方法繼承下來了。簡單看個eval示例:
本文介紹了 JavaScript 中的 this 關(guān)鍵字在各種情況下的含義,雖然這只是 JavaScript 中一個很小的概念,但借此我們可以深入了解 JavaScript 中函數(shù)的執(zhí)行環(huán)境,而這是理解閉包等其他概念的基礎(chǔ)。掌握了這些概念,才能充分發(fā)揮 JavaScript 的特點(diǎn),才會發(fā)現(xiàn) JavaScript 語言特性的強(qiáng)大。
原文:https://dev.to/bhagatparwinder/arrow-function-basics-34cm
箭頭函數(shù)是在 ES6 引入的,相對于函數(shù)表達(dá)式來說是一種更簡潔的方式。
箭頭函數(shù)名稱的來源是因?yàn)槭褂昧?=>。
const functionName = (arg1, arg2, ... argN) => {
return value;
}
const multiply = (a, b) => {
return a * b;
}
console.log(multiply(7, 8)); // 56
console.log(multiply(3, 2)); // 6
const square = x => {
return x * x;
}
console.log(square(2)); // 4
console.log(square(7)); // 49
這個情形的唯一陷阱是當(dāng)只有一個參數(shù)且需要解構(gòu)時:
const foo = ({name = "New User"}) => name;
console.log(foo({})); // New User
console.log(foo({name: "Parwinder"})); // Parwinder
const greeting = () => {
return "Hello World!";
}
console.log(greeting()); // Hello World!
const greeting = () => "Hello World!";
console.log(greeting()); // Hello World
現(xiàn)在我們知道了所有的關(guān)鍵特點(diǎn),讓我們來重寫獲取正方形的面積:
const square = x => x * x;
console.log(square(4)); // 16
JavaScript 中的 this 關(guān)鍵字是執(zhí)行上下文的一個屬性,它可能是全局的、函數(shù)內(nèi)的或者是 eval 中的。對于普通的函數(shù),this 會根據(jù)調(diào)用它方式不同而變化。
我們使用這種行為已經(jīng)很久了,以至于大多數(shù)JavaScript開發(fā)者都已經(jīng)習(xí)慣了。
function foo() {
return this;
};
console.log(foo()); // window object in a browser, global object for node execution
function Order(main, side, dessert) {
this.main = main;
this.side = side;
this.dessert = dessert;
this.order = function () {
return `I will have ${this.main} with ${this.side} and finish off with a ${this.dessert}`;
}
}
const newOrder = new Order("sushi", "soup", "yogurt");
console.log(newOrder.order());
// I will have sushi with soup and finish off with a yogurt
const myObject = {
main: "butter chicken",
side: "rice",
dessert: "ice cream",
order: function () {
return `I will have ${this.main} with ${this.side} and finish off with ${this.dessert}`;
}
}
console.log(myObject.order());
// I will have butter chicken with rice and finish off with ice cream
上面的例子中,this 指向 myObject,可以獲取它上面的屬性。
"use strict";
function foo() {
return this;
};
console.log(foo() === undefined); // true
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。