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 前言 网上还没用发现有人对命名函数表达式进去重复深 ...
随机推荐
- centos7 PXE自动安装环境搭建
原理: 要进行自动安装的主机A,加电启动时以网卡为第一启动设备 1.启动时会向网络广播,找到dhcp服务器B请求分配IP地址信息,服务器B除了给其分配基本的IP信息(ip.netmask.getewa ...
- 我的Android进阶之旅------>Android无第三方Jar包的源代报错:The current class path entry belongs to container ...的解决方法
今天使用第三方Jar包afinal.jar时候,想看一下源代码,无法看 然后像添加jar对应的源码包,也无法添加相应的源代码,报错如下:The current class path entry bel ...
- python实例2-写一个爬虫下载小功能
主要是通过url,和re两个模块对一个网页的固定图片进行模糊匹配后下载下来. #! /usr/bin/python import re import urllib def gethtml(url): ...
- 私有云的迁移:从VMware到OpenStack
VMware和OpenStack经常被描述为相互竞争的两种私有云技术.虽然这两种技术其实可以互补,但一些组织却选择从VMware迁移到OpenStack的私有云上. 让我们来看看这些组织如何能同时使用 ...
- angularjs 中的iframe 标签 ng-src 路径 z-index 必须有position
如果直接写路径到iframe标签里的ng-src中会出现报错: 解决方法: 1.ng里面有个属性是专门用来解决跨域问题的 $sce. 用法: $scope.someUrl = $sce.trustAs ...
- secureCRT linux shell显示中文乱码 解决方法
引:有没有这样的经历: 1.在shell中直接查看包含中文的文件时,出现一堆火星文,不得不下载下来window看. 2.无法正常的在shell中输入中文. 3.make的时候输出一堆乱码. 以下是查阅 ...
- zipfile.BadZipFile: File is not a zip file
zipfile.BadZipFile: File is not a zip file 出现这个问题一般是文件损坏的可能性比较大
- CAS的实现Atomic类库
atomic 原子(atomic)本意是"不能被进一步分割的最小粒子",而原子操作(atomic operation)意为"不可被中断的一个或一系列操作".在多 ...
- HTML 5 本地存储
<!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...
- ios多种语言的本地化思路
多语言在应用程序中一般有两种做法: 一.程序中提供给用户自己选择的机会: 二.根据当前用户当前移动设备的语言自动将我们的app切换对应语言. 第一种做法比较简单完全靠自己的发挥了,这里主要讲第二种做法 ...