1. 函数声明
所有以function开头的都算。
- 函数声明提升
执行代码前会先读取所有的函数声明,因此函数声明语句的位置并不重要。
- 匿名函数和具名函数
函数对象有一个非标准的name属性,匿名函数的name属性是空字符串。
匿名函数,就是把函数当成值来使用。
2. 递归
在函数中调用函数,递归已经很熟悉了。但是在要改函数名的时候,会不方便,内部调用的也要改。
- 函数名只是指向函数实例的指针,因此,在对调用时,使用arguments.callee()来调用自身,arguments.callee是指向正在执行的函数的指针。
- 严格模式下,不能访问callee属性,会导致错误,因此,使用函数表达式,在内部完成递归后再返回值。
var f = (function f(num){
if(num<=1) return 1;
else {
return f(num-1)*num;
}
});
3. 闭包
最最复杂的闭包,看了书后又看了许多博客来了解。
- 本书中的定义——有权访问另一个函数作用域中的变量的函数。
权威指南中的定义——可以访问其所在作用域的函数
- 复习知识:作用域链——实质上,是一个指向各个变量对象的指针的列表/顺序集合。
当外部函数执行完后,它的执行环境的作用域链被销毁,它的活动对象(执行环境)还为闭包的作用域链保留着。
(释放内存——将指向匿名函数的指针置为null)
- 闭包携带了包含它的函数的作用域,因此占更多内存。
4. 变量
- 闭包保存的是整个外部函数的变量对象,不是某一个值。
function createFunctions(){
var result = new Array();
for(var i=0;i<3;i++){
result[i] = function(){
return i;
}
}
return result;
}
result数组中的每个元素是不同的函数对象实例,在调用它们的时候,会顺着作用域链查找i,彼时i已经等于3了。
因此,改变result数组中的函数,使它们都带着不同的i值,在调用的时候就能返回不同的i。
通过传值的IIFE来实现,在返回的函数外再加一层传值函数。
5. this、arguments
- 闭包能访问到的范围内必须放好this、arguments的引用,不然在调用时会自动绑定到window对象。
- 返回函数时,返回的只有函数的光秃秃的功能。
原因:先进行了赋值,仅仅将函数的功能赋给了新的getName,赋值表达式的值是函数本身,然而原来的getName才是带有包含外部函数变量对象的作用域链的。
6. 模仿块级作用域
IIFE来实现
想要变量在括号外消失,那就让变量在括号结束时被销毁,因此用匿名函数。
匿名函数执行完后,在其中声明的变量全部被销毁,因此使用立即执行函数。
在私有的作用域中实现需要的功能后(匿名函数执行),就马上将其中的变量销毁。
7. 私有变量
对象中的属性都是共有的,函数中的变量外部不能访问,是私有的。
- 特权方法、公有方法——用来访问私有的属性、方法,因此——特权函数是闭包。
特权函数是绑定给function对象的方法,私有的属性和方法只是定义在函数内部的而已。
联想到之前的稳妥型构造函数,有相似之处。(但是稳妥型构造函数会创建一个对象实例并返回它)
- 静态私有对象
在函数中,创建的不加var关键字的全局变量不会再执行后被销毁;
立即执行函数中的闭包始终不让它的活动对象被销毁,用原型模式,所有的实例都会调用到唯一的方法,方法都关联着唯一的属性;
因此有静态的效果。
(function(){
var name;
Person = function(str){ name = name };
Person.prototype.getName = function(){ return name; };
Person.prototype.setName = function(newName){ name = newName };
})();
- 模块模式
一句话:返回一个包含特权方法和公有属性的对象给你用。
先在函数中定义好私有的变量,然后再一个返回包含特权方法的对象实例。
因为只有一个对象实例,因此适用于单例的情况。
实现:初始化(var xx = new Array();)+后续操作( xx.push(.....) )。
返回对象实例可以用字面量法,或者返回引用。
因此,既然要返回一个引用,那就不局限于object了,可以是任意类型的对象的实例。
原因:先进行了赋值,仅仅将函数的功能赋给了新的getName,赋值表达式的值是函数本身,然而原来的getName才是带有包含外部函数变量对象的作用域链的。
var name = "window"; var object = {
name : "obj";
getName : function(){
return this.name;
} }
(object.getName=object.getName)();//返回window