## 三、const和let
**3.1 const**
const用来定义常量,所谓的常量就是值一旦给定后就不变,一次定义终身不变的量
const a = 10; a = 20;
上面的a就是一个常量,若给a重新赋值,你会发现报错了 注意:
const通常会定义两种东西:
l 定义函数
l 定义一些特殊的字符串和常数
常量名一般都是全大写,如果由多个单词组成,用下划线隔开,暗示这个是常量
**3.2 let**
let用来定义块级作用域的变量,只要有“{}”包裹,就表示块级作用域。
*注意:不是JSON,if语句体、函数体、while、for是块,for循环的()也是块
注意:我们通常会在for使用let,此时循环中自动加了闭包*:
var arr = [];
for(let i = 0; i < 10;i++){
arr.push(function(){
console.log(i);
});
}
arr[4](); //4
let和for是绝配,let平时没啥用。
var声明的变量,在for循环之外可以访问,导致i变量到最终值的时候,arr[4]()虽然是闭包访问,但是同一个i,所以i就是一个统一的值。
而let相当于每一次都有不同的i,都是新的i,但是i是可以访问上一次的自增或自减的值。
**总结:
**l const用来定义常量,通常定义函数和常数
l let用来定义{}块级的作用域的变量,通常用来定义for循环的变量
常量是块级作用域,很像使用let,语句定义的变量,常量的值不能通过重新赋值来改变,并且不能重新声明。****
## **四、变量的解构赋值**
**4.1数组解构**
数组可以解构,当等号右侧是数组的时候,等于左侧可以将变量装入[]中接收,一一对应接收。
var [a,b,c] = [1,2,3];
console.log(a); //1
console.log(b); //2
console.log(c); //3
上面代码表示,可以从数组中提取值,按照对应位置,给变量赋值。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
如果数组较为复杂,此时左侧结构的那些变量,也要有相同的结构:
var [a,b,[c,d]] = [1,2,[3,4]];
console.log(a); //1
console.log(b); //2
console.log(c); //3
console.log(d); //4
如果结构不同呢?
var [a,b,c] = [1,2,[3,4]];
console.log(a); //1
console.log(b); //2
console.log(c); //[3,4]
另外一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组,这种情况解构依然可以成功。
var [a, [b], d] = [1, [2, 3], 4];
console.log(a); // 1
console.log(b); // 2
console.log(d); // 4
如果解构不成功,变量的值等于undefined
**4.2对象解构**
对象的结构和数组有个不同点,数组的元素是按顺序排列的,数组的取值由他的位置决定。
而对象的属性(键名)没有顺序,但是变量必须与属性同名,才能取到正确的值。
var {name,id,sex} = {"id":1001,"name":"张三","sex":"男"};
console.log(id); //1001
console.log(name); //张三
console.log(sex); //男
上面的例子,等号左边的变量顺序与等号右边3个同名属性的顺序不一致,但是对取值完全没影响
如果变量没有对应的同名属性,导致取不到值,所以是undefined。
如果变量名和属性名不一致,必须写成下面这样:
var {foo: baz} = {foo : 'aaa', bar : 'bbb'}
console.log(baz); //aaa
var {first :f ,last :l} = {first :'hello', last :'world'}
console.log(f); //hello
console.log(l) //world
这实际上说明,对象的解构赋值是上面形式的简写
上面代码中,foo是匹配模式,baz才是变量,真正被赋值的是变量baz,而不是模式foo。
也就说,对象的解构的内部机制,是先找到同名属性,然后再赋值给对应的变量,真正被赋值的是后者,而不是前者。
**4.3默认值**
结构赋值允许指定默认值
var [a = 1] = []
var [b = 1] = [88]
console.log(a);//1
console.log(b);//88
var [c = 1] = [undefined]
var [d = 1] = [null]
console.log(c);//1
console.log(d);//null
注意,ES6内部使用严格相等运算符(===),判断一个位置是否有值,所以,只有当一个数组成员严格等于undefined,默认值才会生效
如果一个数组值是null,默认值不会生效,因为null不严格等于undefined。
**4.4扩展运算符(spread operator)**
扩展运算符(spread)是三个点(...)。将一个数组转为用逗号分隔的参数序列,还能强制展开一个对象,通常用于对象的赋值,使用灵活广泛。
l 第一个作用:称为“展开运算符”,作用和字母意思用于,就是把东西展开,可以用在数组和对象上。
var obj1 = {
"a" :100,
"b" :200,
"c" :300,
}
var obj2 = {
...obj1,
"d" :888
}
console.log(obj1)
console.log(obj2)
console.log(obj1 === obj2);
数组也可以强制展开,通常数组的赋值,比如有两个数组合成一个数组:
**4.5剩余操作符(rest operator)**
l 第二、三个作用:叫“剩余操作符”是解构的一种,意思是把剩余的参数放到一个数组中赋值给它。一般针对数组或对象。
var [a,b,...c] = [1,2,3,4,5,6];
console.log(a);//1
console.log(b);//2
console.log(c);//[3,4,5,6]
注意“...”只能出现最后一个参数,并且通过这个例子发现...能将零散的值收纳为数组。
逻辑上,“...”是一个运算符。“...”能将数组打散为零散值。
l 补充个知识点,在ES6中当一个对象的key和value一致时,可以省略value。
**l 应用场景2:**
以后的函数大概率都是接收一个JSON当参数,并且用ES6解构语法写形参变量。
调用函数的时候传的参数,一般是k:v,省略v
var name = "小明";
var height = 170;
var weight = 100;
function mm({height,name,weight}){
console.log("姓名是:" + name)
console.log("身高是:" + height)
console.log("体重是:" + weight)
};
// mm({name:name,"height":height,"weight":weight})
mm({name,height,weight})
红色部分的语句是在创建JSON,绿色的部分是在进行结构。
调用函数时参数顺序打乱也不影响结构,因为解构,会自动匹配key。
()
**l 展开运算符和剩余操作运算符的区别**
关于展开运算符与剩余操作符的直之间的区别,简单的说,在某种程度上,剩余操作符和展开运算符相反,展开运算符会“展开”数组变成多个元素,剩余操作符会收集多个元素和“压缩”成一个单一的元素。
**4.6对象解构的翻译问题**
注意:babel会将“[]”和“{}”解构变为一个个的var。
但是babel不能翻译“对象解构”,只能翻译数组解构。
原因:object-rest-spread 还处于stage阶段(属于实验性属性)。
解决方法:安装babel插件transform-rest-spread,放到.babelrc文件配置上,即可使用。
{
"presets":["es2015","es2016"],
"plugins":["transform-object-rest-spread"]
}
.babelrc的presets是用来定义预设,plugins定义插件。
安装依赖:
cnpm install babel-plugin-transform-object-rest-spread
**
## 五、数组方法
**
ES6中对数组新增了四大“金刚”函数:forEach()、map()、filter()、reduce(),都是一些语法糖。forEach()是es5语法
**5.1 forEach()遍历数组**
forEach()方法用来循环遍历数组,方法中的function回调函数接收3个参数
第1个是遍历的数组内容(item);第2个是对应的数组索引(index),第3个是数组本身(array)。
var arr = ["白板","幺鸡","红中","发财","三饼"];
arr.forEach(function(item,index,array){
console.log(item,index,array)
});
注意:forEach()没有return返回值。
**5.2 map()映射**
map方法的作用不难理解,“映射”也就是原数组被“映射”成对应的新数组。
var arr = [10,20,30,40,50,99];
var newArr = arr.map(function(item,index,array){
return item * 2; //返回一个新的结果,给变量接收,原数组不变
});
map函数的本质是依次遍历原数组的每一项,将每一项都执行一遍函数中的语句,返回一个新的数组。
注意:
l 函数需要有return值,如果没有,数组所有项都被映射成undefined。
l map返回的数组一定和原数组的长度一样。
l
在实际使用时,可以利用map()方便获取对象数组中的特定属性值们
**5.3 filter()过滤**
filter为“过滤、筛选”之意,指原数组中filter某些项后,返回过滤后的新数组,用法和map相似。
比如想从原数组中,挑选所有的偶数,返回新的数组。
var arr = [312,55,77,11,13,15,18,26,30,40,50,99];
var newArr = arr.filter(function(item,index,array){
return item % 2 == 0;
});
console.log(arr)
console.log(newArr)
描述:arr中的每一项会依次的执行函数,filter的callback函数需要返回布尔值true或false。true则将值放到新数组中,false无情地将你抛弃…
l filter和map相同点:都会遍历数组的每一项
l filter和map不同点:map返回数组不会少项,filter可能少项。
**5.4 reduce()迭代**
arr.reduce(callback[,initialValue])
第一个参数的callback回调函数有四个参数,第二个为设定的初始值(可选)。
callback函数有四个参数:
previous :上一次叠加的结果值或初始值
current : 当前会参与叠加的项
index :当前值的下标
array :原数组本身
求数组的最大值【经典面试】:
var arr = [43,5,4,6,4567,78,8,69,568];
// var max = Math.max.apply(null,arr);
// console.log(max)
var max = arr.reduce(function(prev,cur){
console.log(prev) //43、43、43、43、4567、4567、4567...
return prev > cur ? prev : cur;
});
console.log(max); //4567
reduce的机理:从下标1的项开始遍历,每次return的值将作为遍历的下一项的prev的值,这一次的遍历是cur,prev有种累加的感觉。
reduce可以设置初始参数(参数自定义),当reduce有第二个参数时,此时reduce遍历将从第0项开始遍历,而不是第1项开始。
**5.5数组用途-写纯函数**
上面的四大“金刚”map、filter、reduce特别有用,做“函数式”编程。
什么是纯函数?
纯函数是指不依赖于且不改变它作用域之外的变量状态的函数。
l 这个函数内部不改变传入的参数。
l 传入这个函数一定有参数,一定会返回某一个确定的值。
l 函数中的语句,不能有随机性,比如Math.random()、new Date(),传入的参数相同,返回的值必定相同;
l 这个函数里面不能有任何异步语句,比如$.get()、fs.readFile()、setInterval()
**l【例子1】请写一个纯函数addItem(),接收arr、n当做参数,能够将arr的尾部增加n。**
var arr = [99,77,66];
function addItem(arr,n){
return [...arr,n];
}
var arr2 = addItem(arr,88); //返回一个新数组
console.log(arr); //原数组不变
console.log(arr2);
**【例子4】请写一个纯函数changeItem,接受arr、n、a当做参数,能够将arr第下标为n的那项改变值为a。**
var arr = ["白板","幺鸡","二条","三万"];
function changeItem(arr,n,a){
return arr.map((item,index)=> index == n ? a : item )
}
var arr2 = changeItem(arr,2,"九条");
console.log(arr);
console.log(arr2);
**l 纯函数的条件:**
l 一个函数的返回结果只依赖于它的参数
l 不依赖外部状态
l 执行过程中没有副作用
**什么叫函数执行过程没有副作用?**
一个函数执行过程中对外部产生了编号,那么就说这个函数是有副作用的。
l 更改指定id的name属性
const changeName = function(arr,id,name){
return arr.map(function(item){
if(item.id == id){
return {...item,name}
}
return item;
});
}
var jieguo = changeName(arr,2,"小兰")
console.log(jieguo);
顺口溜:删filter、改map、增map或...
**总结
为什么要煞费苦心地构建纯函数?因为纯函数非常“靠谱”,执行一个纯函数你不用担心它会干什么坏事,它不会产生不可预料的行为,也不会对外部产生影响。不管何时何地,你给它什么它就会乖乖地吐出什么。如果你的应用程序大多数函数都是由纯函数组成,那么你的程序测试、调试起来会非常方便。
l 使用纯函数的好处
最主要的好处是没有副作用。纯函数不会修改作用域之外的状态,做到这一点,代码就变得足够简单和清晰:当你调用一个纯函数,你只要关注它的返回值,而不用担心因为别处的问题导致错误。
纯函数是健壮的,改变执行次序不会对系统造成影响,因此纯函数的操作可以并行执行。
纯函数非常容易进行单元测试,因为不需要考虑上下文环境,只需要考虑输入和输出。
函数是接受一些输入,并产生一些输出的过程。这些输入称为参数,输出称为返回值。
纯函数的返回值只由它调用时的参数决定,它的执行不依赖于系统的状态(比如:何时、何处调用它)。**