js作用域&閉包

js作用域&閉包

作用域

作用域是負責收集并維護由所有聲明的變量組成的一系列查詢,并且實施一套嚴格的規(guī)則,確定當前執(zhí)行的代碼對這些變量的訪問權限 -- 《你不知道的javascript》

作用域最大的用處就是隔離變量诈乒,不同作用域下同名變量不會有沖突

1 作用域鏈

作用域有上下級的關系葵第,會在當前作用域中尋找變量荣刑,如果找不到會沿著創(chuàng)建時作用域鏈一直往上找措嵌,直到找到全局作用域与柑。

var a=1;
function f1(){
   var b=2;   
   function f2(){ 
     var c=3;
     console.log(a,b,c);
}  
f2();
}
f1();//1 ,2 ,3

2 作用域形成時機

作用域是在一個函數(shù)創(chuàng)建時就已經(jīng)形成的谤辜,而不是調用時.

function a () {
    let b = 2;
    return function() {
        console.log(b)
    }
}
let b = 222
a(); // 2 而不是222

以上的示例調用a函數(shù)看似是在全局環(huán)境蓄坏,但是其中的b卻沒有使用全局的b。而且去尋找定義時的b丑念,沒有找到則沿著創(chuàng)建時的作用域鏈往上找涡戳。結果是2

3 作用域類別

作用域包含全局作用域、函數(shù)作用域渠欺、和es6中新增的塊級作用域妹蔽。
在es6沒有出來之前。我們避免變量污染全局的方法是使用函數(shù)作用域挠将。

最常見的是使用自執(zhí)行函數(shù)來包裹模塊胳岂,這樣函數(shù)中的變量只能在局部作用域中生效
(function() {
  // do something
})()

而在es6中新增的let和const可以將變量綁定到所在的任意作用域中通常是{...},為其聲明的變量隱式的劫持了所在的塊級作用域舔稀。 -- 《你不知道的javascript》

for (var i = 0; i < 10; i++) {
    setTimeout(() => {
        console.log(i) // 10 10 ... 10
    }, 500)
}
// es6之前 需要使用自執(zhí)行函數(shù)來創(chuàng)建函數(shù)作用域來隔絕變量
for (var i = 0; i < 10; i++) {
    (function(i){
        setTimeout(() => {
        console.log(i) // 10 10 ... 10
    }, 500)
    })(i)   
}
// 而es6中直接使用let就可以實現(xiàn)隔絕變量的作用
for (let i = 0; i < 10; i++) {
    setTimeout(() => {
        console.log(i) // 0 1 2 ... 9
    }, 500)
}

4 提升

代碼被執(zhí)行前會有聲明提升的過程乳丰。只有聲明本身會被提升,任何作用域都會進行提升操作内贮。

{
    console.log(a);
    var a = 2;  
}
實際上被解析成如下:
{
        var a;
    console.log(a);
    a = 2;  
}

let和const聲明不會被提升产园,但是不代表這個作用域中不會進行提升操作。

{
    console.log(a);  //  ReferenceError
    let a = 2;  
}

函數(shù)的提升是優(yōu)先于變量的

foo();  // 1 打印出來的是1 而不是2
var foo;
function foo () {console.log(1)} 
foo = function() {console.log(2)}

this

this的優(yōu)先級

  1. 函數(shù)是否在new中調用夜郁,如果是的話this綁定的是新創(chuàng)建的對象
var bar = new foo();
  1. 函數(shù)通過call什燕、apply(顯式綁定),或者bind(硬綁定)竞端。this綁定的是指定的對象
var bar = foo.call(obj)
  1. 函數(shù)是否在某個上下文對象中調用(隱式綁定)如果是則綁定的是那個上下文對象
var bar = obj.foo()
  1. 如果都不是的話使用默認綁定屎即,嚴格模式下undefined,否則都是全局對象
var bar = foo()

apply事富、call都是執(zhí)行本身的函數(shù)技俐,而bind是返回一個新函數(shù)。而且可以用來做函數(shù)柯里化

function foo(a,b){
    console.log("a"+a+",b:""+b);
}
var bar = foo.bind(null, 2);
bar(3) // a:2 b:3

1 構造函數(shù)中this

function Kimi() {
    this.name = 'kimi'
    console.log(this)  // 'kimi'
}
new Kimi 

如何直接執(zhí)行Kimi() this指向window

2 函數(shù)作為對象的一個屬性

let kimi = {
    name : 'kimi',
    say: function() {
        console.log(this.name) // 'kimi'
    }
}
kimi.say()  // 這樣調用打印的是‘kimi’

如果不作為屬性調用
let  a = kimi.say;
a(); // 這樣調用打印的是window

3 使用call和apply

let kimi = {
    name : 'kimi'
}
let say = function() {
    console.log(this.name)  // 'kimi'
}
say.call(kimi); 

4 全局

全局中的this一直指向window

let name = 'kimi';
let say = function() {
    console.log(this);  // window
    console.log(this.name);  // 'kimi'
}
say();

有以下情況:

let kimi = {
    name : 'kimi',
    say: function() {
        function a () {
                    console.log(this.name)  // 'undefined'
                }
                a();
    }
}
kimi.say();

函數(shù)a雖然是在kimi.say內部定義的统台,但是它仍然是一個普通的函數(shù)雕擂,this仍然指向window。如果需要重新指向kimi則需要使用箭頭函數(shù)或者用let that = this;保存引用

5 this的用途

this可以動態(tài)的綁定執(zhí)行的對象贱勃,起到復用代碼的作用

jQuery.extend = jQuery.fn.extend = function() {
    target = this;
}

jQuery.extend和jQuery.fn.extend都指向了同一個函數(shù)井赌,但是當執(zhí)行時,函數(shù)中的this是不一樣的贵扰。
執(zhí)行jQuery.extend(…)時族展,this指向jQuery;執(zhí)行jQuery.fn.extend(…)時拔鹰,this指向jQuery.fn仪缸。

閉包

在一個函數(shù)內部定義的另一個函數(shù),當內部函數(shù)在包裹他的函數(shù)之外被執(zhí)行時列肢,就會形成閉包恰画。同時內部函數(shù)仍然可以訪問到包裹函數(shù)中的局部變量與函數(shù)宾茂。
閉包的兩個常見用途

1. 函數(shù)作為返回值

封裝變量 避免全局中被修改 ,并且記錄狀態(tài)拴还。狀態(tài)不會銷毀丟失
function isFirst() {
    var _list = [];
    return function(id) {
        if(_list.includes(id)) {
            return false
        } 
        _list.push(id)
        return true;
    }
}
var first = isFirst()
first(10) // true
first(10) // false
first(20) // true

2. 函數(shù)作為參數(shù)

function wait(message) {
    setTimeout(function timer() {
        console.log(message);
    }, 1000) 
}
wait('hello');

timer 函數(shù)傳遞給setTimeout(),timer就具有了涵蓋wait()作用域的閉包跨晴,因此保有對變量message的引用,等到1000回調執(zhí)行后片林,wait的內部作用域不會消失

在定時器端盆、事件監(jiān)聽器、Ajax請求费封、跨窗口通信焕妙、webworker、或者其他的異步或者同步的任務中弓摘,只要使用了回調函數(shù)焚鹊,實際上就在使用閉包。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末韧献,一起剝皮案震驚了整個濱河市末患,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锤窑,老刑警劉巖璧针,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異渊啰,居然都是意外死亡探橱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門虽抄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人独柑,你說我怎么就攤上這事迈窟。” “怎么了忌栅?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵车酣,是天一觀的道長。 經(jīng)常有香客問我索绪,道長湖员,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任瑞驱,我火速辦了婚禮娘摔,結果婚禮上,老公的妹妹穿的比我還像新娘唤反。我一直安慰自己凳寺,他們只是感情好鸭津,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肠缨,像睡著了一般逆趋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上晒奕,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天闻书,我揣著相機與錄音,去河邊找鬼脑慧。 笑死魄眉,一個胖子當著我的面吹牛,可吹牛的內容都是我干的漾橙。 我是一名探鬼主播杆融,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼霜运!你這毒婦竟也來了脾歇?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤淘捡,失蹤者是張志新(化名)和其女友劉穎藕各,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焦除,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡激况,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了膘魄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乌逐。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖创葡,靈堂內的尸體忽然破棺而出浙踢,到底是詐尸還是另有隱情,我是刑警寧澤灿渴,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布洛波,位于F島的核電站,受9級特大地震影響骚露,放射性物質發(fā)生泄漏蹬挤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一棘幸、第九天 我趴在偏房一處隱蔽的房頂上張望焰扳。 院中可真熱鬧,春花似錦、人聲如沸蓝翰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽畜份。三九已至诞帐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間爆雹,已是汗流浹背停蕉。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钙态,地道東北人慧起。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像册倒,于是被迫代替她去往敵國和親蚓挤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內容

  • 一、作用域 作用域:變量生效(可以被訪問)的范圍崇呵,用來控制變量的可見性和生命周期缤剧。 全局作用域:不單獨屬于某一個函...
    清心挽風閱讀 286評論 0 2
  • 變量的作用域無非就是兩種:全局變量和局部變量。 Javascript語言的特殊之處域慷,就在于函數(shù)內部可以直接讀取全局...
    LJQ21閱讀 212評論 0 1
  • 1.全局變量 定義在函數(shù)外部的變量都是全局變量荒辕。 聲明提前 2.局部變量 定義在函數(shù)內部的變量都是局部變量。 3....
    壬萬er閱讀 256評論 0 0
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持犹褒,譯者再次奉上一點點福利:阿里云產(chǎn)品券抵窒,享受所有官網(wǎng)優(yōu)惠,并抽取幸運大...
    HetfieldJoe閱讀 5,610評論 16 88
  • 今天又看到一本干貨滿滿的動物書,短小精悍座云,與js啟示錄帶來的感覺一樣 (1)重新聲明一個已有的變量疙赠,則并不會將該變...
    西蘭花偉大炮閱讀 183評論 0 0