詳解JS預(yù)解析原理
預(yù)解析的的不同機(jī)制
預(yù)解析也叫預(yù)聲明,是提前解析聲明的意思;預(yù)解析是針對(duì)變量和函數(shù)來(lái)說(shuō)的;但是變量和function的的預(yù)解析是兩套不同的機(jī)制;
- 當(dāng)瀏覽器加載我們的HTML頁(yè)面的時(shí)候,首先會(huì)提供一個(gè)供JS代碼執(zhí)行的環(huán)境->全局作用域global(瀏覽器中的全局作用域,也叫頂級(jí)作用域是window)
- JS中的內(nèi)存空間分為兩種:棧內(nèi)存、堆內(nèi)存
- 棧內(nèi)存;提供JS代碼執(zhí)行的環(huán)境,存儲(chǔ)基本數(shù)據(jù)類型的值;->全局作用域或者私有的作用域其實(shí)都是棧內(nèi)存;
- 堆內(nèi)存;存儲(chǔ)引用數(shù)據(jù)類型的值(對(duì)象是把屬性名和屬性值儲(chǔ)存進(jìn)去,函數(shù)是把函數(shù)體內(nèi)的代碼當(dāng)做字符串儲(chǔ)存進(jìn)去)
- 在當(dāng)前的作用域中,JS代碼執(zhí)行之前,瀏覽器首先會(huì)默認(rèn)的把所有代var和function的進(jìn)行提前的聲明或者定義,->“預(yù)解析”(也叫變量提升)
var的預(yù)解析機(jī)制
var a=1
- 1、代碼運(yùn)行之前,先掃描有沒(méi)有帶var關(guān)鍵字的變量名,有的話,為這個(gè)變量名在內(nèi)存里開一個(gè)空間;這時(shí)候變量名a是不代表任何值的;用undefined來(lái)表示;undefined是一個(gè)標(biāo)識(shí)符/記號(hào),表示找不到這個(gè)變量名所代表的數(shù)據(jù);不存在的意思;這個(gè)階段叫變量的聲明;
- 2、當(dāng)代碼運(yùn)行的時(shí)候,則給數(shù)據(jù)1開辟一個(gè)內(nèi)存空間;
- 3、讓數(shù)據(jù)1和變量名a綁定在一起;變量類型指的就是數(shù)據(jù)類型;按照js語(yǔ)言的原理來(lái)說(shuō)變量類型有undefined類型;但是數(shù)據(jù)類型是沒(méi)有undefined這種數(shù)據(jù)類型的;只有”undecided”這種字符串類型(字符串類型是數(shù)據(jù)類型的一種);同理也沒(méi)有unll這個(gè)數(shù)據(jù)類型,但是有”null”這種字符串類型;
var num; //1、聲明(declare):var num; ->告訴瀏覽器在當(dāng)前作用域中有一個(gè)num的變量了,如果一個(gè)變量只是聲明了但是沒(méi)有賦值,默認(rèn)的值是undefined console.log(num);//->undefined num = 12; //2、定義(defined):num=12; ->給我們的變量進(jìn)行賦值 console.log(num);//->12 //變量提前使用的話,就是undefined console.log(testStr);//undefined var testStr="22222222"
function 關(guān)鍵字的預(yù)解析步驟
function fn(){……}
在代碼執(zhí)行之前,把所有的帶function關(guān)鍵字的腳本都掃描一遍,然后定義變量;并且同時(shí)給變量賦值;
- 1、函數(shù)的定義只是保存一些字符串;預(yù)解析的時(shí)候在內(nèi)存里保存
fn大括號(hào)里面的字符串; - 2、代碼運(yùn)行時(shí)候,讀到
fn()時(shí)候,這個(gè)時(shí)候就是函數(shù)的運(yùn)行;函數(shù)的運(yùn)行,會(huì)先開辟一個(gè)堆內(nèi)存把字符串當(dāng)做代碼在堆內(nèi)存中再次運(yùn)行,函數(shù)產(chǎn)生的作用域內(nèi)還會(huì)再進(jìn)行預(yù)解析和代碼運(yùn)行;
函數(shù)如果多次執(zhí)行;會(huì)產(chǎn)生多個(gè)作用域;但是產(chǎn)生的多個(gè)作用域里面的內(nèi)容都是相互獨(dú)立的;互相沒(méi)有關(guān)系;(在原型和原型鏈時(shí)候再仔細(xì)研究原理;)
fn(100,200);//->可以在上面執(zhí)行,因?yàn)轭A(yù)解釋的時(shí)候聲明+定義就已經(jīng)完成了
function fn(num1, num2) {
var total = num1 + num2;
console.log(total);
}
總結(jié)
1、var和function關(guān)鍵字的在預(yù)解析的時(shí)候操作還是不一樣的
- var -> 在預(yù)解析的時(shí)候只是提前的聲明了這個(gè)變量,只有當(dāng)代碼執(zhí)行的時(shí)候才會(huì)完成賦值操作
- function -> 在預(yù)解析的時(shí)候會(huì)提前的把聲明加定義都完成了(在代碼執(zhí)行的時(shí)候遇到定義的代碼直接的跳過(guò))
2、預(yù)解析只發(fā)生在當(dāng)前的作用域下,例如:開始只對(duì)window下的進(jìn)行預(yù)解析,只有函數(shù)執(zhí)行的時(shí)候才會(huì)對(duì)函數(shù)中的進(jìn)行預(yù)解析;
[重要]剛開始只對(duì)window下的進(jìn)行預(yù)解析,fn函數(shù)中目前存儲(chǔ)的都是字符串,所以var total沒(méi)啥實(shí)際的意義,所以不進(jìn)行預(yù)解析-> “預(yù)解析是發(fā)生在當(dāng)前作用域下的”
綜合題;
console.log(obj);//->undefined
var obj = {name: "xie", age: 25};
function fn(num1, num2) {//代碼執(zhí)行到這一行的時(shí)候直接的跳過(guò)這一塊的代碼,因?yàn)樵陬A(yù)解釋的時(shí)候我們已經(jīng)完成了聲明加定義
var total = num1 + num2;
console.log(total);
}
var num1 = 12;
fn(num1, 100);//執(zhí)行fn,把全局變量num1的值賦值給形參num1,把100賦值給形參num2
下面是一個(gè)預(yù)解析思路
var a,
b = 0,
fn = function () {
var a = b = 2;
};
fn();
console.log(a, b);
把上面解析成下面就好理解了
var a;
window.b = 0;
window.fn = function () {
//var a = b = 2;
var a = 2;//a是私有的和全局沒(méi)關(guān)系
b = 2;//b是全局的
};
fn();//window.fn()
console.log(a, b);//undefined 2
預(yù)解析機(jī)制
- 1、不管條件是否成立都要進(jìn)行預(yù)解析
console.log(a);//->undefined
if (!!("a" in window)) {//"a" in window -> true
var a = "xie";
}
console.log(a);//->xie
例子中的if是不成立的,預(yù)解析的時(shí)候,碰到非functon內(nèi)的var,都會(huì)聲明,無(wú)論你寫在if else 還是別的判斷里; 假設(shè)if語(yǔ)句起作用的話,那么第一次log(a)的時(shí)候,就會(huì)報(bào)錯(cuò)了(沒(méi)有聲明的變量,是不能直接用的,除非typeof ),而聲明并且沒(méi)有賦值的表現(xiàn)才是undefined;假設(shè)不成立; 最開始總結(jié)的預(yù)解析步驟:代碼運(yùn)行之前,先掃描有沒(méi)有帶var關(guān)鍵字的變量名,有的話,為這個(gè)變量名,在內(nèi)存里開一個(gè)空間;預(yù)解釋是發(fā)生在代碼執(zhí)行前的,所以if根本阻擋不了預(yù)解析;
- 2、預(yù)解析只發(fā)生在”=“的左邊,只把左邊的進(jìn)行預(yù)解析,右邊的是值是不進(jìn)行預(yù)解析的
匿名函數(shù)之函數(shù)表達(dá)式:把函數(shù)定義的部分當(dāng)做值賦值給一個(gè)變量或者元素的事件
fn1();//->undefined() Uncaught TypeError: fn is not a function JS中只有函數(shù)可以執(zhí)行 && JS上面的代碼如果報(bào)錯(cuò)了,在不進(jìn)行任何的特殊處理情況下我們下面的代碼都不在執(zhí)行了
var fn1 = function () {
console.log("ok");
};
fn1();
//預(yù)解釋的時(shí)候:fn=xxxfff000
fn2();//->"ok"
function fn2() {
console.log("ok");
}
fn2();//->"ok"
預(yù)解析的時(shí)候:var fn1 = function()... ->fn的默認(rèn)值是undefined;這里即使有function,也是不能進(jìn)行預(yù)解釋的
- 3、函數(shù)體中return下面的代碼都不在執(zhí)行了,但是下面的代碼需要參加預(yù)解析;而return后面的東西是需要處理的,但是由于它是當(dāng)做一個(gè)值返回的,所以不進(jìn)行預(yù)解析;
function fn() {
console.log(total);
return function sum() {};//return是把函數(shù)中的值返回到函數(shù)的外面,這里是把function對(duì)應(yīng)的內(nèi)存地址返回的到函數(shù)的外面,例如:return xxxfff111;函數(shù)體中return下面的代碼都不在執(zhí)行了
var total = 10;
console.log(total);
}
- 4、匿名函數(shù)的function在全局作用域下是不進(jìn)行預(yù)解析的;
匿名函數(shù)之自執(zhí)行函數(shù):定義和執(zhí)行一起完成了;函數(shù)內(nèi)的聲明,只是在函數(shù)內(nèi)使用;
(function(num){
var testStr="test"+num;
console.log(num);
})(100);
console.log(testStr);// testStr is not defined
- 5、在預(yù)解析的時(shí)候,如果遇到名字重復(fù)了,只聲明一次。 不重復(fù)的聲明,但是賦值還是要重復(fù)的進(jìn)行的
預(yù)解析:
var fn; 聲明
fn = xxxfff000; [聲明]不用了+定義
fn = xxxfff111; [聲明]不用了+定義
// ->fn=xxxfff111
var fn = 12;//window.fn=12
function fn() {//window.fn=function(){}
}
JS中作用域只有兩種:
- window全局作用域;
- 函數(shù)執(zhí)行形成的私有作用域;
- {name:“”} if(){} for(){} while(){} switch(){} 這些都不會(huì)產(chǎn)生作用域;
ES6可以用let形成塊級(jí)作用域;http://www.dhdzp.com/article/67732.htm
面試題
// 涉及this的指向和閉包
var num = 20;
var obj = {
num: 37,
fn: (function (num) {
this.num *= 3; // window.num * 3 = 60
// num += 15;
var num = 45;
return function () {
this.num *= 4;
num += 20; // 調(diào)用父作用域的num (45+20)
console.log(num);
};
})(num), //->把全局變量num的值20賦值給了自執(zhí)行函數(shù)的形參,而不是obj下的30,如果想是obj下的30,我們需要寫obj.num
};
var fn = obj.fn;
fn(); //->65 , 執(zhí)行了第1次=> window.num = 240
obj.fn(); //->85 閉包(65+20) // 執(zhí)行了第2次=> obj.num = 37*4 = 148
console.log(window.num, obj.num); // 240,148
以上就是詳解JS預(yù)解析原理的詳細(xì)內(nèi)容,更多關(guān)于JS預(yù)解析原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
中文路徑導(dǎo)致unitpngfix.js不正常的解決方法
本篇文章是對(duì)中文路徑導(dǎo)致unitpngfix.js不正常的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
JS多線程API?webworker應(yīng)用場(chǎng)景有哪些
這篇文章主要介紹了JS多線程API?webworker應(yīng)用場(chǎng)景有哪些,加密數(shù)據(jù),預(yù)取數(shù)據(jù),預(yù)渲染,復(fù)雜數(shù)據(jù)處理場(chǎng)景,預(yù)加載圖片需要的朋友可以參考下2023-02-02
Javascript學(xué)習(xí)筆記之函數(shù)篇(六) : 作用域與命名空間
本文主要講述了javascript中作用域和命名空間的區(qū)別,十分的詳細(xì),這里推薦給大家,希望小伙伴能有所收獲2014-11-11
JavaScript indexOf方法入門實(shí)例(計(jì)算指定字符在字符串中首次出現(xiàn)的位置)
這篇文章主要介紹了JavaScript indexOf方法入門實(shí)例,indexOf方法用于計(jì)算指定字符在字符串中首次出現(xiàn)的位置,需要的朋友可以參考下2014-10-10
以Python代碼實(shí)例展示kNN算法的實(shí)際運(yùn)用
這篇文章主要介紹了以Python代碼實(shí)例展示kNN算法的實(shí)際運(yùn)用,這里舉了一個(gè)用來(lái)預(yù)測(cè)豆瓣電影用戶的性別的例子,需要的朋友可以參考下2015-10-10

