JavaScript函数

什么是函数

函数是专门用于封装代码的,函数是一段可以随时被反复执行的代码

  • 函数的格式
function 函数名称(形参列表) {
       封装的代码
}

使用函数的优点:

  1. 减少冗余代码
  2. 变更需求,修改的代码变少
  • 一个函数可以有返回值也可以没有返回值
  • 函数没有通过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 (){  //匿名函数的格式   
    代码;
}   //匿名函数不能只定义不使用,会报错

匿名函数的用法

  1. 匿名函数作为参数
   function test(fn) {
            fn();
        }
        test(function (){
            console.log( "我是函数");
        }); //结果为   我是函数
  1. 匿名函数作为返回值
function test() {   
    return function (){
            console.log( "我是匿名函数作为返回值");
        };
}
let fn = test();
 fn();   //结果   我是匿名函数作为返回值
  1. 匿名函数作为一个立即执行的函数
(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代码的时候会分成两部分操作:预解析以及逐行执行代码也就是说浏览器不会直接执行代码, 而是加工处理之后再执行,这个加工处理的过程, 我们就称之为预解析.

  • 预解析规则
  1. 将变量声明和函数声明提升到当前作用域最前面
    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定义函数的格式不会预解析
如果变量名称和函数名称同名, 那么函数的优先级高于变量
一定要记住, 在企业开发中千万不要让变量名称和函数名称重名

你可能感兴趣的:(JavaScript函数)