什么是函数
函数是专门用于封装代码的,函数是一段可以随时被反复执行的代码
- 函数的格式
function 函数名称(形参列表) {
封装的代码
}
使用函数的优点:
- 减少冗余代码
- 变更需求,修改的代码变少
- 一个函数可以有返回值也可以没有返回值
- 函数没有通过return明确返回值, 默认返回undefined
- return的作用和break相似, 所以return后面不能编写任何语句(永远执行不到)
- 调用函数时实参的个数和形参的个数可以不相同
实参个数少于形参个数,那么剩下的形参的值就是undefined
形参的个数少于实参的个数,那么多余的实参就直接忽略 - JavaScript中的函数和数组一样, 都是引用数据类型(对象类型)
arguments函数
- 作用
保存所有传递给函数的实参,每个函数中都有arguments - arguments的 用法和数组一样 并且只能在函数内部使用
函数扩展运算符
扩展运算符在函数的形参列表中的作用:
将传递给函数的所有实参打包到一个数组中
注意点: 扩展运算符依然只能写在形参列表的最后
function test(a,...b){
console.log(a);//结果为 1
console.log(b);//结果为 [2,3,4,5]
}
test(1,2,3,4,5);
函数形参的默认值
在ES6之前可以通过逻辑运算符来给形参指定默认值
格式: 条件A || 条件B
如果条件A成立, 那么就返回条件A
如果条件A不成立, 无论条件B是否成立, 都会返回条件B
function test(a,b) {
a = a || 666;
b = b || 888;
console.log(a);
console.log(b);
}
test();//结果为666 888
test(1,2);//结果为 1 2
ES6之后可以直接在形参后面通过=指定默认值
注意点: ES6开始的默认值还可以从其它的函数中获取
function test(a = 666,b = 888) {
console.log(a);
console.log(b);
}
test();//结果为666 888
test(1,2);//结果为 1 2
let fn = function say(){
return "我是函数"
}
function test(a = 666,b = fn()) {//通过其他函数获取默认值
console.log(a);
console.log(b);
}
test();//结果为666 我是函数
test(1,2);//结果为 1 2
函数作为参数和返回值
将函数作为其他函数的参数
let say = function (){
console.log( "我是函数");
}
function test(fn) {
fn();
}
test(say);//结果 我是函数
将函数作为其他函数的返回值
注意点: 在其它编程语言中函数是不可以嵌套定义的,
但是在JavaScript中函数是可以嵌套定义的
let say = function (){
console.log( "我是函数作为返回值");
}
function test() {
return say;
}
let fn = test();
fn(); //结果 我是函数作为返回值
匿名函数
没有名称的函数被称为匿名函数
注意 匿名函数不能只定义不使用,会报错
function (){ //匿名函数的格式
代码;
} //匿名函数不能只定义不使用,会报错
匿名函数的用法
- 匿名函数作为参数
function test(fn) {
fn();
}
test(function (){
console.log( "我是函数");
}); //结果为 我是函数
- 匿名函数作为返回值
function test() {
return function (){
console.log( "我是匿名函数作为返回值");
};
}
let fn = test();
fn(); //结果 我是匿名函数作为返回值
- 匿名函数作为一个立即执行的函数
(function (){
console.log( "我被立即执行了");
}) (); //结果 我被立即执行了
注意点: 如果想让匿名函数立即执行, 那么必须使用()将匿名包裹起来才可以
箭头函数
箭头函数是ES6中新增的定义函数的方法
格式为
let 函数名称 = (形参) => {
代码块;
}
let say = (name) =>{
console.log( "我是箭头函数"+name);
}
say("ns"); //结果 我是箭头函数ns
注意 如果只有一个形参,那么形参外面的括号可以省略
let say = name =>{
console.log( "我是箭头函数"+name);
}
say("ns"); //结果 我是箭头函数ns
注意 如果代码块中只有一句代码,大括号也可以省略
let say = name => console.log( "我是箭头函数"+name);
say("ns"); //结果 我是箭头函数ns
递归函数
递归函数就是在函数中自己调用自己,就叫他递归函数
递归函数在一定程度上可以实现循环的功能
递归函数的注意点
每次调用递归函数都会开辟一块新的存储空间, 所以性能不是很好
function login() {
// 1.接收用户输入的密码
let pwd = prompt("请输入密码");
// 2.判断密码是否正确
if(pwd !== "123456"){
login(); //自己调用自己
}
// 3.输出欢迎回来
alert("欢迎回来");//结果 调用了几次函数就会输出几次 欢迎回来
}
login();
变量在函数中的作用域
作用域分为 全局作用域,块级作用域和局部作用域
- 全局作用域: 大括号外面的作用域
- 块级作用域: 和函数没有关系的大括号里面的作用域
- 局部作用域: 和函数有关的大括号里面的作用域
在局部作用域中 var定义的变量为全局变量
{
var num = 666;
}
console.log(num); //结果 666
在局部作用域中 var定义的变量为局部变量
let say = function (){
var num = 666;
}
say();
console.log(num); //结果 报错
无论是在块级作用域还是在局部作用域, 省略变量前面的let或者var就会变成一个全局变量(在企业开发中不推荐这种用法)
let say = function (){
num = 666;
}
say();
console.log(num); //结果 666
作用域链
需要明确:
1.ES6之前定义变量通过var
2.ES6之前没有块级作用域, 只有全局作用域和局部作用域
3.ES6之前函数大括号外的都是全局作用域
4.ES6之前函数大括号中的都是局部作用域
2.ES6之前作用域链
1.1.全局作用域我们又称之为0级作用域
2.2.定义函数开启的作用域就是1级/2级/3级/...作用域
2.3.JavaScript会将这些作用域链接在一起形成一个链条, 这个链条就是作用域链
0 ---> 1 ----> 2 ----> 3 ----> 4
2.4.除0级作用域以外, 当前作用域级别等于上一级+1
3.变量在作用域链查找规则
3.1先在当前找, 找到就使用当前作用域找到的
3.2如果当前作用域中没有找到, 就去上一级作用域中查找
3.3以此类推直到0级为止, 如果0级作用域还没找到, 就报错
// 全局作用域 / 0级作用域
// var num = 123;
function demo() {
// 1级作用域
// var num = 456;
function test() {
// 2级作用域
// var num = 789;
console.log(num);
}
test();
}
demo();
需要明确:
1.ES6定义变量通过let
2.ES6除了全局作用域、局部作用域以外, 还新增了块级作用域
3.ES6虽然新增了块级作用域, 但是通过let定义变量并无差异(都是局部变量)
2.ES6作用域链
1.1.全局作用域我们又称之为0级作用域
2.2.定义函数或者代码块都会开启的作用域就是1级/2级/3级/...作用域
2.3.JavaScript会将这些作用域链接在一起形成一个链条, 这个链条就是作用域链
0 ---> 1 ----> 2 ----> 3 ----> 4
2.4.除0级作用域以外, 当前作用域级别等于上一级+1
3.变量在作用域链查找规则
3.1先在当前找, 找到就使用当前作用域找到的
3.2如果当前作用域中没有找到, 就去上一级作用域中查找
3.3以此类推直到0级为止, 如果0级作用域还没找到, 就报错
// 全局作用域 / 0级作用域
// let num = 123;
{
// 1级作用域
// let num = 456;
function test() {
// 2级作用域
// let num = 789;
console.log(num);
}
test();
}
函数预解析
预解析是指浏览器在执行JS代码的时候会分成两部分操作:预解析以及逐行执行代码也就是说浏览器不会直接执行代码, 而是加工处理之后再执行,这个加工处理的过程, 我们就称之为预解析.
- 预解析规则
- 将变量声明和函数声明提升到当前作用域最前面
2.将剩余代码按照书写顺序依次放到后面
注意点
通过let定义的变量不会被提升(不会被预解析)
*/
/*
// 预解析之前
console.log(num); //undefined
var num = 123;
// 预解析之后
var num;
console.log(num);
num = 123;
*/
/*
// 不会预解析之前
console.log(num); // 报错
let num = 456;
*/
// ES6之前定义函数的格式
/*
console.log(say);
say();
// ES6之前的这种定义函数的格式, 是会被预解析的, 所以可以提前调用
function say() {
console.log("hello it666");
}
*/
/*
// 预解析之后的代码
function say() {
console.log("hello it666");
}
say();
*/
/*
console.log(say);
say(); // say is not a function
// 如果将函数赋值给一个var定义的变量, 那么函数不会被预解析, 只有变量会被预解析
var say = function() {
console.log("hello itzb");
}
*/
/*
var say; //undefined
say();
say = function() {
console.log("hello itzb");
}
*/
ES6定义函数的格式不会预解析
如果变量名称和函数名称同名, 那么函数的优先级高于变量
一定要记住, 在企业开发中千万不要让变量名称和函数名称重名