函数表达式
函数表达式
函数提升
先了解一下提升的概念:提升-> 是JS 将
所有声明
提升到当前作用域顶部
的默认行为(提升到当前脚本或当前函数的顶部)
- 关于函数声明:一个重要特征就是函数声明提升(在代码执行之前先读取函数声明)
sayHi();
function sayHi(){
alert("Hi!");
}
复习:定义函数的两种方式:函数声明、函数表达式
var functionName = function(arg0, arg1, arg2){
//函数体
};
- 这个函数叫做匿名函数(function关键字后面没有标识符)=>
匿名函数的name属性时空字符串
sayHi(); //错误:函数还不存在
var sayHi = function(){
alert("Hi!");
};
在把函数当成值来使用时,都可以使用匿名函数,(比如将函数作为其他函数的值返回)
递归(与其他语言一样就是调用自身的过程)
- 以一个阶乘函数的递归为例:
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
arguments.callee
是一个指向正在执行的函数的指针,
因此可以用它来实现对函数的递归调用
- 也可以用命名函数表达式:
var factorial = (function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
});
闭包
创建闭包的常见方式,就是在
一个函数内部创建另一个函数
(创建闭包必须维护额外的作用域
,所以过度使用它们可能会占用大量内存
)
-
全局变量能够通过闭包实现局部
不通过关键词 var 创建的变量总是全局的,即使它们在函数中创建。 -
变量的生命周期:
- 全局变量和应用程序(窗口、网页)一样的生命周期
- 局部变量在函数调用时被创建函数完成后删除
-
以一个简单的计数器为例来理解闭包:
// 初始化计数器
var counter = 0;
// 递增计数器的函数
function add() {
var counter = 0;
counter += 1;
}
// 调用三次 add()
add();
add();
add();
//此时计数器应该是 3。但它是 0。
// 递增计数器的函数
function add() {
var counter = 0;
counter += 1;
return counter;
}
// 调用三次 add()
add();
add();
add();
//此时计数器应该是 3。但它是 1。
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
// 计数器目前是 3
理解:(既要做到
不能让其他函数更改cnt的值
即必须处于一个函数内不能处于全局不然就可能被修改,又要做到能增加计数
,所谓“函数的私有变量”的实现)=>闭包也可以理解为指的是有权访问父作用域的函数,即使在父函数关闭之后
- 而闭包有权访问函数内部的所有变量的原理如下:
- 闭包的作用域链包含着它
自己的作用域
、包含函数的作用域
和全局作用域
- 通常,函数的作用域及其所有变量都会在
函数执行结束后被销毁
- 当函数返回了一个闭包时,这个函数的作用域将会
一直在内存中保存到闭包不存在为止
- 闭包的作用域链包含着它
this
- 在方法中,this 指的是所有者对象。
- 单独的情况下,this 指的是全局对象。
- 在函数中,this 指的是全局对象。
- 在函数中,严格模式下,this 是 undefined。
- 在事件中,this 指的是接收事件的元素
匿名函数的执行环境具有全局性,因此this对象通常指向window
- 一个例子的理解:
这个返回结果是the window,匿名函数的全局性并且,每个函数在被调用时
自动取得
:this和arguments,内部函数在搜索这两个变量时,只会搜索到其活动对象为止。
将外部作用域的this对象保存在闭包,就
可以让闭包访问
,因此这个例子的结果是the object,当然如果想要arguments也是如此
var name = "The Window";
var object = {
name : "My Object",
getName: function(){
return this.name;
}
};
object.getName(); //"My Object"
(object.getName)(); //"My Object"
(object.getName = object.getName)(); //"The Window",在非严格模式下
理解第三句话:赋值语句有返回值,这个语句返回的是object.getName(后面没有括号,
即函数不执行只是传递了引用
),所以返回指向的函数体本身,原式其实相当于(function(){return this.name})();
而该段代码的调用者是window
模仿块级作用域
- 模仿的语法格式:
(function(){
//这里是块级作用域
//作用:限制向全局作用域中添加过多的变量和函数,即避免内部临时变量影响全局空间
})();
理解:将函数声明包含在()中,表示它实际是一个函数表达式(注:函数表达式后面可以跟(),
要将函数声明转换成函数表达式,只要用()将函数声明包括起来即可
),而紧随其后的另一对()会立即调用
这个函数。
- 所以(function(){})();可以理解成:
var someFunction=function(){};
someFunction();
此时要注意模拟创建块级作用域后在
匿名函数中定义的任何变量都会在执行结束时被销毁
,例如:
function outputNumbers(count){
(function () {
for (var i=0; i < count; i++){
alert(i);
}
})();
alert(i); //导致一个错误!
}
在私有作用域中能够访问变量 count ,是因为
这个匿名函数是一个闭包,
它能够访问包含作用域中的所有变量
私有变量
即使 JavaScript 中没有正式的私有对象属性的概念,但可以使用
闭包
来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量——《JavaScript高级程序设计》
- 任何在函数中定义的变量,都可以
认为是
私有变量,因为不能在函数的外部访问这些变量。(但是没有私有成员的概念)私有变量包括- 函数的参数
- 局部变量
- 在函数内部定义的其他函数
- 特权方法(闭包)
- 概念:有权访问私有变量和私有函数的公有方法
- 两种方式:
- 使用构造函数模式、原型模式来实现自定义类型的特权方法
- 使用模块模式、增强
的模块模式来实现单例的特权方法
- 以构造函数方式为例:(基本模式如下)
function MyObject(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权方法
this.publicMethod = function (){
privateVariable++;
return privateFunction();
};
}
1、能够在构造函数中定义特权方法,是因为特权方法作为
闭包
有权访问在构造函数中定义的所有变量和函数
2、这个例子中,变量 privateVariable 和函数 privateFunction() 只能通过特
权方法 publicMethod() 来访问