1.5.6函数作用域
1.5.6 函数作用域
1.作用域 定义变量并能找到变量的规则
在函数里面中可以使用全局变量,在全局中不能够使用函数中的变量
在js中 变量有两个存放的区域
1.全局作用域:
全局变量会自动成为window对象的属性
window对象也可以叫做Global Object GO对象
打开浏览器 自动生成window对象,即生成GO对象
关闭浏览器 window对象就自动销毁了
eg:当打开浏览器的时候,会自动生成GO对象,会自动把变量名生成GO对象的属性名,变量名的值会变成GO对象的属性值,上图的auto不在花括号内,所以是全局变量
函数表达式也可以放到全局变量中
GO { 属性名:属性值
c:'heaven',
auto:function(){},
heaven:function(){}
}
2.函数作用域
函数的预编译四部曲: /* 函数执行时,会生成一个AO对象,活动的*/
①创建AO对象 ==> Activated Object(活动对象)
②找到形参和变量,把形参和变量作为AO对象的属性名,值是undefined
③把实参的值赋给形参。此时不再是undefined
④在函数中找到函数声明,把函数名作为AO对象的属性名,值是函数体。
预编译之后才能执行JS代码 函数执行后必然生成自己的AO对象,函数执行完后被销毁
全局的预编译三步:
1.创建GO对象 ==> Global Object(GO对象)
2.找到变量,把变量作为GO对象的属性名,值是undefined
3.在全局中找到函数声明,把函数名作为GO对象的属性名,值是函数体
开始执行代码
函数表达式也可以放到全局变量中
预编译练习: function outer(){ /* * AO{ * b:3, * inner:function(){} * } * */ function inner(){ /* * AO{ * a:4 * } * */ var a = 4; c = 5; //一个变量 没有var关键词,直接赋值,则这个是全局的,叫做暗示全局变量 console.log(a); //4 inner的AO中找到变量a console.log(b); //3 outer的AO中找到变量b console.log(c); //5 GO中找到变量c } var b = 3; inner(); } outer(); var a = 2; console.log(outer);
找变量的时候,先找里面,再找外面
函数里面var a=4;先是走第二步,a:undefind,之后再变成a=3,打印的时候会从AO对象里面找
3.变量提升:预编译时,从上往下,第一句是打印,第二句是定义,则打印出是undefined var b 所以被提升
变量提升只会把变量和函数声明
4.作用域链
console.dir()下的函数的[[Scopes]]这个属性 存放在函数的作用域链
inner函数未执行(死函数)
--->inner函数的作用域链 = outer:AO+GO (就是不算的时候)
inner() -->inner函数执行(激活的函数)
--->inner函数的作用域链 = inner:AO + outer:AO + GO (执行时)
所以a在当前的函数里面找不到,会顺着作用链找,就是从内部往上找
outer函数未执行
--->outer函数的作用域链 = GO (outer所在的环境是GO)
outer()函数执行
--->outer函数的作用域链 = outer:AO + GO
例子
GO: auto:function(){}
AO: b:undefined
a:undefined 然后执行第四不步,找到函数声明
b:function() {} 所以上面的b被覆盖了
所以b打印出来的是一个函数,往上找
GO a:2 auto:function(){} AO a:undefined 没有实参不执行第三步骤 没有函数声明 var a =2 function auto(a){ if(typeof a){ //所以a是undefined console.log("海文"); //type(undefined)== "undefined" }else{ //转换为布尔值是true console.log("heaven"); } var a; } auto();
5.闭包
当内部函数被保存在外部时,由于内部函数的作用域链上存在内部函数创建时的环境(即父级函数和祖先函数的AO对象,全局对象GO),导致内部函数可以顺着作用域链寻找变量,所以形成了闭包,同时内部函数的作用域链上(即父级函数和祖先函数的AO对象,全局对象GO)无法被垃圾回收机制回收,导致了内存泄漏
GO:outer:function(){},
result:undefined--> function inner(){} 通过闭包把内置函数扔出全局变量中
outer:AO: b:4
inner:function(){}
inner:AO:{ 空 }
function outer(){
var b = 3;
function inner(){ //outer:AO +GO
b++;
console.log(b); //4 在outer的AO中找到的
}
return inner; //是有return抛出里面的未激活函数的这样子形式叫闭包结构
}
var result = outer();
console.log(result); //打印的是inner的函数体
result(); //让抛出来的inner函数在全局变量中执行
还有 var result = outer(); 是被提升了到GO,系统先var result,属性值是undefined, 原来的就变成 result = outer() // 不会显示出来的
1.对象里面的属性值还可以创建自己的对象,属性值可以放任何数据类型,也可以是函数
2.对象属性里面存的属性值是一个函数,我们就把这个属性称为对象的方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
var aiqi={ name:"考拉", age:16, child:true, peiou:{ name:"母考拉", age:10, son:{ name:"小考拉", age:2, } }, wife:function(){ aiqi.peiou={ //就是把aiqi的配偶换掉 name:"小奶狗", age:20, son:{ name:"小aiqi", age:10 } } } } console.log(aiqi.peiou.son.age) // aiqi.wife()
1.5.7.函数表达式和函数声明之间的转换
函数声明转换成函数表达式的方法
在函数声明前面加上 !, ~ ,+, -,把函数声明整体放置在()
重要结论:
* 1.函数表达式的名字只能在自身作用域中使用 (是会把自身函数打印出来)
! function auto(){
console.log(auto);
}
(函数表达式是没有名字的,var a=function 这里不能有名字(){},如果有名字,打印auto(),会报错,因为系统默认会把auto干掉)
* 2.只有函数表达式后面紧跟()才能执行,后面()里的是用来传实参的
例子:
(function auto(){
console.log('我被启动了');
})()