函数声明与函数表达式的区别:(2点)
前者会在代码执行以前被加载到作用域中,而后者则是代码被执行到那一行时才会有定义。
函数声明会给函数指定一个名字,而函数表达式则是创建一个匿名函数,然后将这个匿名函数赋给一个变量。
递归
arguments.callee是一个指向正在执行的函数的指针。
1 //经典的递归阶乘函数 2 function factorial(num) { 3 if (num<=1) { 4 return 1; 5 }else { 6 return num * arguments.callee(num-1); 7 } 8 } 9 debug(factorial(4));
闭包
闭包的概念很简单:一个闭包是一个函数及定义它的环境的组合。
例如,在一个函数A内部定义其他函数B时,就创建了闭包。闭包有权访问包含函数(A)内部的所有变量。
1 //返回一个函数 2 function createComparison(propertyName) { 3 return function(obj1, obj2) { 4 var value1 = obj1[propertyName], 5 value2 = obj2[propertyName]; 6 //return value1 - value2; 7 if (value1 > value2) { 8 return 1; 9 }else if(value1 < value2) { 10 return -1; 11 }else { 12 return 0; 13 } 14 } 15 } 16 var compareName = createComparison('name'); 17 debug(compareName({name:'mack'},{name:'an'})); //1 18 compareName = null; //解除对匿名函数的引用,释放内存
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。
- 闭包与变量
a. 当某个函数第一次被调用时,会创建一个执行环境及相应的作用域链,并把作用域链赋给一个特殊的内部属性([[scope]])。
b. 然后使用this、arguments和其他命名参数的值来初始化函数的活动对象。在作用域链中外部函数的活动对象处于第二位……
c. 在函数执行过程中,为读取和写入变量的值,需要在作用域链中查找变量。
1 闭包只能取得包含函数中任何变量的最后一个值: 2 function createFunctions() { 3 var result = new Array(); 4 for (var i=0; i<10; i++) { 5 result[i] = function() { 6 return i; 7 } 8 } 9 return result; 10 } 11 //funcs 存储的是10个匿名函数 12 var funcs = createFunctions(); 13 //debug(funcs); 14 //输出数组中的元素值(全部都是10) 15 for (var i=0; i < funcs.length; i++) { 16 debug(funcs[i]()); 17 }; 18 更改写法: 19 function createFunctions() { 20 var result = new Array(); 21 for (var i=0; i<10; i++) { 22 //把i的当前值赋值给局部变量 23 result[i] = function(num) { 24 return function() { 25 return num; 26 } 27 }(i); 28 } 29 return result; 30 } 31 //funcs 存储的是10个匿名函数 32 var funcs = createFunctions(); 33 //输出数组中的元素值(0.1..9) 34 for (var i=0; i < funcs.length; i++) { 35 debug(funcs[i]()); 36 }
2.关于this对象
关于this对象(arguments存在同样的问题)
this对象是在运行时基于函数的执行环境绑定的,但匿名函数的执行环境具有全局性,因此this对象指向window.
1 var name = 'The Window'; 2 var obj = { 3 name: 'mackxu', 4 getName: function() { 5 return function() { 6 return this.name; 7 } 8 } 9 }; 10 debug(obj.getName()()); //The Window 11 //把外部作用域的this对象保存在一个闭包能够访问到的变量中 12 var name = 'The Window'; 13 var obj = { 14 name: 'mackxu', 15 getName: function() { 16 var that = this; 17 return function() { 18 return that.name; 19 } 20 } 21 }; 22 debug(obj.getName()()); //mackxu
3.IE中的内存泄露
如果闭包作用域链中保存着一个HTML元素,那么就意味着该元素将无法被销毁
1 function assignHandler() { 2 //无法减少对element 的引用数 3 var element = document.getElementById(‘#id’); 4 element.onclick = function() { 5 debug(element.id); 6 }; 7 } 8 解决办法: 9 function assignHandler() { 10 var element = document.getElementById(‘#id’); 11 var id = element.id; 12 element.onclick = function() { 13 debug(id); 14 }; 15 element = null; 16 }
模仿块级作用域
Js没有块级作用域的概念
1 Js没有块级作用域的概念 2 function output() { 3 //在for中定义的变量i,可以在for外面访问 4 for (var i=0; i<10; i++) { 5 //... 6 } 7 debug(i); //output:10 8 } 9 output(); 10 私有作用域:用作块级作用域的匿名函数,函数被执行后,里面的变量会马上销毁 11 (function(){ 12 //这里是块级作用域 13 })(); 14 改写上一个例子: 15 function output() { 16 //匿名函数被执行后,i变量将被销毁 17 (function(){ 18 for (var i=0; i<10; i++) { 19 //... 20 } 21 })(); 22 debug(i); //error 23 }
这种技术经常在全局作用域中被用在函数外面,限制向全局作用中添加过多的变量和函数。
私有变量
任何在函数中定义的变量,可认为是私有变量,因为不能在函数外部访问这些变量。
私有变量包括函数的参数、局部变量、在函数内定义的其他函数。
特权方法:有权访问私有变量和私有函数的公有方法。
1 function MyObject() { 2 var private_var = 10; //函数的私有变量 3 //函数的私有方法 4 function private_method() { 5 return private_var; //闭包能访问所在函数的所有属性 6 //return 'private_method; 7 } 8 //特权方法[能访问私有变量、方法的公有方法] 9 this.public_method = function() { 10 private_var ++; 11 return private_method(); 12 } 13 } 14 15 var obj = new MyObject(); 16 debug(obj.public_method());
- 静态私有变量[避免每个实例都要创建同一组新方法]
在私有作用域中定义私有变量或函数,同样也可以创建特权方法
1 (function() { 2 //私有变量和函数 3 //根据作用域链,private_var相对于特权方法(闭包)是外部函数的变量 4 //其会变成一个静态的、由所有实例共享的属性[可以想象原型链继承父类的属性] 5 var private_var = 10; 6 function private_func() { 7 return 'private function'; 8 } 9 //没有被var声明,会自动添加到全局对象中 10 MyObject = function() {}; 11 MyObject.prototype.public_method = function() { 12 private_var ++; 13 private_func(); 14 return 'OK'; 15 }; 16 })(); 17 //可以在私有作用域外,访问MyObject 18 var obj = new MyObject(); 19 debug(obj.public_method()); 20 送上一个实例说明: 21 (function() { 22 var name = ''; //静态私有变量 23 24 Person = function(value) { 25 name = value; 26 }; 27 Person.prototype.getName = function() { 28 return name; 29 }; 30 Person.prototype.setName = function(value) { 31 name = value; 32 }; 33 })(); 34 35 var p1 = new Person('mackxu'); 36 debug(p1.getName()); //mackxu 37 var p2 = new Person('zhangsan'); 38 debug(p2.getName()); //zhangsan 39 p2.setName('anan'); 40 debug(p1.getName()); //anan
模块模式
JavaScript是以对象字面量的方式来创建单例对象的。
1 //函数返回的是对象 2 var singleton = function() { 3 //私有变量和方法 4 var private_var = 10; 5 function private_func() { 6 return 'private function'; 7 } 8 9 //返回公有方法和属性 10 return { 11 public_var: 22, 12 public_method: function() { 13 //访问私有变量和函数 14 private_var ++; 15 return private_func(); 16 } 17 }; 18 }(); 19 debug(singleton.public_method());
这个模式在需要对单例进行某些初始化,同时又需要维护其私有变量时很有用。
增强的模块模式
单例必须是某种类型的实例
1 function CustomType() { 2 //... 3 } 4 //函数返回的是对象 5 var singleton = function() { 6 //私有变量和方法 7 var private_var = 10; 8 function private_func() { 9 return 'private function'; 10 } 11 12 //创建特定类型的对象,并返回 13 var obj = new CustomType(); 14 //添加公有方法和属性 15 obj.public_var = true; 16 obj.public_method = function() { 17 private_var++; 18 return private_func(); 19 } 20 return obj; 21 }(); 22 23 debug(singleton.public_method()); 24 debug(singleton instanceof CustomType); //true