咱們幾乎天天都在和this打交道,先不去管他的概念,看一個小栗子,預熱一下javascript
⚠️約定:
由於使用let和const聲明的變量沒有掛載到全局變量下,因此在全局下聲明的變量咱們使用var前端
var heroName = '黃蓉'
function hero() {
const heroName = "黃藥師"
console.log(this)//window
console.log(this.heroName);//=>黃蓉
}
hero()
複製代碼
咱們執行hero(),經過打印咱們發現java
this指向的是window全局,因此this.heroName,應當在全局下查找heroName,因此輸出=>黃蓉
當全局下沒有heroName呢?this指向的是全局,全局沒有就是沒有,那就是undefined數組
function hero() {
const heroName = "黃藥師"
console.log(this)//window
console.log(this.heroName);//=>undefined
}
hero()
複製代碼
咱們繼續看一個栗子app
var heroName = '黃蓉'
function hero() {
const heroName = "黃藥師"
console.log(this)//oHero
console.log(this.heroName);//=>歐陽鋒
}
const oHero={
heroName:'歐陽鋒',
hero
}
oHero.hero()
複製代碼
提示:在聲明oHero中的hero中,使用了ES6標準,當key和value同名是,能夠像上面這樣簡寫
上面的栗子中,調用hero()的環境變了 ,根據輸出咱們發現,this的指向變了
this的指向是oHero對象,因此this.heroName=>歐陽鋒,一樣的當oHero下沒有heroName,毫無懸念,就會輸出=>undefined
若是前面兩個小栗子搞懂了,咱們就繼續
一個栗子函數
var heroName = '黃蓉'
function hero() {
console.log(this.heroName);
}
const oHero1 = {
heroName:'郭靖',
hero
};
hero()//=>黃蓉
oHero1.hero()//=>郭靖
複製代碼
如今咱們嘗試着給this下一個定義:學習
this指的是函數運行時所在的環境,若是一個函數內部有this,this就會指向一個對象,指向哪一個對象呢?取決於這個函數的執行環境。 補充(若是在全局下調用這個函數,則this指向全局,若是在某個對象下調用this,則this指向這個對象)ui
再看一個栗子this
var heroName = '黃蓉'
function hero1() {
const heroName = "黃藥師"
this.hero2()
}
function hero2() {
console.log(this.heroName);
}
hero1()
複製代碼
先不關心輸出 咱們在hero2中的console.log前加一個debugger,分別看下兩個this的指向 spa
能夠發現。兩個this均指向window 在腦中跑一遍這段代碼,但願你還清醒 咱們一塊兒捋一下: 全局下執行hero1() 在hero1中,this指向的是全局=>window, 全局下存在hero2(), 在hero2中,this指向哪裏?取決因而哪裏調用的hero2(),是在hero1()中調用的hero2(), 因此hero2中的this指向hero1?輸出=>黃藥師? 可是咱們經過debugger得出,hero2中的this指向的是window,因此輸出必然是=>黃蓉 爲何呢? 咱們再看一個栗子
function hero(){
console.log(this.heroName);
}
var HERO1 = {
heroName : '黃蓉',
hero
}
var HERO2 = {
heroName : '郭靖',
HERO1
}
var HERO3 = {
heroName : '郭靖',
HERO2
}
HERO3.HERO2.HERO1.hero()//=>黃蓉
複製代碼
我相信不會有人寫出這樣的代碼,除了我! 這依然是個調用鏈 咱們在輸出前打一個斷點
做爲一個函數調用this指向window,做爲一個對象的方法調用,this指向當前調用的對象
回顧以前的栗子
var heroName = '黃蓉'
function hero() {
console.log(this.heroName);
}
const oHero1 = {
heroName:'郭靖',
hero
};
hero()//=>黃蓉
oHero1.hero()//=>郭靖
複製代碼
hero(),是做爲一個函數進行調用的,因此this指向window oHero1.hero(), 是做爲一個函數的方法調用的,因此this指向oHero1對象
繼續看一個栗子
function hero1(){
this.hero2()
}
function hero2(){
console.log(this.heroName);//Uncaught TypeError: this.hero2 is not a function
}
var HERO1 = {
heroName : '黃蓉',
hero1
}
var HERO2 = {
heroName : '郭靖',
HERO1
}
HERO2.HERO1.hero1()
複製代碼
報錯了
HERO1對象下調用的hero1(),因此hero1()中this指向的是HERO1,可是HERO1中不存在方法hero2(),因此報錯 稍做修改
function hero1(){
HERO3.hero2()
}
function hero2(){
console.log(this.heroName);//=>郭靖
}
var HERO1 = {
heroName : '黃蓉',
hero1
}
var HERO2 = {
heroName : '郭靖',
HERO1
}
var HERO3 = {
heroName : '郭靖',
hero2
}
HERO2.HERO1.hero1()
複製代碼
咱們彷佛總結出來一點小竅門,咱們從最後的輸出反推執行順序 是誰調用的hero2(),是HERO3,因此hero2,中的this指向HERO3,因此輸出是=>郭靖
上面說了前兩種狀況,其實(1)是(2)的一種特殊狀況, 即看成爲一個函數調用的時候,就是做爲window對象的一個方法進行調用
一個小栗子進行簡單回顧
var heroName = "郭靖";
function hero1() {
console.log(this.heroName);
}
var HERO1 = {
heroName: "黃蓉",
hero1:hero1
};
HERO1.hero1();//=>黃蓉
hero1();//=>郭靖
複製代碼
都是執行hero1()方法,一樣都是輸出this.heroName,卻獲得了不一樣的結果
this的指向是在函數執行的時候定義的,而不是在函數建立時定義的
基於上面的栗子,咱們再看一個栗子
var heroObj = {
heroName: "郭靖",
heroFoo: {
heroName: "黃蓉",
hero: function() {
console.log(this.heroName);
}
}
};
heroObj.heroFoo.hero();//=>黃蓉
var h = heroObj.heroFoo.hero;
h();//=>undefined
複製代碼
爲了直觀表達咱們作一下改動
//部分代碼省略
window.heroObj.heroFoo.hero();//=>黃蓉
var h = heroObj.heroFoo.hero;
window.h();//=>undefined
複製代碼
輸出的結果是相同的,我要表達的內容也很就很直觀明顯了, 這些對象和方法,最終都會掛載到window對象下,咱們看這一句
window.heroObj.heroFoo.hero();//=>黃蓉
複製代碼
hero()中的this是指向調用它的對象,那是哪一個對象調用的hero()呢?
這是個問題
這是個問題?
這不是個問題
this指向的是最後調用它的對象
上面的栗子
window.heroObj.heroFoo.hero();//=>黃蓉
複製代碼
最後調用hero()的是heroFoo,因此hero()中this指向了heroFoo對象
var h = heroObj.heroFoo.hero;
window.h();//=>undefined
複製代碼
最後調用h()的是window對象,因此hero()中this指向了window對象
綜上
this的指向是在函數執行的時候定義的,而不是在函數建立時定義的,this指向的是最後調用它的對象
下面討論剩下的兩種狀況
以前在介紹面向對象的時候,談一談javascript面向對象,討論過,使用構造函數來建立對象
function Hero(name, nickname, skill) {
this.name = name;
this.nickname = nickname;
this.doSth = function() {
return skill
};
}
const hero = new Hero("黃藥師", "東邪", "碧海潮生曲");
console.log(hero);
複製代碼
使用自定義構造器構造一個對象須要四步
const hero = new Hero("黃藥師", "東邪", "碧海潮生曲");
複製代碼
hero.__proto__=Hero.prototype;
複製代碼
說this指向hero,Hero內全部針對this的操做,都會發生在hero上面
console.log(hero.name);//=>黃藥師
console.log(hero.nickname);//=>東邪
console.log(hero.doSth());//=>碧海潮生曲
複製代碼
構造函數是不須要return的, return在普通函數中也不是必須存在的,咱們知道,在普通函數中,若是沒有手動return ,會默認return undefined 可是若是在構造函數中使用了return,會存在一些坑 咱們一塊兒來填坑 咱們把上面的栗子進行簡化
function Hero(name) {
this.heroName = name;
return 123
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>郭靖
複製代碼
function Hero(name) {
this.heroName = name;
return null
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>郭靖
複製代碼
function Hero(name) {
this.heroName = name;
return undefined
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>郭靖
複製代碼
再也不進行一一列舉,上面return的都是基本數據類型 繼續看 數組類型
function Hero(name) {
this.heroName = name;
return []
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>undefined
複製代碼
object類型
function Hero(name) {
this.heroName = name;
return {
heroName: "黃蓉"
};
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>黃蓉
複製代碼
funciton類型
function Hero(name) {
this.heroName = name;
return function(){}
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>undefined
複製代碼
funciton升級
function Hero(name) {
this.heroName = name;
return (function() {
return {heroName:'黃蓉'}
})()
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>黃蓉
複製代碼
總結一下 構造函數會改版this的指向,指向經過new實例化出來的對象 構造函數中不須要return,當存在return時,如下幾點須要注意
以前在講面向對象的時候,對call進行過討論,javascript 面向對象之一篇文章搞定call()方法 它們兩個也是用來改變this的指向的,
爲何要改變this的指向呢?
看一個栗子
const hero1 = {
name: "歐陽鋒",
doSth: function (skill) {
console.log(`${this.name}學習${skill}`);
}
};
const hero2 = {
name: "洪七公"
};
hero1.doSth('九陰真經')//=>歐陽鋒學習九陰真經
複製代碼
執行了hero1對象下面的doSth方法,並傳入參數「九陰真經」,最後輸出=>歐陽鋒學習九陰真經 結合上面的介紹講解,咱們知道,doSth中的this是指向的hero1,這個沒問題吧
好了,如今hero2下面有一個「洪七公」,「洪七公」也想調用hero1下面的doSth方法,怎麼辦呢?
const hero1 = {
name: "歐陽鋒",
doSth: function (skill) {
console.log(`${this.name}學習${skill}`);
}
};
const hero2 = {
name: "洪七公"
};
hero1.doSth.call(hero2, "降龍十八掌");//=>洪七公學習降龍十八掌
複製代碼
doSth是由hero1調用的,默認狀況下doSth中的this指向的是hero1,可是使用了call,因此,this的指向變了,指向了call方法中的第一個參數hero2,
apply呢?apply和call非常相似
const hero1 = {
name: "歐陽鋒",
doSth: function(skill, favourite) {
console.log(`${this.name}學習${skill}`);
console.log(`${this.name}喜歡${favourite}`);
}
};
const hero2 = {
name: "洪七公"
};
hero1.doSth.apply(hero2, ["降龍十八掌", "吃雞"]);
hero1.doSth.call(hero2, "降龍十八掌", "吃雞");
複製代碼
這兩種寫法等效,只是傳入的參數格式略有不一樣
hero1.doSth.apply(hero2, ["降龍十八掌", "吃雞"]);
hero1.doSth.call(hero2, "降龍十八掌", "吃雞");
複製代碼
bind也能夠改變this的指向,在用法上和call和apply略有不一樣,bind的使用更加靈活
const hero1 = {
name: "歐陽鋒",
doSth: function(skill, favourite) {
console.log(`${this.name}學習${skill}`);
console.log(`${this.name}喜歡${favourite}`);
}
};
const hero2 = {
name: "洪七公"
};
const foo = hero1.doSth.bind(hero2, "降龍十八掌", "吃雞");
foo();
複製代碼
以前說的call和apply都是當即執行,而bind不會當即執行,須要手動執行,因此bind的使用更加靈活 不止於此 上面的foo方法在調用的時候能夠額外的傳入參數
const hero1 = {
name: "歐陽鋒",
doSth: function(skill, favourite, q, n, b) {
console.log(`${this.name}學習${skill}`);//=>洪七公學習降龍十八掌
console.log(`${this.name}喜歡${favourite}`);//=>洪七公喜歡吃雞
console.log(q, n, b);//=>1 2 3
}
};
const hero2 = {
name: "洪七公"
};
const foo = hero1.doSth.bind(hero2, "降龍十八掌", "吃雞");
foo(1, 2, 3);
複製代碼
我是陌上寒,咱們一塊兒學前端
END