第五章讲了函数的使用方式, 要么函数声明, 要么函数表达式.
关于函数声明, 有一个重要的特征, 就是会函数声明提升:
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) //window
function 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 here
var 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这个属性, 只能通过方法去访问或者修改.但是要记住本示例的代码也是有缺陷的, 为了访问私有变量而生成许多功能相同的特权方法, 是一种资源的浪费.
静态私有变量
略
模块模式
略
后面会另外写一篇文章讲设计模式
本章完