本文以后的文章主要是介绍ES6+语法。
目录
1.作用域
1.1 局部作用域
1.1.1 函数作用域
1.1.2 块作用域
1.2 全局作用域
1.3 作用域链
1.4 垃圾回收机制GC
1.4.1 内存生命周期
1.4.2 注意
1.4.3 内存泄漏
1.5 闭包
1.5.1 概念
1.5.2 闭包的作用
1.5.3 闭包应用
1.6 变量提升
2. 函数进阶
2.1 函数提升
2.2 函数参数
2.2.1 动态参数
2.2.2 剩余参数
2.2.3 补充展开运算符
2.3 箭头函数(重要)
2.3.1 各种语法
2.3.2 对象的箭头函数语法
2.3.3 箭头函数的参数
2.3.4 箭头函数的 this
3. 解构赋值
3.1 数组解构
3.1.1 基本语法
3.1.2 交换变量
3.1.3 必须要加分号情况
3.1.4 单元值数量与变量数量不匹配
① 单元值少 变量多
② 变量少 单元值多
③ 防止undefined传递
④ 按需导入
3.1.5 多维数组解构
3.2 对象解构
3.2.1 基本语法
3.2.2 数组对象的解构
3.2.3 多级对象解构
① 对象对象嵌套
② 数组对象嵌套
③ 操作案例(后更)
3.2.4 遍历数组forEach
3.3 渲染商品案例(后更)
1.作用域
规定了变量能够被访问的范围
1.1 局部作用域
1.1.1 函数作用域
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
函数的参数也是函数内部的局部变量。
不同函数内部声明的变量无法互相访问。
函数执行完毕后,函数内部的变量实际被清空了。
1.1.2 块作用域
在JS中使用 { } 包裹的代码块内声明的变量外部将(有可能)无法被访问。
注意:
1. let 声明的变量会产生块作用域,var不会有块作用域
2. const 声明的会产生块作用域
3. 不同代码块之间的函数无法互相使用
<script>function fn(){//块作用域}// for(let i=0; i<3; i++){ // //块作用域// console.log(i);// }// console.log(i); // 报错for(var i=0; i<3; i++){ //块作用域console.log(i);}console.log(i); // 3// var没有块作用域</script>
1.2 全局作用域
<script>标签 和 .js 文件的 最外层 就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。全局作用域中声明的变量,任何其他作用域都可以被访问。
注意:
1. 为window对象动态添加的属性默认也是全局的,不推荐!
2. 函数中未使用任何关键字声明的变量为全局变量,不推荐!
3. 尽可能少的使用全局变量,防止全局变量被污染。
1.3 作用域链
例如:
a最终的输出结果是多少?
就近原则
输出 a = 2
作用域链 本质上 是底层的变量查找机制
在函数被执行时优先查找当前函数的作用域,如果当前作用域查找不到则会依次逐级查找父级作用域 (类型冒泡),直到全局作用域。
嵌套关系的作用域串联起来形成了作用域链。
但是父级作用域 无法 向下查找子作用域。
1.4 垃圾回收机制GC
JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。
1.4.1 内存生命周期
1. 内存分配
当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
2. 内存使用
即读写内存:使用变量函数等
3. 内存回收
使用完毕,由垃圾回收机制自动回收不再使用的内存
1.4.2 注意
1. 全局变量一般不会被回收 (关闭页面会被回收)
2. 一般情况下的局部变量的值不用了就会被自动回收
1.4.3 内存泄漏
程序中分配的内存由于某种原因未释放或无法释放的内存泄漏
这里就不再介绍回收的算法了,可自行了解。
1.5 闭包
1.5.1 概念
一个函数对周围状态的引用捆绑在一起,内层函数中访问到其他外层函数的作用域
闭包 = 内层函数 + 外层函数的变量
且内层函数用到了外层的变量
function outer() {let a = 1;function f() {console.log(a);}f();
}
outer();
如上述的举例
f() 用到了外层的变量
const a和 f() 加在一起 称为闭包
1.5.2 闭包的作用
封闭数据,提供操作,外部也可以访问函数内部的变量
基本格式:
// 常见闭包 外部可以访问函数内部变量function outer(){let a = 10;function fn(){console.log(a);}// fn();return fn; // outer是接收者}// outer() ---- fn ---- function fn() {}// const fun = outer()// const fun = function fn() {}const fun = outer();fun(); // 外部函数访问函数内部变量
1.5.3 闭包应用
实现数据的私有
比如,我们要做个统计函数调用次数,函数调用一次就++
<script>// 统计函数调用次数// 普通形式let i = 0;function fn(){i++;console.log(`函数被调用了${i}次`); // 风险:i是全局变量 容易被修改}</script>
// 闭包形式 实现数据私有// 这里的i不会被垃圾回收 因为被反复调用 产生内存泄漏function count(){let i = 0;function fn(){i++;console.log(`函数被调用了${i}次`);}return fn;}const fun = count();
1.6 变量提升
JS的缺陷 与var有关
在作用域执行之前,所有var声明的变量提升到当前作用域的最前面。
但是,只提升的是“声明”,不提升“赋值”
let const 没有变量提升
<script>// 所有var声明的变量提升到 当前作用域 的最前面// 只提升的是“声明”,不提升“赋值”// 相当于只提升了 num// var num console.log(num + '件'); //undefined件var num = 10;</script>
2. 函数进阶
2.1 函数提升
示例:
<script>fn(); // 可以输出 成功提升// 会把函数声明提升到当前作用域的最前面// 只提升函数声明 不提示函数调用function fn(){console.log('函数提升'); }</script>
但是有一种情况不能:
// 函数表达式fun(); // 报错// 只提升声明// 不提升赋值 这里相当于对fun赋值var fun = function(){console.log('函数表达式');}// 函数表达式必须先声明和赋值 后调用
2.2 函数参数
2.2.1 动态参数
例如 产品需求:写一个求和函数
不管传来几个参数,都要把和求出来
arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参。
<script>function getSum(){// 动态参数 arguments没有形参 只存在于函数里面console.log(arguments);// 是个伪数组let sum = 0;for(let i=0; i<arguments.length; i++){sum += arguments[i];}console.log(sum);}getSum(2,3);</script>
2.2.2 剩余参数
允许将一个不定数量的变量传入函数并存入数组
<script>// ... 剩余参数// 要求至少传来X个参数时使用// 例如至少传来两个 剩下的存入arr数组// 剩余参数灵活 且 是真数组 可以使用数组的方法pop push等等function getSum(a,b,...arr){// arr 或 其他随便写 可以得到 传来的参数 [2,3]console.log(arr);}getSum(2,3); // 空数组getSum(1,2,3,4); // [3,4]</script>
2.2.3 补充展开运算符
展开运算符(...)能将一个数组进行展开
注意:不会修改原数组
应用:求数组最值、合并数组等
<script>const arr = [1,2,3];// console.log(...arr); // 展开数组// 求最值console.log(Math.max(...arr)) // 3console.log(Math.min(...arr)); // 1// 合并数组const arr2 = [3,4,5];// 合并arr arr2const arr1 = [...arr,...arr2];console.log(arr1);</script>
2.3 箭头函数(重要)
要求能写出箭头函数的不同写法
引入箭头函数的目的是为了写更简短的代码并且不用绑定 this,箭头函数的语法比函数表达式更简洁。
箭头函数更适用于那些本来需要匿名函数的地方。
2.3.1 各种语法
<script>// function fn() {// console.log(123);// }// 箭头函数写法// 主要是函数表达式形式// () 代表参数 => 代表functionconst fn = () => {console.log(123);}fn();// 带参数const fn1 = (x) => {console.log(x);}fn1(1);// 如果只有一个参数 小括号可以省略const fn2 = x => {console.log(x);}fn2(2);// 只有一行代码 可以省略大括号const fn3 = x => console.log(x);fn3(3);// 只有一行时需要返回值时 可以省略 {} 和 returnconst fn4 = x => x + x;console.log(fn4(4));</script>
比如,阻止表单的默认提交
const form = document.querySelector('form')
form.addEventListener('click', ev => ev.preventDefault());
2.3.2 对象的箭头函数语法
// 箭头函数可以直接返回一个对象 需要()包着const fn5 = (uname) => ({uname:uname}) // 实参传给形参 第一个uname是属性名 uname是属性值console.log(fn5('刘德华'));
2.3.3 箭头函数的参数
普通函数有 arguments 动态函数
箭头函数没有arguments 动态函数
但是有剩余参数 ...args
<script>// 1.利用箭头函数求和const getSum = (...arr) => {let sum = 0;for(let i=0; i<arr.length; i++){sum += arr[i];}return sum;}// 箭头函数一定要在声明后调用const result = getSum(2,3);console.log(result);</script>
2.3.4 箭头函数的 this
箭头函数之前的普通函数是根据它是被如何调用的来定义这个函数的this值,很麻烦。
<script>console.log(this); // window// 普通函数function fn(){console.log(this); // window 指向的是函数的调用者}fn(); // window.fn();// 声明一个对象// 对象方法里的thisconst obj = {name: 'zzz',sayHi: function(){console.log(this); // obj调用sayHi // 指向的是obj}}obj.sayHi();</script>
箭头函数不会创建自己的this,它只会沿用自己作用域链的上一层的this
// 箭头函数的thisconst fn1 = () => {console.log(this); // window 是这个作用域的上一级// 箭头函数本身没有this 只能指向上一层作用域的this}fn1(); // 对象方法的箭头函数的 thisconst obj1 = {uname : 'bbb',ff: () =>{console.log(this); // window // 本作用域ff没有this 上一层obj1的作用域是window// 因为调用obj1的是window 指向的是调用上一级的 即window}}obj1.ff();// 普通与箭头嵌套的函数const obj2 = {uname: 'nnn',bb:function(){console.log(this); // 普通函数obj2let i = 10;// 普通函数里再写一个箭头函数const count = ()=>{console.log(this);}count(); // 指向obj2}}obj2.bb();
注意:
在DOM事件的回调函数不太推荐箭头函数。
3. 解构赋值
需要知道解构的语法与分类,使用解构简洁语法快速为变量赋值
3.1 数组解构
将数组的单元值快速批量赋值给一系列变量的简洁语法
3.1.1 基本语法
1. = 左侧的 [ ] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
2. 变量的顺序对应数组单元值的位置依次进行赋值操作
<script>const arr = [100,60,80];// 数组解构 批量复制const [max,min,avg] = arr;// max = arr[0] min = arr[1] avg = arr[2]console.log(max);console.log(min);console.log(avg);</script>
将最大值和最小值函数返回值 解构为 max min
<script>function getValue(){return [60,80];}const [min,max] = getValue();console.log(min,max);</script>
3.1.2 交换变量
// 交换变量let a = 1;let b = 2; // 这里必须加分号[b,a] = [a,b];console.log(a,b); // 2,1
3.1.3 必须要加分号情况
js前面加分号
1. 立即执行函数
( function t() { })();
// 或者
;( function t() { })()
2. 数组解构
;[b,a] = [a,b]
<script>// const arr = [1,2,3];const str = 'zzz';// map遍历 // 前面必须有分号[1,2,3].map(function(item){console.log(item);})</script>
3.1.4 单元值数量与变量数量不匹配
① 单元值少 变量多
// 单元值少,变量多const [a,b,c,d] = [1,2,3];console.log(a);console.log(b);console.log(c);console.log(d); // undefined
② 变量少 单元值多
// 单元值多,变量少const [x,y] = [1,2,3];console.log(x); //1console.log(y); //2
可以用剩余参数解决:
// 单元值多,变量少 剩余参数解决const [x,y,...m] = [1,2,3];console.log(x); //1console.log(y); //2console.log(m); //真数组 [3]
③ 防止undefined传递
给一个初始默认值即可
// 单元值少,变量多 给定所有的变量 初始默认值const [a=0,b=0,c=0,d=0] = [1,2,3];console.log(a);console.log(b);console.log(c);console.log(d); // 0
④ 按需导入
按需导入 忽略某些返回值的传入
// 按需导入const [a=0,,c=0] = [1,2,3];console.log(a); // 1console.log(c); // 3
3.1.5 多维数组解构
// 多维数组解构// 普通const arr = [1,2,[3,4]];console.log(arr[0]);console.log(arr[1]);console.log(arr[2]); // [3,4]console.log(arr[2][0]); // 3 // 解构const [a,b,[c,d]] = [1,2,[3,4]];console.log(a); //1console.log(b); //2console.log(c); //3console.log(d);
3.2 对象解构
对象解构是将对象的属性和方法快速批量赋值给一系列变量的简洁语法。
3.2.1 基本语法
1. = 左侧的{ } 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
2. 对象属性的值将被赋值给与属性名相同的变量
3. 注意解构的变量名不要和外面的变量名冲突,否则报错
4. 对象中找不到与变量名一致的属性时变量值为undefined
<script>const obj = {uname: 'zzz',age:18}// 一般 obj.uname obj.ageconst {uname,age} = obj// 变量名 和 属性名 必须相同// 但是外面不能有 uname 和 age一样的变量名// 等同于 const uname = obj.unameconsole.log(uname);console.log(age);</script>
但是当外面有和属性名一样的变量名时
<script>const uname = 'bbb';const obj = {uname: 'zzz',age:18}// 外面的变量名和属性名一样// 旧名: 新名const {uname:username,age} = obj;console.log(username);</script>
3.2.2 数组对象的解构
const pig = [{uname:'佩奇',age:6}
]
// 解构数组对象const pig = [{uname: '佩奇',age: 6}]// [{ }]const [{uname,age}] = pig;console.log(uname);console.log(age);
3.2.3 多级对象解构
① 对象对象嵌套
// 多级对象解构// 对象对象时const pig = {name:'佩奇',family:{mother:'猪妈妈',father:'猪爸爸',brother:'乔治'},age:6}// 多级嵌套时 用 : 打开内部的对象const {name,family:{mother,father,brother},age} = pig;console.log(name);console.log(mother);console.log(father);console.log(brother);console.log(age);
② 数组对象嵌套
// 多级对象解构// 数组对象const person = [{name:'佩奇',family:{mother:'猪妈妈',father:'猪爸爸',brother:'乔治'},age:6}]const [{name,family:{mother,father,brother},age}] = person;console.log(name);console.log(mother);console.log(father);console.log(brother);console.log(age);
③ 操作案例(后更)
后更
3.2.4 遍历数组forEach
forEach调用数组的每个元素,并将元素传递给回调函数。
它不会返回数组,只遍历值。
与map的区别在于是否能返回数组
它适合遍历数组对象
语法
被遍历的数组.forEach(function (当前数组元素,当前元素索引号){// 函数体// 当前数组元素必须写 索引号可以不写
})
<script>const arr = ['red','green','pink'];// function()回调函数arr.forEach(function(item,index){console.log(item); // red green pinkconsole.log(index); // 0 1 2})</script>
3.3 渲染商品案例(后更)
后更
本文主要介绍ES6中的作用域+箭头函数+解构赋值的相关知识点,相关综合案例后续编辑。