乐趣区

前端培训 – ECMAScript核心语法结构之函数详解

一、函数的概念
函数是一段可以反复调用的代码块。接受输入的参数,不同的参数会返回不同的值。
1. 函数的定义
ECMAScript 中是使用 function 关键字来声明,后面跟一组参数以及函数体。
1) 声明函数
function sum (num1,num2){
return num1 + num2;
}
// 函数声明提升(是预编译吗?)- 可以把函数声明放在调用她的语句后面(可以先使用后定义)

2) 函数表达式:将一个匿名函数赋值给一个变量
var sum = function (num1,num2){
console.log(num1+num2);
}
// 必须定义后使用(否则会报错)

3) 构造函数(几乎不用)
var sum = new Funtion(
‘num1’,
‘num2’,
‘return num1 + num2’
)
// 等同于上面的声明函数

2. 函数的调用(括号运算符)
函数可以通过其函数名调用,后面还要加上一对圆括号和参数(如果有多个参数,用逗号隔开)
function sum(num1, num2) {
return num1 + num2;
}
sum(1, 1) //2

3. 函数的参数
函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数。2. 参数的概念, 参数的省略,参数的传递方式,同名参数 1.arguments 对象
由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是 arguments 对象的由来。
命名参数:
4. 函数的返回值(return)
函数可以通过 return 语句跟要返回的值来实现返回值。函数在执行完 return 语句之后停止并立即退出。因此,在 return 语句之后的任何代码永远都会回执行。
function sum(num1, num2) {
return num1 + num2;
alert(‘Hello world’) // 永远不会执行
}

return 语句不是必须的,如果没有的话,该函数就不会返回任何值,或者说返 undefined;
function sum(num1, num2) {
alert (num1 + num2)
} //undefined
5. 函数没有重载(函数的重复声明)
函数重载: 在其他语言 (Java) 中可以为一个函数体编写两个定义,只要定义的签名(接收的参数的类型和参数不同即可))在 ECMASctipt 中如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。
function f(){
console.log(1);
}
f() //1
function f(){
console.log(2);
}
f() //2

6. 函数的属性和方法
(1) name 属性(返回 name 的名字)
function sum (){}
sum.name // ‘sum’

(2) length 属性
函数的 length 属性返回函数预期传入的参数个数,即函数定义之中的参数个数。
function f(a,b){
return f.length
}
f(1,2,3,4) //2 ==> length 属性就是定义是的参数个数。不管调用时传入了多少参数,length 属性的始终等于 2

(3)apply()和 call() 每个函数都包含两个非继承而来的方法:apply()和和 call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。apply()方法接收两个参数,一个是在其运行函数体内的作用域,另一个是参数数组。其中第二个参数可以是 Array 的实例,也可以是 arguments 对象。
call()方法和 apply()的作用相同,它们的区别在仅在于接收参数的方式不同。对于 call() 方法而言,第一个参数是 this 值没有变化,变化的是其余参数都是直接传递给函数。换句话说,在使用 call()时,传递给函数的参数必须逐个列举出来, 如下面的例子所示:
function sum(num,num2){
return num1 + num2;
}
function callSum(){
return sum.call(this.num1,num2);
}

作用:
1. 传递参数
2. 扩充函数赖以运行的作用域(call、apply、bind)

二、函数的作用域和作用域链
1. 作用域
2. 作用域链
3. 延长作用域链
三、函数的其他知识点
1. 闭包函数
2. 回调函数
3. 递归函数
4. 自执行函数
一、函数的声明
函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值。
ECMAScript 中是使用 function 关键字来声明,后面跟一组参数以及函数体。
1. 声明函数
function sum (num1,num2){
return num1 + num2;
}
* 函数声明提升(是预编译吗?)- 可以把函数声明放在调用她的语句后面(可以先使用后定义)

2. 函数表达式(匿名函数或者拉姆达函数):将一个匿名函数赋值给一个变量
var sum = function(num1,num2){
return num1 + num2;
}
* 必须定义后使用(否则会报错)

3. 构造函数(几乎不用)
var sum = new Funtion(
‘num1’,
‘num2’,
‘return num1 + num2’
)
// 等同于上面的声明函数

4. 箭头函数(es6)
var sum = (num1,num2) => {num1+mum2}

5. 调用函数(括号运算符)
函数可以通过其函数名调用,后面还要加上一对圆括号和参数(如果有多个参数,用逗号隔开)
function sum(num1, num2) {
return num1 + num2;
}
sum(1, 1) //2
6. 函数的返回值
return 语句
ECMAscript 中的函数在定义是不必指定是否返回值。实际上,任何函数在任何时候都可以通过 return 语句跟要返回的值来实现返回值。
function sum(num1, num2) {
return num1 + num2;
alert(‘Hello world’) // 永远不会执行
}

函数体内的 return 语句,表示返回。JavaScript 引擎遇到 return 语句,就直接返回 return 后面的那个表达式的值,后面及时还有语句,也不会得到执行。也就是说,return 语句所带的那个表达式,就是函数的返回值。return 语句不是必须的,如果没有的话,该函数就不会返回任何值,或者说返 undefined;
函数体中的 return 语句,表示返回该函数会返回一个值,并且会在执行完 return 语句之后停止并立即退出,因此,在 return 语句之后的任何代码永远都会回执行。

5. 函数没有重载(函数的重复声明)
函数重载: 在其他语言 (Java) 中可以为一个函数体编写两个定义,只要定义的签名(接收的参数的类型和参数不同即可))
在 ECMASctipt 中如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。
function f(){
console.log(1);
}
f() //1
function f(){
console.log(2);
}
f() //2

4. 函数的内部属性(arguments、this,eval)
arguments.callee

5. 函数的属性和方法
(1)name 属性(返回 name 的名字)
function sum (){}
sum.name // ‘sum’
(2)length 属性
函数的 length 属性返回函数预期传入的参数个数,即函数定义之中的参数个数。
function f(a,b){
return f.length
}
f(1,2,3,4) //2 ==> length 属性就是定义是的参数个数。不管调用时传入了多少参数,length 属性的始终等于 2
(3)toString() (返回一个字符串,内容是函数的源码)

(4)apply()和 call()
每个函数都包含两个非继承而来的方法:apply()和和 call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。
apply()方法接收两个参数,一个是在其运行函数体内的作用域,另一个是参数数组。其中第二个参数可以是 Array 的实例,也可以是 arguments 对象。

call()方法和 apply()的作用相同,它们的区别在仅在于接收参数的方式不同。对于 call()
方法而言,第一个参数是 this 值没有变化,变化的是其余参数都是直接传递给函数。换句话说,在使用 call()时,传递给函数的参数必须逐个列举出来, 如下面的例子所示
function sum(num,num2){
return num1 + num2;
}
function callSum(){
return sum.call(this.num1,num2);
}

作用:
1. 传递参数
2. 扩充函数赖以运行的作用域(call、apply、bind)

二、函数的参数和理解参数

1.arguments 对象
由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是 arguments 对象的由来。
2. 参数的概念, 参数的省略,参数的传递方式,同名参数

二、作用域
1. 什么是作用域
作用域 (scope) 指的是变量存在的范围。在 es5 中的,Javascript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,
所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。(es6 中又新增了块级作用域)

三、作用域链
标示符:指变量、函数、属性的名字,或者函数的参数。
作用域链的用途:是为了保证对执行环境有权访问的所有变量和函数的有序的访问。
标示符的解析是沿着作用域链一级一级的搜索标示符的过程。所搜过程始终是从作用域链的前端开始,然后逐级的
内部环境可以通过作用域链访问所有外部环境,但是外部环境不能访问内部环境中的任何变量和函数。这些环境之间的联系是线性、有序的。每个环境都可以向上搜索作用域链,以查询变量和函数名;但任何环境都不能通过向下搜索作用域链而进入另一个执行环境。

延长作用域链

四、回调函数
五、闭包函数
1. 什么是闭包函数
闭包函数是指有权访问另一个函数作用域中的变量的函数

2. 闭包会把函数中变量的值保存下来,供其他函数使用,这些变量会一直保存在内存当中,这样占用大量的内存,使用不当很可能造成内存泄漏,故要及时清除,清楚方法有两种,一是标记清除,二便是引用计数清除。

3. 闭包的常用场景有一是函数作为返回值,二是函数作为参数来传递。不适用于返回闭包的函数是个特别大的函数, 很多高级应用都要依靠闭包实现.

4. 使用闭包的好处是不会污染全局环境,方便进行模块化开发,减少形参个数,延长了形参的生命周期,坏处就是不恰当使用会造成内存泄漏

六、匿名函数
七、递归函数
1. 什么是递归函数?
递归函数是一个在函数通过名字调用自身的情况下构成的
function factorial(num){
if(num <= 1){
return 1
}else {
return num * factorial(num – 1);
}
}

2.arguments.calls()是一个指向正在执行的函数指针 (严格模式下不支持)
function factorial(num){
if(num == 1){
return 1;
} else {
return num * arguments.callee(num-1)
}
}
3. 严格和非严格都有效
var factorial = (function f (num){
if(num == 1){
return 1;
} else {
return num * arguments.callee(num-1)
}
})

八、立即执行函数

!function(a){
console.log(a); //firebug 输出 12345, 使用!运算符
}(12345);

+function(a){
console.log(a); //firebug 输出 123456, 使用 + 运算符
}(123456);

-function(a){
console.log(a); //firebug 输出 1234567, 使用 - 运算符
}(1234567);

var fn=function(a){
console.log(a); //firebug 输出 12345678,使用 = 运算符
}(12345678)

()、!、+、-、= 等运算符,都将函数声明转换成函数表达式,消除了 javascript 引擎识别函数表达式和函数声明的歧义,告诉 JavaScript 引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数代码。

小结:
在 JavaScript 编程中,函数表达式是一种非常有用的技术。使用函数表达式可以无需对函数命名,从而实现动态编程。匿名函数,也称之为拉姆达函数,是一种使用 JavaScript 函数的强大方式。以下总结了函数表达式的特点

函数表达式不同于函数声明。函数声明要求有名字,但函数表达式不需要。没有名字的函数表达式也叫匿名函数。
在无法确定如何引用函数的情况下,递归函数也会变得比较复杂
递归函数应该始终使用 arguments.callee 来递归地调用本身,不使用函数名 – 函数名可能发生变化 在函数内部定义了其他函数时,就创建了闭包。闭包有权访问包含函数内部的所有变量,原理如下:
在后台执行环境环境中,闭包的作用域包含着它自己的作用域、包含函数的作用域和全局作用域。
通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。
但是,当函数返回一个闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止。使用闭包可以 JavaScript 中模仿块级作用域(JavaScript 本身没有块级作用域的概念),要点如下:
创建并立即调用一个函数,这样就既可以执行其中的代码,又不会再内存中留下对该函数的引用。
结果就是函数内部的所有便令都会被立即销毁 – 除非将某些变量赋值给了包含作用域(即外部作用域)中的变量。闭包还可以用于在对象中创建私有变量,相关概念和要点如下:
即时 JavaScript 中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量。
有权访问私有变量的公有方法叫做特权方法。
可以使用构造函数模式、原型模式来实现自定义实现的自定义类型的特权方法,也可以使用模块模式来实现单例的特权方法。JavaScript 中的函数表达式和闭包都是极其有用的特性,利用他们可以实现很多功能。不过,因为创建闭包必须维护额外的作用域,所以过度的使用它们可能会占用大量内存。

参考:https://www.cnblogs.com/chenh… http://javascript.ruanyifeng….https://wangdoc.com/javascript/https://www.cnblogs.com/caoru…https://www.cnblogs.com/ukerx… 作用域和作用域链 https://www.cnblogs.com/bucho…

退出移动版