js预编译

预编译:
预编译中存在两个重要的知识点:函数声明提升和变量声明提升,函数声明提升是一种整体提升,它会把函数声明和函数体一起提升到前面。变量声明提升则是一种局部提升,它仅仅将变量的声明提前了,但是并没有将赋值也一起提前。
说预编译前先说两个知识点:

(1)暗示全局变量:
未经变量声明直接赋值的变量,归window所有。
(2)一切声明的全局变量,都是window的属性:
在全局作用域中通过var关键字声明的变量,会被当成window的属性,无法被删除。又称不可配置属性
不可配置属性:在window中,经过var声明的全局变量不能通过delete操作来删除,不可配置的属性delete不掉。但是未经声明的全局变量可以被删除。

预编译发生在函数执行前一刻:

步骤:

  1. 隐式的创建一个函数的执行期上下文对象即ActivationObject,简称AO。
  2. 找出函数的形参和通过var关键字声明的变量,将其当做AO对象的属性名添加在AO对象中,值设为undefined。
  3. 将实参和形参相统一。
  4. 将函数内的函数名作为AO对象的属性名添加到AO对象中,值为其函数体。
    例子:
function test (a, b) {
      console.log(a) ;
      function a () {}
      a = 222;
      console.log(a);
      function b () {};
      console.log(b);
      var b = 111; 
      var a ;
	  console.log(b);
	}
	test(1);

分析:

  1. 创建AO对象:
    AO{
    }

  2. 找出形参和var声明的变量,当成AO的属性名添加在AO对象上
    AO{
    a:undefined,
    b:undefined,
    }

  3. 将形参和实参相统一
    AO{
    a:1,
    b:undefined,
    }

  4. 找到函数体中的函数声明,将其函数名作为AO的属性添加到AO中,值为其函数体。
    AO{
    a:function a(){},
    b:function b(){},
    }

预编译环节结束,开始函数的执行。

//打印	    console.log(a)//打印function a(){} 
//a赋值	    a = 222;
//打印		console.log(a);//打印222 	
//打印		console.log(b);//打印 function b(){}
//b赋值   	b = 111; 	
//打印		console.log(b);//打印111

在函数预编译环节,其实不仅在函数内部创建了局部对象AO,也在全局内创建了全局对象GO(Global Object)。

例如:

function test(a,b){
console.log(a);//function a(){}
console.log(c);//function c(){}
    function a(){};
    var c = 3;
    d = 3;//暗示全局变量
    console.log(d);//3
console.log(c);//3
b = 5;
    function  b() {};
    console.log(a);//function a(){}
    console.log(b);//function b(){}
    a = 10;
    console.log(a);
}
test(1,2);
console.log(d);//3

分析预编译环节:
//分析
1.
AO{
}
GO{
}
2.
AO{
a:undefined,
b:undefined,
c:undefined
}
GO{
d:undefined
}
3.
AO{
a:1,
b:2,
c:undefined
}
GO{
d:undefined
}
4.
AO{
a:function a(){},
b:function b(){},
c:undefined
}
GO{
d:undefined
}


预编译结束,开始函数执行:

test执行://函数执行的操作,最终就是针对AO对象和GO对象进行操作
console.log(a);//function a(){}
console.log(c);//undefined
c = 3;
d = 3;//程序执行到此处,发现没有声明d呀,好吧,添加到window上吧,当成window属性
console.log(d);//3
console.log(c);//3
b = 5;
console.log(a);//function a(){}
console.log(b);//5
a = 10;
console.log(a);//10

 //函数外部:
console.log(d);//检测是否为暗示的全局变量,虽在函数内部,实际为window对象的属性,可以配置

再如例子:

//对于一般的例如:
function test(a){
	a = a + '10';
}
var a = 10;
console.log(a);
test(a);
console.log(a);

分析:

  • 1.创建全局作用域GO对象,和局部作用域AO对象

     GO{
     	a:undefine,
     }
     AO{
     	a:undefine;
     }
    
  • 2.寻找变量声明和形参并当成各自对象的属性添加到各自对象上

     GO{
     	a:undefine,
     }
     AO{
     	a:undefine;
     }
    
  • 3.实参形参相统一,

     GO{
     	a:undefine,
     }
     AO{
     	a:undefine;
     }
    
  • 4.找函数声明,并添加

     GO{
     	a:undefine,
     	test:function test(){}
     }
     AO{
     	a:undefine;
     }
    

预编译环节结束,开始函数执行:

1.a=10
GO{
a:10,
test:function test(){}
}
AO{
a:undefine;
}
2.console.log(a);
在全局作用域内,寻找自身的属性是否有,很明显有打印10,此处说一下,函数没被调用就是坨废物.不用管它里面写什么,
3.test(a);
这里就不是废物了,此时a=10,传入函数体内执行
a = a+‘10’;//此处的a为’1010’
GO{
a:10,
test:function test(){}
}
AO{
a:‘1010’;
}
4.console.log(a);//按照所处的作用域,寻找自身所属对象上是否含有该属性,全局对象上a属性的值依旧为10,这里特别强调一点,此时的a指的是全局对象上的a属性即GO的,不要因为执行了test而混淆,test的a是test作用域下的属性,也就是AO的,外部访问不到。如果给函数test内部a=a+‘10’后再加上一句console.log(a);
此时就会打印出’1010’,因为此时访问的是自身AO对象的a属性值。
通俗的说:**局部作用域内没有的,可以到全局作用域上去找,但是全局作用域内没有,不能到局部作用域去找.作用域链可以从儿子到祖辈找,不能反过来.就像儿子可以拿取爹的财产一样,但是爹不能拿儿子的(不过现实中都可以拿,自己意会)**还有如果局部作用域中的属性与全局作用域的属性名相同,不用管太多,就近原则,自己有就用自己的,这是作用域链的查找方式.

下章讲讲作用域链吧


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值