一、javascript语言特性:
1.单线程
2.解释性语言
二、js运行三部曲
1.语法分析
2.预编译
3.解释执行
三、几个简单例子先体会下(一下例子都是相互独立的,不会相互影响)
console.log(a) //undefined 变量提升
var a = 123
复制代码
console.log(b) //error 变量未声明就使用
复制代码
test() //123 函数声明,整体提升
function test() {
console.log(123)
}
复制代码
function test() {
var b = 123
}
test()
console.log(b) //error: b is not defined
复制代码
console.log(a)
// ƒ a(a) {
// var a = 234
// var a = function () { }
// a()
// }
function a(a) {
var a = 234
var a = function () { }
a()
}
var a = 123
复制代码
总结:
1.函数声明,整体提升!
2.变量,声明提升!
还是不太明白,正常,一起接着学,保证很快就会啦!!
四、想要了解以上到底发生了什么,为什么,一起来学习预编译吧
预编译发生在函数执行的前一刻!!!
以下面函数为例
function fn(a) {
var a = 123
function a() { }
var b = function () { } //函数表达式,不是函数声明!
function d() { }
}
fn(1)
复制代码
函数体内的预编译,发生时的四个步骤:
1.创建AO(Activation Object)对象,即执行期上下文(作用域)
AO:{} //创建一个OA对象
复制代码
2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
AO:{
a:undefined,//既有形参a,又有变量a,但值都是undefined
b:undefined,//变量b
}
复制代码
3.将实参和形参相统一
AO:{
a:1,//步骤2时是undefined,步骤3实参传入,所以变成了1
b:undefined,//变量b
}
复制代码
4.找函数体里面的函数声明,将函数名作为AO对象的属性名挂起来,值为函数体
AO:{
a:function a() { },//步骤2时是undefined,步骤3实参传入,所以变成了1,步骤4函数名作为OA对象名,值为函数体
b:undefined,//变量b
d:function d() { } //函数名作为OA对象名,值为函数体
}
复制代码
有了以上四步,我们重新看下着道题,并尝试输出结果,一起来看下执行步骤给出答案
function fn(a) {
console.log(a) //ƒ a() { }
var a = 123
console.log(a)
function a() { }
console.log(a)
var b = function () { }
console.log(a)
function d() { }
}
fn(1)
复制代码
预编译阶 step1->step4,OA结果如下:
AO:{
a:function a() { },//步骤2时是undefined,步骤3实参传入,所以变成了1,步骤4函数名作为OA对象名,值为函数体
b:undefined,//变量b
d:function d() { } //函数名作为OA对象名,值为函数体
}
复制代码
执行阶段 step5 console.log(a) 此时直接从OA里面找,所以是:ƒ a() { }
执行阶段 step6 var a【不看(变量提升,步骤2拿过了)】,a=123,此时 OA如下:
AO:{
a:123,//step6 赋值的
b:undefined,//变量b
d:function d() { } //函数名作为OA对象名,值为函数体
}
复制代码
执行阶段 step7 console.log(a) 此时直接从OA里面找,所以是:123
执行阶段 step8 遇到function a() { }【不看(函数提升,步骤4拿过了)】
执行阶段 step9 console.log(a) 此时直接从OA里面找,所以是:123
执行阶段 step10 var b【不看(变量提升,步骤2拿过了)】,b = function () { },赋值,此时OA如下:
AO:{
a:123,//step6 赋值的
b:function () { },//step10
d:function d() { } //函数名作为OA对象名,值为函数体
}
复制代码
执行阶段 step11 console.log(a)此时直接从OA里面找,所以是:123
执行阶段 step12 遇到function d() { }【不看(函数提升,步骤4拿过了)】
此时执行结束!!
现在是不是很清楚的了解了,下面一起练习下吧
五、全局预编译
1.全局预编译和函数体内预编译基本一样,只是第一步生成的是GO(Global Object),并且没有第三部实参形参统一过程!!
2.其实GO就是window -> GO === window
3.任何全局变量都是window的属性
两个概念
1.imply global暗示全局变量:任何变量如果未经声明就赋值,此变量就归全局变量所有。
如: a = 123; var a = b =123;
2.一切声明的全局变量,全是window的属性,window就是全局的域。
如: var a = 123; window.a = 123;
function test() {
console.log(a)
var a = b = 123
console.log(a)
}
test()
console.log(b)
console.log(a)
复制代码
程序编译过程
对于全局来说 预编译阶段 s1 GO:{}
对于全局来说 预编译阶段 s2 GO:{}
对于全局来说 预编译阶段 s3 GO:{ test:function test(){…}}
执行函数test
对于函数test来说 预编译阶段 s1 AO:{}
对于函数test来说 预编译阶段 s2 AO:{a:undefined}
对于函数test来说 预编译阶段 s3 AO:{a:undefined}
对于函数test来说 预编译阶段 s4 AO:{a:undefined}
对于函数test来说 执行阶段 s5 console.log(a)//undefined
对于函数test来说 执行阶段 s6 b = 123【b是暗示全局变量,所以GO:{b:123,test:function test(){…}}】,a = 123 ->AO:{a:123}
对于函数test来说 执行阶段 s7 console.log(a)//123
函数test执行完毕
对于全局来说 执行console.log(b) //123,找的全局GO上的属性
对于全局来说 执行console.log(a) //error:a is not defined,因为a不在全局作用域window(GO)上
2.
console.log(test)
function test(test) {
console.log(test)
var test = 234
console.log(test)
function test(){}
}
test(1)
var test = 123
console.log(test)
复制代码
程序编译过程
对于全局来说 预编译阶段 s1 GO:{}
对于全局来说 预编译阶段 s2 GO:{test:undefined}
对于全局来说 预编译阶段 s3 GO:{test:function test(){…}}
console.log(test) //function test(){…}
执行函数test
对于函数test来说 预编译阶段 s1 AO:{}
对于函数test来说 预编译阶段 s2 AO:{test:undefined}
对于函数test来说 预编译阶段 s3 AO:{test:1}
对于函数test来说 预编译阶段 s4 AO:{test:function test(){}}
对于函数test来说 执行阶段 s5 console.log(test) //function test(){}
对于函数test来说 执行阶段 s6 test = 234 ->AO:{test:234}
对于函数test来说 执行阶段 s7 console.log(test) //234
对于函数test来说 执行阶段 s8 —
函数test执行完毕
对于全局来说 执行test = 123 ->GO:{test:123}
对于全局来说 执行console.log(test) //123
这种链式的嵌套关系查找变量时,找离自己最近的。自己的AO有就找AO,AO没有就找GO
六、练习
function test(a, b) {
console.log(a) //step5
console.log(b) //step6
var b = 234 //step7
console.log(b) //step8
a = 123 //step9
console.log(a) //step10
function a() { } //step11
var a //step12
b = 345 //step13
var b = function () { } //step14
console.log(a) //step15
console.log(b) //step16
}
test(1)
复制代码
预编译阶 step1 OA:{}
预编译阶 step2 OA:{a:undefined,b:undefined}
预编译阶 step3 OA:{a:1,b:undefined}
预编译阶 step4 OA:{a:function a() { },b:undefined}
执行阶段 step5 打印a //ƒ a() { }
执行阶段 step6 打印b //undefined
执行阶段 step7 var【step2提升了】不管,b = 234,此时OA:{a:function a() { },b:234}
执行阶段 step8 打印b //234
执行阶段 step9 a = 123,此时OA:{a:123,b:234}
执行阶段 step10 打印a //123
执行阶段 step11 function a() { }【step4提升了】不管
执行阶段 step12 var step2提升了】不管
执行阶段 step13 b=345,此时OA:{a:123,b:345}
执行阶段 step14 var【step2提升了】不管,b = function () { },此时OA:{a:123,b:function () { }}
执行阶段 step15 打印a //123
执行阶段 step16 打印b //ƒ () { }
function test(a, b) {
console.log(a) //s5
c = 0 //s6
var c //s7
a = 3 //s8
b = 2 //s9
console.log(b) //s10
function b() { } //s11
function d() { } //s12
console.log(b) //s13
}
test(1)
复制代码
预编译阶 step1 OA:{}
预编译阶 step2 OA:{a:undefined,b:undefined,c:undefined}
预编译阶 step3 OA:{a:1,b:undefined,c:undefined}
预编译阶 step4 OA:{a:1,b:function b() { },c:undefined,d:function d() { }}
执行阶段 step5 打印a //1
执行阶段 step6 c = 0 -> OA:{a:1,b:function b() { },c:0,d:function d() { }}
执行阶段 step7 —
执行阶段 step8 a = 3 -> OA:{a:3,b:function b() { },c:0,d:function d() { }}
执行阶段 step9 b = 2 -> OA:{a:3,b:2,c:0,d:function d() { }}
执行阶段 step10 打印b //2
执行阶段 step11 —
执行阶段 step12 —
执行阶段 step13 打印b //2
var glob = 100
function fn(){
console.log(glob) //100
}
fn()
复制代码
global = 100
function fn() {
console.log(global) //undefined
global = 200
console.log(global) //200
var global = 300
}
fn()
var global
console.log(global) //100
复制代码
5
function test() {
console.log(b) //undefined (2)
if (a) {
var b = 100
}
console.log(b) //undefined (3)
c = 234
console.log(c) //234 (4)
}
var a
console.log(a)//undefined (1)
test()
a = 10
console.log(a)//10 (5)
console.log(c) //234 (6)
复制代码
()里面的数字表示输出顺序,不要考虑 if 遇到 var 就拿出来,记得我们是【预编译】,只有真正的编译执行才去考虑if!
6
function bar() {
return foo;
foo = 10;
function foo() { }
var foo = 11
}
console.log(bar()) //function foo(){}
复制代码
7
console.log(bar()) //11
function bar() {
foo = 10;
function foo() { }
var foo = 11
return foo;
}
复制代码
8
console.log(b)//undefined
var b = function () { }
复制代码
9
a = 100
function demo(e) {
function e() { }
arguments[0] = 2
console.log(e) //2 (2)
if (a) {
var b = 123
function c() { } //tips:此处不允许在if中声明函数
}
var c
a = 10
var a
console.log(b) //undefined (3)
f = 123
console.log(c) //function(){} 如果tips处可以那么输出function(){},但不允许所以输出undefined(4)
console.log(a) //10 (5)
}
var a
console.log(a) //100 (1)
demo(1)
console.log(a) //100 (6)
console.log(f) //123 (7)
复制代码