1. 预解析
-
关于预解析的定义
1.什么是预解析
- 在当前作用域下,在JS代码执行之前,浏览器会对带var和带function进行提前声明或定义;— 这种机制叫预解析。 2.作用域(全局作用域 和 私有作用域)
-
全局作用域:当浏览器打开HTML页面的时候,会形成一个供JS代码执行的全局作用域(全局环境),在这个全局作用域下,
所有的全局变量,都属于window的全局属性;所有的全局函数,都属于window的全局方法 -
私有作用域: 函数在调用的时候会形成一个私有作用域
私有变量有两个:
1)形参
2)带var的;
3.声明和定义
- 声明:告诉浏览器,有这么一个变量; 举例: var a;
- 定义:给已经声明过的变量赋值; eg: var a=123; 4.关于var和function在定义阶段的不同
- 带var的: 只声明,不定义;
- 带function的: 声明+定义;
2. 函数的定义和执行
-
定义3阶段:
- 1)开辟一个空间地址
- 2)把函数体中的所有JS代码做为字符串存在这个空间中
- 3)把空间地址赋值给函数名 执行
-
1)形成一个私有作用域
2)给形参赋值
3)预解析
4)代码从上到下的执行;
3. 带var和不带var的区别
带var:1)会进行预解析 2)在全局环境下,window的全局属性
不带var:1)不会进行预解析 2)window的全局属性
4. 作用域链
-
当函数执行的时候,会形成一个私有作用域;查看这个作用域中是否有私有变量A:
- 1)如果有,这个作用域中所有出现的A都属于私有变量,跟外界没有任何关系;
-
2)如果没有:
1.获取阶段: 往上级作用域进行查找,找到直接弹出,找不到,继续往上级的上级找……一直找到window还没有找到,报错; xx is not defined;
2.设置阶段: 往上级作用域进行查找,找到进行重新赋值,找不到,继续往上级的上级找……一直找到window还没有找到,属于window的全局属性;
5. 上级作用域
上级作用域跟在哪里调用无关,只跟他对应的堆内存在哪里开辟(定义)有关;
6. 内存(栈内存,堆内存)
-
栈内存:全局作用域和私有作用域
-
内存释放:
全局作用域释放:只有我们关闭浏览器打开的HTML页面,才能释放;
私有作用域释放:一般情况,当我们执行完函数的时候,他会自动释放; 但特殊情况除外:
1)如果函数里面的东西被外面占用,就无法释放;
2)不立即释放:当函数执行完成的时候会返回一个函数,这个返回的函数还需要再执行一次;只有返回值执行完成后才能释放
堆内存:
-
对象数据类型:存的是对象的属性名和属性值
函数数据类型:存的是代码字符串;
var a={};
a=null;
堆内存的释放:如果堆内存被变量占用,无法得到释放,只有把变量的指针指向一个空指针null;这样,当浏览器空闲的时候,会把指向空指针的变量自动收回;浏览器 的这种处理机制,叫做垃圾回收机制
7. 预解析的特点
-
预解析无节操
-
1)只对等号左边带var的,进行只声明,不定义;
2)自执行函数不进行预解析,只有执行到他的时候,声明+定义+执行同步完成
3)条件判断语句中,无论条件是否成立,都会进行预解析;
注意:为了代码规范性,不要在条件判断语句中写函数定义阶段;因为预解析时各大浏览器不同;
4)已经声明过的变量,不会进行重复声明
5)return后面的返回值不会进行预解析,return下面的语句虽然不执行,但会进行预解析;
8. 预解析经典问题
例1: — 作用域
<script>
//每个script都是一个域;两个script属于不同的域;
alert(n)
</script>
<script>
var n=456;
</script>
例2:— 变量提升
<script>
function abc(){
a=12;//window.a
alert(a);//12
}
function b(){
alert(a) //12
}
abc();
b();
</script>
例3:
if(!('a' in window)){ //in:用来判断该属性是否为对象上的属性 (公有属性+私有属性)
var a=15;
alert(a)
}
alert(a)
例4:
(function f(){
function f(){ return 1; }
alert (f());
function f(){ return 2; }
})();
例5:
(function f(){
var f=function(){return 3}
function f(){ return 1; }
alert (f());
function f(){ return 2; }
})();
例6:
function fn(){
alert(n);
fn2();
fn1();
return function fn1(){
alert('fn1')
};
var n=456;
alert(n)
function fn2(){
alert('fn2')
}
}
fn();
例7:
var name='定义';
var age=500;
name=(function(name,age){//2.定义了形参age,但没有赋值,拿到的值就是undefined;
arguments[0]='形参';
age=age||this.age;//3. || 前面为假,才会走后面 4.自执行函数中的this-永远都是window;
console.log(name,age);//'形参' 500
})(name);//1.只执行函数的返回值:因为没有return,所以返回值是undefined
console.log(name,age) //undefined 500
例8:
var obj={
fn:function(){
alert(this);
return function(){
alert(this);
}
}
};
var f=obj.fn;
f(); // window
f()(); // window window
obj.fn()(); //obj window
例9:
闭包的作用:
1.避免变量名冲突 : 1)在闭包中重新定义变量 2)给形参赋值;
2.在闭包中影响全局:1)在闭包中不定义跟全局变量一样的变量名 2)用自执行函数中的this/直接用window;
3.封装
var name='aaa';
var age=8;
(function(name,age){
window.name='qiang';
window.age=28;
alert(window.name)
})();
alert(name)
利用闭包来封装— 获取节点的兼容方法
var aLi=document.getElementsByTagName('li');
(function(){
function prev(curEle){
//previousElementSibling:直接可以拿到上一个哥哥元素,但是他不兼容,他只支持标准浏览器;
if('previousElementSibling' in curEle){
return curEle.previousElementSibling;
}
//先保存上一个哥哥节点;
var pre=curEle.previousSibling;
//判断条件:pre是节点,并且不是元素节点;
while(pre && pre.nodeType !== 1){
pre=pre.previousSibling;//只要条件成立,继续在当前节点上找他的上一个节点,直到找到元素节点后,就没法进while循环了;
}
return pre;
}
window.prev=prev;//在闭包中封装的方法,如果想让外面用,需要通过window.xx把他输送出去;否则,外面用不了
})();
console.log(prev(aLi[3]))
例10:
var num = 10;
var obj = {
num: 20,
fn: (function (num) {
this.num *= 3;
num += 10;
return function () {
this.num *= 3;
num += 1;
console.log(num);
}
})(num)
};
var fn = obj.fn;
fn();
obj.fn();
console.log(window.num, obj.num);
例11: – 函数声明优于变量声明
function change() {
alert(typeof fn) //function
function fn() {
alert('hello')
}
var fn;
}
change();
例12:函数预解析
//只能在IE10及10以下执行;
f=function(){return true};
g=function(){return false};
(function(){
//运算符的优先级:算术>比较>逻辑>赋值
if(g()&&[]==![]){ //条件判断语句中,无论条件是否成立,都会进行预解析
f=function(){return false;}
function g(){return true;}
}
})();//自执行函数,不会进行预解析,只有当执行到他的时候,声明+定义+执行同步完成;
alert(f());
alert(g());
9. 预解析难题集
题1:
var n=0;
function a(){
var n=10;
function b(){
n++;
alert(n);
}
b();
return b;
}
var c=a();
c();
alert(n);
题2
var a=4;
function b(x,y,a) {
alert(a);
arguments[2]=10;
alert(a);
}
a=b(1,2,3);
alert(a);
题3
var foo='hello';
(function(foo){
console.log(foo);
var foo=foo||'world';
console.log(foo);
})(foo);
console.log(foo);
题4
var a=9;
function fn(){
a=0;
return function(b){ return b+a++; }
}
var f=fn()
var m=f(5);
alert(m);
var n=fn()(5);
alert(n);
var x=f(5);
alert(x);
alert(a);