function a() {
function b() {
var bb = 234;
}
var aa = 123;
b();
}
var global = 100;
a();
b函数在被创建的时候直接获取到a函数作用域链的引用
问题:b函数中a的AO和a函数中a的AO是不是同一个AO?
可以做个测试:
function a() {
function b() {
var bb = 234;
aa=111;//在函数b中把a函数中的变量aa的值修改了
}
var aa = 123;
b();
console.log(aa);//控制台打印出 111 说明是同一个AO
}
var global = 100;
a();
例子:
function a() {
function b() {
var bbb = 234;
console.log(aaa);
}
var aaa = 123;
return b;
}
var glob = 100;
var demo = a();
demo();//123
a函数在执行的时候遇到b函数的创建,于是b函数的作用域链和a函数的作用域链指向同一块区域。如图:
a函数执行完毕之后,a函数的执行上下文被销毁,但是b函数的还在,如图:
应用:
function a() {
var num = 100;
function b() {
num++;
console.log(num);
}
return b;
}
var demo = a();
demo();//101
demo();//102
当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄露。
function test() {
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function () {
document.write(i + " ");
}
}
return arr;
}
var myArr = test();
for (var j = 0; j < 10; j++) {
myArr[j](); //10 10 10 10 10 10 10 10 10 10
}
首先,test()函数执行的时候创建自己的AO对象,执行完毕之后test()的AO中的i为10,注意此时里面的函数还没有开始执行,所以还没有创建自己的AO对象,当在下面的for循环中执行myArr[i]函数时,函数创建自身的AO对象,指向test函数的AO对象,此时test函数的AO对象里面的i为10,所以myArr[i]函数的面打印的i为10。
但是如果要打印出来的是0 1 2 3 4 5 6 7 8 9,该怎么做?
如下,在函数表达式外面嵌套一层立即执行函数,再次执行myArr[i]函数的时候,myArr[i]函数创建自己的AO并继承立即执行函数的AO,立即执行函数中的AO中的j在每次执行for循环的时候已经被改变,所以myArr[i]打印的是立即执行函数中的变量j的值,而不是test函数中i的值。
function test() {
var arr = [];
for (var i = 0; i < 10; i++) {
(function (j) {
arr[j] = function () {
document.write(j + " ");
}
}(i));
}
return arr;
}
var myArr = test();
for (var j = 0; j < 10; j++) {
myArr[j](); //0 1 2 3 4 5 6 7 8 9
}
function add() {
var count = 0;
function demo() {
count++;
console.log(count);
}
return demo;
}
var counter = add();
counter(); // 1
counter(); // 2
counter(); // 3
counter(); // 4
function test() {
var num = 100;
function a() {
num++;
console.log(num);
}
function b() {
num--;
console.log(num);
}
return [a, b];
}
var arr = test();
arr[0](); //101
arr[1](); //100
// 需要明白的是num在函数test()的AO中,而函数a和函数b操作的是同一个AO,即同一个mun,所以会出现上面的结果
例子:
function eater() {
var food = "";
var obj = {
eat: function () {
console.log('I am eating ' + food);
food = '';
},
push: function (myFood) {
food = myFood;
}
}
return obj;
}
var eater1 = eater();
eater1.push('banana');
eater1.eat(); //I am eating banana
//这里多个函数和同一个函数形成闭包,操作的是同一块区域
function Deng(name, wife) {
var prepareWife = "xiaozhang";
this.name = name;
this.wife = wife;
this.divorce = function () {
this.wife = prepareWife;
}
this.changeWife = function (target) {
prepareWife = target;
}
this.sayPrepareWife = function () {
console.log(prepareWife);
}
}
var deng = new Deng("deng","xiaoliu");
console.log(deng.wife);// xiaoliu
deng.divorce();
console.log(deng.prepareWife);// undefined
deng.sayPrepareWife();// xiaozhang
/*
类的定义里面没有prepareWife这个属性,但是在实例化对象的时候
可以利用函数操作prepareWife属性,从而实现属性的私有化
*/
// 形式:先写一对大括号,在里面定义一个匿名函数,后面加一个小括号
(function () {
var a = 123;
var b = 234;
console.log(a + b);
}())
//控制台打印出357
可以用变量来接收立即执行函数的返回值
var res = (function () {
return 'hello';
}());
console.log(res);// hello
拓展:
// 立即执行函数有两种定义方式:
// (function () {}()); W3C建议使用这种定义方式
// (function () {})();
// 只有表达式才能被执行符号执行
function test() {
var a = 123;
}()
// 报错:Uncaught SyntaxError: Unexpected token ),
// 因为此处为函数声明,不属于表达式,换成函数表达式就会被执行,如下
var test = function () {
console.log('hello');
}()// hello
//表达式被执行之后就会忽略表达式的名字
// 例如,当函数没有被执行的时候,可以打印出test的值
var test = function(){
console.log('a');
};
console.log(test);//ƒunction (){console.log('a');}
// 当函数执行完之后
var test2 = function(){
console.log('a');//a
}();
console.log(test2);//undefined
// 利用运算符(+ - && ||可以,但是* /不可以,因为这里的+ -是正负号的意思,而不表示加减)可以将函数声明转化为表达式,然后函数名字就会失效
+ function test(){
console.log('a');
}
console.log(test);// Uncaught ReferenceError: test is not defined
// 同样,转换后的函数可以添加小括号让其立即执行
+ function test(){
console.log('a');
}();// a
// 由此引申出来()也算是运算符,()里面的函数也可以转化为表达式,例如(function test(){}),既然是表达式,就可以被执行,后面加上括号就会被执行,如下
(function test(){
console.log('a');
})();// a
// 由于在有多级括号的时候先执行最外面的括号,所以外面的括号也可以放在里面,于是就有了下面的写法:
(function test(){
console.log('a');
}());// a
// 又因为在外界不能引用函数名test,所以函数名test就失去了意义,就将函数名省略了,就形成了现在的立即指向函数。