深入學(xué)習(xí)this、call乎串、apply店枣、bind

標(biāo)簽: js


this的指向問(wèn)題一直是面試?yán)锏某?吞居R彩莈s5的眾坑之一鸯两。對(duì)于es6而講,它極大地避免了this帶來(lái)的錯(cuò)誤长豁。但是為了為了維護(hù)一些老代碼钧唐,還是有必要了解this的。

this的指向原則:

  • this 永遠(yuǎn)指向最后調(diào)用它的那個(gè)對(duì)象(es5)匠襟。
  • 匿名函數(shù)的this總指向Window對(duì)象(全局對(duì)象)(es5)钝侠。
  • 箭頭函數(shù)中的 this 只和定義它時(shí)候的作用域的 this 有關(guān),而與在哪里以及如何調(diào)用它無(wú)關(guān)宅此,同時(shí)它的 this 指向是不可改變的机错。

全局環(huán)境中的 this 是什么

瀏覽器:

console.log(this); //Window

node:

console.log(this); //global

函數(shù)里執(zhí)行

全局函數(shù)執(zhí)行

一個(gè)函數(shù)被直接調(diào)用的時(shí)候,屬于全局調(diào)用父腕,這時(shí)候它的 this 指向 全局對(duì)象
例如:

function test(){
    console.log(this);
}
test();//Window

作為對(duì)象的方法調(diào)用

例如:

var obj={
    name:"JoeWright",
    foo:function(){
        console.log(this);
    }
};
obj.foo();//Object {name: "JoeWright", foo: ?}

如果把對(duì)象的方法賦值給一個(gè)變量弱匪,然后直接調(diào)用這個(gè)變量,這時(shí)候,this 指向全局對(duì)象。
例如:

 var obj={
    name:"Joe",
    foo:function(){
        console.log(this);
    }
};
var test=obj.foo;
test();//Window

作為一個(gè)構(gòu)造函數(shù)使用

this 指向了這個(gè)構(gòu)造函數(shù)調(diào)用時(shí)候?qū)嵗鰜?lái)的對(duì)象
例如:

function Person(name){
    this.name=name;
    console.log(this);
}
//this 指向了這個(gè)構(gòu)造函數(shù)調(diào)用時(shí)候?qū)嵗鰜?lái)的對(duì)象
var p1=new Person("JoeWright"); //Person
//構(gòu)造函數(shù)其實(shí)也是一個(gè)函數(shù)璧亮,如果我們把它當(dāng)作一個(gè)普通函數(shù)執(zhí)行萧诫,這個(gè) this 仍然執(zhí)行全局
var p2=Person("Wright");//Window

匿名函數(shù)使用(this的一些坑)

匿名函數(shù)的this總指向Window對(duì)象
例如:

var obj={
    name:"JoeWright",
    f1:function(){
        console.log(this);//Window
    },
    f2:function(){
        console.log(this);//Object
        setTimeout(this.f1,1000);//此時(shí)this.f1相當(dāng)于setTimeout             函數(shù)的形參fun,即做了fun=this.f1這個(gè)操作
    }
};
obj.f2();

解決方法(改變this的指向):

1.使用箭頭函數(shù)

 var obj={
    name:"JoeWright",
    f1:function(){
        console.log(this);
    },
    f2:function(){
        console.log(this);//Object
        setTimeout(()=>{
            console.log(this);//Object
        },1000);
    }
};
obj.f2();

2.在函數(shù)內(nèi)部使用that=this(或者_(dá)this=this)

  var obj={
    name:"JoeWright",
    f1:function(){
        console.log(this);
    },
    f2:function(){
        console.log(this);//Object
        var that=this;//上面的this指向的是obj4,這里我們用that變量存儲(chǔ)下來(lái)
        setTimeout(function(){
            console.log(that);//Object
        },1000);
    }
};
obj.f2();

改變this的指向

改變 this 的指向有以下幾種方法:

  • 使用 ES6 的箭頭函數(shù)
  • 在函數(shù)內(nèi)部使用 that = this(或_this = this
  • 使用 apply枝嘶、call帘饶、bind
  • new 實(shí)例化一個(gè)對(duì)象

1,2兩點(diǎn)上面已經(jīng)講過(guò),下面介紹下apply群扶、call及刻、bind的用法
例如:

function Cat(name){
    this.name=name;
}
function Dog(name){
    this.name=name;
}
Cat.prototype.eat=function(food){
    console.log(this.name+" eat "+food);
}
Dog.prototype.eat=function(food){
    console.log(this.name+" eat "+food);
}
var c=new Cat("kitty"); 
var d=new Dog("Bubby"); 
c.eat("fish");  //kitty eat fish
d.eat("meat");  //Bubby eat meat
c.eat.apply(d,["fish"]);  //Bubby eat fish
c.eat.call(d,"fish");    //Bubby eat fish
d.eat.call(c,"apple");   //kitty eat apple
d.eat.bind(c,"apple")(); //kitty eat apple

從上述的例子可知:

  • apply接收的是一個(gè)由若干參數(shù)組成的數(shù)組镀裤,而call接收的是若干個(gè)參數(shù)列表。
  • bindcall的用法類似缴饭,但是bind 創(chuàng)建了一個(gè)新的函數(shù)暑劝,我們必須 手動(dòng)去調(diào)用它

new關(guān)鍵字改變this的指向

如果函數(shù)調(diào)用前使用了 new 關(guān)鍵字, 則是調(diào)用了構(gòu)造函數(shù)颗搂。

例如:

function Cat(name){
    this.name=name;
}
var c=new Cat("kitty"); 
console.log(c.name);  //kitty

new的過(guò)程
偽代碼:

var c=new Cat("kitty"); 
new Cat{
    var obj={};
    obj.__proto__=Cat.prototype;
    var res=Cat.call(obj,"kitty");
    return typeof res==="obj"?res:obj;
}
  1. 先創(chuàng)建一個(gè)空對(duì)象obj
  2. 將新創(chuàng)建的空對(duì)象的隱式原型指向其構(gòu)造函數(shù)的顯式原型
  3. 使用call改變this的指向
  4. 如果沒有返回值或者返回的是一個(gè)非對(duì)象值担猛,則將obj返回為一個(gè)新對(duì)象;如果返回值是一個(gè)對(duì)象的話丢氢,就直接返回該對(duì)象傅联。

apply,call疚察,bind的應(yīng)用場(chǎng)景

嗯蒸走,既然call,apply稍浆,bind可以改變this的指向载碌,那我們?cè)谀睦锟梢杂玫侥?/p>

(1) 處理偽數(shù)組
什么是偽數(shù)組?
先看一個(gè)例子吧:

<div>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
    </ul>
</div>
const oLi=document.getElementsByTagName("li");
console.log(oLi.length);//4
console.log(oLi);  //HTMLCollection(4) [li, li, li, li]
console.log(oLi.slice(1,3)); //Uncaught TypeError: oLi.slice is not a function

看到?jīng)]衅枫,上面的oLi就是一個(gè)偽數(shù)組嫁艇。它有數(shù)組的length屬性,但是不可用數(shù)組的slice方法弦撩。

偽數(shù)組的定義:

  • 存在length屬性
  • 可以通過(guò)數(shù)組下標(biāo)的方式對(duì)元素進(jìn)行訪問(wèn)
  • 能像遍歷數(shù)組一樣遍歷
  • 不能使用數(shù)組的slice()步咪、push()等方法。

常見的偽數(shù)組

  1. 函數(shù)的argument對(duì)象
  2. document.getElementsByTagName()益楼、document.getElementsByClassName()猾漫、childNodes/children等方法的返回值(返回的是一個(gè)節(jié)點(diǎn)列表)

解決辦法:偽數(shù)組沒有這些方法那就借用Array的方法

[].slice.call(oLi,1,3)  //[li, li](推薦)

或者:

oLi.slice=[].slice;  //為oLi添加一個(gè)slice方法(會(huì)污染數(shù)組對(duì)象)
console.log(oLi.slice(1,3));//[li, li]

如果可以隨意改變?cè)瓕?duì)象感凤,可以 直接將其轉(zhuǎn)成真正的數(shù)組對(duì)象悯周。

Array.prototype.slice.call(oLi);
console.log(oLi.slice(1,3)); //[li, li]

(2)繼承

  • 單繼承
function Person(name){
    this.name=name;
}
function Man(sex,name){
    this.sex=sex;
    Person.call(this,name); //繼承Person的name
}
var p=new Man("male","JoeWright");
console.log(p.sex,p.name); //male JoeWright
  • 多繼承
function Person(name){
    this.name=name;
}
function Man(sex){
    this.sex=sex;
}
function Coder(name,sex,learn){
    this.learn=learn;
    Person.call(this,name);
    Man.call(this,sex);
}
var p2=new Coder("JoeWright","male","FE");
console.log(p2.name,p2.sex,p2.learn);//JoeWright male FE

(3)獲取數(shù)組的最值

Math.max()方法,支持傳遞多個(gè)參數(shù)陪竿,比如:Math.max(1,4,2,3,7,5,6)
但是它不支持直接傳遞一個(gè)數(shù)組作為參數(shù)禽翼,比如:Math.max(new Array(1,4,2,3,7,5,6))
這里族跛,只要我們有方法把數(shù)組闰挡,一個(gè)一個(gè)拆分開來(lái),傳遞到Math.max()方法中礁哄,就實(shí)現(xiàn)了傳遞數(shù)組的方法长酗。而apply接收一個(gè)數(shù)組,并且是將數(shù)組中的每個(gè)值桐绒,分開來(lái)夺脾,傳遞給Math.max()方法

例如:

var arr=[1,3,6,10,9];
console.log(Math.max.apply(null,arr)); //10
console.log(Math.min.apply(null,arr)); //1
//等價(jià)于
console.log(Math.max.apply(Math,arr)); //10
console.log(Math.min.apply(Math,arr)); //1
console.log(Math.max.apply("a",arr)); //10 
console.log(Math.min.apply(a,arr)); //Uncaught ReferenceError: a is not defined(a未定義報(bào)錯(cuò))

但為什么上面的例子中apply傳的第一個(gè)參數(shù)為null和Math都會(huì)得到相同的結(jié)果呢之拨?在網(wǎng)上看了很多解答后,我得出一個(gè)結(jié)論: 按apply的語(yǔ)法上來(lái)講咧叭,apply的第一個(gè)參數(shù)此時(shí)是Math.max()方法運(yùn)行時(shí)指定的 this 值敦锌。需要注意的是,指定的 this 值并不一定是該函數(shù)執(zhí)行時(shí)真正的 this 值佳簸,如果這個(gè)函數(shù)處于非嚴(yán)格模式下,則指定為 null 或 undefined 時(shí)會(huì)自動(dòng)指向全局對(duì)象(瀏覽器中就是window對(duì)象)颖变,同時(shí)值為原始值(數(shù)字生均,字符串,布爾值)的 this 會(huì)指向該原始值的自動(dòng)包裝對(duì)象腥刹。 換句話說(shuō):當(dāng)我們傳Math就相當(dāng)于Math對(duì)象調(diào)用max方法马胧,我們傳window相當(dāng)于window調(diào)用這個(gè)方法,傳Number就相當(dāng)于Number函數(shù)對(duì)象調(diào)用max方法... 但是傳入的參數(shù)必須是一個(gè)對(duì)象(或者參數(shù)的原始值的自動(dòng)包裝對(duì)象)衔峰,所以我們傳123佩脊,"aaa",undefined都可以,但是傳入一個(gè)未定義的變量是不行的垫卤,當(dāng)然傳入null也是可以的威彰。

(4)合并數(shù)組
例如:

var arr=[1,3,6,10,9];
var arr2=[2,4,8];
[].push.apply(arr,arr2);
console.log(arr); //[1, 3, 6, 10, 9, 2, 4, 8]

最后:大家覺得有幫助的話,請(qǐng)點(diǎn)個(gè)贊哈哈穴肘,如果文章中有寫的不恰當(dāng)?shù)牡胤叫危瑲g迎大家指出~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市评抚,隨后出現(xiàn)的幾起案子豹缀,更是在濱河造成了極大的恐慌,老刑警劉巖慨代,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邢笙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡侍匙,警方通過(guò)查閱死者的電腦和手機(jī)氮惯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)丈积,“玉大人筐骇,你說(shuō)我怎么就攤上這事〗酰” “怎么了铛纬?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)唬滑。 經(jīng)常有香客問(wèn)我告唆,道長(zhǎng)棺弊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任擒悬,我火速辦了婚禮模她,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘懂牧。我一直安慰自己侈净,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布僧凤。 她就那樣靜靜地躺著畜侦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪躯保。 梳的紋絲不亂的頭發(fā)上旋膳,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音途事,去河邊找鬼验懊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛尸变,可吹牛的內(nèi)容都是我干的义图。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼振惰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼歌溉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起骑晶,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤痛垛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后桶蛔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體匙头,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年仔雷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蹂析。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碟婆,死狀恐怖电抚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情竖共,我是刑警寧澤蝙叛,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站公给,受9級(jí)特大地震影響借帘,放射性物質(zhì)發(fā)生泄漏蜘渣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一肺然、第九天 我趴在偏房一處隱蔽的房頂上張望蔫缸。 院中可真熱鬧,春花似錦际起、人聲如沸拾碌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)倦沧。三九已至,卻和暖如春它匕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背窖认。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工豫柬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扑浸。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓烧给,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親喝噪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子础嫡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344