JavaScript学习笔记(六)

本文深入讲解JavaScript函数的基础概念、声明方式、参数使用、作用域规则、预解析现象等关键知识点,并通过实例演示函数的各种应用场景。

3.2 函数(Function)

3.2.1 定义
  • 一个对象
  • 可重复执行的代码块
  • 完成特定功能的一段代码
  • 使用typeof检查一个函数对象时,会返回function
3.2.2 作用
  • 部分代码使用次数过多,封装起来,在使用时直接调用即可,方便简单
3.2.3 特点
  • 封装到函数中的代码不会立即执行
  • 函数中的代码会在调用时才执行
  • 调用函数 语法:函数对象()
  • 调用函数时,函数中封装的代码会按照顺序执行
3.2.4 基本使用
  • 不常用:var fun = new Function(“console.log(‘大家好, 我是函数!’);”);
  • 常用
  • 声明:function 函数名([形参1,形参2, …,形参N]){函数体}
  • 调用:函数名()
3.2.5 常见声明方式

1.函数声明方式:
function 函数名([形参1,形参2,…]){函数体}
2.函数表达式声明方式:
var add= function(num1,num2){
return num1+num2;
}; 
3.使用Function构造函数(不推荐使用):
var add = new Function(‘num1’,‘num2’,‘return num1+num2’);

3.2.6 函数的参数
  • 为什么要设置参数:增强函数的功能性与拓展性,便于交互
  • 形参:占位用,无实际意义
    function add(a,b){return a + b ;} // a, b 为形参
  • 实参:实际参与运算的值
    add(1,2); //1,2为实参
  • 注意点:在其它语言中实参个数必须和形参个数一致,但是JavaScript中没有函数签名的概念,实参个数和形参个数可以不相等,实参若超过形参个数,超过的都会以 undefined 传递给函数
3.2.7 函数使用中的注意点

1.调用函数时,在()中指定实参, 实参将会赋值给函数中对应的形参
2.调用函数时解析器不会检查实参的类型, 所以开发中一般需要对参数进行类型的检查
3.函数的实参可以是任意的数据类型
4.调用函数时,解析器不会检查实参的数量, 多余实参不会被赋值, 如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined

3.2.8 arguments对象
  • arguments对象是一个伪数组
    arguments并不是一个数组,只是与数组相似, 除了拥有length属性,数组的所有属性和方法都不具备

  • arguments对象的使用
    使用特殊对象 arguments时无需明确指出参数名,就可以访问它们.
    例如:在write函数中,如果需要访问其第一个参数 words1 ,不用指明其具体参数名,用 arguments[0] 就可以访问这个值(该函数第一个参数的值)。

    function write() {
    if (arguments[0] == “hello”) {
    return;
    }
    alert(arguments[0]);
    }

  • 用arguments对象检测参数个数
    可以用 arguments 对象检测函数的参数个数,引用属性 arguments.length 即可

eg:

function judjenum() {
  console.log(arguments.length);
}
judjenum("string", 45); 
// 返回 2 
judjenum(); 
// 返回 0
judjenum(12); 
// 返回 1
  • 注意点:
    在其它语言中实参个数必须和形参个数一致,但是JavaScript中没有函数签名的概念,实参个数和形参个数可以不相等,实参若超过形参个数,超过的都会以 undefined 传递给函数
  • 用 arguments 对象判断传递给函数的参数个数
    用 arguments 对象判断传递给函数的参数个数,即:模拟函数重载

eg:

function doAdd() {
      if(arguments.length == 1) {
        alert(arguments[0] + 5);
      } else if(arguments.length == 2) {
        alert(arguments[0] + arguments[1]);
      }
    }
    doAdd(10);	
    //输出 "15"
    doAdd(40, 20);	
    //输出 "60"
  • 用arguments对象比较形参与实参个数是否一致

eg:

unction sum(num1,num2){
        console.log(arguments.length); 
        // 返回 2 
        console.log(sum.length); 
        // 返回 2 
        console.log(arguments.length == sum.length); 
        // 返回 true
    }
    sum(10,20);
  • 用 arguments.callee属性可判断/返回当前函数
    callee是arguments对象的一个属性,该属性是一个指针, 指向拥有这个arguments对象的函数

eg:

function sum(num1,num2){
    console.log(arguments.callee);
}
sum(10,20);
3.2.9 length属性
  • 函数体中,arguments.length表示传入函数的实参数量。
  • 函数本身的length属性代表的是函数定义时给出的参数(形参)个数。

eg:

function a(b, c, d){
console.info(arguments.length)
}
console.info(a.length);  // 3
a(1);  //1
3.2.10 函数的返回值(return)

当一个函数被调用,通常会从函数的开始执行到结束。如果想提前结束该函数的执行可以使用return语句,return语句后面的所有语句将永远不会执行。

  • 一般return用于返回结果
  • 注意点:
    1.如果函数没有显式的使用 return语句 ,那么函数有默认的返回值:undefined
    2.如果函数使用 return语句,那么跟在return后面的值,就成了函数的返回值
    3.如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined
    4.推荐的做法:要么让函数始终都返回一个值,要么永远都不要返回值。
3.2.11 函数直接声明与函数表达式声明的区别
1. 函数的直接声明

函数声明可以定义所命名的函数的变量,而不用给变量赋值。

eg:function func( ) {  }
2. 函数的表达式声明

函数表达式声明是将函数定义为表达式语句的一部分。

eg:var func = function( ) {  }
3. 两种方式的区别

1.函数的直接声明

alert(f(5,6));
function f(a, b) {
     return a + b;
}

运行结果:
在这里插入图片描述
2. 函数表达式声明

 alert(myFun);
    var myFun = function (a,b){
        return a + b;
    }

运行结果:
在这里插入图片描述

4. 不同运行结果产生的原因分析:

JavaScript在执行代码时,存在一种变量声明被提升的机制,即JS解析器首先会把当前作用域的函数声明提前到整个作用域的最前面,即使写的时候是写在后面,也还是会被提至最前。因此f()函数会被提前alert(f(5,6));便可以执行;但是myFun()函数并不会被提前alert(myFun);便不会被执行。

3.2.12 匿名函数
  • 没有命名的函数:function () {}
  • 作用:
    1.用在绑定事件的时候

eg:

document.onclick = function () {
    alert(1);

}
2.定时器

eg:

setInterval(function () {
    console.log(444);

},1000);
3.立即执行函数
函数定义完,立即被调用,这种函数叫做立即执行函数立即执行函数往往只会执行一次

eg:

(function(num1,  num2){
console.log("mum1 = "+ num1);
console.log("num2 = "+ num2);
})(100, 101);
3.2.13 函数的数据类型
  • 函数属于object类型
    console.log(typeof fn); // function
    console.log(typeof fn()); //undefined
3.2.14 回调函数
  • 回调函数就是一个通过函数调用的函数
  • 如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数

eg:

function fn(num1,num2,demo){
    return demo(num1,num2);
}

//定义四个规则:加减乘除
function test1(a,b){
    return a+b;
}
function test2(a,b){
    return a-b;
}
function test3(a,b){
    return a*b;
}
function test4(a,b){
    return a/b;
}

console.log(fn(10,5,test1));
console.log(fn(10,5,test2));
console.log(fn(10,5,test3));
console.log(fn(10,5,test4));
回调函数练习:

求Fibonacci数列(1 1 2 3 5 8 13 21…)的第n个数?

解:

        function f1 (n) {
        if (n == 1) return 1;
        if (n == 2) return 1;
        
        return f1(n-1) + f1(n-2);
    }

    console.log(f1(7));

求n个数的累加
解:

        function getSum (n) {
        if (n == 1) { return 1;}
        return n + getSum(n - 1);
    }

    console.log(getSum(100));

从前有座山,山里有座庙, …
解:

        var i = 100;

    function f1 () {
        i--;
        //递归的结束条件
        if (i >= 0) {
            console.log("从前有座山,山上有座庙" + i);
            //递归调用
            f1();
        }

    }
    f1();
3.2.15 变量的作用域
  • 作用域:变量可以起作用的地方
  • 全局变量:定义在script或者不属于某个函数的变量
  • 局部变量:定义在函数内部的变量
  • 注意点:
    1.不使用var声明的变量是全局变量,不推荐使用(XXXX)
    2.函数内部可以访问到该函数所属的外部作用域的变量(作用域链)
    3.变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁
  • 块级作用域:
    1.在其它语言中,任何一对花括号中的语句都属于一个块,在这之中定义的所有变量在代码块外都是不可见的
    2.ES6之前都没有这个概念,之前只有函数作用域,ES6中{ }才有了作用域,ES5及之前{ }无意义
    ES5中只有函数有作用域
  • 作用域链
  1. 概念:
    通俗地讲,当声明一个函数时,局部作用域一级一级向上包起来,就是作用域链。(当需要找某一个变量时,一层一层往外找)
    做题的时候可以使用画图的方法(在线画图工具:ProcessOn
    在这里插入图片描述

  2. 其它语言中变量i只能在for循环内部访问(是局部变量),但是在JS中,i是全局变量
    for (var i = 0; i < 10; i++) {
    }
    console.log(i);

  3. 两个案例:

    eg1:  
    {
         var num = 5;
    }
     console.log(num);  // 返回 5
    

eg2:

   var num = 5;
   if (num > 3) {
        var sum = 7;       
    }
    console.log(sum);  // 返回 7

4.全局变量:

var name = "旋之华";
function f() {
    name = "刘德华";
}
f();
console.log(name);  // 返回 “刘德华   ”
>>>说明name是全局变量,在全局任何地方都可以使用
  1. 局部变量
function f(){
    var str = '撩课学院';
    console.log(str);
}
f(); //返回 撩课学院 (因为此处调用了f()函数)
console.log('------' + str); //  报错(因为str是局部变量)
3.2.16 预解析
  • 概念:
    1.JS代码的执行是由浏览器中的JS解析器来执行的
    2.JS解析器执行代码的时候,分为两个过程:预解析过程和代码执行过程

  • 预解析过程
    1.把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值
    2.把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用
    3.先提升var,再提升function

  • 案例

     var num1 = 10 ;
     function fn1(){
     console.log(num1);
     var num1 = 20;
     }
     fn1();   // 返回 undefined 
    

结果分析: 返回 undefined 是因为 fn1()函数会先在当前作用域找 num1 ;虽然 var num1 = 20; 会被提升,但也只是提升了声明,而没有提升赋值,因此返回 undefined

console.log(fn2); // 返回fn2()函数
function fn2(){
    console.log("是函数");
}
var fn2 = "是变量";
console.log(fn2); // 返回 “是变量”

在这里插入图片描述
结果分析:
在这里插入图片描述

3.2.17 变量提升
  • 变量提升:定义变量的时候,变量的声明会被提升到作用域的最上面,变量的赋值不会提升
  • 函数提升:JavaScript解析器首先会把当前作用域的函数声明提前到整个作用域的最前面
3.2.18 其他
  • 始终考虑当前作用域
  • 没用var声明为全局变量
  • 用var声明为局部变量
  • 作用域链从里往外看
  • 想不明白时,画图解决
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值