目录
- 提升作用域:全局定义,局部赋值(强烈不推荐使用,存在变量不安全的风险)
- 闭包解决局部变量生命周期:局部变量绑定闭包函数,闭包函数存在,局部变量存在
- ES6语法简单处理变量污染:块级作用于不存在全局变量的重命名。
- 对象属性解决变量污染:对象思想,利用属性的局部性和临时性保存数据。
一、函数
1、创建函数
ES5
function 函数名 (参数列表) { 函数体; } var 函数名 = function (参数列表) { 函数体; }
ES6
let 函数名 = (参数列表) => { 函数体; }
匿名函数
(function (参数列表) { 函数体; }) // 匿名函数需要自调用 (function (参数列表) { 函数体; })(参数列表);
2、函数的调用
函数名(参数列表)
function myFunction() { var x=5; return x; } myFunction()
3、函数的参数
个数不需要统一(多舍,少undefined)
function fn (a, b, c) { console.log(a, b, c); // 100 undefined undefined } fn(100); function fn (a) { console.log(a) // 100 } fn(100, 200, 300) // 200,300被丢弃
可以任意位置具有默认值
function fn (a, b=20, c, d=40) { console.log(a, b, c, d); // 100 200 300 40 } fn(100, 200, 300);
通过 ... 接收多个值
function fn (a, ...b) { console.log(a, b); // 100 [200 300] } fn(100, 200, 300); // ...变量必须出现在参数列表最后
4、返回值
function fn () { return 返回值; } // 1.可以空return操作,用来结束函数 // 2.返回值可以为任意js类型数据 // 3.函数最多只能拥有一个返回值 若:return data,a;则只返回后者a
5、函数回调
在一个函数(fn)中,满足条件情况下,调用另外一个函数(func)作为参数
注:func函数 是 fn函数 的参数(函数作为函数参数传入)// 回调的函数 function callback(data) {} // 逻辑函数 function func(callback) { // 函数回调 if (callback) callback(data); } // 函数回调的本质:在一个函数中(调用函数),当满足一定条件,调用参数函数(回调函数) // 回调函数作为调用函数的参数传入 // 回调函数通过参数将调用还是内部数据传出 <script type="text/javascript"> // 解决问题: // 请求数据 => 数据(return | 函数回调) => 外界 function a_fn(data) { console.log('a_fn'); // 如果要使用数据,那么定义形参接收参数 console.log(data); } function b_fn(a_fn) { var data = "b_fn 的 数据"; console.log('b_fn'); // 条件: a_fn有值(有函数实现体) if (a_fn) { // 调用参数函数 a_fn(data); } } b_fn(a_fn); // 1.一个函数(函数名)作为另外一个函数的参数 // 2.满足一定的条件,调用参数函数 // 3.形成了函数回调,回调的函数可以获取调用函数的局部变量(将数据携带出去) </script>
- 传输数据 普通传输 and 回调传输
<!-- 普通函数实现数据的传输 --> <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>数据请求</title> </head> <body> </body> <!-- 外部要接收数据 --> <!-- 函数callback接受数据传输 --> <script type="text/javascript"> var callback = function (data,a) { // 使用数据 console.log(data); console.log(a); } </script> <!-- 模拟请求数据 --> <script> var a_fn = function(){ console.log("开始请求数据..."); // ... var data = [1, 2, 3, 4, 5]; a = 123 console.log("数据请求完毕!!!"); return data,a; } // 模拟接受数据数据 callback(a_fn()) </script> </html>
<!-- 回调函数模拟数据传输 --> <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>数据请求</title> </head> <body> </body> <!-- 外部要接收数据 --> <!-- 1.外部给内部提供回调函数(函数名callback) --> <!-- 2.内部将数据反馈给外部回调函数.外部就可以使用内部数据 --> <script type="text/javascript"> var callback = function (data,a) { // 使用数据 console.log(data); console.log(a); } </script> <!-- 请求数据 --> <script type="text/javascript"> try { // 采用匿名函数自调用请求数据,不存在返回值 (function (callback) { console.log("开始请求数据..."); // ... var data = [1, 2, 3, 4, 5]; a = 123 console.log("数据请求完毕!!!"); // 如果回调函数存在,那么回调对应的函数,并将数据携带出去 if (callback) { callback(data,a); } })(callback) // 请求数据完毕之后,需要让外界获取请求的数据: // 1.函数返回值: 函数的自调用,不存在其他调用者,也就无法获取函数的执行结果 // 2.函数回调 } catch (err) { } </script> <!-- 外部要接收数据 --> </html>
数据传输方式总结:
- 普通函数进行传值,使用return返回,并且只能返回一个值
- 回调函数传值,使用匿名函数,可以直接传输多个值
- 钩子函数:系统中默认存在回调
<script type="text/javascript"> // 通常情况下,我们的页面标签存不存在点击事件? => 存在 // 点击事件触发后,可不可以对外传送数据 => 可以,eg:点在页面的什么位置 // 系统已经书写好了这种函数回调,但是没有回调体,回调体(回调函数)由普通开发者提供 // 钩子:满足条件情况下被系统回调的函数(方法),称之为钩子函数(方法) <=> 回调函数 document.onclick = function (a, b , c) { console.log("点击事件"); console.log(a, b , c); } </script>
- 钩子函数获取网页点击坐标数据等
document.onclick = function (event) { console.log("点击事件"); console.log(event); console.log(event.clientX); console.log(event.clientY); }
6、闭包:函数的嵌套
闭包: 函数的嵌套定义,内层函数就是闭包
闭包产生的原因:
- 需求:外部使用,函数局部作用域内的参数, -- 解决方法:返回值 | 函数回调 | 闭包 | 提升作用域
- 需求:外部,另一个函数中使用该函数体内的局部变量 -- 解决方法: 函数回调 | 闭包
- 需求:不能使用函数回调(函数已有固定参数,或不能拥有参数),只能将函数定义到拥有局部变量函数的内部 => 闭包
- 闭包总结:内部函数要使用,外部函数的局部变量
闭包函数的优点:
- 外部函数不需要强制拥有参数以及返回值
- 外部函数的局部变量也无需提升作用域,可以保证参数的安全性
- 内部函数不需要强制拥有参数与返回值,便可以直接使用外部函数的局部变量
- 局部变量的持久化(提升局部变量的生命周期)
- 变量污染(页面标签循环绑定)
function outer() { var data = {} function inner() { return data; } return inner; } // 闭包目的:不允许提升变量作用域时,该函数的局部变量需要被其他函数使用 // 闭包本质:函数的嵌套,内层函数称之为闭包 // 闭包的解决案例:①影响局部变量的生命周期,持久化局部变量;②解决变量污染 function a_fn() { var data = [1, 2, 3, 4, 5]; // 闭包 function b_fn() {console.log('>>>', data);} b_fn();} a_fn();
- 提升作用域:全局定义,局部赋值(强烈不推荐使用,存在变量不安全的风险)
// 提升作用域 var a; // 作用域被提升 function fn() { a = 10; // 局部变量 => 在外部定义 } fn(); // 外部使用局部变量: 返回值 | 函数回调 | 闭包 | 提升作用域 console.log(a);
- 闭包解决局部变量生命周期:局部变量绑定闭包函数,闭包函数存在,局部变量存在
生命周期:就是一个变量在程序执行过程中的“有效期”,比如说全局变量,那它在整个程序执行过程中都有效,及它的生命周期是整个程序执行过程,而对于一些在函数里定义的局部变量,它只是在调用函数是有效,函数调用结束,它的生命周期也完了。
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>局部变量生命周期</title> </head> <body> 局部变量生命周期 </body> <script type="text/javascript"> function outer() { // 方法执行完毕后,数据data就会被销毁 var data = [1, 2, 3, 4, 5]; console.log(data); // 若通过闭包函数绑定局部变量,则可保证局部变量不被销毁 function inner() { return data; } // 数据被inner操作返回,inner属于outer,属于需要outer将inner返回出去(跟外界建立起联系) return inner; } // 将局部变量生命周期提升于inner函数相同,inner存在(即inner未被调用),局部变量data就一直存在 var inner = outer(); //执行inner 生命周期结束 console.log(inner()); </script> </html>
二、循环绑定
循环绑定产生的变量污染问题的解决方法:
- ES5下闭包解决(优先推荐)
- ES6的块级作用域:let定义循环内变量
- 对象属性解决
- 闭包解决变量污染
变量污染:全局变量的重命名覆盖问题,例:a=1; a=2则后者覆盖前者造成变量污染问题
循环绑定造成的变量污染问题(本质是var定义的全局变量循环,赋值):
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>闭包解决变量污染</title> <style type="text/css"> ul { margin: 0; padding: 0; list-style: none; } li { width: 80px; height: 35px; background-color: pink; border-radius: 5px; float: left; margin-left: 3px; } </style> </head> <body> <ul> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> </body> <script type="text/javascript"> // 需求:点击li,打印li在ul中的索引 => 0, 1, 2, 3, 4 var lis = document.querySelectorAll('ul li'); for (var i = 0; i < lis.length; i++) { lis[i].onclick = function () { alert(i); } } </script> </html>
解决方法(闭包):本质是将var定义的全局变量i,变成闭包内的局部变量进行存储、
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>闭包解决变量污染</title> <style type="text/css"> ul { margin: 0; padding: 0; list-style: none; } li { width: 80px; height: 35px; background-color: pink; border-radius: 5px; float: left; margin-left: 3px; } </style> </head> <body> <ul> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> </body> <script type="text/javascript"> // 需求:点击li,打印li在ul中的索引 => 0, 1, 2, 3, 4 // 1.lis var lis = document.querySelectorAll('ul li'); // 2.循环绑定 for (var i = 0; i < lis.length; i++) { // 解决的原理:一共产生了5个外层函数,存储的形参i的值分别是0, 1, 2, 3, 4 // 内层函数也产生了5个,且和外层函数一一对应,打印的i就是外层函数的形参i // 外层函数 (function (i) { // 内层函数:闭包 lis[i].onclick = function () { alert(i) } })(i) } console.log(i); </script> </html>
- ES6语法简单处理变量污染:块级作用于不存在全局变量的重命名。
!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>块级作用域解决循环绑定</title> <style type="text/css"> ul { margin: 0; padding: 0; list-style: none; } li { width: 80px; height: 35px; background-color: pink; border-radius: 5px; float: left; margin-left: 3px; } </style> </head> <body> <ul> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> </body> <script type="text/javascript"> // ES6语法中支持块级作用域 let lis = document.querySelectorAll('li'); for (let i = 0; i < lis.length; i++) { lis[i].onclick = function () { alert(i) } } </script> </html>
- 对象属性解决变量污染:对象思想,利用属性的局部性和临时性保存数据。
<script type="text/javascript"> var lis = document.querySelectorAll('li'); for (var i = 0; i < lis.length; i++) { lis[i].index = i; lis[i].onclick = function () { // var temp = lis[i].index; // lis[i]中的i一样存在变量污染 alert(this.index) // 当前被点击的li } } </script>