ECMAScript函数表达式
——《JavaScript高级程序设计》Chapter7总结
1.匿名函数的作用
(1)动态定义函数
var sayHi;
var a=1;
if (a>0) {
sayHi=function(){
console.log("Hi");
}
}
else{
sayHi=function(){
console.log("Yo");
}
}
sayHi();
(2)把函数当成其他函数的返回值来使用
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;
}
}
}
var data=[{name:"Zachary",age:28},{name:"Nicholas",age:29}];
data.sort(createComparisonFunction("name"));
console.log(data[0].name);//"Nicholas"
data.sort(createComparisonFunction("age"));
console.log(data[0].name);//"Zachary"
2.递归
(1)通过函数声明实现
function factorial(num) {
if (num<=1) {
return 1;
}
else{
return num*arguments.callee(num-1);
}
}
console.log(factorial(5));
arguments.callee是一个指向正在执行的函数的指针
使用arguments.callee而非原函数名factorial可以解决如下报错问题:
var anotherFactorial=factorial;
factorial=null;
anotherFactorial(5);
问题:不能在严格模式下访问arguments.callee
(2)通过命名函数表达式实现
var factorial=(function f(num){
if(num<=1){
return 1;
}
else{
return num*f(num-1);
}
})
console.log(factorial(5));
3.闭包
(1)简介闭包
闭包指有权访问另一个函数作用域中变量的函数。
闭包的常见形式是在一个函数内部创建另一个函数。
(2)闭包的作用域链
一般的作用域链的情况
- 后台每个执行环境都有一个表示变量的对象——变量对象。全局环境的变量对象始终存在,函数这样的局部环境的变量对象只在函数执行的过程中存在。
- 作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。
- 一般来讲,当某函数执行完毕后,其局部活动对象就会被销毁,内存中仅保存全局作用域的变量对象。
闭包的情况不一样
- 在另一个函数内部定义的函数会将外部函数的活动对象添加到它的作用域中。
- 在外部函数执行完毕后,其活动对象也不会被销毁,因为其内部函数的作用域仍然在引用这个活动对象。例如上述createComparisonFunction()函数被返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然停留在内存中,因为其返回的匿名函数仍在引用它的活动对象;直到内部匿名函数被销毁,createComparisonFunction()的活动对象才会被销毁。
闭包的内存占用问题
因为闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能导致内存占用过多,故建议只在绝对需要闭包的时候再考虑用闭包。
(3)闭包只能访问包含函数中的任何变量的最后一个值
(4)作为闭包(函数内部的函数)的匿名函数的this对象
对比以下代码1和代码2:
代码1:
var name="The window";
var object={
name:"My Object",
getNameFunc:function(){
return function(){
return this.name;
}
}
}
console.log(object.getNameFunc()());//"The window"
代码2:
var name="The window";
var object={
name:"My Object",
getNameFunc:function(){
return this.name;
}
}
console.log(object.getNameFunc());//"My Object"
原因:
匿名函数的执行环境具有全局性。为什么匿名函数没有取得其包含作用域的this对象呢?因为作用域链的原理,内部匿名函数在搜索this和arguments两个特殊变量是都是从自身的活动对象开始,在搜索外部函数的活动对象,因此不可能直接访问外不函数的这两个变量;而其自身的this是指向全局环境的。
下面那个不是匿名函数,是函数getNameFunc()。可以姑且这么理解吧??
可以把外部作用域的this用其他变量来保存,这样内部闭包可以通过访问这个变量来访问外部作用域的this:
var name="The window";
var object={
name:"My Object",
getNameFunc:function(){
var that=this;
return function(){
return that.name;
};
}
}
console.log(object.getNameFunc()());//"My Object"
(5)闭包可能引起的内存泄漏
P183,待整理
4.匿名函数模仿块级作用域
(1)JS无块级作用域
function outputNumbers(count) {
for (var i=0;i<count;i++) {
console.log(i);
}
var i;
console.log(i);
}
outputNumbers(10);//0 1 2 3 4 5 6 8 8 9 10
即使多次声明一个变量,JS会忽略后续的声明(不过,它会执行后续声明中的变量初始化)。
(2)立即执行的匿名函数
用作块级作用域(又叫私有作用域)的匿名函数语法如下:
(IIFE,(Immediately-Invoked Function Expression),立即执行的函数表达式)
function outputNumbers(count) {
(function(){
for (var i=0;i<count;i++) {
console.log(i);
}
})();
console.log(i);//(1)
}
outputNumbers(10);//0 1 2 3 4 5 6 7 8 9 报错(语句(1)错误)
- 这样在内部匿名函数中定义的任何变量,在执行结束时都会被销毁。故(1)不能访问i。
- 该匿名函数是闭包,故可以访问外部函数的count。
- 这种技术继承在全局作用域中被用在函数外部,从而限制向全局作用域添加过多的变量和函数。我们都一个尽量少向全局作用域添加变量和函数。因为在有多个开发人员参与的应用程序中,过多全局变量和函数容易导致命名冲突。而通过创建私有作用域,每个开发人员可以使用自己的变量,又不用担心搞乱全局作用域)
5.私有变量
1)私有变量概念
任何在函数中定义的变量,都可以认为是私有变量。因为不能在函数外访问。
私有变量包括函数的参数、局部变量、函数内部定义的其他函数
2)特权方法概念
有权访问私有变量和私有函数的方法叫做特权方法。
创建这样的公有方法的思想是:在函数内部创建一个闭包,利用闭包可以通过自己的作用域链访问这些变量。
3)特权方法创建方式
(1)构造函数模式
在构造函数中定义特权方法。
例一:
function MyObject() {
var privateVariable=20;//私有变量
function privateFunction() {//私有函数
return false;
}
this.publicMethod=function(){//特权方法
privateVariable++;
return privateFunction();
};
}
var object=new MyObject()
console.log(object.publicMethod());//false
例二:
function Person(name) {//参数为私有变量
this.getName=function(){//特权方法
return name;
}
this.setName=function(newName){//特权方法
name=newName;
}
}
var aperson=new Person("Tom");
console.log(aperson.getName());//Tom
aperson.setName("Ben");
console.log(aperson.getName());//Ben
(2)原型模式
通过在私有作用域中定义私有变量和函数,也可以创建特权方法。
(function () {
var privateVariable=20;//私有变量
function privateFunction() {//私有函数
return false;
}
MyObject=function(){//构造函数,没有var声明就是全局变量
};
MyObject.prototype.publicMethod=function(){//公有/特权方法
privateVariable++;
return privateFunction();
}
})();
var object=new MyObject();
console.log(object.publicMethod());//false
该模式在定义构造函数时没有使用函数声明,因为函数声明只能创建局部函数,而不带var的函数表达式可以创建全局函数。
该模式私有变量和函数是由实例共享的,由于原型方法是在原型上定义的,故所有实例都引用同一个函数(如下例)。而特权方法作为一个闭包保存着对包含作用域的引用。
(function(){
var name=""; Person=function(value){
name=value;
}; Person.prototype.getName=function(){
return name;
}; Person.prototype.setName=function(value){
name=value;
};
})(); var person1=new Person("Nicholas");
console.log(person1.getName());//"Nicholas"
person1.setName("Greg");
console.log(person1.getName());//"Greg" var person2=new Person("Michael");
console.log(person1.getName());//"Michael"
console.log(person2.getName());//"Michael"
(3)模块模式
作用:是为单例创建私有变量和特权方法。
单例:只有一个实例的对象。
方式:在匿名函数内部,首先定义私有变量和函数,然后将一个对象字面量作为匿名函数的返回值。返回的对象字面量里面只包括可以公开的属性和方法。该对象是在匿名函数内部定义的,故它的公有方法有权访问私有变量和函数。
var singleton=function(){
var privateVariable=10;//私有变量
function privateFunction() {//私有函数
return false;
}
return {
publicProperty:true,//特权/公有属性
publicMethod:function(){//特权/公有方法
privateVariable++;
return privateFunction();
}
};
}();
适用:如果必须创建一个对象并以一些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,就可以使用模块模式。
(4)增强的模块模式
对模块模式的改进,即在返回对象之前加入对其增强的代码。
适用于:单例必须是某种类型的实例,同时还必须添加某些属性和方法对其增强的情况。
var singleton=function(){
var privateVariable=10;//私有变量
function privateFunction() {//私有函数
return false;
}
var object=new CustomType();//创建对象
object.publicProperty=true;
object.publicMethod=function(){
privateVariable++;
return privateFunction();
}
return object;
}();
ECMAScript函数表达式的更多相关文章
- 立即执行函数表达式(IIFE)
原文地址:benalman.com/news/2010/11/immediately-invoked-function-expression/ 译者:nzbin 也许你还没有注意到,我是一个对术语比较 ...
- 深入理解javascript系列(4):立即调用的函数表达式
本文来自汤姆大叔 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行. 在详细了解这个之前,我们来谈了解一下“自执行”这个叫法,本文对这个功能的叫法 ...
- javascript中函数声明和函数表达式浅析
记得在面试腾讯实习生的时候,面试官问了我这样一道问题. //下述两种声明方式有什么不同 function foo(){}; var bar = function foo(){}; 当初只知道两种声明方 ...
- 深入理解javascript---命名函数表达式
简单的说,命名函数表达式只有一个用户,那就是在Debug或者Profiler分析的时候来描述函数的名称,也可以使用函数名实现递归,但很快你就会发现其实是不切实际的.当然,如果你不关注调试,那就没什么可 ...
- javascript 函数声明与函数表达式的区别
先看一段代码 var f = function g() { return 1; }; if (false) { f = function g(){ return 2; }; } alert(g()); ...
- js高级程序设计(七)函数表达式
定义函数的方式有两种:一种是函数声明,另一种就是函数表达式.函数声明的语法是这样的. function functionName(arg0, arg1, arg2) { //函数体 } Firefox ...
- [Effective JavaScript 笔记] 第14条:当心命名函数表达式笨拙的作用域
js函数会根据上下文改变其含义. function double(x){return x*2;} 这是一个函数声明,也可以是一个命名函数表达式(named function expression),取 ...
- [JS]深入理解JavaScript系列(4):立即调用的函数表达式
转自:汤姆大叔的博客 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行.在详细了解这个之前,我们来谈了解一下"自执行"这个叫法 ...
- 深入理解javascript:揭秘命名函数表达式
这是一篇转自汤姆大叔的文章:http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html 前言 网上还没用发现有人对命名函数表达式进去重复深 ...
随机推荐
- java操作文件流对象
所有流对象 InputStream 字节流 FileInputStream 字节流 专门读写非文本文件的 BufferedInputStream 高效流 OutPutS ...
- openssl将私钥和crt证书合成pfx证书
pfx是什么文件:公钥加密技术12号标准(Public Key Cryptography Standards #12,PKCS#12)为存储和传输用户或服务器私钥.公钥和证书指定了一个可移植的格式.它 ...
- 小程序JSON数组操作
- 我的Android进阶之旅------>Android关于Log的一个简单封装
android.util.Log类,可以方便地用于在编码调试过程中打印日志.但是在发布后的产品中,如果有太多的日志打印,则会严重地影响性能.对android.util.Log类做一个简单的封装,当产品 ...
- 我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(三)Android客户端功能实现
我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(一)PC服务器端(地址:http://blog.csdn.net/ouyang_pen ...
- Java语言实现简单FTP软件------>源码放送(十三)
Java语言实现简单FTP软件------>FTP协议分析(一) Java语言实现简单FTP软件------>FTP软件效果图预览之下载功能(二) Java语言实现简单FTP软件----- ...
- mysql(root用户密码设置)
root密码重置 修改root用户的密码: /*登录mysql*/ mysql -uroot -p123 /*切换数据库*/ use mysql /*修改root用户的密码*/ update user ...
- python微信库 --- itchat
python实现微信接口——itchat模块 安装 pip install itchat 登录 itchat.auto_login() # 这种方法将会通过微信扫描二维码登录,但是这种登录的方式确实短 ...
- Elasticsearch的几种架构(ELK,EL,EF)性能对比测试报告
Elasticsearch的几种架构性能对比测试报告 1.前言 选定了Elasticsearch作为存储的数据库,但是还需要对Elasticsearch的基础架构做一定测试,所以,将研究测试报告输出如 ...
- Data Structure Array: Sort elements by frequency
http://www.geeksforgeeks.org/sort-elements-by-frequency-set-2/ #include <iostream> #include &l ...