(1珠峯18)JS的變量提高與閉包

 

3、JavaScript

JS:輕量級的客戶端腳本編程語言。html

1.編程語言node

編程語言是具有必定邏輯的,擁有本身的編程思想(面向對象編程[OOP]、面向過程編程)es6

-面向對象編程

   +C++設計模式

   +JAVA數組

   +PHPpromise

   +C#(.net)瀏覽器

   +JS服務器

   +...閉包

-面向過程

   +C

2.目前JS已經不只僅是客戶端語言了,基於Node能夠作服務器端程序,因此JS是全棧編程語言。

3.學習js,咱們學習它的幾部分組成

-ECMAScript(ES):JS的核心語法

-DOM:document Object Model 文檔對象模型,提供各類API(屬性和方法)讓JS能夠獲取或操做頁面中的HTML元素(DOM元素)。

-BOM:Browser Object Model 瀏覽器對模型,提供各類API讓JS能夠獲取或操做瀏覽器。

 

4、ECMAScript

它是js的語法規劃,JS中的變量、數據類型、語法規範、操做語句、設計模式等等都是ES規定的。

1997 ES1.0  => 1998 ES2.0  =>  1999 ES3.0 (最爲普遍的應用,奠基JS基礎,目前咱們用的大部分都是ES3定下的規則語法)  =》 2000 ES4(激進顛覆式更新,最後夭折)  =》 2015.6 ES6...   (ES5和ES6是同樣的,目前的說法是ES5表明老版本也就是ES3)

 

-------------------------------------------

 

5、變量(variable)

它不是具體的值,只是一個用來存儲具體值得容器或者代名詞,由於存儲的值可變因此稱爲變量。

基於ES語法規範,在JS中建立變量有如下方式

-var (ES3)

-function (ES3) 建立函數(函數名也是變量,只不過存儲的值是函數類型而已)

-let (ES6)

-const (ES6) 建立的是常量

-import (ES6) 基於ES6的模塊規範導出須要的信息

-class (ES6) 基於ES6建立類

 

建立變量,命名的時候要遵循一些規範:

- 嚴格區分大小寫

- 遵循駝峯命名法:按照數字、字母、下劃線來命名(數字不能爲名字開頭),命名的時候基於英文單詞拼接成一個完整的名字(第一個單詞字母小寫,其他每個有意義的單詞首字母大寫)

- 不能使用關鍵字和保留字:在JS中有特殊含義的叫作關鍵詞,將來可能會成爲關鍵字的叫作保留字。

 

6、數據類型

數據值是一門編程語言進行生產的材料,JS包含如下類型:

- 基本數據類型(值類型)

  + 數字 number

  + 字符串 string

  + 布爾 boolean

  + null

  + undefined (JS獨有)

- 引用類型

  + 對象object

     + 普通對象

     + 數組對象

     + 正則對象

     + 日期對象

     + ...

  + 函數 function 

- ES6中新增長的一個特殊的類型:Symbol ,惟一的值  

 

擴展:JS代碼如何被運行以及運行後如何輸出結果。

【如何被運行】

- 把代碼運行在瀏覽器中(瀏覽器內核來渲染解析)

- 基於node來運行(NODE也是一個基於V8引擎渲染和解析的JS的工具) //node 是環境和工具,js來作後臺,依託node來運行。node不是語言仍是js。

【如何輸出結果】

- alert:在瀏覽器中經過彈窗的方式輸出(瀏覽器提示框)// window.alert(); alert(1+1);=> '2' 基於alert輸出的結果都會利用toString()轉換爲字符串。 js中表達式先行,先計算結果再輸出;       

- confirm:和alert用法一致,只不過提示的內容不同,有肯定和取消兩個按鈕,因此是確認提示框。

- prompt:在confirm的基礎上增長輸入框。

- console.log:在瀏覽器控制檯輸出日誌。  console(控制檯).log(日誌);

   + Elements:當前頁面中的元素和樣式在這裏均可以看到,還能夠調節樣式和修改結構等。

   + Console:控制檯,能夠經過JS代碼中經過.log輸出到這裏,也能夠在這裏直接編寫js代碼。

   + Sources:當前網站的原文件都在這裏。

   +...

- console.dirI:比log輸出的更詳細一些(尤爲是輸出對象數據值的時候)

- console.table:把一個JSON數據按照表格的方式輸出。

- ...(本身擴展更多console輸出方法)

 思考:爲何對象轉換爲字符串是 '[object object]' ,toSting()結果。 //  var flag = confirm('肯定要退出麼?'); if(flag){  //點擊的確認 }else{  //點擊的取消 } ; 返回的是true和false;

 

 

 

 

 

 

 

 

 

 

JS的變量提高與閉包

【JS渲染機制堆棧內存】

  當瀏覽器去加載界面加載js時,首先會建立提供window全局做用域,而後,代碼開始自上而下執行,

代碼:var a = 7;

①聲明變量a,默認值爲undefined

②在當前做用域中開闢一個位置存儲7這個值

③讓變量a和值12關聯在一塊兒(賦值)

基本類型和引用類型的區別就是,存儲方式的不一樣。

基本類型直接在做用域中,引用類型由於相對複雜,因此須要單獨開闢的存儲空間。

var ary1 =  [12,23];

var ary2 = ary1;

ary2.push(100);

console.log(ary1); //[12,23,100]

全部做用域的兩個做用:提供代碼的執行環境 存儲基本類型值。

 

IE靠的是計數器,引用加一,反之減一。Chrom則是,固定時間查看一下,是否還被引用。

IE計數器,記混的時候,就發生了內存泄漏。

想讓堆內存銷燬,直接賦值爲 null ,經過空對象指針 null 可讓 原始變量或者其它 指向空,那麼原有被佔用的堆內存就沒有被佔用了,瀏覽器就會在空閒的時候銷燬它。

 

【變量提高機制】

變量提高:當棧內存(做用域)造成,JS代碼自上而下執行以前,瀏覽器受限會把全部帶"VAR"/"FUNCTION"關鍵詞的進行提早"聲明"或者"定義",這種預先處理機制,叫作"變量提高"。 

=》 聲明(declare): var a (默認值undefined)

=》 定義(defined): a = 12 (定義其實就是賦值操做)

    [變量提高階段]

=》帶"VAR"的只聲明未定義

=》帶"FUNCTION"的聲明和賦值都完成了

=》變量提高只發生在當前做用域。(例如:開始加載頁面的時候只對全局做用域下的進行提高,由於此時函數中存儲的都是字符串而已)

=》在全局做用域聲明的函數或者邊阿玲是"全局變量",同理,在私有做用域下聲明的變量是"私有變量" [帶VAR/FUNCTION的纔是聲明]

自從學了預解釋,今後節操是路人。

=》瀏覽器很懶,從不會作相同的事情。當代碼執行遇到建立函數這部分代碼後,會直接跳過(由於在提高階段就已經完成了函數的賦值操做了)

 

【重名問題】 

 

【暫時性死區】

在ES6中Let和const等方式建立變量或者函數,不存在變量提高機制。

=》切斷了全局變量和window屬性的映射機制。

=》在相同做用域,基於Let 不能聲明相同名字的變量。(無論是是什麼方式只要聲明瞭,在用let重複聲明就會報錯,var也同樣)

雖然,沒有變量提高機制,可是在當前做用域代碼自上而下執行以前,瀏覽器會作一個重複性檢測:自上而下查找當前做用域下全部變量,一旦發現重複的,直接拋出異常,代碼中止,不會繼續執行。(雖然沒有把變量提高,可是瀏覽器已經記住了,當前做用域下有哪些變量。)

 

 

 

   

a,b變爲私有的,與全局無關,c爲全局。 

 

【私有變量練習】

 

 

【上級做用域查找】

 

 =》arguments:實參集合

=》arguments.callee:函數自己

=》arguments.callee.caller:當前函數在哪執行的,CALLER就是誰(記錄的是執行它的宿主環境),在全局下執行的結果是null。(嚴格模式編程禁止使用這兩個屬性。)

=》當函數執行時,造成一個私有做用域A,A的上級做用域是誰,和他在哪執行沒有關係,和它在哪建立定義有關係,在哪建立的,它的上級做用域就是誰。

 

【堆棧內存銷燬機制】 

=》JS分爲堆內存和棧內存

=》堆內存(存儲引用類型值),和代碼分開(對象:鍵值對  函數:代碼字符串)

=》棧內存,提供JS代碼的執行環境存儲基本類型值

[堆內存釋放] :讓全部引用堆內存空間地址的變量賦值爲 NULL 便可(沒有變量佔用這個堆內存,瀏覽器在空閒的時候就會將其釋放)

[棧內存釋放] :通常狀況下,當函數執行完成後,所造成的私有做用域(棧內存)都會自動釋放掉,(在棧內存佔用存儲的值也會釋放掉),可是也存在特殊狀況:

                         ①函數執行完成,當前造成的棧內存中,某些內容被棧內存之外的變量佔用。

                         ②全局棧內存,只有當頁面關閉後纔會被釋放掉。 

                          ...

                         若是當前棧內存沒有被釋放掉,那麼以前在棧內存中存儲的基本值也不會被釋放掉,可以一直保存下來。

 i++ :自身累加1 ,和別人運算的時候 ,先拿原有值和其其它進行運算,運算結束後,自己累加1 。

++i  :自身累加1 ,先自身累加1,再和別人運算。

 

 

 6 12 16 8 

 

【帶不帶var的區別】

=>在全局做用域下聲明一個變量,也至關於給Window全局對象設置了一個屬性,變量的值就是屬性值(私有做用域中聲明的私有變量和Window無關)

用  in 操做符 能夠檢測某個屬性名是否隸屬於這個對象。 'AZUKI'  in  BoZai ,rue;

=》全局變量和window中的屬性存在"映射機制" , 雙方互相同步。

=》不加var本質是win的屬性,建立變量必需加var,養成良好的編程習慣。

 

【閉包】

函數執行造成一個私有的做用域,保護裏面的私有變量不受外界干擾,這種保護機制稱之爲「閉包」。

函數執行造成一個不銷燬的私有做用域(私有棧內存)纔是閉包。

//=>閉包:柯理化函數 fn執行返回一個堆內存被f佔用,因此fn做用域不銷燬
function fn(){
  return function (){

  }
}

var f=fn();
//=>閉包:惰性函數 自執行匿名函數造成的私有做用域被utils佔用
var utils=(function(){
  return{
  
  }
})();

小紙條:()內表示聲明一個函數,正常聲明函數是不能直接在後面加括號調用的。

閉包應用舉例:
真實項目爲了保證JS性能(堆棧內存的性能變化),應該儘量減小閉包的使用(不銷燬的堆棧內存是消耗性能的)。 1.閉包的保護做用:保護私有變量不受外界干擾 真實項目開發中,尤爲是團隊協做開發,應儘可能減小全局變量的使用,防止衝突,形成全局變量污染,那麼此時咱們能夠把本身這一部份內容封裝到一個閉包中,讓全局變量轉換爲私有變量。 (function(){})(); 封裝插件的時候,也會把程序都放到閉包中保護起來,防止和用戶的程序衝突,這時對於一些須要暴露給用戶的方法能夠拋到全局。 JQ:把須要暴露的方法拋到全局 Zepto:基於RETURN把須要外面使用的方法暴露出去

2.閉包的保存做用:造成不銷燬的棧內存,把一些值保存下來,方便後面調取使用

 

tips:

進入函數:形參賦值 變量提高 代碼自上而下執行

在傳統的ES規範中,只有全局做用域和函數執行產生的私有做用域,判斷和循環並不會產生做用域。

原生JS:閉包,oop,異步編程,promise,async和await,es6新特性,js事件機制

全部的事件綁定都是異步編程(當前事件沒有完成,再也不等待,繼續執行下面的任務。),同步編程(一件事一件事作,當前事件沒完成,下一個任務不能處理。)

小結:

有佔用,不釋放,不管是變量,仍是事件什麼佔用,就是閉包。

ES6中判斷循環都是塊級做用域,通常大括號內部都是塊級做用域。(對象除外)

每一輪循環,都會造成一個單獨的做用域。

ES6

自帶嚴格模式,嚴格模式下的一些限制:

 

經常使用數組遍歷方法:

-forEach

-map

-find(ES6)

-findIndex(ES6)

-filter

-some

-every

以上均可以改this

-reduce(數組去重,對象屬性求和,數組元素出現次數,這裏主要就是利用reduce第一個參數是迭代,能夠經過初始化這個參數的數據類型,達到想實現的效果。)

-reduceRight

ES6數組空位統一爲undefined處理;

擴展運算符將一個數組,變爲參數序列。

set Map【小紙條:詳情連接】:

set:

不存儲value。因爲key不能重複,因此,在Set中,沒有重複的key

[ ...new Set(arr)] 去重,set 是一個類數組對象;

 set.has(Nan) 判斷有無,返回true/false;

 set.clear() 清空,無返回值undefined

 map:

 對象的屬性名必是字符串,便是不寫字符串也會因默認數據類型而變爲字符串。

 

Symbol:

基本數據類型,typeOf 可檢測,不能和字符串進行拼接,

不能進行運算,能夠轉布爾,

只要經過Symbol()函數獲得的值就是惟一的值,只作本身,和任何人都不同

Symbol("參數做爲描述,兩個Symbel建立的值描述相同,也不會相等")

 

 對象的屬性用 [ ]  中括號括起來,表明着裏面是變量。

若是屬性名是Symbol形式,必須經過中括號,不能經過點的形式

 

 

Iterator【小紙條:詳情連接詳情連接2:

 es6中有三類結構生來就具備Iterator接口:數組、類數組對象、Map和Set結構。(Promise.all())

只要具有Iterator,遍歷接口,就可用擴展運算符 [...arr]

默認的遍歷接口:__proto__[Symbol.Iterator] 

 

prototype與__proto__【小紙條:詳情連接:

1.在JS裏,萬物皆對象。方法(Function)是對象,方法的原型(Function.prototype)是對象。所以,它們都會具備對象共有的特色。
即:對象具備屬性__proto__,可稱爲隱式原型,一個對象的隱式原型指向構造該對象的構造函數的原型,這也保證了實例可以訪問在構造函數原型中定義的屬性和方法。

2.方法(Function)
方法這個特殊的對象,除了和其餘對象同樣有上述_proto_屬性以外,還有本身特有的屬性——原型屬性(prototype),這個屬性是一個指針,指向一個對象,這個對象的用途就是包含全部實例共享的屬性和方法(咱們把這個對象叫作原型對象)。原型對象也有一個屬性,叫作constructor,這個屬性包含了一個指針,指回原構造函數。

 

proxy和defineProperty:

new Proxy(target目標對象,{代理方法});

 

 

相關文章
相關標籤/搜索