预解释:
在当前作用域下,JS代码执行之前,首先把所有带var和function关键字的进行提前声明或者定义
var只是提前声明,function是提前的声明+定义都完成了
总结
创建AO对象
找形参和变量声明并作为AO对象属性名,值为undefined, 将实参值和形参值统一
在函数体里面找函数声明,值赋予函数体
1)不管条件是否成立里面的都要进行预解释
if(!("a" in window)){
var a=12;
}
console.log(a);//->undefined
2)只对=左边的进行预解释,右边的是赋的值,是不进行预解释的
fn1();
function fn1(){}
fn1();
var fn2=function(){};
fn2();
->在项目中我们一般用函数表达式的方式定义函数:就是想让它在上面定义只能在下面执行
3)自执行函数不进行预解释(window下预解释的时候不声明和定义move了)
(function move(){
//执行的时候需要在形成的私有作用域中进行预解释
window.setTimeout(move,10);
//window.setTimeout(arguments.callee,10);//->一般情况下不用arguments.callee,因为在严格模式("use strict")下屏蔽了它的使用
})();
var fn=function(num){
arguments[0]=12;//->把第一个传递进来的参数值修改为了12,在非严格模式下可以映射到我们的num形参变量->num=12;在严格模式下不会和形参产生映射
};
严格模式在代码最顶端的位置,我们写上 “use strict” 这样的话我们接下来的JS代码都要使用严格模式了 (高级程序设计三,最后面的章节->严格模式和非严格模式)
4)函数执行形成一个私有的作用域,在里面预解释还有一些细节的问题
var fn=function(){
//给形参赋值
//预解释 ->形参和预解释的是私有的变量
return function(){};//->会返回值,但是这个函数不进行预解释,当遇到return的时候开辟一个堆内存返回一个地址
var num=12;//->这句话不执行但是会把num提前的声明
};
fn();
5)函数的名字和变量的名字相同了就算重复,一但重复了,不会重新的声明但是会重新的定义,后面定义的会把前面的覆盖掉
fn();//->2
function fn(){alert(1);};
fn();//->2
var fn=13;//->fn=13
fn();//->13() ->fn is not a function
function fn(){alert(2);};
作用域
window全局作用域->页面关掉才销毁
函数执行会形成私有的作用域
1)作用域的销毁
一般情况下,函数执行形成一个私有的作用域,当执行完成后就销毁了->节省内存空间
2)作用域的不立即销毁
function fn(){
var i=10;
return function(n){
console.log(n+i++);
}
}
fn()(15);//->先执行fn,有一个私有的变量i=10,返回一个堆内存地址 xxxfff111,我们发现这个地址还用到了一次,那么当前的这个fn形成私有作用域(A)就不能立即销毁了,xxxfff111(15)->输出25,A中的i变为11;当xxxfff111执行完了,发现这个地址没用了,浏览器就把A、xxxfff111都释放了
fn()(20);//->在执行fn的时候一切都从新开始了,和上面的步骤是一样的->输出30
3)作用域的不销毁:形成一个私有作用域,里面的内容被外面占用了
function fn(){
var i=10;
return function(n){
console.log(n+i++);
}
}
var f=fn();//->fn执行形成一个私有的作用域A,A中有一个私有的变量i=10,A中返回一个地址xxxfff11,被外面的f占用了,那么当前的A就不能销毁了
f(15);//->输出25,让A中的i=11
f(20);//->输出31,让A中的i=12
…
当我们知道f用完的时候,为了优化性能,我们让f=null,这样的话A中的xxxfff111没人占用了,浏览器会把A和xxxfff111都释放了
几种不销毁常用到的形式:
1)函数执行,返回一个引用数据类型的值,并且在函数的外面被别人接收了,那么当前函数形成的私有作用域就不在销毁了–>例如上面的案例
2)在函数执行的时候,里面的一个小函数的地址赋值给了我们的外面元素的点击事件,那么当前小函数也相当于被外面占用了,大函数执行形成的私有的作用域也不销毁了
//每一次循环都执行自执行函数形成一个私有的作用域(循环三次就有三个作用域,每一个作用域中都有一个i,第一个存储的是0,第二个存数的是1…),在每一个私有的作用域中都把里面的函数绑定给了外面元素的点击事件,这样的话每一次形成的作用域都不销毁了(三个不销毁的作用域)
var oLis=document.getElementsByTagName("li");
for(var i=0;i<oLis.length;i++){
~function(i){
oLis[i].onclick=function(){
tabChange(i);
}
}(i);
}
3)在使用setTimeout实现轮询动画的时候,我们如果move需要传递参数值,那么像下面这样的写法会行成很多的不销毁的作用域,非常的耗性能
function move(tar){
<js code>
//window.setTimeout(move,10); ->第二次执行move的时候我们没有给它传值(这样写不行)
window.setTimeout(function(){
move(tar);
},10);//->这样写实现了,但是每一次执行定时器都会形成一个私有的所用域(匿名函数形成的)A,在A中使用了上级作用域中的tar的值,而且执行了move又形成了一个小的作用域(而在小的作用域中会使用tar的值),这样每一次定时器形成的A都不能销毁了
}
move(100);//->第一次这样执行传递100
//解决办法:
function move(tar){
~function _move(){
<js code>
window.setTimeout(_move,10);
}();
}
move(100);//->第一次这样执行传递100
JS中内存空间释放的问题(堆内存、栈内存)
[谷歌浏览器]
我们开辟一个内存,可能或有一些其他的变量等占用了这个内存,谷歌浏览器都会间隔一段时间看这个内存还有没有被占用,如果发现有没有被占用的内存了,就自己帮我们回收了(内存释放)
[火狐和IE]
我们开个内存,当我们引用了它,就在内存中记录一个数,增加一个引用浏览器就把这个数+1,减少一个引用,浏览器就把这个数-1…当减到零的时候浏览器就把这个内存释放了;但是有些情况下(尤其是IE)记着记着就弄乱了,内存就不能释放了–>浏览器的内存泄露
var obj={};
我们养成一个好的习惯,当我们obj这个对象使用完成了,我们手动的obj=null (null空对象指针),浏览器会自己把刚才的堆内存释放掉