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
}