JS編譯過程
- 語法分析:JS引擎通篇掃描,有低級錯誤直接報錯不執行。
- 預編譯
- 解釋執行
執行期上下文
- 當函數執行時,會創建一個稱爲執行期上下文的內部對象。一個執行期上下文定義了一個函數執行時的環境,函數每次執行時對應的執行上下文都是獨一無二的,所以多次調用一個函數會導致創建多個執行上下文,當函數執行完畢,執行上下文被銷燬。
預編譯
window是全局對象,在全局範圍聲明一個變量,則該變量成爲window的一個屬性。
imply global暗示全局變量:任何變量如果未經聲明就賦值,都會變成window全局對象的屬性。
var age = 18;
function (){
var haha = age = 20;
}
show();
console.log(a) // age -> 20
函數預編譯過程:
- 創建執行期上下文 AO = { };
- 找出全部變量(變量聲明!var)及形參,並賦值undefined。
AO –> {a:undefined,b:undefined,c:undefined}; - 形參實參相統一
AO –>{a:3,b:4,c:undefined}; - 尋找函數聲明(函數表達式不好使!),值賦給函數體
AO –>{a:3,b:4,c:function c () {} };
function show(a,b){
console.log(a); //3
console.log(b); //4
console.log(c); //function c(){}
var c = 20;
function c (){};
c = 30;
a = 20;
}
show(3,4);
全局預編譯過程:
- 創建全局執行期上下文GO = { }
- GO -> { this:window,document:,a:,b: }
- GO -> {this:window,document:,a:undefined,b:function(){} }
作用域[[scope]]:
只有JS引擎有權限訪問。
存儲了執行期上下文的集合。
一個函數被定義時,它的[[scope]]指向的對象(scope chain)的第0位存儲的就是該函數所在的執行環境。
作用域鏈(scope chain):
[[scope]]中所存儲的執行期上下文對象的集合,這個集合呈鏈式鏈接,我們把這種鏈式鏈接叫做作用域鏈。
a{[[scope]]:{}}
// 屬性(作用域scope) 值是一個對象(作用域鏈scope chain)
1.語法分析後創建全局執行期上下文GO -> {...}
2.此時定義 a.[[scope]] -> {0:GO}
3.函數執行時創建函數執行期上下文AO -> {...}
4.此時執行 a.[[scope]] -> {0:AO,GO}
var global = 300;
function test() {
console.log(global); // AO: undefined
var global = 400;
console.log(global); // 400
}
test();
var global = 100;
function test() {
console.log(global); // AO :沒有global GO:100
global = 200; //不是變量聲明
console.log(global); // 200
}
test();
console.log(global); // 200
function a(){
function b(){ // b.[[scope]] -> {0:AO2,1:AO1,2:GO}
var b = 234;
}
var a = 123;
b();
}
var glob = 100;
a();
閉包
- 當內部函數(內部引用值)被保存到外部時,將會生成閉包。
- 閉包會導致原有作用域鏈不釋放,造成內存泄露。
function a() {
function b() {
var bbb = 234;
document.write(aaa);
}
var aaa = 123;
return b;
}
var glob = 100;
var demo = a();
demo(); // 函數b被保存到外部,因此產生閉包
//此時延用之前的作用域鏈,函數執行期上下文不會被刪除
// 用處,原本輸出aaa的值是undefined,但是這樣之後是111
// b.[[scope]] -> {0:aAO,1:GO}
// b/demo.[[scope]] ->{0:bAO,1:aAO,2:GO}
// demo.[[scope]] -> {0:aAO,1:GO}
使用閉包
- 緩存
function add() {
var num = 0;
function get() {
num++;
console.log(num);
}
return get;
}
//get函數被保存到外部,
//它的作用域鏈scope chain -> {0:getAO,1:addAO,2:GO}
var outerGet = add();
outerGet(); // 1
outerGet(); // 2
outerGet(); // 3
// 一直操作的是作用域鏈當中的num值,即 接着上一次的num運算結果
// 每次執行,使用的都是同一個AO, AO沒有被銷燬
//---------------------------------------------------------
function add() {
var num = 0;
num++;
console.log(num);
}
add(); // 每次執行會創建一個新的AO
add();
add();
// ----------------------------------------------------------
function add(){
var num1 = 1;
var num2 = 1;
function get1(){
num1++;
console.log(num1);
}
function get2(){
num2++;
console.log(num2);
}
return [get1,get2]; // 注意返回多個值時的技巧
}
var outerGetALL = add();
outerGetALL[0]();
outerGetALL[0]();
outerGetALL[1]();
outerGetALL[1]();
- 私有化變量
立即執行函數
- 此類函數沒有聲明,在一次執行過後即釋放。
- 可以做初始化工作。
(function () {}());
(function () {})();
//函數聲明被()包裹,被強制轉換成函數表達式
//函數([圓括號包裹函數聲明形式],[函數表達式形式])定義時就用()執行,
//如果不產生閉包,函數就會被銷燬。
+function() show(){}()
// + 等其它運算符 可以將函數聲明轉換成函數表達式
fucntion show (){
}();
//不能執行,只有函數表達式可以被()直接執行
--------------------------------------------------------------
var test = function(){
}();
//可以執行,因爲是函數表達式
--------------------------------------------------------------
var test = function show (){
}();
//可以執行,因爲是函數表達式
--------------------------------------------------------------
var test = function(){
}
//執行方式有兩種:
變量表達式執行:test();
非變量表達式執行(定義時直接執行):[var test(){}] ();
//當函數表達式選擇執行方式----(執行非變量的函數表達式)方式時
//表達式會在執行後銷燬
--------------------------------------------------------------
var test2 = function show (){
console.log(1);
return show;
}();
test2();// 可以執行,形成閉包纔沒被銷燬
應用
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i); // i在GO中
}
}
a[0](); // 10
a[1](); // 10
var a = []; //定義了一個數組,每次循環都會向數組各索引存儲一個輸出函數
for (var i = 0; i < 10; i++) {
(function(i) { // [[scope]] -> {0:nAO -> {i:0} 1:GO -> {}} 保存到外部,形成閉包,存檔
a[i] = function() { // a[i].[[scope]] -> {0:a[i]AO,1:nAO,2:GO} a[i]AO每次執行完銷燬
console.log(i);
}
// return 因爲將函數存到了全局的數組當中,相當於將函數return到外部
})(i);
}
a[1](); // 產生閉包,立即執行函數不會被銷燬,相當於普通函數
a[9](); // 每一次循環,將i=1~9傳入函數
對象知識點補充
對象屬性的調用:o.attributes
function test (){
}
test();
// 相當於
function test (){
this:window
}
window.test();
//誰(對象)通過.方式調用了函數,誰就是this
如果一個函數是構造函數,則會隠式的創建this對象,並返回this
function CarFactory (){
// var this = {
// wheel:4,
// door:2,
// distance:0,
// run:function(){}
// }
this.wheel = 4;
this.door = 2;
this.distance = 0;
this.run = function (){
this.run = function(){
this.distance += 100;
}
}
// return this
}