JS中的 變量提升、作用域、閉包 核心原理解讀

作者:日期:2018-03-25 19:27:41 點擊:876

數據類型的操作原理

基本數據類型

1.var a=12;
2.var b=a;
3.b=13;
4.console.log(a); //=>12

直接在當前作用域中創建了基本數據類型的值(或者說基本類型值直接存儲在當前作用域中),然后把這個值和變量關聯起來(一個變量只能關聯一個值,關聯下一個值后和之前關聯的值就沒關系了),我們把關聯這個操作叫做 變量賦值,基本數據類型是直接 按值操作

引用數據類型

1.var o={name:'珠峰培訓'};
2.var p=o;
3.p.name='中國最權威的前端培訓機構';
4.console.log(o.name);

引用數據類型不是直接按值操作的(它的結構復雜,要存儲很多值,無法直接的創建值),在JS中遇到引用數據類型(對象或者函數),按照如下操作進行:

1、首先開辟一個新的內存空間(瀏覽器為其分配一個16進制的地址)
2、把需要存儲的內容存儲到內存空間中

  • 對象是把鍵值對依次存儲到空間中
  • 函數是把函數體中的代碼當做 ‘字符串’ 存儲到內存中

3、把空間的地址賦值給對應的變量,所以我們也說:引用數據類型是按照空間的引用地址操作

1.function fn(){
2. var total=null;
3. total=1+1;
4. return total;
5.}
6.fn();
7.fn();
8....

函數執行的時候也有屬于自己的一系列操作
1、函數執行首先會形成一個新的私有作用域,目的是為函數體中的代碼提供一個執行的環境(而且這個環境是私有的)
2、把創建時候存儲的代碼字符串copy一份到自己的私有作用域中,然后把字符串轉換為JS表達式,再然后依次自上而下執行

[總結]
每一次執行函數都是形成一個新的私有作用域,然后把代碼重新自上而下執行(一切都從新開始),所以多次執行函數之間是沒有直接聯系的,互不干擾;

函數執行形成的私有作用域,保護了里面的私有變量不受外界的干擾,我們把函數執行的這種保護機制叫做 閉包(如果當前變量是私有的,那么在函數體中出現的所有這個變量都和外界沒有任何關系)

全局作用域

瀏覽器渲染頁面(渲染頁面中的JS)的時候,會提供一個供代碼執行的環境 =>全局作用域(window/global)

Alt text

堆內存 & 棧內存

JS中的內存一共兩種:堆內存和棧內存

堆內存

作用:用來存儲引用數據類型值的內存空間叫做堆內存(對象存儲的是鍵值對,函數存儲的是代碼字符串)

形成:只要遇到對象/數組/正則/函數等引用類型的值,瀏覽器首先第一步就是創建一個堆內存…

釋放:如果當前的堆內存被變量(或者函數以及元素事件等)占用了(占用了:堆內存地址賦值給變量了),此時的堆內存是有用的,不能銷毀;我們想要手動釋放堆內存,只需要讓存儲地址的變量等于其它值即可(最好等于null,null是空對象指針,本意就是不指向任何的堆內存);

1.var o={name:'珠峰培訓'};//<=> o=AAAFFF000
2.o=null;//=> 此時堆內存不被占用,瀏覽器會在空閑的時間,把所有不被占用的堆內存進行自動回收釋放(谷歌瀏覽器的機制,IE瀏覽器是靠計數器來統計當前堆內存被占用的次數:當計數器統計為零次,說明沒有人占用它,瀏覽器銷毀這個堆內存)

棧內存

作用:又稱為作用域,目的就是提供JS代碼執行的環境(供代碼執行的),基本數據類型值都是直接的存儲在棧內存中

全局作用域(棧內存):
形成:瀏覽器渲染頁面,首先就會形成一個全局作用域
銷毀:關閉當前頁面(F5刷新頁面:先把頁面關閉,然后再重新打開)

私有作用域(棧內存):
形成:函數執行會形成私有的作用域
銷毀:一般情況下,函數體中的代碼執行完成,形成的棧內存會立即釋放;當然也有不釋放的情況,后面再詳細來講。

變量提升(預解釋)

當前作用域中,JS代碼自上而下執行之前,瀏覽器首先會把所有帶var/function關鍵字的,進行提前的聲明(declare)/定義(defined),這種提前聲明變量的機制,我們稱之為變量提升

1、變量提升只對當前作用域下的變量或者函數起作用
2、帶var的在變量提升階段只是提前的聲明(告訴當前作用域有這個變量了而已)
3、帶function關鍵字的,在變量提升階段不僅僅是聲明,而且還定義了(定義:其實就是賦值的操作)

1.console.log(total);//=>不會報錯:變量提升階段已經告訴全局有一個total了,只聲明沒有定義,默認值undefined
2.//fn();//=>可以執行:變量提升階段就已經完成了fn的聲明和賦值操作,此時的fn已經是一個函數了
3.
4.function fn(num1,num2){
5. var total=null;
6. total=num1+num2;
7. return total;
8.}//->代碼執行遇到此處直接跳過:變量提升階段已經完成過一次了
9.var total=fn(10,20);//->給total賦值:變量提升階段只是完成了它的聲明,沒有賦值,所以代碼執行到這一步需要賦值
10.console.log(total.toFixed(2));

Alt text

函數執行會形成一個新的私有作用域,和全局作用域一樣,進來的第一步不是代碼執行,私有作用域和全局也有一些區別:私有作用域進來的第一步也不是變量提升

第一步:如果有形參,第一步是形參賦值
第二步:變量提升
第三步:代碼自上而下執行

[私有變量]
形參在私有作用域中聲明的變量(函數)都是私有的,和外界的其它變量互不干擾

作用域鏈

函數執行形成私有作用域,在私有作用域中出現的變量可能是私有的,有可能不是自己私有的,私有和非私有我們的操作步驟是不一樣的

[ 私有的 ]
如果是私有的,和外面的就沒有任何的關系了,以后在函數體中操作的當前變量都按照私有的處理

[ 非私有的 ]
不是自己私有的,首先向其上級作用域查找,如果也不是上級作用域私有的,則繼續向上查找,一直到window全局作用域為止,我們把這種查找機制稱為作用域鏈

1.var a=10,b=20,x=30,y=40;
2.function fn(x){
3. console.log(x,y,a,b);
4. var a=b=x=y=100;
5. a=x+y;
6. b=x-y;
7. console.log(x,y,a,b);
8.}
9.fn(x);
10.console.log(x,y,a,b);
11.
12./*
13. var a=10,b=20;
14. <=>
15. var a=10;
16. var b=20;
17.
18. var a=b=20;
19. <=>
20. var a=20;
21. b=20;
22.*/

Alt text

查找上級作用域

當前函數的上級作用域和它在哪執行的沒有關系,只和它在哪定義的有關系:在哪個作用域下定義的,那么它的上級作用域就是誰

1.var n=10;
2.function fn(){
3. console.log(n);
4.}
5.fn();//=>10
6.
7.~function(){
8. var n=100;
9. fn();//=>10 和在哪執行沒關系,FN是在全局下定義的,它的上級作用域是全局作用域,當前自執行函數形成的私有作用域僅是它的‘宿主環境’
10.}();

不釋放的棧內存

一般情況下,函數執行完成,形成的私有作用域都會自動釋放;

但是有很多時候,函數執行完,形成的作用域(棧內存)無法釋放:當前私有棧內存中的某些東西被棧內存以外的其它內容占用了

當我們手動把棧內存以外占用其內容的東西清除掉(或者不讓其在占用了),之前沒有銷毀的棧內存會在瀏覽器空閑的時候自動銷毀釋放

1.var obj={
2. name:'珠峰培訓',
3. fn:(function(){
4. var i=10;
5. return function(){
6. i++;
7. console.log(i);
8. }
9. })()//=>如果傳obj.name會報錯:因為此時鍵值對還沒有完全存儲到堆內存中呢,obj和堆內存還沒有關系呢,obj=undefined (錯誤:Cannot ready property 'name' of undefined)
10.};
11.obj.fn();
12.obj.fn();
13....
14.
15.
16.//var obj={name:'xxx'};
17.//obj.fn=(function(){})(obj.name); 這樣是可以的,因為此時的obj已經和堆內存關聯在一起了

Alt text

i++和++i

i++:先去拿原有的值和別人運算,運算完成后自身累加1
++i:先自身累加1,累加后,拿最新的結果和別人運算

1.var i=4;
2.console.log(10+(i++)); //=>14
3.console.log(i); //=>5
4.
5.i=4;
6.console.log(10+(++i)); //=>15
7.console.log(i); //=>5

案例練習

1.var i = 0;
2.function fn() {
3. i += 2;
4. return function (n) {
5. console.log(n + (++i));
6. }
7.}
8.var f = fn(3);
9.f(4);
10.fn(5)(6);
11.f(7);
12.fn(8)(9);

Alt text

this

在js中this代表當前函數執行的主體(誰執行的這個函數)

this是誰和函數在哪定義以及在哪執行的,都沒有任何關系

1.function fn(){
2. console.log(this);
3.}
4.var obj={name:'珠峰培訓',fn:fn};
5.obj.fn();//->this:obj
6.fn();//->this:window <=> window.fn();

1、自執行函數中的this一般都是window

1.var obj={
2. name:'珠峰培訓',
3. fn:(function(){
4. console.log(this);//->window(全局對象也可以叫做瀏覽器對象)
5. return function(){}
6. })()
7.};

2、給元素的某個事件綁定方法,事件觸發方法被執行,此時方法中的this一般都是當前操作元素本身

1.oBox.onclick=function(){
2. //->當觸發點擊事件,執行這個匿名函數的時候,方法中的 this:oBox
3.}

3、方法執行看方法名前面是否有點,有點,點前面是誰,方法中的this就是誰,沒有點,方法中的this就是window

1.function fn(){
2. console.log(this);
3.}
4.var obj={name:'珠峰培訓',fn:fn};
5.obj.fn();//->this:obj
6.fn();//->this:window

4、以上三條都是限定在非嚴格模式下,在JS嚴格模式下,上述部分需要換一下

不指定執行主體,this是undefined而不是之前非嚴格模式下默認的window

1.~function(){
2. //this->window
3.}();
4.fn(); //->fn中的this:window
1."use strict";//=>放在JS代碼第一行(一定要是當前作用域第一行),讓整個JS開啟嚴格模式
2.~function(){
3. //this->undefined
4.}();
5.fn(); //->fn中的this:undefined
6.window.fn(); //->fn中的this:window

綜合練習

1.var num = 10;
2.var obj = {num: 15};
3.obj.fn = (function (num) {
4. this.num += 10;
5. num *= 2;
6. return function (n) {
7. this.num += n;
8. console.log(n + (--num));
9. }
10.})(obj.num);
11.var fn = obj.fn;
12.fn(10);
13.obj.fn(15);
14.console.log(window.num, obj.num);

Alt text

閉包匯總

函數執行,形成一個私有作用域,保護里面的私有變量不受外界的干擾,這種保護機制叫做 閉包

但是現在市面上,99%的IT開發者都認為:函數執行,形成一個不銷毀的私有作用域,除了保護私有變量以外,還可以存儲一些內容,這樣的模式才是閉包

1.var utils=(function(){
2.
3. return {
4.
5. }
6.})();

閉包作用:
1、保護
團隊協作開發,每個開發者把自己的代碼存放在一個私有的作用域中,防止相互之間的沖突;把需要供別人使用的方法,通過return或者window.xxx暴露在全局下即可;

jQuery源碼中也是利用保護機制實現的

1.~function(){
2. var jQuery=function(){
3. ...
4. }
5. ...
6. window.$=window.jQuery=jQuery;
7.}();

2、保存
選項卡閉包解決辦法

單例模式(JS高階編程技巧:惰性思想/柯理化函數思想…)

上一篇: 面向對象深入解讀

下一篇: 正則詳解一

我要看A级毛片_多多屋影院,中文字幕国产在线播放,日本近親倫亂中文字幕AV視頻 <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>