JavaScript高级 函数表达式 《JavaScript高级程序设计(第三版)》

  • 函数表达式的特征
  • 使用函数实现递归
  • 使用闭包定义私有变量

前面我们说到定义函数有两种方式:函数声明、函数表达式。

两者的区别在于函数声明提升,前者在执行之前的上下文环境中直接被赋值,而后者不会。

一、递归

递归函数是一个函数通过名字调用自身的情况下构成的。

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来递归自身,以防止函数名变化。

当在函数内部定义其他函数时就创建了闭包。闭包有权访问 包含函数 的 内部的所有变量,原理:

在后台执行环境中,闭包的作用域链包含它自身的作用域、包含函数的作用域、全局作用域。

通常,函数的作用域及其变量都会在函数执行结束了被销毁。

但是,当函数返回一个闭包时,这个函数的作用域会在内存中一直保存到闭包不存在为止。

使用闭包可以在对象中创建私有变量:

可以使用构造函数模式、原型模式、来实现自定义类型的特权方法;也可以使用模块模式、增强模式的模块模式来实现单利的特权方法。

来自为知笔记(Wiz)

时间: 2024-08-10 02:09:10

JavaScript高级 函数表达式 《JavaScript高级程序设计(第三版)》的相关文章

Java高级程序设计第三版:基本概念

严格模式 ? 1 2 3 4 5 6 function fun(){   "use strict";  //启用严格模式,作用域函数  }   "use strict";  //全局 变量 变量定义需要用var,如果没有通过var定义的变量会由于相应变量不会马上有定义而导致不必要的混乱. 数据类型 5种基本数据类型:Undefined.Null.Boolean.NUmber.String 1种复杂数据类型:Object typeof 对未初始化的和未声明的变量执行t

《JavaScript高级程序设计 第三版》 前2章 Javascript简介与HTML 读书笔记

第一章:Javascript简介 1.JavaScript诞生于1995年,当时,它的主要目的是处理以前由服务器端语言(如Perl)负责的一些输入验证操作.现在,JavaScript是一种专为与网页交互而设计的脚本语言. 注:Netscape(网景)公司研发,Java是sun公司研发,原名为LiveScript,为了搭上媒体热炒的Java的顺风车,更名为JavaScript 2.微软推出JSript的和网景的JavaScript相竞争,最后微软胜利.ECMA指定了规定并重新命名为ECMAScri

谈谈javascript的函数表达式及其应用

我们都知道定义函数的方式有两种,一种是函数声明,另外一种就是函数表达式. 函数声明 语法为:function关键字后跟函数名.例如: function functionName(arg0) { //函数体 } alert(functionName.name) // "functionName" 函数声明最主要的特征就是函数声明的提升,所以我们可以把函数声明放在调用它的语句的后面,因为执行代码前会先读取函数声明.比如: hello(); function hello(){ alert(&

JavaScript基础——函数表达式、闭包

简介 函数表达式是JavaScript中的一个既强大又容易令人困惑的特性.定义函数的方式有两种:一种是函数声明,另一种就是函数表达式.函数声明的语法是这样的: function functionName(arg0 , arg1 , arg2--){ //函数体 } 首先是function关键字,然后是函数的名字,这就是指定函数名的方式.Firefox.Safari.Chrome和Opera都给函数定义了一个非标准的name属性,通过这个属性可以访问到给函数指定的名字.这个属性的值永远等于跟在fu

浅谈JavaScript的函数表达式(闭包)

前文已经简单的介绍了函数的闭包.函数的闭包就是有权访问另一个函数作用域的函数,也就是函数内部又定义了一个函数. 1 var Super=function(num){ 2 var count=num; 3 return function(){ 4 console.log(count); 5 } 6 } 7 var result=Super(3);//此时result是一个函数 8 result();//输出3 上面的代码定义了一个函数Super,同时在Super函数内部又定义了一个匿名函数作为返回

javascript中函数表达式的问题讨论

#函数表达式 ##函数声明和函数表达式的区别 函数的定义有两种形式,一种是函数声明,一种是函数表达式 使用声明时,要注意函数声明提升现象,比如说在if语句中使用声明会出错,但是表达式就不存在这个问题 表达式要在使用前定义,而声明不用 通过声明会获得一个name属性,而表达式中其name为空 function fn() {}     var fn1 = function() {};     console.log(fn.name);    //fn     console.log(fn1.name

浅谈JavaScript的函数表达式(递归)

递归函数,在前面的博客中已经简单的介绍了.递归函数是一个通过函数名称在函数内部调用自身的函数.如下: 1 function fac(num){ 2 if(num<1){ 3 return 1; 4 } 5 else{ 6 return num*fac(num-1); 7 } 8 } 上面的代码,在第一行声明了一个fac函数,同时在6行调用了fac函数本身.这是一个求阶乘的递归函数. 1 var anthorfacc=fac; 2 fac=null; 3 anthorfacc(4);//抛出异常

JS高级程序设计第三版——JavaScript简介

JavaScript简史 JavaScript由Netscape(网景)公司在1995年发布,最开始的主要目的是处理以前由服务器端语言负责的一些输入验证操作,以便提高用户体验,后来就慢慢的发展为一门强大的编程语言.作者建议:要想全面理解和掌握JavaScript,关键在于弄清楚它的本质.历史和局限性. JavaScript实现 一个完整的JavaScript实现=核心(ECMAScript)+文档对象模型(DOM)+浏览器对象模型(BOM). 1.ECMAScript:由ECMA-262定义的E

2.1 &lt;script&gt;元素【JavaScript高级程序设计第三版】

向 HTML 页面中插入 JavaScript 的主要方法,就是使用<script>元素.这个元素由 Netscape 创造并在 Netscape Navigator 2 中首先实现.后来,这个元素被加入到正式的 HTML 规范中. HTML 4.01 为<script>定义了下列 6 个属性. async:可选.表示应该立即下载脚本,但不应妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本.只对外部脚本文件有效. charset:可选.表示通过 src 属性指定的代码的字符