第五章讲了函数的使用方式, 要么函数声明, 要么函数表达式.
关于函数声明, 有一个重要的特征, 就是会函数声明提升:
sayHi(); // 这样是可以的, 如果是函数表达式 就会报错function sayHi(){alert("Hi!");}
因为函数声明的这个特性, 千完不要在判断语句中使用函数声明:
// 不要这么做!if(condition){function sayHi(){alert("Hi!");}} else {function sayHi(){alert("Yo!");}}
递归
我们看三个示例.
示例1:
function factorial(num){if (num <= 1){return 1;} else {return num * factorial(num-1); // 耦合问题}}
示例2:
function factorial(num){if (num <= 1){return 1;} else {return num * arguments.callee(num-1); //严格模式下会报错}}
示例3:
// 这里使用了函数表达式, 实际上就是示例1的包装var factorial = (function f(num){// 同时使用函数表达式和函数声明, 那么函数声明的函数名只能在本函数中被访问if (num <= 1){return 1;} else {return num * f(num-1);}});
闭包
闭包是指有权访问另一个函数作用域中的变量的函数。
我们看这个示例:
function createComparisonFunction(propertyName) {return function(object1, object2){var value1 = object1[propertyName];var value2 = object2[propertyName];if (value1 < value2){return -1;} else if (value1 > value2){return 1;} else {return 0;}};}
其中返回的匿名函数就是一个闭包.
闭包与变量
function createFunctions(){var result = new Array();for (var i=0; i < 10; i++){result[i] = function(){ // 闭包保存的是包含环境的变量, 而这个变量经过多次改变, 根据闭包的特性, 只能获取到最后一个改变的值.return i;};}return result; // 注意这里返回的是闭包数组}
发现了这个特点, 我们可以再创建多一层闭包:
function createFunctions(){var result = new Array();for (var i=0; i < 10; i++){result[i] = (function(index){return function(){ //注意这里是没有参数的return index; //强行保留起来};})(i);}return result;}
this对象
我们在第五章讲过, 函数内部始终有两个特殊对象, 一个是arguments, 一个是this.
this指的是最后调用它的环境对象, 在返回的匿名函数中, this指向window,在构造函数中, this就是将要生成的对象, 在DOM中, this指向当前的html节点(这个后面的章节会讲).
你可能不太理解 this为啥指的是最后调用它的环境对象, 我们来看几个例子:
function a(){console.log('this in a:',this) //windowfunction b(){console.log('this in b:',this) //window}b()return function(){console.log('this in c:',this) //window}}var c = a();c();
可以看得出, 无论你函数有没有嵌套, 或者是否为匿名函数, 只要它没有被某一个明确的非window对象显式调用, 那么this都指向window. 这是JS的设计,便于this在函数中的表现一致性.
我们再来看一个例子:
var name = 'window';var object = {name:'object'}function sayName(){console.log(this.name);}sayName(); // 'window', 没毛病// 但是我想让object调用sayName怎么办? 前面我们学的call和apply就排上用处了sayName.call(object); // 'object';//或者用es5的bind方法, 手动添加到object上sayName.bind(object)();// 'object';
内存泄露
主要是针对旧版IE, 略
模仿块级作用域
ES5没有块级作用域:
function outputNumbers(count){for (var i=0; i < count; i++){alert(i);}alert(i); //依然可以访问}
为了实现块级作用域, 我们通常用立即执行的匿名函数实现:
(fnuction(){// your code is herevar i =10;})();alert(i); //报错
著名的jQuery就是用这种包装形式.
私有变量
严格来讲,JavaScript 中没有私有成员的概念;所有对象属性都是公有的。不过,倒是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。
我们来看一个例子:
function Person(name){// this.name = name; // 通常我们会这样设置属性this.getName = function(){return name;};this.setName = function (value) {name = value;};}var person = new Person("Nicholas");alert(person.getName()); //"Nicholas"person.setName("Greg");alert(person.getName()); //"Greg"
上面的示例可以看出, 对象实例无法直接访问name这个属性, 只能通过方法去访问或者修改.但是要记住本示例的代码也是有缺陷的, 为了访问私有变量而生成许多功能相同的特权方法, 是一种资源的浪费.
静态私有变量
略
模块模式
略
后面会另外写一篇文章讲设计模式
本章完
