什么是函数

函数,是一个大型程序中的某部份代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。(维基百科)

函数的特点

第一类对象

在JavaScript世界中函数是一等公民,它不仅拥有一切传统函数的使用方式(声明和调用),而且可以做到像简单值一样赋值、传参、返回,这样的函数也称之为第一级函数(First-class Function)。不仅如此,JavaScript中的函数还充当了类的构造函数的作用,同时又是一个Function类的实例(instance)。这样的多重身份让JavaScript的函数变得非常重要。在JavaScript中,函数又被称作第一类对象[frist-class object]。

提供作用域

  • 函数可以在运行时动态创建,也可以在程序执行过程中创建
  • 函数可以分配给变量,可以将他们的引用复制给其他变量,可以被扩展,此外,除了少数情况下,函数还可以被删除
  • 可以作为参数传递给其他函数,并且还可以由其他函数返回
  • 函数可以由自己的属性和方法

函数声明

JavaScript函数像一般语言一样也是遵循先声明后使用的原则,函数名只能包含字母、数字、下划线或$,且不能以数字开头。函数常见的声明方式有以下两种:

函数表达式

直接上代码:

```javascript
//普通函数表达式
var wr1 = function(){
// body...
}
//命名函数表达式
var wr2 = function wr2(){
// body...
}
```

以上广义上称为函数表达式,并且命名函数表达式是一个函数表达式的一种特殊情况,通常发生在定义可选的命名时。唯一的区别在于命名函数表达式保留的add 会被当作add.name 而继续保留,这在你使用Firebug或则从自身调用同一个函数时,name属性是非常有用的。

普通函数声明

示例如下:

```javascript
function wr(){
//body...
}
```

区别和注意

注意,上面两种函数声明方式存在细微的差别:

  • 第一种方式在赋值之前这个函数不能被任何代码访问到,也就是说这个赋值必须在调用之前完成,否则调用时会出现错误(详细请看下面的函数提升)
  • 第二种方式在声明时就是一个命名的函数,无论是声明在调用之前、调用之后,甚至是不会执行到的位置(例如return语句之后或是永远不会为真的分支里),都在整个作用域可访问

函数提升

```javascript
(function() {
wr1(); //wr1
function wr1() {
console.log("wr1");
}
var wr2 = function() {
console.log("wr2");
}
wr2(); //wr2
wr3(); //Uncaught TypeError: undefined is not a function
var wr3 = function() {
console.log("wr3");
};
})();
```

在以上代码说明,函数声明的方式(无论在上下文哪里声明)会自动将函数定义提升到上下文顶部,而函数表达式的方式只能在表达式执行以后才可以正常调用。

函数提升经常会导致JS的很多bug,我们来看下一个示例:

```javascript
var wr = (function() {
a = function() {
return 1;
}; function a() {
return 2;
}
return a;
})(window);
console.log(wr);
```

这段代码,我们期望的结果可能是输出第二个a,可是由于函数提升,函数声明在进入上下文阶段已经存在于变量对象中(即代码执行之前),所以输出的是我们函数表达式定义的a。

匿名函数和嵌套函数

在JavaScript可以声明一个没有名称的函数,称为匿名函数(Anonymouse Function)。同时JavaScript还允许在函数内部声明函数,称为嵌套函数(Nested Function),嵌套函数的作用域为整个父函数。

JavaScript运行时中有一个特殊的全局环境(Global Object),这个对象上面存放全局的函数和变量,实际开发中经常会使用若干第三方的库或多个js文件,若不小心在全局对象引入重复的变量或函数声明,则会造成代码执行混乱。例如先后引入两个js文件,分别定义了自己的函数log作为内部使用,则第二引入的函数会覆盖第一个的定义且不会抛出任何错误,在后续的执行中调用log函数可能会造成错误。这时候使用一个匿名函数将整个js内的逻辑包装起来,就可以避免这种错误,这种方法已经被绝大多数开源js库使用。

```javascript
//匿名函数
(function() {
function log(msg) {
console.log(msg);
}
// other code ...
})();
```

以上代码就是一个简单的示例,log函数的作用域被限制在这个匿名函数之内,而匿名函数则因为被外面一对小括号()包括起来,形成一个函数表达式,表达式的值是一个函数,紧接着一对小括号表示立即执行这个函数,让原有的代码正常执行一次。不过,这种方式声明的函数、通过var声明的变量等等都是内部的,不能被任何匿名函数以外的代码访问到。如果你需要对外暴露一些函数作为接口的话有如下几种方法:

```javascript
var wr = (function(global) { function log(msg) {
console.log(msg);
} log1 = log; // 法一:利用没有var的变量声明的默认行为,在log1成为全局变量(不推荐) global.log2 = log; // 法二:直接在全局对象上添加log2属性,赋值为log函数(推荐) return { // 法三:通过匿名函数返回值得到一系列接口函数集合对象,赋值给全局变量wr(推荐)
log: log
}; }(window));
```

以上代码所示的几种方式,第一种虽然可行,但是这样导致了全局变量的污染(==埋坑),根据不同的环境和需求,我们可以选用第二种或第三种。

高阶函数

如果函数作为参数或返回值使用时,就称为高阶函数,JavaScript中的函数都可以作为高阶函数来使用,这也是第一类对象的特征。下面我们就分析一下使用方法。

```javascript
function add(num, value) {
return num + value;
} function subtract(num, value) {
return num - value;
} function excute(nums, callback, value) {
var arr = [];
nums.forEach(function(val, index) {
arr.push(callback(val, value));
});
return arr;
} var nums = [1, 2, 3, 4, 5];
var add1 = excute(nums, add, 1);
console.log(add1); //[2, 3, 4, 5, 6]
var subtract1 = excute(nums, subtract, 1);
console.log(subtract1); //[0, 1, 2, 3, 4]
```

以上代码展示了把函数作为参数传入另一个函数excute调用的示例,在excute函数的实现中,把callback作为一个黑盒子看待,负责把参数传给它,然后获取返回值,在调用之前并不清楚callback的具体实现。只有当执行到18行和20行时,callback才被分别代表add或subtract分别对每个元素进行+1或-1的操作。

其实高阶函数结合闭包才是真正体现了JavaScript的灵活,在接下来的博中我会把闭包(Closure)专门提出来。

结语

真正写起这篇来,才发现函数居然还有这么多知识点,在网上借鉴了诸多大牛的文章,也看了很多书,还是觉得不到位(好多东西自己也没掌握到位,所以也不敢冒失的写出来),接下来函数中的几个点我会专门拎出来几个大的块,进行扩展,并结合实例(JS的坑)。同样,如果你在我的文中发现了bug(错误或则理解错误),欢迎指正。

初涉JavaScript模式 (8) : 函数 【概述】的更多相关文章

  1. 初涉JavaScript模式 (10) : 函数 【进阶用法】

    写在前面 不知不觉写到第10篇了.这篇写起来很忐忑,终于和高级搭上边了(呵呵),这篇我们 主要 说一下 JS 方法的部分高级用法(我知道的),笔者水平有限,难免有错.废话不多少,进入正文. 初始化 我 ...

  2. 初涉JavaScript模式 (9) : 函数 【常用方式】

    回调模式 上一篇,对JavaScript函数进行了大体的介绍,这一篇对一些在工作中经常遇到的情况进行扩展. 在工作中,我们经常遇到很多需求,比如现在有一个需求: 一栋10层的大楼,当我们在坐电梯时,电 ...

  3. 初涉JavaScript模式系列 阶段总结及规划

    总结 不知不觉写初涉JavaScript模式系列已经半个月了,没想到把一个个小点进行放大,竟然可以发现这么多东西. 期间生怕对JS的理解不到位而误导各位,读了很多书(个人感觉JS是最难的oo语言),也 ...

  4. 【读书笔记】读《JavaScript模式》 - 函数复用模式之现代继承模式

    现代继承模式可表述为:其他任何不需要以类的方式考虑得模式. 现代继承方式#1 —— 原型继承之无类继承模式 function object(o) { function F() {}; F.protot ...

  5. 【读书笔记】读《JavaScript模式》 - 函数复用模式之类式继承模式

    实现类式继承的目标是通过构造函数Child()获取来自于另外一个构造函数Parent()的属性,从而创建对象. 1.类式继承模式#1 —— 默认方式(原型指向父函数实例) function Paren ...

  6. 初涉JavaScript模式 (11) : 模块模式

    引子 这篇算是对第9篇中内容的发散和补充,当时我只是把模块模式中的一些内容简单的归为函数篇中去,在北川的提醒下,我才发觉这是非常不严谨的,于是我把这些内容拎出来,这就是这篇的由来. 什么是模块模式 在 ...

  7. 初涉JavaScript模式 (2) : 基本技巧

    尽量少用全局变量 大量使用全局变量会导致的后果 全局变量创建以后会在整个JavaScript应用和Web页面中共享.所有的全局变量都存在于一个全局命名空间内,很容易发生冲突 不知不觉创建了全局变量 其 ...

  8. 初涉JavaScript模式 (12) : 沙箱模式

    引子 上一篇说了模块模式,而对于其中的命名空间模式其实也是有着一些问题,比如每添加一个模块或则深入叠加都会导致长命名,并且对于多个库的不同版本同时运行,一不小心就会污染全局标识,而这两天也发现了JSe ...

  9. 初涉JavaScript模式 (6) : 原型模式 【二】

    原型与in操作符 有两种方式使用in操作符:单独使用和在for-in循环中使用. 在单独使用时,in操作符会遍历实例公开(可枚举)的属性,如果找到该指定属性则返回true,无论该指定属性是存在与实例中 ...

随机推荐

  1. C语言基础课程 第二课 HelloWorld不为菜鸟所知的秘密

    1    愉快的开端hello world    4 1.1    include头文件包含    4 1.2    main函数    4 1.3    注释    4 1.4    {}括号,程序 ...

  2. Android RSA加密对象数据

    前几天说了手头项目用MD5和双向加密DES来进行加密的,因为产品是在不断的改造中完善的.考虑到DES和MD5加解密还是会存下一定的风险,于是产品一拍,不行,听你们说MD5加密是不安全的,咱们产品以后做 ...

  3. 数学概念——A 几何概型

    You are going from Dhaka to Chittagong by train and you came to know one of your old friends is goin ...

  4. python编译环境发掘——从IDLE到sublime到pycharm到Anaconda

    一个好的编译器对于我们处理日常的科研很关键,好的编译器无论是从界面,字体风格,提示,调试等各方面都能从用户角度出发,提供最好的使用体验.Python本身自带的IDLE或者在CMD里进行操作和调试,对于 ...

  5. bzoj 3876 [Ahoi2014]支线剧情(有上下界的最小费用流)

    3876: [Ahoi2014]支线剧情 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 484  Solved: 296[Submit][Status ...

  6. Square(强大的剪枝)

    http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=2410 /* 题意; 给出一定数量的棍子用这 ...

  7. multipath.conf

    # This is a basic configuration file with some examples, for device mapper# multipath.# For a comple ...

  8. [PWA] 1. Intro to Service worker

    Service worker stays between our browser and noetwork requests. It can help to fetch data from cache ...

  9. PHP CodeBase: 判断用户是否手机访问(转)

    随着移动设备的普及,网站也会迎来越来越多移动设备的访问.用适应PC的页面,很多时候对手机用户不友好,那么有些时候,我们需要判断用户是否用手机访问,如果是手机的话,就跳转到指定的手机友好页面.这里就介绍 ...

  10. 关于cocostudio加载UI json CCUIHELPER未声明问题

    查看官方的文档,在文档的最后添加了如何加载项目.如下代码: UILayer* ul =UILayer::create(); ul->addWidget(CCUIHELPER->create ...