【JS从入门到精通】06-函数

函数

一、函数的简介

函数也是一个对象,可以封装一些功能(代码),在需要时可以执行这些功能(代码),可以保存一些代码在需要的时候调用

使用typeof检查一个函数对象时,会返回function

JAVASCRIPT
// 创建一个函数对象
// 可以将要封装的代码以字符串的形式传递给构造函数
var fun = new Function("console.log('Hello World.');");
// 封装到函数中的代码不会立即执行
// 函数中的代码会在函数调用的时候执行
// 调用函数语法:函数对象()
// 当调用函数时,函数中封装的代码会按照顺序执行
fun(); // Hello World.

1.1 使用函数声明来创建一个函数  

JAVASCRIPT
function 函数名([形参1, 形参2...形参N]) {
	语句...
}
// 调用函数
函数名();

示例

JAVASCRIPT
function fun1(){
    console.log("Hello world.");
    alert("Hello World!");
    document.write("Helloworld");
}
fun1();

1.2 使用函数表达式(匿名函数)来创建一个函数

JAVASCRIPT
var 函数名 = function([形参1, 形参2...形参N]) {
	语句...
};
// 调用函数
函数名();    

示例

JAVASCRIPT
var fun1 = function(){
    console.log("Hello world.");
    alert("Hello World!");
    document.write("Helloworld");
};
fun1();

1.3  规则

  • 不能使用关键字以及保留字

  • 不能是纯数字或者是数字开头

  • 可以出现英文+数字+下划线,一般下划线在中间位置

  • 建议使用驼峰命名法,见名知意

  • 方法名字与变量名一样都必须是JavaScript合法的标识符。

  • 在函数体中,大括号是必不可少的,缺少大括号, JavaScript将会抛出语法错误

二、函数的参数

定义一个用来求两个数和的函数

可以在函数的()中来指定一个或多个形参(形式参数)多个形参之间使用,隔开,声明形参就相当于在函数内部声明了对应的变量

在调用函数时,可以在()中指定实参(实际参数)

  • 调用函数时解析器不会检查实参的类型。所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查

  • 调用函数时,解析器也不会检查实参的数量,多余实参不会被赋值。如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined

JAVASCRIPT
// 创建一个函数,用来计算三个数的和
function sum(a, b, c) {
    alert(a + b + c);
}
sum(1, 2, 3, 4); // 6

三、函数的返回值

3.1 什么情况下使用函数的返回值

当在函数的外部想要获取到函数内部的运行结果(或者是某一个数据)的时候就必须使用函数的返回值,哪里调用函数,哪里接收返回值

3.2 如何设置

可以使用return来设置函数的返回值语法:return 值

return后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果

在函数中return后的语句都不会执行

如果return语句后不跟任何值,就相当于返回一个undefined;如果函数中不写return,则也会返回undefined

return后可以跟任意类型的值

2.语法设置:
          function 函数名(){
              return 值;
          }
注意事项:
  •      没有return关键字
  •      return后面代码不执行
  •      有return,后面没值
  •      有且一个return,多个使用分支语句
  •      return后面默认返回一个值
 
JAVASCRIPT
// 创建一个函数,用来计算三个数的和
function sum(a, b, c) {
    // var result = a + b + c;
    // return result;
    return a + b + c;
}

// 调用函数
// 变量result的值就是函数的执行结果
// 函数返回什么result的值就是什么
var result = sum(1, 2, 3);
console.log("result = " + result);

实参可以是任意的数据类型,也可以是一个对象。当我们的参数过多时,可以将参数封装到一个对象

JAVASCRIPT
function sayHello(o){
    console.log("我是" + o.name
                + ",今年我" + o.age 
                + "岁了,我是一个" + o.gender 
                + "人,我住在" + o.address);
}			
var obj = {
    name: "孙悟空",
    age: 1000,
    gender: "男",
    address: "花果山"
};
sayHello(obj); // 我是孙悟空,今年我1000岁了,我是一个男人,我住在花果山

实参可以是一个对象,也可以是一个函数

JAVASCRIPT
function calCirc(radius) {
    return Math.PI * Math.pow(radius, 2);
}
function fun(a){
    console.log("a = " + a);
}
fun(calCirc);  
// a = function calCirc(radius) {
//     return Math.PI * Math.pow(radius, 2);
// }
fun(calCirc(10)); // a = 314.1592653589793

calCirc(10)

  • 调用函数
  • 相当于使用的函数的返回值

calCirc

  • 函数对象
  • 相当于直接使用函数对象

函数也是一个对象,特殊在其具有功能

break、continue、return对比

  • break可以退出当前的循环
  • continue用于跳过当次循环
  • return可以结束整个函数

在函数内部再声明一个函数

JAVASCRIPT
function fun3(){
    function fun4(){
        console.log("I'm fun4.");
    }
    fun4();
}
fun3(); // I'm fun4.

function fun5(){
    function fun6(){
        console.log("I'm fun6.");
    }
    return fun6;
}
var a = fun5(); 
a(); // I'm fun6.
fun5()();  // I'm fun6.

四、作用域(scope)

4.1 概念

通常来说一段程序代码中使用的变量函数并不总是可用的,限定其可用性的范围,

作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少名字冲突。

  • 作用:无论是变量还是函数在代码中的某一个区域中的有效生命周期范围

  • 域:区域,位置

  • 作用域: 通俗的说,就是数据起作用的范围(某条数据可以在什么范围内使用)

4.2 分类

  • 全局作用域:在script标签的任意位置都可以进行访问变量/函数

    通过var或function声明在全局(声明在任意函数之外和代码块之外)中的数据,

    在全局的任意地方都可以调用或修改(即全局变量)和在window下的属性

    //声明在全局中的变量
    var a  = 0;
    console.log(a); //可在全局任意地方使用
    function fn(){
        console.log(a); //可以在函数内部中使用
        a = 10;//可以在任意地方修改全局中的变量
    }
    //函数调用
    fn();
    console.log(a); 

    运行:

  • 函数作用域(局部作用域):变量仅能在声明的函数内部可见,函数外是不允许访问的

    声明在函数内部的某个数据(var,function,参数),只能在函数内部使用(函数的局部作用域)

    function fun(){
        //局部作用域下的变量:局部变量
        var b = 2; //生命周期只能在这个fun函数的内部
        console.log(b); // 2//局部作用域下的函数:局部函数
        function fun1(){
            console.log(b); //2  b默认和fun1都是同作用域,可以之间相互访问
        }
        fun1(); //局部函数的自身调用
    }
    fun();

    运行:

4.3 作用域变量

  • 局部变量:在函数内部定义的变量是局部变量

  • 全局变量:在函数之外定义的变量是全局变量

    特别需要注意:如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。

    var b = 100; //全局作用域下声明的变量 周期范围是在整个script标签的任意位置
    //先在全局变量声明了a变量 但是没有值
    //var a;
    function fun(){
        var c = 80;  //局部作用域下声明的变量  周期范围只能在当前fun函数的内部的任意位置
        a = 100; //全局变量
        //在函数内部访问
        console.log(a); //100
    }
    fun();
    //在函数外部访问
    console.log(a);

    运行:

    但是如果该变量出现在形式参数中,则为局部变量。

    function fun(a){
         //在调用函数进来解析这个函数体的时候,发现了形式参数
         //然后再函数体内部创建了一个与形参同名的局部变量
       //var a = 10; //作用域:属于fun函数的局部
         console.log(a); //此时的a为局部变量  10
    }
    fun(10);
    //在函数外部无法直接访问,报错
    console.log(a); // 报错

运行:

总结以下函数内部写变量中不带var的情况:

  • 首先找形参

  • 如果形参有,当做是局部变量对待。

  • 如果形参没有,就去找外部全局看有没有,有就是函数内部在操作全局变量

  • 如果全局变量中也没有,那么此时这个变量相当于在全局定义了一个变量;

五、window对象

全局下的变量和函数都会默认挂载到window对象身上,,只不过在全局上window可以被省略
          
总之:咱们声明的变量会作为window的属性;咱们声明的函数会作为window对象的方法
         
           因为不管是什么类型的对象,里面都有两个重要的成员(属性、方法)

六、作用域链(scope chain)

6.1 概念

其实就是JS中数据的查找规则

作用域链决定了哪些数据能被函数访问。

当一个函数创建后,它的作用域链会被创建此函数的作用域中可访问的数据对象填充。

6.2 查找规则

在JS中我们查找一个变量时,会先在当前作用域进行查找,如果能找到,就直接使用这个变量,如果找不到,就从向上找父作用域的数据,还找不到就接着向上,一直找到全局作用域(window对象),window都找不到就报错。 

var a = 2;
function test(){
    var a = 1;
    console.log('结果:',a); //1
}
test();

运行:

//调用fn()时,在其子函数fn2被调用时,首先会在fn2()自己的作用域内查找变量a
//找不到就在其父级作用域即fn()作用域中查找,即a=10,然后打印a=10
function fn(){
    var a = 10;
    function fn2(){
        console.log('结果:',a); //10
    }
    fn2()
}
fn();

七、函数预解析

JS在读取到一个script标签(或者一个函数作用域)时,会先进行一个预解析的过程,在这个过程中,会把var声明的变量和function声明的函数体,提升到整个script标签(或者一个函数作用域)最前边去。在预解析完之后,JS才会从上到下一行一行解析代码并执行。

7.1 分类

  • 变量预解析(变量提升)

  • 函数预解析(函数提升)

7.2 解析过程

  • 先去解析声明定义的函数,再去解析所有的带var变量

  •  提取重点:
                - 函数提升一定在变量提升之前的
                - 变量只有带有var标识的才有提升
  • 如果函数与函数重名,会覆盖(后面的函数会把前面的函数覆盖掉)
  • 如果变量与函数重名,会忽略变量,保函数舍变量

  • 注意:
            - 变量如果不带var,变量是不会进行预解析的
            - 表达式定义的函数也是当做变量去解析。
console.log(a);
var a = 10;
function a(){
    console.log('函数',a);
}
​
/*
 function a(){
    console.log('函数',a);
 }
 console.log(a); //function(){}
 var a = 10;
 console.log(a); //在当前这行a就是前面的function(){} 所以利用10覆盖function(){}  10
*/

运行:

7.3 特点

7.3.1 变量解析时
变量在预解析时,会把声明语句提升到最前边(赋值前打印返回undefined)。

注意:

变量如果不带var,变量是不会进行预解析的;

只有带var的变量才会进行预解析,只提升声明,不会把赋值过程进行提升

表达式定义的函数也是当做变量去解析。

7.3.2 函数解析时

解析函数的时候,函数定义方式不同,解析过程也不大一样:

  • 函数式声明

function的函数体在预解析时,会把整个函数体提升至最前边,整个函数都要被提升:
func();
function func(){};
func();
  • 表达式函数

只会提升函数表达式的声明,不会执行(真正执行函数表达式前调用会返回undefined),只会提升变量 
var func = function(){};
func();

解析过程中:

如果函数重名,会覆盖(后面的函数会把前面的覆盖掉)

如果变量重名,会忽略;

function a(){
   console.log(a);
}
a();

 

//var变量的预解析
//var a;
console.log('var变量的预解析',a); //undefined
a = 10;
​
//函数体的预解析
/*
 * function fn(){
    console.log('函数');
}
*/
console.log('函数体的预解析',fn);
function fn(){
    console.log('函数');
}
​
//函数表达式的预解析
//var fn1
console.log('函数表达式的预解析',fn1);
fn1 = function(){
    console.log('函数表达式');
}

八、函数类型

8.1 具名函数

function fn(){
    console.log("I am 普通函数");
}

8.2 匿名函数

顾名思义,就没有名字的函数,在实际开发中使用的频率非常高!也是学好JS的重点。

var obj = {
    a:1,
    b:function(){}
}
var sum = function(){
    console.log('I am 匿名函数');
}
sum();

8.3 立即执行函数 IIFE

函数定义完,立即被调用,这种函数叫做立即执行函数

立即执行函数往往只会执行一次

立即执行函数也叫做匿名函数自调用

function (){
    console.log("i am IIFE");
}

发现报错啦!!!

  • 解决方法只需要给匿名函数包裹一个括号即可:

(function (){
    //由于没有执行该匿名函数,所以不会执行匿名函数体内的语句。
    console.log("匿名函数不会报错了");
})
  • 匿名函数后面加上一个括号即可立即执行!

(function (){
        console.log("运行匿名函数");
    })()
  • 在函数前添加 !~ + - 一元运算符

!function (){
        alert("heng");
    }()  
  • 倘若需要传值,直接将参数写到括号内即可:

(function (str){
    console.log("Hello   "+str);
    return str;
})("World")
8.3.1 IIFE特点
  • 匿名函数自调用是在定义的同时执行函数

  • 匿名函数自调用只能执行一次

    • 如果需要一个函数可以执行多次,这个函数必须是有名函数

    • 如果函数没有名字要想执行必须是自调用,但是只能执行一次

  • 匿名函数自调用,函数整体不会发生预解析,但是函数内部执行代码是要去预解析

(function(){
    var a = 1;
    function fun(){
        console.log(a);
    }
    fun();
    
    /*
    function fun(){
        console.log(a); // 1
    }
    var a = 1;
    fun();*/
})()
8.3.2 IIFE作用(背)
  • 防止外部命名空间污染

    当如果在一个script标签中出现两个或两个以上的立即执行函数,那么必须在每一个立即里执行函数的后面添加一个分号;

    (function(){
        var name = '张三';
        function f1(){
            console.log(name); //张三
        }
        f1();
    })();
    ​
    (function(){
        var name = '李四';
        function f1(){
            console.log(name); //李四
        }
        f1();
    })();
  • 隐藏内部代码暴露接口

    后期开发都使用的是一个外部的js链接,但是实际的代码看不到
  • 对项目的初始化,只执行一次

8.3.3 全局污染

全局变量污染:

大家都在全局中写代码,很容易造成命名冲突,导致代码冲突。

ES6中代码冲突会直接报错。所以要养成好的习惯不要在全局去声明变量。

var age = 12;
var age = 21;
function fun2() {
    var name = 'zhangsan';
    var name = 'lisi';
    console.log('姓名:',name);
}
console.log('年龄:',age);
fun2();

运行:

解决策略:不要声明全局变量

(function(){
    var name = 'wangwu';
    console.log(name);
})();
​
(function(){
    var name = 'zhaoliu';
    console.log(name);
})();

立即执行函数本身就是为了避免在全局写代码,避免冲突的。

立即执行函数自执行也叫开启一个新的命名空间。即开启新的作用域,此作用域和其他的不会冲突。

九、函数参数的高级

9.1 获取函数参数个数

  • 使用arguments对象的length属性可以获取函数的实参个数。

  • arguments对象只能在函数内可见,因此arguments.length也只能在函数体内使用。

  • 使用函数对象的length属性可以获取函数的形参个数,

    该属性为只读属性,在函数体内和函数体外都可以使用

function add(a,b,c) {
    console.log(add.length)//函数形参的个数
    console.log(arguments.length)//获取实参的个数
    //arguments:实参集合列表对象 也是伪数组 但是可以按照数组的简单操作(for、[下标]、length) object类型
    return a+b+c;
}
console.log(add.length)//函数形参的个数
console.log(add(1, 2));

9.2 使用arguments对象

  • arguments对象表示函数的实参集合,仅能够在函数体内可见,并可以直接访间。

  • 参数对象是一个伪类数组,不能够继承Array的原型方法。

    可以使用数组下标的形式访问每个实参

    例如参数[0]表示第一个实参

  • 通过修改length属性值,可以改变函数的实参个数。

posted @   泡芙_L  阅读(39)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示