ECMA Script 5学习笔记—2.6 数据类型:函数

1.概述

1.1函数的声明

js函数有三种声明方法。

1.1.1 函数的申明

function命令声明的代码区块,就是一个函数。function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。

function print(s) {
  console.log(s);
}

1.1.2 函数表达式

除了用function命令声明函数,还可以采用变量赋值的写法。

var print = function(s) {
  console.log(s);
};

这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression),因为赋值语句的等号右侧只能放表达式。

1.1.3 Function构造函数

第三种声明函数的方式是Function构造函数。

var add = new Function(
  'x',
  'y',
  'return x + y'
);

// 等同于
function add(x, y) {
  return x + y;
}

上面代码中,Function构造函数接受三个参数,除了最后一个参数是add函数的“函数体”,其他参数都是add函数的参数。

Function构造函数可以不使用new命令,返回结果完全一样。

总的来说,这种声明函数的方式非常不直观,几乎无人使用。

1.2函数的重复声明

如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。

1.3圆括号运算符,return 语句和递归

调用函数时,要使用圆括号运算符。圆括号之中,可以加入函数的参数。

函数体内部的return语句,表示返回。JavaScript 引擎遇到return语句,就直接返回return后面的那个表达式的值,后面即使还有语句,也不会得到执行。也就是说,return语句所带的那个表达式,就是函数的返回值。return语句不是必需的,如果没有的话,该函数就不返回任何值,或者说返回undefined。

函数可以调用自身,这就是递归(recursion)。

1.4 第一等公民

JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。

由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为第一等公民。

1.5函数名的提升

JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。
但是,如果采用赋值语句定义函数,不会提升。

2.函数的属性和方法

2.1 name属性

函数的name属性返回函数的名字。

2.2 length属性

函数的length属性返回函数预期传入的参数个数,即函数定义之中的参数个数。

2.3 toSring()

函数的toString()方法返回一个字符串,内容是函数的源码。

3.函数的作用域

3.1定义

作用域(scope)指的是变量存在的范围。在 ES5 的规范中,JavaScript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。

对于顶层函数来说,函数外部声明的变量就是全局变量(global variable),它可以在函数内部读取。

在函数内部定义的变量,外部无法读取,称为“局部变量”(local variable)。

函数内部定义的变量,会在该作用域内覆盖同名全局变量。

注意,对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量。

3.2函数内部的变量提升

与全局作用域一样,函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。

3.3函数本身的作用域

函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。

外部声明的绑定外部,内部声明的绑定内部。

4.参数

4.1 概述

函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数。

4.2 参数的省略

函数的参数不是必须的,js允许省略参数。
但是,没有办法只省略靠前的参数,而保留靠后的参数。如果一定要省略靠前的参数,只有显式传入undefined。

4.3 传递方式

函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value)-值的拷贝。这意味着,在函数体内修改参数值,不会影响到函数外部。

但是,如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。

注意,如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值。

4.4同名函数

如果有同名的参数,则取最后出现的那个值,即使后面的没有值或被省略,也是以其为准。

4.5 arguments对象

由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。
arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
需要注意的是,虽然arguments很像数组,但它是一个对象。数组专有的方法(比如slice和forEach),不能在arguments对象上直接使用。
如果要让arguments对象使用数组方法,真正的解决方法是将arguments转为真正的数组。
arguments对象带有一个callee属性,返回它所对应的原函数。

5.函数的其他知识点

5.1闭包

闭包(closure)是 JavaScript 语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
如果出于种种原因,需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。返回。
可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境。

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。

闭包的另一个用处,是封装对象的私有属性和私有方法。

注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。

5.2立即调用的函数表达式(IIFE)

在 JavaScript 中,圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。

(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();

上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称 IIFE。
注意,上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个 IIFE,可能就会报错。

任何让解释器以表达式来处理函数定义的方法,都能产生同样的效果,比如下面三种写法。

var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();

6.eval命令

eval命令接受一个字符串作为参数,并将这个字符串当作语句执行。

如果参数字符串无法当作语句运行,那么就会报错。
放在eval中的字符串,应该有独自存在的意义,不能用来与eval以外的命令配合使用。

eval没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值