- 函数表达式的特征
- 使用函数实现递归
- 使用闭包定义私有变量
前面我们说到定义函数有两种方式:函数声明、函数表达式。
两者的区别在于函数声明提升,前者在执行之前的上下文环境中直接被赋值,而后者不会。
一、递归
递归函数是一个函数通过名字调用自身的情况下构成的。
function factorial(num){
if(num<1){
return 1;
}else{
return num * arguments.callee(num-1);
}
}
alert(factorial(10));
二、闭包
闭包的核心概念就是指有权访问另一个函数作用域中变量的函数。
创建闭包的常见方式主要就是在一个函数内部创建另一个函数。
function createCompareFunction(proprtyName){
return function(obj1,obj2){
if(obj1[proprtyName]>obj2[proprtyName]){
return 1;
}else if(obj1[proprtyName]<obj2[proprtyName]){
return -1;
}else{
return 0;
}
}
}
var o1 = {name : 'zjh'};
var o2 = {name : 'azz'};
var f = createCompareFunction('name');
alert(f(o2,o1)); //-1
内部匿名函数中调用的proprtyName就是另一个函数作用域中的变量。当f被赋值后,外层函数的作用域链被销毁,但是他的活动对象仍然在内存中,因为它的活动对象被返回的匿名函数引用了。
所以闭包会占用很多内存,要在必要的时候使用。
2.1闭包与变量
作用域链的这种机制也会有一定的副作用:
function createFunction(){
var array = new Array();
for(var i = 0 ; i < 10 ; i++){
array[i] = function(){
return i;
}
}
i = 20;
return array;
}
var a = createFunction();
alert(a[6]())//20
应该是6,这是却是20。原因就是数组中的每一项都是function(){return i},而此时的i的值是createFunction执行完的20。
function createFunction(){
var array = new Array();
for(var i = 0 ; i < 10 ; i++){
array[i] = function(num){
return num;
}(i)
}
i = 20;
return array;
}
var a = createFunction();
alert(a[6])//20
利用了函数参数按值传递的特性。
2.2关于this对象
this对象时在运行时基于函数执行环境绑定的。在全局函数中,this等于window,而当函数作为某个对象的方法调用时,this等该对象。
匿名函数执行环境具有全局性。因此this通常指向window。
var k = 'window';
var o = {
k : 'object',
getName : function(){
return function(){
return this.k;
}
}
}
alert(o.getName()())
2.3内存泄漏
在IE浏览器中,因为dom对象和js对象存在浏览器的不同地方,如果闭包的作用域链中保存着一个HTML元素,那么该元素就无法被销毁。
function createF(){
var ele = document.getElementById('aa');
ele.onclick = function(){
alert(ele.value);
}
}
避免泄漏:
function createF(){
var ele = document.getElementById('aa');
var k = ele.value;
ele.onclick = function(){
alert(k);
}
ele = null;
}
三、模仿块级作用域
JS中没有块级作用域,如果要使用需要在函数中定义来模仿。
(function(){
for(var i =0 ; i<10 ; i++){
var k = 10;
}
})()
alert(i) //错误
因为i在块级作用域执行完后 就销毁了。
只要临时需要一些变量,可以使用私有作用域。这种技术经常用在全局作用域中的外部函数,可以限制向全局作用域中添加过多的变量和函数。
也能有效的减少闭包占用内存的问题。因为没有指向匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域了。
四、私有变量
JS中没有私有成员的概念,所以属性都是公有的,但是有一个私有概念,那就是在任何函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量。
这些私有变量包括:函数的参数,局部变量,函数内部定义的其它函数。
function add(num1,num2){
var a = '1500';
}
在这个函数中,有三个私有变量,num1,num2,a。我们可以在函数内部访问他们,或者通过函数内部的闭包通过作用域来访问他们。那么我们利用这一点就可以创建用于访问私有变量的特权方法。
有两种方式可以创建特权方法:
4.1私有变量
第一种:利用闭包
function Person(name){
this.setName = function(value){
name = value;
}
this.getName = function(){
return name;
}
}
var p = new Person();
p.setName('zjh');
alert(p.getName());
所以,利用私有成员和特权成员可以隐藏那些不该被直接修改的数据。
缺点:是必须使用构造函数模式来达到这个目的。前面提到过,构造函数模式的缺点是:每次实例化一个对象,都会创建一组同样的新方法。
4.2静态私有变量
在私有作用域中定义私有变量或者函数,也可以创建特权方法。
var privateVariable = 10;
function privateFuntion(){
return false;
}
MyObject = function(){};
MyObject.prototype.publicMethod = function(){
privateVariable++;
return privateFuntion();
}
var o = new MyObject();
alert(o.publicMethod());
这个模式在定义构造函数时没有使用函数声明,而是使用了函数表达式。函数表达式只能创建局部函数,所以我们不使用关键字var 来定义了MyObject ,未经过初始化声明的变量总是会创建一个全局变量。(在严格模式下会错误)
这个模式与在构造函数中定义特权方法的区别在于:私有变量和函数是→实例共享的,由于特权方法是在原型上定义的,因此所有实例都使用同一个函数。而这个特权方法作为一个闭包,总是保存着对包含作用域的引用。
(function(){
var name = '';
Person = function(value){
name = value;
}
Person.prototype.setName = function(value){
name = value;
}
Person.prototype.getName = function(){
return name;
}
})()
var p = new Person('zjh');
alert(p.getName());//zjh
var pp = new Person('zzz');
alert(pp.getName());//zzz
alert(p.getName());//zzz
可见这种方式创建的是静态私有变量。
4.3模块模式
之前的模式是为自定义类型添加私有变量。而这种模块模式是为单例模式的对象添加私有属性。
方法如下:
var singleton = function(){
var privateVar = 10;
function privateFun(){
return false;
}
return {
publicVar : true,
publicMethod : function(){
privateVar++;
return privateFun();
}
}
}()
alert(singleton.publicMethod());
如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以用到这种模块方式。
4.3增强的模块模式
function Person(){}
var singleton = function(){
var privateVar = 10;
function privateFun(){
return false;
}
var o = new Person();
o.publicVar = true;
o.publicMethod = function(){
privateVar++;
return privateFun();
}
return o;
}()
alert(singleton.publicMethod());
这种模式 在匿名函数中创建了一个指定类型的对象,用来返回。
五、小结
在JS中,函数表达式是一种非常有用的技术。使用函数表达式无须命名,从而实现动态编程。
特点:
函数表达式不一定有名字,没有名字的函数表达式叫做匿名函数。
在无法确定如何引用函数的情况下,递归函数变得比较复杂。
递归函数中应该用arguments.callee来递归自身,以防止函数名变化。
当在函数内部定义其他函数时就创建了闭包。闭包有权访问 包含函数 的 内部的所有变量,原理:
在后台执行环境中,闭包的作用域链包含它自身的作用域、包含函数的作用域、全局作用域。
通常,函数的作用域及其变量都会在函数执行结束了被销毁。
但是,当函数返回一个闭包时,这个函数的作用域会在内存中一直保存到闭包不存在为止。
使用闭包可以在对象中创建私有变量:
可以使用构造函数模式、原型模式、来实现自定义类型的特权方法;也可以使用模块模式、增强模式的模块模式来实现单利的特权方法。
- JavaScript高级程序设计第三版.CHM【带实例】
从驱动全球商业.贸易及管理领域不计其数的复杂应用程序的角度来看,说 JavaScript 已经成为当今世界上最流行的编程语言一点儿都不为过. JavaScript 是一种非常松散的面向对象语言,也是 ...
- 22.1 高级函数【JavaScript高级程序设计第三版】
函数是JavaScript 中最有趣的部分之一.它们本质上是十分简单和过程化的,但也可以是非常复杂和动态的.一些额外的功能可以通过使用闭包来实现.此外,由于所有的函数都是对象,所以使用函数指针非常简单 ...
- javascript高级程序设计第三版书摘
在HTML 中使用JavaScript <script>元素 在使用<script>元素嵌入 JavaScript 代码时,只须为<script>指定 type 属 ...
- JavaScript Function(函数表达式)
创建函数 创建函数的方式有两种:1.函数声明,2.函数表达式 函数声明的语法为 functionName(); //不会报错,函数声明提升function functionName(arg0,arg1 ...
- 谈谈javascript的函数表达式及其应用
我们都知道定义函数的方式有两种,一种是函数声明,另外一种就是函数表达式. 函数声明 语法为:function关键字后跟函数名.例如: function functionName(arg0) { //函 ...
- 【JavaScript】函数表达式
一.前言 接着上一篇的内容,继续学习JavaScript. 二.内容 函数的声明 function functionName(arg0,arg1,arg2){ //函数体 } ...
- 浅谈JavaScript的函数表达式(闭包)
前文已经简单的介绍了函数的闭包.函数的闭包就是有权访问另一个函数作用域的函数,也就是函数内部又定义了一个函数. var Super=function(num){ var count=num; retu ...
- 浅谈JavaScript的函数表达式(递归)
递归函数,在前面的博客中已经简单的介绍了.递归函数是一个通过函数名称在函数内部调用自身的函数.如下: function fac(num){ if(num<1){ return 1; } else ...
- javascript中函数表达式的问题讨论
#函数表达式 ##函数声明和函数表达式的区别 函数的定义有两种形式,一种是函数声明,一种是函数表达式 使用声明时,要注意函数声明提升现象,比如说在if语句中使用声明会出错,但是表达式就不存在这个问题 ...
随机推荐
- web.xml配置
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" ...
- js中如何操作json数据
一.要想熟练的操作json数据,就先要了解json数据的结构,json有两种结构:对象和数组. 1.对象 一个对象以“{”开始,“}”结束.每个“名称”后跟一个“:”:“‘名称/值’ 对”之间使用“, ...
- [ActionScript 3.0] AS3.0 给flash事件传递参数的方法
有时我们想要给flash内置的事件(比如MouseEvent)传递参数,这时我们可以用到下面的方法. import flash.events.MouseEvent; mc.addEventListen ...
- [Java] 内部类的用法
package test.file; import java.io.File; import java.io.FilenameFilter; /** * 内部类的使用 * @author Frost. ...
- eclipse导入myeclipse的web项目
1.import> 2.进入项目目录,找到.project文件,打开 3.找到<natures> ...</natures>代码段,添加如下标签内容并保存 <nat ...
- C# 中将多个空格替换成一个空格
2013-04-17 15:36 C# 中如何将多个空格替换成一个空格? 1 myString = Regex.Replace(myString, @"\s+", " & ...
- iOS中FMDB的使用【单例】
DYDB.h Objective-C 12345678910111213141516 #import <Foundation/Foundation.h> #import <FMDB/ ...
- (转)C#操作PPT
原文地址:http://blog.163.com/loveyingchun_1314/blog/static/2382425120124312627530/ 引用Microsoft.Office.Co ...
- Functions类,一个Javascript的函数加法类,将两个函数加起来,顺序执行
以下是类的代码: var Functions = { oFunctions: null, add: function (oFunc, oNewFunc) { var oNew = function ( ...
- Oracle 启动状态解说
oracle 启动状态由nomount-mount-open 一. nomount状态下操作 08:09:49 idle> startup nomount; ORACLE instance st ...