Loading... ## 什么是函数 在JavaScript中,函数类似于Java中的方法,它是负责执行特定功能的JavaScript代码。但是JavaScript中的函数使用更为简单,不需要定义函数属于哪个类,因此在调用的时候不需要使用“`对象名.函数名()`”的方式,直接使用函数名名称调用即可。 ## 函数的分类 1. 系统函数 2. 自定义函数 ## 常见的系统函数 ### parseInt() **作用:** 将字符串转换为整型数字 **语法:**`parseInt("字符串");` <div class="tip inlineBlock error simple"> **⚠️ 注意:** parseInt()转换字符串时,是将字符串转为字符串数组并从下标0开始,依次判断每个字符是否可以转换为一个有效数字 * 如果不是有效数字,则返回NaN,不再继续执行其他操作 * 如果是有效数字,则该函数将查看下标为1的字符,进行同样的测试,直到发现非有效数字的字符或全部检测完为止 </div> **演示案例:** ```javascript var num1 = parseInt("56.64"); //返回值为56 var num2 = parseInt("123abc"); //返回值为123 var num3 = parseInt("hello999"); //返回值为NaN ``` ### parseFloat() **作用:** 将字符串转换为浮点型数字 **语法:**`parseFloat("字符串");` **演示案例:** ```javascript var num1 = parseFloat("3.1415926"); //返回值为3.1415926 var num2 = parseFloat("123abc"); //返回值为123 var num3 = parseFloat("hello999"); //返回值为NaN var num4 = parseFloat("52.18.97"); //返回值为52.18 ``` ### isNaN() **作用:** 检查其参数是否是非数字 **语法:** `isNaN(值、变量);` **演示案例:** ```javascript var num1=isNaN("20.5"); //返回值为false var num2=isNaN("123abc"); //返回值为true var num3=isNaN(48.98); //返回值为false ``` <div class="tip inlineBlock success simple"> **🙌🙌经验分享:** 通常,使用isNaN()函数检测parseInt()和parseFloat()的运算结果,判断它们表示的是否是合格的数字;也可以使用isNaN()函数检测操作数是否有错误,例如:用0作为除数的情况 </div> ## 自定义函数 ### 普通函数声明 **定义函数语法:** ```javascript function 函数名([参数1[, 参数2[, 参数3,…] ] ]) { //参数可选 //JavaScript语句; [return 返回值] //可选 } ``` **html表单元素调用函数语法:** `事件名="函数名([参数值1[, 参数值2[, 参数值3,…] ])"` **案例演示:** ```html <!-- 用户点击按钮时,则弹出警告提示框,内容为:你好呀! --> <input type="button" value="点击弹框" onclick="showMsg('你好呀!')"> <script> /** * 弹出系统警告提示框 * @param {any} msg 需要提示的内容 */ function showMsg(msg) { alert(msg); } </script> ``` ### 函数表达式声明 **定义函数语法(将函数赋给变量):** ```javascript var 变量 = function([参数值1[, 参数值2[, 参数值3,…] ]) { //JavaScript语句; }; ``` **调用语法:** ```javascript 变量([参数值1[, 参数值2[, 参数值3,…] ]); ``` ### 函数表达式和普通函数有和区别? <div class="tip inlineBlock warning simple"> 使用**函数声明**方式定义两个同名的函数时,后面的函数将会***覆盖***前面的函数; 使用**函数表达式**方式定义同名的函数时,会从上到下,逐行执行代码,并输出结果 </div> **演示案例:** ```javascript //函数声明 function fn1() { console.log("你好~"); } fn1(); function fn1() { console.log("Hello~"); } fn1(); /* * 输出结果: * Hello~ * Hello~ */ /***********************分割线**************************/ //函数表达式 var fn2 = function () { console.log("你好!"); }; fn2(); var fn2 = function () { console.log("Hello!"); }; fn2(); /* * 输出结果: * 你好! * Hello! */ ``` <div class="tip inlineBlock warning simple"> **🤔️想一想:**使用函数声明的方式定义两个函数名都为fn1()的函数,为什么输出了两次“Hello~”?答案: **预解析** </div> ### 预解析 **概念:** 提前解析代码。在JavaScript中,代码都是一行一行安顺序从上至下依次执行,然而,预解析的出现则打破了这个规则。 **预解析主要完成两件事情:** 1. **变量**的声明会被提前 2. **函数**的声明会被提前 **案例演示:** ```javascript //使用变量 console.log(nums); //nums is not defined console.log(num); //undefined //声明变量 var num = 10; /********************分割线************************/ //调用函数 fn1(); /** * 声明函数 */ function fn1() { console.log("Hello") } ``` <div class="tip inlineBlock success simple"> **📑结论** 按照我们之前的规则,代码从上至下依次执行,那么在读取num变量时,系统应该也会提示"num is not defined",但是,系统在使用num时,显示状态为:undefined(该变量是已经声明但未赋值的状态)。 第二个案例中,在调用fn1()函数的时候,该函数尚未被创建,然而系统能够正常执行fn1()函数中的代码,这也充分说明了:**JavaScript在执行预解析的时候,函数和变量会被优先加载与解析,所以允许他们被提前访问。** </div> ### 匿名函数 **声明匿名函数和调用语法:** ```javascript (function() { //函数体 })(); ``` **匿名函数的特点和好处:** 函数没有名字,在声明的同时便直接调用,匿名函数不会出现命名冲突。 ### 将方法作为参数进行传递 ```javascript function f1(fn) { //f1函数被调用后,fn则表示传递过来的函数 console.log("f1函数调用了"); fn(); //此处表示调用了传递过来的函数 ➡️ f2() } function f2() { console.log("f2函数调用了"); } f1(f2); //在调用f1函数时,将f2函数作为方法参数传递给了f1函数 /** * 输出结果: * f1函数调用了 * f2函数调用了 */ ``` <div class="tip inlineBlock error simple"> ⚠️**注意:** 如果没有指定回调函数的名称,则称之为***匿名回调函数*** </div> ### 面试题分享 **下面两个函数的返回值是一样的吗?为什么?** ```javascript function foo1() { return { bar: "hello" } } function foo2() { return { bar: "hello" } } ``` <div class="tip inlineBlock success simple"> **🖥 输出结果:** 函数foo1()返回值是: **{ bar: "hello" }** 函数foo2()返回值是: **undefined** **🔖 原因:** 在编程语言中,基本都是使用分号(;)表示一行代码的结束,这可以增加代码的可读性和整洁性。 在JavaScript中,如果每个语句独立各占一行,通常可以省略语句之间的分号(;),JavaScript解析器会根据能否正常编译来决定是否自动填充分号,如: ```javascript var test = 1 + 2 //解析器将会自动变更为: var test = 1 + 2; console.log(test); //输出结果:3 ``` 在上述情况中,如果一行代码的末尾存分号(;)则为了正确解析代码,解析器将不会自动填充分号了。 但是,对于return、break、continue等语句,如果其后面紧跟换行,解析器一定会自动在后面填充分号(;),所以面试题中的代码foo2()函数就会变成了: ```javascript function foo2() { return; //解析器自动填充分号(;) { bar: "hello" }; } ``` 正是由于解析器会自动添加分号(;),所以,foo2()函数返回undefined。 </div> ## 变量的作用域 **局部变量** * 在函数内部声明的变量(必须使用var) * 只能在函数内部访问它 * 可以在不同的函数中使用名称相同的局部变量 **全局变量** * 在函数外声明的变量 * 网页的所有脚本和函数都能访问它 **代码演示:** ```javascript var x=10; //全局变量 function f1() { var y=5; //局部变量 console.log(x); //输出:10 console.log(y); //输出:5 } f1(); console.log(x); //输出:10 console.log(y); // ❌ 错误信息:y is not defined ``` **局部变量与全局变量的区别** | | 局部变量 | 全局变量 | | -------- | -------------------- | -------------------------------------------- | | 作用域 | 仅作用在函数中 | 作用在整个页面中,页面中的所有脚本均可以访问 | | 声明位置 | 函数中 | 使用之前的任何位置 | | 生命周期 | 在函数运行以后被删除 | 在页面关闭后被删除 | <div class="tip inlineBlock info simple"> **⚠️ 注意:** 局部变量与全局变量同名时,局部变量拥有更高的优先级,且遵循就近原则! 此处涉及知识点:作用域链(Scope Chain) **作用域链** * 是JavaScript内部一种变量、函数查找机制 * 决定了变量和函数的作用范围 * 当执行函数时,先从函数内部寻找局部变量 * 如果内部找不到,则向创建函数的作用域寻找,依次向上 * 如果最终没有找到,通常会报错 </div> ### 隐式全局变量 **概念:** * 如果变量声明时,没有使用关键字 `var`,则被称为隐式全局变量 * 如果在函数内部声明变量时,没有使用关键字 `var`,则也是隐式全局变量 **隐式全局变量和普通全局变量的区别:** 隐式全局变量可以被删除,全局变量不可以被删除 **案例:** ```javascript var a1 = 1; //全局变量 a2 = 2; //隐式全局变量 function f1() { num = 200; } f1(); console.log(num); //正常输出:200 //对隐式全局变量和普通全局变量执行删除后 delete a1; delete a2; console.log(typeof(a1)); //number console.log(typeof(a2)); //undefined ``` ## 闭包 ### 概念: * 能够读取其他函数内部变量的函数 * 是将函数内部和函数外部链接起来的桥梁 ### 实现步骤 1. 在一个函数的内部,再定义一个函数 2. 把内部的函数作为返回值 ### 特性和应用 * 函数嵌套 * 内部函数可以访问外部函数的变量 * 参数和变量不会被回收 * 最典型的应用: **实现回调函数** ### 作用 * 可以读取函数内部的变量 * 让这些变量的值始终保存在内存中 ### 缺点 * 在父函数外部,可以改变父函数内部变量的值 * 常驻内存,会增大内存使用量,使用不当很容易造成内存泄露 **案例分析:** ```javascript function f1() { var num = 1; function f2() { //此处f2函数则为:闭包 alert(num); } return f2; } var result = f1(); //调用f1函数,获得f2闭包函数 result(); //调用f1中的f2闭包函数 //可以将上述两行代码简化至: (f1())(); ``` ## 闭包案例演示 **需求描述:** 页面显示后,有4个按钮,现要求实现用户每点击某个点赞按钮,则点赞值加1的要求。 html页面: ```html <body> <p><input type="button" value="👍点赞-0"></p> <p><input type="button" value="👍点赞-0"></p> <p><input type="button" value="👍点赞-0"></p> <p><input type="button" value="👍点赞-0"></p> </body> ``` JavaScript代码: ```javascript //页面加载完毕后,执行代码 window.onload = function () { //获取页面中所有的input标签 var elements = document.getElementsByTagName('input'); //循环elements节点,绑定点击事件 for (let index = 0; index < elements.length; index++) { //绑定点击事件 elements[index].onclick = like(); } //点赞 function like() { //初始化变量 var number = 1; return function () { //返回匿名函数,闭包处理 this.value = "👍点赞-" + (number++); } } } ``` ## 闭包案例演示-扩展 1️⃣:下面这段代码想要循环 **延时(后续课程内容)** 输出结果 0 1 2 3 4, 请问输出结果是否正确, 如果不正确, 请说明为什么, 并修改循环内的代码使其输出正确结果? ```javascript for (var i = 0; i < 5; ++i) { setTimeout(function() { console.log(i + " "); }, 100); } //输出结果为 5 5 5 5 5 ``` <div class="tip inlineBlock warning simple"> **🔖 分析原因** JavaScript运行环境为单线程,setTimeout 注册的函数需要等到线程空闲时才能执行,此时 for 循环已经结束,i 值为 5,又因为循环中 setTimeout 接受的参数函数通过闭包访问变量 i,所以 5 个定时输出都是 5。 **📑 解决方案** ```javascript for (var i = 0; i < 5; ++i) { (function (value) { //创建匿名方法(闭包),定义参数value setTimeout(function () { //创建定时器,100毫秒后输出value值 console.log(value + " "); }, 100); })(i); //循环时,访问匿名方法,并传递参数i } //输出结果为: 0 1 2 3 4 ``` </div> 2️⃣ :如何优化下方代码才能使每次执行方法后实现变量加1对效果呢? ```javascript function add() { var x = 1; console.log(++x); } add(); //执行输出:2 add(); //执行输出:2 /********************优化方案*******************/ function add() { var x = 1; return function () { console.log(++x); }; } var num = add(); num(); //输出:2 num(); //输出:3 num(); //输出:4 ``` ## 事件 **概念:** 事件(Event)是使用JavaScript实现网页特效的灵魂内容,浏览器会触发各种事件实现网页中的各种特效 **常见事件:** | 名称 | 说明 | | ----------- | -------------------------- | | onload | 一个页面或一幅图像完成加载 | | onlick | 鼠标单击某个对象 | | onmouseover | 鼠标指针移到某元素上方 | | onmouseout | 鼠标指针离开某元素上方 | | onkeydown | 某个键盘按键被按下 | | onchange | 域的内容被改变 | | onmousedown | 鼠标按钮被按下 | | onmouseup | 鼠标按键被松开 | --- <div class="tip inlineBlock info simple"> ▶️ 开始学习下一章: [JavaScript+Jquery+ES6入门到放弃之BOM操作](https://jbea.cn/archives/531.html) </div> 最后修改:2022 年 09 月 02 日 © 允许规范转载 赞 1 都滑到这里了,不点赞再走!?