定义函数的方式有2种:

  • 函数声明
  • 函数表达式

函数声明是最常用的,函数声明的一个特征就是:在执行代码之前,就已经读取了函数声明。

这个特征还有一个专门的术语:函数声明提升

递归函数

所谓递归函数,就是在函数体中使用函数本身。

这就需要用到arguments.callee,他总是指向正在执行的函数,不论函数名怎么改变。

function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));

上面的例子,如果将arguments.callee改为factorial,就会报错!因为函数名称改变了。

但是,在严格模式下,上面的例子还是错误的!因为严格模式没有arguments.callee这个属性。

需要使用命名函数表达式来解决:

var factorial = (function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
});
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));

像这样的,不论严格模式,都能成功执行。

闭包

复习:作用域链的本质就是一个包含指向变量对象的指针列表。每个执行环境都对应着一个变量对象。

全局变量对象会一直存在,而活动变量对象在函数执行完成就会被销除。

闭包和匿名函数非常容易弄混淆,其实他们很不一样。

闭包是函数没错,但是他是可以访问其他函数作用域中变量的函数。

所以就有人说了,那么内部函数不就是闭包么?这个不太正确。因为闭包,一般会将闭包本身返回,

或者将闭包赋值给一个变量,然后返回这个变量。而内部函数不一定会返回本身。

先来看一个例子:

function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}

这个数组中,位置1的函数会返回10,其实是所有元素都会返回10。为什么呢?因为所有元素都是匿名函数:

ƒ (){
return i;
}

而当你调用这个函数时,变量i的值就是10。

如果想实现期望的效果,可以这样做,在每次循环后,将i的值记录下来并返回:

function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}

像这样,变量num每次的值都是不一样的。

关于闭包的另一个问题就是,函数中的变量始终会保留在内存中,而不是像通常的那样被销除:

function f1(){

    var n=999;

    nAdd=function(){n+=1}

    function f2(){
      alert(n);
    }     return f2;   }   var result=f1();   result(); // 999   nAdd();   result(); // 1000

上面,变量n在函数执行完毕后,还是在内存中,所以才有1000的返回。

关于this和arguments

一般来说,内部函数是能够访问外部函数的变量对象的,即能够访问外部作用域中的所有变量。

但是,this和arguments是例外,内部函数在搜索这2个变量时,只会搜索到本身的活动对象为止,

所以外部函数的this和arguments变量。内部函数是不能够访问的。

但是注意,全局环境中的this和arguments是可以访问的。

ar name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下)

这就是为什么,最后返回的值是"The Window"而不是"My Object"。

模仿块级作用域

块级作用域也叫做私有作用域。

一般来说,实际开发中应该尽量避免过多的在全局环境中定义变量和函数,这个时候私有作用域的价值就体现出来了!

(function(){
var now = new Date();
if (now.getMonth() == 0 && now.getDate() == 1){
alert("Happy new year!");
}
})();

把上面这段代码放在全局作用域中,可以用来确定哪一天是 1 月 1 日;如果到了这一天,就会向用
户显示一条祝贺新年的消息。其中的变量 now 现在是匿名函数中的局部变量,而我们不必在全局作用域
中创建它。

私有变量

私有变量有:函数的参数,局部变量和内部函数。

可以访问私有变量的方法就叫做特权方法,一般创建特权方法的方式是通过闭包来实现。

function Person(name){
this.getName = function(){
return name;
};
this.setName = function (value) {
name = value;
};
}
var person = new Person("Nicholas");
alert(person.getName()); //"Nicholas"
person.setName("Greg");
alert(person.getName()); //"Greg"

JS高程3:函数表达式的更多相关文章

  1. js学习之函数表达式及闭包

    来自<javascript高级程序设计 第三版:作者Nicholas C. Zakas>的学习笔记(七) 直接切入主题~ 定义函数的方式有两种: 函数声明 function functio ...

  2. JS立即执行函数表达式(IIFE)

    原文为 http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife ----------------- ...

  3. js基础:函数表达式和函数声明

    函数表达式和函数声明的区别.实际上,解析器在向执行环境中加载数据是,对函数表达式和函数声明并非一视同仁.解析器会率先读取函数声明,并使其在执行任何代码之前可用.而函数表达式,则必须等到解析器执行到它所 ...

  4. js自执行函数表达式

    // 下面2个括弧()都会立即执行 (function () { /* code */ } ()); // 推荐使用这个(function () { /* code */ })(); // 但是这个也 ...

  5. JS_高程7.函数表达式(2)递归

    递归函数:一个函数通过名字调用自身的情况构成的.eg: //递归实现阶乘 function factorial(num){ if(num <= 1){ return 1; }else{ retu ...

  6. JS_高程7.函数表达式(1)

    定义函数的两种常见的方法: 1 . 函数声明 2. 函数表达式 # 差异 (1)函数声明 ,具有函数声明提升的特征. (2)函数声明的函数的name属性为函数的名称:使用函数表达式定义的函数在ES5中 ...

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

    函数表达式的特征 使用函数实现递归 使用闭包定义私有变量 前面我们说到定义函数有两种方式:函数声明.函数表达式. 两者的区别在于函数声明提升,前者在执行之前的上下文环境中直接被赋值,而后者不会. 一. ...

  8. js 之 箭头函数 (未学完)

    js之箭头函数表达式 箭头函数表达式的语法比函数表达式更短,并且没有自己的this,arguments,super或 new.target.这些函数表达式更适用于那些本来需要匿名函数的地方,并且它们不 ...

  9. js 匿名函数-立即调用的函数表达式

    先提个问题, 单独写匿名函数为什么报错?return 匿名函数 为什么不报错? 如图: 第二种情况在 f 还没有执行的时候,就报错了,,,当然这得归因于函数声明语句声明提前(发生在代码执行之前)的原因 ...

随机推荐

  1. ALCHEMY 2 (FLASCC)新手入门 (WINDOWS 版)

    Adobe Alchemy(炼金术) 2的预发布版本已经对开发者开放,并且已经更名为 FlasCC.炼金术简单来说就是把c/c 代码编译成swf文件,它吸收了c/c 高效的执行效率,比传统开发的swf ...

  2. [Linux] Proc 文件系统

    转载自:http://linux.chinaunix.net/doc/2004-10-05/16.shtml#324lfindex0 目录: /proc --- 一个虚拟文件系统 加载 proc 文件 ...

  3. JS,jquery动态向已经存在的css文件中添加class样式

    $("h1,h2,p").addClass("blue") $(".class")addClass("class_name&quo ...

  4. JS夯实基础:Javascript 变态题解析 (上)

    ["].map(parseInt) 1.知识点: Array/map Number/parseInt Global_Objects/parseInt JavaScript parseInt ...

  5. Spark详解

    原文连接 http://xiguada.org/spark/ Spark概述 当前,MapReduce编程模型已经成为主流的分布式编程模型,它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的 ...

  6. 转: iOS崩溃堆栈符号表使用与用途

    转:http://bugly.qq.com/blog/?p=119 iOS崩溃堆栈符号化,定位问题分分钟搞定! 2015.3.16 腾讯Bugly 微信分享   最近一段时间,在跟开发者沟通过程中,萝 ...

  7. [Android Pro] 查看 keystore文件的签名信息 和 检查apk文件中的签名信息

    1: 查看 keystore文件的签名信息 keytool -list -v -keystore keystoreName -storepass keystorePassword 2: 检查apk文件 ...

  8. Java基础大家必看啊

    写代码: 1,明确需求.我要做什么? 2,分析思路.我要怎么做?1,2,3. 3,确定步骤.每一个思路部分用到哪些语句,方法,和对象. 4,代码实现.用具体的java语言代码把思路体现出来.   学习 ...

  9. Word2007中插入公式之后,公式上下有很大的空白

    word 2007 选中一个公式,选择页面布局,点击页面设置右下角的小箭头,在弹出的对话框中,选择文档网格,在网格选项中选择无网格,确定,行距正常了. 选择一个公式,所有的公式行距都会变. Word ...

  10. vue - 条件语句

    1.与小程序不同之处一,小程序无论变量还是常亮都可以用双向绑定来解决{{}},而vue一旦双(单)引号包起来以后就失效了. 2.注意一点,切记双引号注意不要混淆哈,这里是一排双引号包单引号,那里是一排 ...