博客原文地點:
https://finget.github.io/2018/03/01/javascriptPrecompile/看不邃曉的處所迎接發問,有明白的不對的處所願望能夠指出來
JavaScript在運行時,要閱歷三步
- 語法剖析 2.預編譯 3.剖析實行(自上而下)
JavaScript預編譯
先思索這麼一個題
function fn (a) {
console.log(a);
var a = 123;
console.log(a);
function a(){};
console.log(a);
var b =function (){};
console.log(b);
}
fn(1);
預編譯四部曲
- 建立AO對象 Activation Object(實行期上下文)
- 找形參和變量聲明,將變量和形參名作為AO屬性名,值為undefined
- 將實參值和形參一致
- 在函數體內里找函數聲明,值給予函數體
這四步的權重比4>3>2>1,也就是一個掩蓋的歷程.
函數聲明在變量聲明的前面
函數聲明才存在變量提拔。即
function a(){};
,而
var b =function (){};
不會提拔。
細緻剖析
先看一個口試中常碰到的題目
console.log(a); // function a(){}
var a = 1;
function a(){};
逐行實行,在AO中是:
AO{
a: undefied
}
AO{
a: function(){}
}
換一換
var a = 1;
console.log(a); // 1
function a(){};
逐行實行,在AO中是:
AO{
a: undefied
}
AO{
a: function(){}
}
// js是自上而下實行的,先實行var a = 1; 一切AO中的a就被掩蓋
AO{
a: 1
}
按步驟剖析文章開首的例子
- 第一步
AO{
}
- 第二步
AO{
a: undefined,
b: undefined
}
- 第三步
AO{
a: 1,
b: undefined
}
- 第四步
AO{
a: function a(){},
b: undefined
}
詮釋實行
實行的時刻:
AO{
a: function a(){},
b: undefined
}
// a = 123;
AO{
a: 123,
b: undefined
}
效果:
function fn (a) {
console.log(a); // function(){}
var a = 123;
console.log(a); // 123
function a(){};
console.log(a); // 123
var b =function (){};
console.log(b); // function(){}
}
fn(1);
到場window,全局環境
global = 100;
function fn() {
console.log(global);
global = 200;
console.log(global);
var global = 300;
}
fn();
var global;
在全局環境中會天生一個 GO對象 (Global Object),照樣依據上面的四步實行。
GO {
global: undefined
}
// 實行到 global = 100
:
GO {
global: 100
}
當實行fn
之前會先天生一個AO:
AO {
global: undefined
}
所以第一次打印global
是undefined
。
這個時刻雖然全局變量中的
global
已經是
100
,然則
fn
函數中本身有
global
變量,所以不會援用全局中的。
當實行到global = 200
:
AO {
global: 200
}
所以第二次打印global
是200
這裏這中狀況觸及到了‘作用域’。
作用域
這裏參考的是
https://github.com/mqyqingfeng/Blog/issues/3,能夠去細緻看看JavaScript 採納的是詞法作用域,函數的作用域在函數定義的時刻就決議了。
詞法作用域是一種靜態作用域
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar();
這個例子的效果:1
按靜態作用域來剖析
實行 foo 函數,先從 foo 函數內部查找是不是有局部變量 value,假如沒有,就依據謄寫的位置,查找上面一層的代碼,也就是 value 即是 1,所以效果會打印 1。
JavaScript的預編譯歷程來剖析
這裡有全局的環境:
// 這裏我就寫末了一步了
GO {
value: 1,
foo: function() {console.log(value)},
bar: function() {var value = 2; foo()}
}
// 函數在實行時也會天生本身的AO
fooAO{
// 沒有
}
barAO{
value: 2,
foo: foo()
}
foo函數中沒有定義value,所以它會到它地點的上一層去找,並不會去bar內里找
把這個題做一個小小的轉變:
var value = 1;
function foo() {
console.log(value);
}
function bar() {
value = 2;
foo();
}
bar();
如許效果就是2了。
在bar函數中不定義value,而是讓它直接轉變value的值,他本身沒有定義,它也會去全局GO內里找,如許bar內里的value和全局中的value就是同一個內存中的數,當代碼實行到value=2
,再實行foo()時,全局中的value也是2,所以輸出2。在第一種,狀況中,var value=2
只轉變了barAO中的值。
再來,我們再變一下:
function foo() {
console.log(value);
}
function bar() {
value = 2;
foo();
}
bar();
效果照樣2.
此次我們沒有全局定義value,在bar中也沒有定義,而是直接賦值。在JavaScript中假如一個變量未聲明就直接賦值,那末這個變量就是個全局變量。所以GO中會定義一個value:2
,foo也沒有去bar內里找value。
靜態作用域,決議的是作用域鏈的遞次。
末了思索一個題目
function fn(){
var a = b = 100;
console.log(window.a);
console.log(window.b);
}
var a = b =100;
先將100賦值給b,即b=100,此時b沒有聲明就被賦值。