![747994f4199e250d953be8aa55ee3ee2.png](https://i-blog.csdnimg.cn/blog_migrate/d4d95e203d1bceb9f5688c8fd6ebc773.jpeg)
函数的声明
先声明,再调用,调用才是执行
如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。
1、function
//语法
function 函数名(函数参数) {
函数体
}
//示例
function sum(a, b) {
return a + b
}
//之后使用sum()的形式,就可以调用相应的代码
let val = sum(3, 4)
console.log(val)
2、函数表达式
使用函数表达式,则不需要再加函数名,加上函数名表示该函数名只在函数体内部有效,在外部是无效的
// 语法
let 变量 = function(函数参数) {
函数体;
}
//示例
let val = function (a, b) {
return a + b
};
// 使用val()的形式就可以调用相应的代码
val(3, 4)
3、Function 构造函数
可以接受多个参数,但最后一个参数会被当成函数体,如果只有一个参数,那这个参数就是函数体
var add = new Function(
'x',
'y',
'return x + y'
);
// 等同于
function add(x, y) {
return x + y;
}
arguments对象
使用arguments对象可以获取该函数的所有传入参数,它只在函数体内部才可以使用
var f = function (one) {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
//arguments对象包含了函数运行时的所有对象,arguments[0]是第一个参数,arguments[2]是第二个参数,
// arguments[3]是第三个参数
f(1, 2, 3)
// 1
// 2
// 3
正常模式下,可以修改函数参数
var f = function(a, b) {
arguments[0] = 3;
arguments[1] = 2;
return a + b;
}
f(1, 1) // 5
严格模式下,arguments对象不会影响函数的实际参数
var f = function(a, b) {
'use strict'; // 开启严格模式
arguments[0] = 3;
arguments[1] = 2;
return a + b;
}
f(1, 1) // 2
arguments的length属性可以判断函数有几个参数
function f() {
return arguments.length;
}
f(1, 2, 3) // 3
f(1) // 1
f() // 0
转为数组的两种方法
var args = Array.prototype.slice.call(arguments);
// 或者
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
用arguments的callee属性调用函数自身(严格模式下不可用)
var f = function () {
console.log(arguments.callee === f);
}
f() // true
return 返回值
使用return的作用是终止函数的执行,并返会一个值,如果没有return将返回undefined
function sum(a, b) {
return a + b
}
console.log(sum(3, 4)) // 7
function sum(a, b) {
}
console.log(sum(3, 4)) // undefined
return和console.log的区别
return是返回一个值,这个值是能够赋值在另外一个变量的,而console.log仅仅是展示在控制台,并不会赋值
function sum(a, b) {
return a + b
}
let a = sum(3, 4)
a // 7
function sum(a, b) {
console.log(a + b)
}
let a = sum(3, 4)
a // undefined
箭头函数
箭头函数简化函数表达式
//语法
const fn = name => 'hello' + neme
//等价于
const fn function(neme) {
return 'hello' + neme
}
只有一个参数, ( )
可以省略,多个参数要加括号
const sum = (a + b) => a + b
函数体如果只有一个表达式, { }
可以省略,如果有多个表达式必须要加上,并且用return返回
const a = x => {
if (x > 0) {
return x * x;
}
else {
return - x * x;
}
}
sonsole.log(a(5)) // 25
// 等同于
const a = function(x) {
if(x > 0) {
return x * x
} else {
return - x * x
}
}
sonsole.log(a(5)) // 25
如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错
var func = () => ({foo: 1});
箭头函数嵌套简写
const add = a => b => a + b
add(3)(4) // 7
//等同于
const add = function(a) {
return function(b) {
return a + b
}
}
add(3)(4) // 7
函数的命名规范
- 前缀应为动词、或动词加宾语
- 小驼峰方式 ( 构造函数使用大驼峰命名法 )
![2bb49fd0a6611b4091cb80a97729b70a.png](https://i-blog.csdnimg.cn/blog_migrate/41bf170c7620f519174ece5de2d7b672.png)
示例:
求值(sum)、隐藏(hide)、展现(show)打印名字(printName)
说你好(sayHello)、得到数据(getDate)、设置数据(setDate)
声明前置(变量提升)
var声明的变量会提升到代码的头部
// 表明上是先调用,再声明
console.log(a)
var a = 3
//实际上等同于
var a
console.log(a)
a = 3
// 输出结果为undefined
在函数作用域var声明的变量也存在变量提升
function fn() {
console.log(a)
var a = 3
console.log(a)
}
//等价于
function fn() {
var a
console.log(a)
a = 3
comsole.log(a)
}
// 输出结果为 undefined 3
注意:用let声明不会存在变量提升,上面的写法用let声明就会报错
在JavaScript中,函数名视同变量名,所以也存在变量提升
// 表明上是先调用,再声明
fn()
function fn() {
console.log(hello)
}
// 实际不会报错,等同于
functiom fn() {
console.log(hello)
}
fn()
如果是函数表达式会报错
fn()
var fn = function () {
console.log(hello)
}
// 会报错,等同于
var fn
fn()
fn = function() {
console.log(hello)
}
// 调用fn的时候,fn只是被声明了,还没有赋值,等于undefined,所以会报错
如果用function 和var 声明的函数表达式是同一个函数,会采用var声明的函数表达式
var f = function () {
console.log('1');
}
function f() {
console.log('2');
}
f() // 1 因为变量提升,后面的函数不会覆盖前面的
函数作用域
全局变量和局部变量
对于顶层函数来说,在函数外部声明的变量就是全局变量,它可以在函数内部读取,在函数内部声明的变量,就是局部变量,外部无法读取,并且会在局部变量所在的作用域中覆盖同名的全局变量
// 全局变量
var a = 1
function fn() {
console.log(a)
}
// 输出结果1
//局部变量
function fn() {
var a = 1
}
a // 报错,外部无法读取
//局部变量与全局变量同名
var a = 1
function fn() {
var a = 2
console.log(a)
}
fn() // 2 覆盖同名的全局变量
a // 1
作用域链
函数在执行的过程中,先从自己内部找变量,如果找不到,再从创建当前函数所在的作用域去找,以此往上,直到全局变量(找的是变量此刻的状态)
![862ae8574419ec73c8e4d5846eb6ff22.png](https://i-blog.csdnimg.cn/blog_migrate/a6592a4fde936a246a299fa63548cada.jpeg)
![aab5a5bcae75d7adaaa870223ee93ae7.png](https://i-blog.csdnimg.cn/blog_migrate/4f058e69d22528222cf38ef11080d735.jpeg)
![52ccb9bae15807b66fce29f2f49c7170.png](https://i-blog.csdnimg.cn/blog_migrate/2c2602ee3633aa12a2aaf7bd3160515e.jpeg)
ket a = 0
function foo() {
bar() // 执行的时候,a还未被赋值,只是声明前置了
var a = 1
function bar() {
console.log(a) // 输出结果为undefine,内部和创建bar函数所在的作用域找,而且是a此时的状态
}
}
// 等价于
let a = 0
function foo() {
var a // 此时的a为undefined
bar()
a = 1
function bar() {
console.log(a)
}
}
立即执行函数表达式
作用是隔绝彼此的作用域
// 语法
( function () {
函数体
})()
// 示例
function () {
let a = 3
let b = 4
console.log(a + b)
};
function () {
let a = 2
let b = 1
console.log(a - b)
}
// 上面这段代码会报错
(function () {
let a = 3
let b = 4
console.log(a + b)
})();
(function () {
let a = 2
let b = 1
console.log(a - b)
})()
//改用立即执行函数表达式不会报错
递归
函数调用自身,就是递归,必须设置终止条件,否则会出现死循环
//用递归实现n的阶乘
// 5!5*4!4*3!3*2!2*1!1!
function fact(n) {
if(n === 1) return 1
return n*fact(n-1)
}
console.log(fact(6)) // 720
// 实现斐波那契数列 fib(n) = fib(n-1) + fib(n-2)
// 1 1 2 3 5 8 13 21 34
function fib(n) {
if(n === 1 || n===2) return 1
return fib(n-1) + fib(n-2)
}
console.log(fib(5)) // 5
递归优化:https://zhuanlan.zhihu.com/p/141440886
柯里化
把接受多个参数的函数变成接受一个单一参数的函数,然后里面返回新的函数
function add(a) {
return function(b) {
return a + b
}
}
add(3, 4)
// add(3)(4)
回调
回头再调用
function callMe(name,callBack) {
callBack(name)
}
callMe('hello',function(name) {
console.log('hi' + name)
})
参考来源:
饥人谷课件
ES6阮一峰教程