亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

You Don't Know Js 閱讀筆記

wanglu1209 / 700人閱讀

摘要:回調(diào)傳遞函數(shù)是將函數(shù)當(dāng)做值并作為參數(shù)傳遞給函數(shù)。這個(gè)例子中就是因?yàn)槭录壎C(jī)制中的傳入了回調(diào)函數(shù),產(chǎn)生了閉包,引用著所在的作用域,所以此處的數(shù)據(jù)無法從內(nèi)存中釋放。

javascript作用域

一門語言需要一套設(shè)計(jì)良好的規(guī)則來存儲(chǔ)變量,并且之后可以方便的找到這些變量,這逃規(guī)則被稱為作用域。

這也意味著當(dāng)我們訪問一個(gè)變量的時(shí)候,決定這個(gè)變量能否訪問到的依據(jù)就是這個(gè)作用域。

一、詞法作用域

作用域共有兩種主要的工作模型,第一種是最為普通的,被大多數(shù)編程語言(包括javascript)采用的詞法作用域,另一種叫做動(dòng)態(tài)作用域。而我們平時(shí)所提及的作用域,就是這里所說的詞法作用域。

要了解詞法作用域,必須要了解javascript引擎以及編譯器的大概工作方式。一般程序中的源碼在執(zhí)行前會(huì)進(jìn)行編譯三步驟。

分詞/語法分析

解析/語法分析

代碼生成

而在分詞/詞法分析這個(gè)步驟,就已經(jīng)確定了詞法作用域。也就說作用域在我們書寫代碼的時(shí)候就已經(jīng)確定了,引用書中的文字

詞法作用域就是定義在詞法階段的作用域,換句話說,詞法作用域是由你在寫代碼時(shí)將變量和塊作用域?qū)懺谀睦飦頉Q定的。

具體結(jié)合編譯器作用域、引擎來講,編譯器在分詞階段,針對(duì)特定的環(huán)境就會(huì)生成一個(gè)詞法作用域,然后對(duì)源代碼中的var a = 3;類似的聲明進(jìn)行識(shí)別,當(dāng)遇到var a,編譯器會(huì)詢問作用域中是否有a變量,若無,則在作用域中新增一個(gè)a變量。編譯完成之后,引擎執(zhí)行編譯后的代碼,引擎在執(zhí)行的過程中遇到a變量,會(huì)去作用域中查找是否有a變量,若有,則將a賦值2。對(duì)于var a = 2;一條語句會(huì)在兩個(gè)過程中操作,正是變量提升現(xiàn)象的原因。(稍后講到)

那什么時(shí)候會(huì)生成一個(gè)詞法作用域呢?

二、函數(shù)作用域

這幅圖所展示的三個(gè)氣泡,就代表了三個(gè)作用域,而編譯器遇到一個(gè)函數(shù)定義,就會(huì)生成一個(gè)作用域。例如當(dāng)編譯器遇到foo函數(shù),會(huì)創(chuàng)建一個(gè)作用域,再將這個(gè)函數(shù)內(nèi)部的標(biāo)識(shí)符(a/b/bar)放到詞法作用域中。這個(gè)步驟在編譯階段就完成了。當(dāng)js引擎執(zhí)行foo函數(shù)的時(shí)候,遇到a變量,就會(huì)去詢問早就創(chuàng)建好的作用域是否有a變量存在。

在作用域外,是無法訪問作用域內(nèi)的變量的。

例如

function foo() {
    var a = 3;
}
console.log(a); //undefied

正是這個(gè)特性,可以被用來實(shí)現(xiàn)隱藏內(nèi)部變量
將重要變量聲明放入一個(gè)函數(shù)聲明的作用域中,可以防止被作用域外部的語句所引用甚至更改。

根據(jù)函數(shù)作用域,可以引申出如何判斷一個(gè)函數(shù)是函數(shù)聲明還是一個(gè)函數(shù)表達(dá)式。
最重要的區(qū)別是他們的名稱標(biāo)識(shí)符將會(huì)綁定在何處。

先聲明一點(diǎn),任何匿名函數(shù)都是可以添加名稱標(biāo)識(shí)符的。例如

setTimeout(function timer() {
    console.log(1)
}, 1000)

對(duì)于函數(shù)聲明,名稱標(biāo)識(shí)符是綁定在當(dāng)前作用域上的。即可在函數(shù)當(dāng)前作用域調(diào)用這個(gè)名稱標(biāo)識(shí)符。

而函數(shù)表達(dá)式,名稱標(biāo)識(shí)符是綁定在自身的函數(shù)作用域中的。

按照這個(gè)區(qū)別,來看以下幾個(gè)函數(shù)。

function foo1() {console.log(1)}
foo1(); // 1
var bar = function foo2() {console.log(1)}
foo2() // undefined
(function foo3() {console.log(1)})()
foo3() // undefined

以上的函數(shù)就只有foo1是函數(shù)聲明。

三、塊作用域

在js語言中,除了函數(shù),創(chuàng)建作用域的方式還可以通過塊作用域。對(duì)于js而言,循環(huán)、ifelse塊并沒有創(chuàng)建塊作用域的功能。

通過ES3規(guī)范的try/catch的catch語句可以創(chuàng)建一個(gè)塊作用域,其中聲明的變量僅在catch中有效。
try-catch也正是let關(guān)鍵字的向前兼容方。

try {
 undefined(); // 執(zhí)行一個(gè)非法操作來強(qiáng)制制造一個(gè)異常
} catch(err) {
    console.log(err);
}
console.log(err); // err not found

ES6引入了let關(guān)鍵字,提供了除var以外的另一種變量聲明方式,let為其聲明的變量隱式地劫持了所在的塊作用域。

if (true) {
    {
        let bar = 3;
        bar = someting(bar);
        console.log(bar)
    }
}
console.log(bar) // undefined

作于的一個(gè)中括號(hào)起到劃分塊作用域的作用,顯示的區(qū)別于var等變量。我們可能在之后會(huì)修改代碼,看到這個(gè)中括號(hào)會(huì)直白的認(rèn)識(shí)到這個(gè)是一個(gè)塊作用域。

四、變量提升

在第一節(jié)我已經(jīng)提到了,對(duì)于var a = 3;這樣一條語句,編譯器通過分詞、解析、最后生成機(jī)器可以讀的代碼。

而javascript實(shí)際上會(huì)將其看成兩個(gè)聲明:var a、a = 3。第一個(gè)聲明在編譯階段進(jìn)行,第二個(gè)賦值聲明會(huì)留在原地等待執(zhí)行。

所以在引擎工作去執(zhí)行代碼時(shí),進(jìn)入到函數(shù)作用域內(nèi)時(shí),首先會(huì)執(zhí)行var a操作,而這個(gè)過程就好像變量從原先的位置被移動(dòng)作用域最上面一樣。

console.log(a); // undefined
var a = 3;

相當(dāng)于

var a;
console.log(a); // undefined
a = 3;

另外函數(shù)聲明也會(huì)發(fā)生變量提升的現(xiàn)象(連實(shí)際函數(shù)值也提升,即可以在函數(shù)聲明前調(diào)用)。而行數(shù)表達(dá)式var a = function foo1() {}發(fā)生提升的是a變量,函數(shù)本身不會(huì)發(fā)生提升。

foo(); // 不是ReferenceError 而是 TypeError
var foo = function bar() {}

ReferenceError TypeError
這是兩個(gè)錯(cuò)誤標(biāo)記,第一個(gè)錯(cuò)誤標(biāo)記是查詢變量時(shí),若在作用域中查找不到這個(gè)變量則發(fā)出,第二個(gè)標(biāo)記是能查找到變量(即使是endefined),但是這個(gè)變量被錯(cuò)誤的調(diào)用(比如對(duì)null,undefined進(jìn)行調(diào)用),發(fā)出。

作用域閉包 一、經(jīng)典的閉包

閉包是基于詞法作用域書寫代碼時(shí)所產(chǎn)生的自然結(jié)果。

基于詞法作用域產(chǎn)生的結(jié)果,這有點(diǎn)類似于詞法作用域的產(chǎn)生條件。這也意味著閉包在書寫代碼的時(shí)候就已經(jīng)形成了。

看一個(gè)最經(jīng)典的閉包例子

function foo () {
    var a = 1;
    function bar () {
        console.log(a); //1
    }
    return bar;
}
var baz = foo();
baz();

基于這個(gè)經(jīng)典的例子,結(jié)合書中的話

一個(gè)函數(shù)在定義時(shí)的詞法作用域以外的地方被調(diào)用,可以記住并訪問原先所在的詞法作用域時(shí),就產(chǎn)生了閉包。也即被返回出去的函數(shù)被調(diào)用時(shí)依然持有對(duì)該作用域的引用。這個(gè)引用就是閉包。

先確定一點(diǎn),javascript中函數(shù)是可以作為值被傳遞的。基于這個(gè)特性,有多種方法可以行成閉包。只要在一個(gè)作用域中,將函數(shù)作為值傳遞到另一個(gè)詞法作用域中并調(diào)用,就會(huì)形成閉包。

function foo() {
    var a = 2;
    function baz() {
        console.log(a);
    }
    bar(baz);
}
function bar(fn) {
    fn();
}
// 回調(diào)傳遞函數(shù)
var fn;
function foo() {
    var a = 2;
    function baz() {
        console.log(a);
    }
    fn = baz;
}
function bar() {
    fn();
}
foo();
bar(); //2
// 間接傳遞函數(shù)

無論通過何種手段將內(nèi)部函數(shù)傳遞到所在的詞法作用域以外,它都會(huì)持有對(duì)原始定義作用域的引用,無論在何處執(zhí)行這個(gè)函數(shù)都會(huì)使用閉包。

二、回調(diào) == 閉包

再看上一節(jié),回調(diào)中傳遞函數(shù)的例子。

function foo() {
    var a = 2;
    function baz() {
        console.log(a);
    }
    bar(baz);
}
function bar(fn) {
    fn();
}
// 回調(diào)傳遞函數(shù)

是將函數(shù)當(dāng)做值并作為參數(shù)傳遞給函數(shù)。再來看

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

setTimeout作為js內(nèi)置的工具函數(shù),將timer 函數(shù)當(dāng)做值傳進(jìn)去,在setTimeout定義函數(shù)內(nèi)對(duì)傳進(jìn)來的timer進(jìn)行了調(diào)用。類似于

function setTimeout(fn) {
    // 延遲多少毫秒
    fn();
}

回調(diào)函數(shù)timer在另一個(gè)詞法作用域內(nèi)調(diào)用,但是能訪問原先作用域內(nèi)的參數(shù)(message)。

類似jquery中的事件綁定,涉及到傳遞回調(diào)函數(shù),就都有閉包的產(chǎn)生!

三、閉包在循環(huán)中的表現(xiàn)

最令人困惑的閉包表現(xiàn)就是在循環(huán)中了。像我們剛剛提及到的setTimeout、事件綁定等回調(diào)函數(shù)都會(huì)產(chǎn)生閉包。

for(var i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i);
    }, i*1000)
}

這個(gè)循環(huán)的本意是想間隔1秒打印1、2、3、4、5,結(jié)果卻每隔1秒輸出了5次6!
結(jié)合在第二節(jié)中對(duì)setTimeout函數(shù)的解析,這個(gè)誤區(qū)將很快解開。

首先要明白for循環(huán)沒有塊作用域的概念,即在這個(gè)循環(huán)中5次迭代都是在同一個(gè)作用域中進(jìn)行的。
要清楚timer函數(shù)不是在這個(gè)作用域中被調(diào)用的,它作為參數(shù)在其他的作用域中調(diào)用。

function timer() {
    console.log(i);
}

這個(gè)函數(shù)包括其中的形式參數(shù)i原原本本的被傳遞,在迭代過程中i不會(huì)被賦值。
而五次迭代完成后,共用的作用域中的i的值已經(jīng)變成了6 。在其他作用域中的timer函數(shù)調(diào)用過程中需要查詢i,因?yàn)楫a(chǎn)生了閉包,i的值會(huì)去原始的作用域中查找,即全是6。

得不到預(yù)期效果的錯(cuò)其實(shí)都在于for循環(huán)中共用一個(gè)作用域。想改進(jìn)也很簡單,即在迭代的過程中,創(chuàng)建對(duì)應(yīng)的作用域。另外值得注意的一點(diǎn)是需要把每次迭代的i值傳到作用域內(nèi)。

for(var i = 1; i <= 5; i++) {
    (function (j) {
        setTimeout(function timer () {
            console.log(j)
        }, j* 1000)
    })(i)
}
四、閉包的垃圾回收

本來一個(gè)變量被使用完之后就可以利用垃圾回收機(jī)制進(jìn)行垃圾回收,但因?yàn)殚]包的產(chǎn)生,阻止了這一行為。

function process(data) {
    //
}
var someReallyBigData = {};
process( someReallyBigData );
var $btn = $(".j_Btn");
$btn.on("click", function clicker() {});

這個(gè)例子中就是因?yàn)槭录壎C(jī)制中的傳入了clicker回調(diào)函數(shù),產(chǎn)生了閉包,引用著clicker所在的作用域,所以此處的someReallyBigData數(shù)據(jù)無法從內(nèi)存中釋放。

解決辦法也有,聲明一個(gè)塊作用域,讓引擎清楚的知道沒有必要保存someReallyBigData餓了。

function process(data) {
    //
}
{
    let someReallyBigData = {};
    process( someReallyBigData );
}
var $btn = $(".j_Btn");
$btn.on("click", function clicker() {});

閱讀心得,轉(zhuǎn)載請注明出處。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/78518.html

相關(guān)文章

  • 讀書筆記(you don&#039;t know js): this的理解(沒寫完...)

    摘要:基本概念首先,函數(shù)不能存儲(chǔ)的值,指向哪里,取決于調(diào)用它的對(duì)象。如果沒有這個(gè)對(duì)象,那默認(rèn)就是調(diào)用非嚴(yán)格模式下。也就是說是在運(yùn)行的時(shí)候定義的,不是在綁定的時(shí)候定義的。 基本概念 首先,函數(shù)不能存儲(chǔ)this的值,this指向哪里,取決于調(diào)用它的對(duì)象。如果沒有這個(gè)對(duì)象,那默認(rèn)就是window調(diào)用(非嚴(yán)格模式下)。也就是說this是在運(yùn)行的時(shí)候定義的,不是在綁定的時(shí)候定義的。 funct...

    freewolf 評(píng)論0 收藏0
  • You Don&#039;t Know JS閱讀理解——作用域

    摘要:在我們的程序中有很多變量標(biāo)識(shí)符,我們現(xiàn)在或者將來將使用它。當(dāng)我們使用時(shí),如果并沒有找到這個(gè)變量,在非嚴(yán)格模式下,程序會(huì)默認(rèn)幫我們在全局創(chuàng)建一個(gè)變量。詞法作用域也就是說,變量的作用域就是他聲明的時(shí)候的作用域。 作用域 定義 首先我們來想想作用域是用來干什么的。在我們的程序中有很多變量(標(biāo)識(shí)符identifier),我們現(xiàn)在或者將來將使用它。那么多變量,我咋知道我有沒有聲明或者定義過他呢,...

    codeKK 評(píng)論0 收藏0
  • You don&#039;t know cross-origin

    摘要:為什么會(huì)存在跨域問題同源策略由于出于安全考慮,瀏覽器規(guī)定不能操作其他域下的頁面,不能接受其他域下的請求不只是,引用非同域下的字體文件,還有引用非同域下的圖片,也被同源策略所約束只要協(xié)議域名端口有一者不同,就被視為非同域。 showImg(https://segmentfault.com/img/remote/1460000017093859?w=1115&h=366); Why 為什么...

    hersion 評(píng)論0 收藏0
  • You Don&#039;t Know JS閱讀理解——this

    摘要:運(yùn)行規(guī)則根據(jù)的運(yùn)作原理,我們可以看到,的值和調(diào)用棧通過哪些函數(shù)的調(diào)用運(yùn)行到調(diào)用當(dāng)前函數(shù)的過程以及如何被調(diào)用有關(guān)。 1. this的誕生 假設(shè)我們有一個(gè)speak函數(shù),通過this的運(yùn)行機(jī)制,當(dāng)使用不同的方法調(diào)用它時(shí),我們可以靈活的輸出不同的name。 var me = {name: me}; function speak() { console.log(this.name); }...

    tianren124 評(píng)論0 收藏0
  • 一起來讀you don&#039;t know javascript(一)

    摘要:一到底是一門什么樣的計(jì)算機(jī)編程語言表里不一表面上是動(dòng)態(tài)解釋執(zhí)行的腳本語言,實(shí)際上它是一門編譯語言。與眾不同與傳統(tǒng)語言不同的是,它不是提前編譯的,編譯記過也不能在分布式系統(tǒng)中進(jìn)行移植。千篇一律引擎進(jìn)行編譯的步驟和傳統(tǒng)的編譯語言非常相似。 一、JavaScript到底是一門什么樣的計(jì)算機(jī)編程語言? JavaScript表里不一:表面上是動(dòng)態(tài)、解釋執(zhí)行的腳本語言,實(shí)際上它是一門編譯語言。 ...

    Anchorer 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<