最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

老生常談JavaScript設(shè)計(jì)模式之call,apply,this詳解

 更新時(shí)間:2025年06月01日 09:39:23   作者:天生我材必有用_吳用  
這篇文章主要介紹了JavaScript設(shè)計(jì)模式之call,apply,this的相關(guān)知識(shí),本文通過示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下

前言:

最近在看曾探老師的《JavaScript設(shè)計(jì)模式》,說實(shí)話,可能是我基礎(chǔ)還不夠扎實(shí),書里的很多內(nèi)容對(duì)我來說不是“一看就懂”,而是“看了三遍才懂一點(diǎn)點(diǎn)”。特別是像代碼邏輯、概念解釋這些地方,常常是一次看不懂,就來兩次;兩次還迷糊,就再來一次。但我慢慢也想通了,學(xué)習(xí)本來就是一個(gè)不斷重復(fù)的過程。每一次重讀,都能從字里行間中發(fā)現(xiàn)新的理解,就像挖寶藏一樣,每次翻一翻都有新收獲。為了讓自己記得更牢,也為了讓以后復(fù)習(xí)的時(shí)候不那么枯燥,我在反復(fù)閱讀第二章“this、call、apply”之后,試著用自己的話,把知識(shí)點(diǎn)編成了一個(gè)小故事的形式,講給自己聽。
我想,這不僅是一種學(xué)習(xí)方式,也是我對(duì)這本書的一種致敬。

this找家

想象一下,this就像是一個(gè)四處旅行的人,他總在尋找它的“家”,但是問題來了,這個(gè)人(this)的家并不固定,取決于他是怎么被邀請(qǐng)這個(gè)地方的,在javaScript中,this就是一個(gè)旅行者,他的指向完全依賴于函數(shù)的調(diào)用方式。

如果是對(duì)象的方法調(diào)用:比如obj.myMethod(),那么this的家就安在obj里面。
如果是作為構(gòu)造函數(shù)調(diào)用:比如new MyClass(),這時(shí)候this就有了自己的新家,一個(gè)新的對(duì)象實(shí)例
如果是直接調(diào)用函數(shù),比如myFunction(),那么嚴(yán)格模式下,this就會(huì)變得無家可歸,即undefined,而在非嚴(yán)格模式下,則默認(rèn)回到了全局對(duì)象瀏覽器的window的懷抱。

call 和 apply 導(dǎo)游

現(xiàn)在,比如我們的this旅行者到了一個(gè)陌生的城市,不知道如何找到正確的家,這時(shí)候就可以請(qǐng)callapply兩位熱心的導(dǎo)游出現(xiàn)了。
call這個(gè)導(dǎo)游很細(xì)心,他會(huì)親自帶著this去正確的地方,并且還允許你指定額外的參數(shù)列表,就像myFunction.call(context,arg1,arg2)。在這里,context就是我們希望this去的家,而后面參數(shù)就是這次旅行所需要的裝備。
applycall不同的是,apply更喜歡一次性把所有的東西都準(zhǔn)備好再出發(fā),所以除了第一個(gè)參數(shù)用來指定this應(yīng)該去的"家"外,它接收第二個(gè)參數(shù)為一個(gè)數(shù)組或類數(shù)組對(duì)象,里面包含了所需要傳遞給函數(shù)的參數(shù),如myFunction.apply(context,[arg1,arg2]),總之,當(dāng)你想要控制this的指向,或者需要特定的方式傳遞參數(shù)的時(shí)候,callapply就是你的得力導(dǎo)游,他們不僅能幫this找到回家的路,還能確保旅途愉快順利。

this的指向

第一站、作為對(duì)象的方法被調(diào)用(this回到家)

有一天,this被一個(gè)叫做obj的大叔收留了,成為了大叔家的傭人。

var obj = {
    a: 1,
    getA: function() {
        console.log(this === obj); // true
        console.log(this.a);       // 1
    }
};
obj.getA();

著obj家里,this每天多的都很開心,this開心的喊:“我終于找到加啦!我是obj家的孩子”,因?yàn)樗窃趏bj家里被調(diào)用的,所以他就是的孩子。

第二站、普通函數(shù)調(diào)用(this離家出走)

但是有一天,this不小心跑出了obj的家,在外面遇到一個(gè)叫g(shù)etName的陌生人:

var getName = obj.getName;
getName(); // 輸出 undefined 或 globalName

??這時(shí)候的this已經(jīng)不是obj的孩子了,他又變成了流浪漢,回到了全局大哥window家,不過別擔(dān)心,如果this在全局如果window家過的不開心,可以開啟"use strict"【嚴(yán)格模式】模式,this更加自尊自愛,寧愿當(dāng)個(gè)“無家可歸的孤獨(dú)者”,也不愿意隨便認(rèn)爹! 很有骨氣吧,用孤獨(dú)換的骨氣 哈哈哈。

第三站、構(gòu)造器調(diào)用(this自己當(dāng)?shù)?

后來啊,有骨氣的this長大了,學(xué)會(huì)了獨(dú)立,決定自己當(dāng)爸爸!

function Person(name) {
    this.name = name;
}
var tom = new Person('Tom');
console.log(tom.name); // Tom

結(jié)果this發(fā)現(xiàn),自己辛辛苦苦當(dāng)?shù)牡?,娃娃居然被別人搶走了。

第四站、Function.prototype.call 或 Function.prototype.apply 調(diào)用

這時(shí)候,兩位神秘人物登場(chǎng)了,他們就是大名鼎鼎JavaScript劇場(chǎng)的導(dǎo)演call和apply!,他們告訴this:“this你很堅(jiān)強(qiáng),我們被你打動(dòng)了,現(xiàn)在開始,你想演誰都可以,我?guī)湍銚Q身份。”,于是:

var obj1 = { name: '鋼鐵俠' };
var obj2 = { name: '蜘蛛俠' };
function sayHi() {
    console.log("我是" + this.name);
}
sayHi.call(obj1);   // 我是鋼鐵俠
sayHi.apply(obj2);  // 我是蜘蛛俠

this得到兩位大哥的幫助,開始在不同的角色之間切換,一會(huì)是鋼鐵俠,一會(huì)是蜘蛛俠,忙的不亦樂乎,體驗(yàn)?zāi)欠N既可以當(dāng)英雄,又可以有家的感覺。
有一天,this在div家做客,突然被一個(gè)叫做callback的函數(shù)騙走了身份信息:

<div id="div1">我是一個(gè) div</div>
<script>
document.getElementById('div1').onclick = function () {
    alert(this.id); // div1 ?
    var callback = function () {
        alert(this.id); // window ?
    }
    callback();
};
</script>

這個(gè)時(shí)候,this又跑回了window家,而它本應(yīng)該屬于哪個(gè)div,怎么辦呢,this想了個(gè)辦法:

var that = this;
var callback = function () {
    alert(that.id); // div1 ?
}

就像this孩子給自己辦一張身份證,再也不怕他迷路啦。

修復(fù) document.getElementById 的 this

有一次,this被借去演習(xí),結(jié)果卻跑到了document.getElementById里面,導(dǎo)致整個(gè)劇組都亂了套。

<html> 
 <body> 
 <div id="div1">我是一個(gè) div</div> 
 </body> 
 <script> 
 var getId = document.getElementById; 
 getId( 'div1' );   // 報(bào)錯(cuò),this不在指向document
 </script> 
</html>

getId( ‘div1’ );就會(huì)報(bào)錯(cuò),this不在指向document,原來this應(yīng)該是docuement家的人,結(jié)果卻被當(dāng)成window使用了,apply導(dǎo)演不舍得this,就決定動(dòng)用自己的能力,立刻修復(fù):

document.getElementById = (function( func ){ 
 return function(){ 
 return func.apply( document, arguments ); 
 } 
})( document.getElementById ); 
var getId = document.getElementById; 
var div = getId( 'div1' ); 
alert (div.id); // 輸出: div1

終于,this回到了正確的家庭,拯救了整個(gè)劇組,經(jīng)過這次慘痛的教訓(xùn),導(dǎo)演對(duì)這次事故進(jìn)行分析總結(jié):

在這里插入圖片描述

導(dǎo)演發(fā)現(xiàn),在Chrome執(zhí)行過后發(fā)現(xiàn),var getId = document.getElementById拋出了一個(gè)異常,因?yàn)樵S多的引擎document.getElementById方法的內(nèi)部實(shí)現(xiàn)張需要用到this。這個(gè)this本來被期望指向document,當(dāng)getElementById方法作為document對(duì)象的屬性被調(diào)用時(shí),方法內(nèi)部的this確實(shí)是指向document的,但是當(dāng)getId來引用document.getElementById之后,再調(diào)用getId,此時(shí)就成了普通函數(shù)調(diào)用,函數(shù)內(nèi)部的this指向了Window,而不是原來的document。
導(dǎo)演發(fā)現(xiàn)可以使用apply,把document當(dāng)作this傳入getId函數(shù),幫助“修正”this

 <script>
 document.getElementById = (function (func) {
    return function () {
      return func.apply(document, arguments);
    };
  })(document.getElementById);
  var getId = document.getElementById;
  var div = getId("div1");
  alert(div.id); // 輸出: div1
</script>

深入理解JavaScript設(shè)計(jì)模式之call,apply,this的深入理解


終于通過導(dǎo)演的不懈努力與分析讓this回到了正確的家庭總結(jié)了整個(gè)劇組,甚至還總結(jié)了口訣:

this是誰調(diào)用了我,我就跟誰混。
call/applythis的導(dǎo)演,想讓this演誰都可以。

發(fā)生了上面的事情之后,this學(xué)會(huì)了如何在javaScript世界中找到自己的歸屬,它不再輕易迷路,也不再害怕被借來借去,從此要記?。?/p>

多問問this:“whu ar you ?" 你是誰?多請(qǐng)callapply來幫忙。別忘了用bindthat=thisthis上個(gè)保險(xiǎn)!

call和Apply

Apply序

apply是一個(gè)豪放派的導(dǎo)演,他習(xí)慣把所有演員打包成一個(gè)團(tuán)隊(duì)(數(shù)組或類數(shù)組),然后一次性推到主演面前。

func.apply(thisArg, [arg1, arg2, ...]);

盡管風(fēng)格不同,但是目標(biāo)是一致的,幫助函數(shù)找到正確的舞臺(tái)背景(改變this的指向),并讓每個(gè)演員都能發(fā)揮最大的作用(正確的傳遞)。

Apply正文:

apply接收兩個(gè)參數(shù),第一個(gè)參數(shù)指定了函數(shù)體內(nèi)this的指向,第二個(gè)參數(shù)為一個(gè)帶下標(biāo)的集合,這個(gè)集合可以為數(shù)組,也可以為類數(shù)組,apply方法把這個(gè)結(jié)合中的元素作為參數(shù)傳遞給被調(diào)用的函數(shù):

var func = function( a, b, c ){ 
 alert ( [ a, b, c ] ); // 輸出 [ 1, 2, 3 ] 
}; 
func.apply( null, [ 1, 2, 3 ] ); 

在這段代碼中,參數(shù) 1、2、3 被放在數(shù)組中一起傳入 func 函數(shù),它們分別對(duì)應(yīng) func 參數(shù)列表中的 a、b、c

call序

call是一位細(xì)心講究的導(dǎo)演,他喜歡把演員(參數(shù))一個(gè)個(gè)親自介紹給主演(函數(shù)),確保每個(gè)演員都有明確的角色。

func.call(thisArg, arg1, arg2, ...);

call正文:

call傳入的參數(shù)數(shù)量不固定,跟apply相同的是,第一個(gè)參數(shù)也是代表函數(shù)體內(nèi)的this指向,從第二個(gè)參數(shù)開始往后,每個(gè)參數(shù)被依次傳入函數(shù):

var func = function( a, b, c ){ 
 alert ( [ a, b, c ] ); // 輸出 [ 1, 2, 3 ] 
}; 
func.call( null, 1, 2, 3 );

當(dāng)調(diào)用一個(gè)函數(shù)的時(shí)候,JavaScript的解釋器并不會(huì)計(jì)較形參和實(shí)參的數(shù)量、類型、以及順序上的區(qū)別,JavaScript的參數(shù)在內(nèi)部就用一個(gè)數(shù)組來表示的,從這個(gè)意義上說,applycall使用率更高,不必關(guān)心具體多少個(gè)參數(shù)被傳入函數(shù),只要作用apply一股腦地推過去即可。

注意:

當(dāng)使用call或者apply的時(shí)候,如果我們傳入的第一個(gè)參數(shù)為null,函數(shù)體內(nèi)的this會(huì)默認(rèn)值想宿主對(duì)象,在瀏覽器中則是windows

Function.prototype.bind

Function.prototype.bind是一個(gè)秘密裝備,能讓你隨心所欲的控制函數(shù)內(nèi)部的this指向,即使在遙遠(yuǎn)的地方調(diào)用這個(gè)函數(shù)時(shí)也能保持“它”的初心,首先我們要打造一個(gè)屬于自己的 Function.prototype.bind我們先把 func 函數(shù)的引用保存起來,然后返回一個(gè)新的函數(shù)。當(dāng)我們?cè)趯韴?zhí)行 func 函數(shù)時(shí),實(shí)際上先執(zhí)行的是這個(gè)剛剛返回的新函數(shù)。在新函數(shù)內(nèi)部,self.apply( context, arguments )這句代碼才是執(zhí)行原來的 func 函數(shù),并且指定 context對(duì)象為 func 函數(shù)體內(nèi)的 this

Function.prototype.bind = function(context) { 
    var self = this; // 保存原函數(shù),就像把寶劍藏在腰間
    return function() { // 返回一個(gè)新的函數(shù),這就是我們的新武器
        return self.apply(context, arguments); // 當(dāng)使用這把寶劍時(shí),它會(huì)自動(dòng)指向正確的方向
    } 
}; 
var obj = { name: 'sven' }; // 我們的英雄 sven
var func = function() { 
    alert(this.name); // 輸出:sven
}.bind(obj); 
func(); // 英雄登場(chǎng)!

上面代碼中,bind就像給函數(shù)穿上了一層魔法鎧甲,無論什么時(shí)候,只要調(diào)用它,它就會(huì)帶著obj的這個(gè)身份出現(xiàn)。但是,真正的超級(jí)英雄不能只有一技之長!我們需要讓 bind更加強(qiáng)大,讓它不僅能綁定 this,還能預(yù)先填入一些參數(shù)。這就像是給我們的寶劍裝上了“魔法石”,讓它變得更強(qiáng)大:

Function.prototype.bind = function() { 
    var self = this, // 保存原函數(shù)
        context = [].shift.call(arguments), // 需要綁定的 this 上下文,也就是英雄的身份
        args = [].slice.call(arguments); // 把剩下的參數(shù)轉(zhuǎn)換成數(shù)組,作為魔法石
    return function() { // 返回一個(gè)新的函數(shù),這是我們的終極武器
        return self.apply(context, [].concat.call(args, [].slice.call(arguments))); 
        // 組合兩次傳入的參數(shù),作為新函數(shù)的參數(shù)
    } 
}; 
var obj = { name: 'sven' }; // 英雄 sven 再次登場(chǎng)
var func = function(a, b, c, d) { 
    alert(this.name); // 輸出:sven
    alert([a, b, c, d]); // 輸出:[1, 2, 3, 4]
}.bind(obj, 1, 2); 
func(3, 4); // 召喚英雄,帶上所有的魔法石!

改變 this 指向:為函數(shù)找到真正的家

在一個(gè)戲劇性的場(chǎng)景中,有一名為getName的函數(shù),它渴望知道自己到底屬于哪個(gè)家族:

var obj1 = { name: 'sven' };
var obj2 = { name: 'anne' };
window.name = 'global';
function getName() {
    alert(this.name);
}
getName(); // 輸出: global
// 現(xiàn)在,Call 導(dǎo)演登場(chǎng),幫助 getName 找到了它的真正歸屬——obj1 家族
getName.call(obj1); // 輸出: sven

通過callapply大導(dǎo)演的幫助下,getName終于找到了它真正的家!

借用其他對(duì)象的方法:杜鵑鳥的智慧

JavaScript中也有一種"借殼生蛋"的藝術(shù),就像杜鵑鳥將自己的蛋托付給其他鳥類孵化一樣,這里callapply也能幫我們借用其他對(duì)象的方法來完成一些任務(wù)。
例1:

var A = function( name ){ 
 this.name = name; 
}; 
var B = function(){ 
 A.apply( this, arguments ); 
}; 
B.prototype.getName = function(){ 
 return this.name; 
}; 
var b = new B( 'sven' ); 
console.log( b.getName() ); // 輸出: 'sven'

上面例子中,B借用了A的構(gòu)造函數(shù)邏輯來初始化自己的實(shí)例屬性,可以說是繼承了A的部分功能,準(zhǔn)確來說是構(gòu)造函數(shù)繼承借用構(gòu)造函數(shù)。
函數(shù)的參數(shù)列表 arguments 是一個(gè)類數(shù)組對(duì)象,雖然它也有“下標(biāo)”,但它并非真正的數(shù)組,所以也不能像數(shù)組一樣,進(jìn)行排序操作或者往集合里添加一個(gè)新的元素。

我們常常會(huì)借用 Array.prototype 對(duì)象上的方法。比如想往 arguments 中添加一個(gè)新的元素,通常會(huì)借用Array.prototype.push

(function() {
    Array.prototype.push.call(arguments, 3);
    console.log(arguments); // 輸出: [1, 2, 3]
})(1, 2);

Call 導(dǎo)演巧妙地讓 Array.prototype.push 方法在非數(shù)組的對(duì)象上施展魔法,成功地添加了一個(gè)新成員。

無論是改變this的指向,還是靈活地傳遞參數(shù),甚至是借用其他對(duì)象的方法,CallApply 總能以其獨(dú)特的技巧讓每一個(gè)函數(shù)都成為舞臺(tái)上的明星。

到此這篇關(guān)于老生常談JavaScript設(shè)計(jì)模式之call,apply,this的文章就介紹到這了,更多相關(guān)js call,apply,this內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

尼勒克县| 徐州市| 宝应县| 清徐县| 湖南省| 西乌珠穆沁旗| 梅河口市| 彭州市| 于都县| 内乡县| 韶关市| 新宁县| 深圳市| 华亭县| 太原市| 连州市| 六枝特区| 弋阳县| 洛宁县| 泾阳县| 博兴县| 镇江市| 青田县| 麦盖提县| 威信县| 桦川县| 中阳县| 龙南县| 石嘴山市| 墨江| 容城县| 塔河县| 遵义市| 阿巴嘎旗| 义马市| 万源市| 庆云县| 滕州市| 宜君县| 克拉玛依市| 裕民县|