私有变量有两个:
1)形参
2)带var的;
带var:1)会进行预解析 2)在全局环境下,window的全局属性
不带var:1)不会进行预解析 2)window的全局属性
1.获取阶段: 往上级作用域进行查找,找到直接弹出,找不到,继续往上级的上级找……一直找到window还没有找到,报错; xx is not defined;
2.设置阶段: 往上级作用域进行查找,找到进行重新赋值,找不到,继续往上级的上级找……一直找到window还没有找到,属于window的全局属性;
上级作用域跟在哪里调用无关,只跟他对应的堆内存在哪里开辟(定义)有关;
全局作用域释放:只有我们关闭浏览器打开的HTML页面,才能释放;
私有作用域释放:一般情况,当我们执行完函数的时候,他会自动释放; 但特殊情况除外:
1)如果函数里面的东西被外面占用,就无法释放;
2)不立即释放:当函数执行完成的时候会返回一个函数,这个返回的函数还需要再执行一次;只有返回值执行完成后才能释放
如果堆内存被变量占用,无法得到释放,只有把变量的指针指向一个空指针null;这样,当浏览器空闲的时候,会把指向空指针的变量自动收回;浏览器 的这种处理机制,叫做垃圾回收机制
例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());
题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);