1、 let和const命令
es5只有全局作用域和函数作用域,没有块级作用域。
下面看一个例子:for(var i = 0;i < 10;i++){ //...}alert(i) //10;
对于有块级作用域的语言来说,for语句初始化变量的表达式所定义的的变量,只会存在于循环的环境中。然而对于JavaScript来说,由for语句创建的变量i,即使在for语句循环结束后,也依旧存在于外部的执行环境中,这就是所说的变量提升。
es6新增了let和const命令,用来声明变量和常量,let命令只在所在的代码块内有效。例子:{ let a = 10; var b = 1; } console.log(a) //ReferenceError:a is not defined. console.log(b) //1
在上面的代码块之中,分别用let和var声明了两个变量。在代码块之外调用这两个变量,
发现let声明的变量错误,因为let的作用域是在它所在的当前代码块,不会被提升到当前函数的最顶部。常见的面试题:
var a = [];for (var i = 0;i < 10;i++){ a[i] = function(){ console.log(i); }}a[6](); //10
上面的代码中,变量i是var声明的,在全局范围内都是有效的,所以全局只有一个变量i,每一次循环,变量i的值都会发生变化,被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时的输出的时最后一轮的i的值,也就是10.
如何使a[6]()输出的是6哪?//es5闭包解决循环绑定问题var a = [];for (var i = 0;i < 10;i++){ a[i] = (function(value){ return function(){ console.log(value) } })(i)}a[6](); //6//es6//使用let,声明的变量仅在块级作用域内有效var a = [];for (let i = 0;i < 10;i++){ a[i] = function(){ console.log(i) } }a[6](); //6 上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。因为JavaScript引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上运算。 另外,for循环的一个特别的地方,就是设置循环变量的部分是一个父作用域,而循环体内部是一个单独的子作用域。for(let i =0;i < 3;i++){ let i = 'abc'; console.log(i)}//abc//abc//abc 上面代码输出3次abc。这说明函数内部的变量i与循环变量i不在同一个作用域内,有各自单独的作用域。
const声明一个只读的常量。一旦声明常量的值就不能改变.
const一旦声明变量,就必须立即初始化,不能留到以后赋值。const PI = 3.1415;PI; //3.1415PI = 3; // TypeError: Assignment to constant variableconst foo;//SyntaxError: Missing initializer in const declaration
2、变量的解构赋值
什么是解构?
解构不是构造一个新的对象或者数组,而是逐个拆分现有的对象或数组、字符串,来提取需要的数据。es6中被解构的数据项位于赋值运算符=的右侧,可以是数组、对象、字符串。
2.1、数组的解构
数组解构:使用一个数组作为一个数据项,根据数组模式(只要等号两边的模式相同,左边的变量就会赋予对应的值)从这个数组里面提取提取一个或者多个变量赋值。
基本用法://把数组中所有的数值赋给一个个单独的变量let [a,b,c] = [1,2,3];a //1b //2c //3//第一个不提取,其余都提取let [,b,c] = ['li','yong','good'];b //'yong'c //'good'//中间第二个不提取,其余都提取let [a,,c] = ['li','yong','good'];a //'li'c //'good'//最后一个不提取,其余都提取(不完全解构)let [a,b] = ['li','yong','good']a //'li'b //'good'//第一个提取,其余的放到一个数组里,要用到rest操作符//rest操作符:用来获取多余的参数,将多余的参数放入数组中。let [head,...tail] = [1,2,3,4];head //1tail //[2,3,4]
默认值:
解构赋值允许指定默认值:let [foo = true] = [];foo //truelet [x,y = 'yong'] = ['li'];x //'li'y //'yong'let [x,y = 'yong'] = ['li',undefined];x //'li'y //'yong'let [x = 'li'] = [null]x //null如果一个数组成员是null,默认值不会生效。
以上代码中,undefined 和 null的区别:
undefined使用默认值;null是有值,但值是空。2.2、对象的解构
对象的解构与数组的解构不同,数组的元素是按次序排列的,变量的取值由它的位置决定;对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
基本用法://变量名和属性同名let {a,b} = {a:'li',b:'yong'}a //'li'b //'yong'//变量名与属性名不一致let {a:xxx} = {a:'li',b:'yong'}xxx //'li'对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。
默认值:
let {x,y = 5} = {x:1};x //1y //5let {x = 3} = {x: undefined};x //3let {x = 3} = {x:null};x //null如果将一个已经声明的变量用于解构赋值,必须小心//错误的写法let x;{x} = {x:1};// SyntaxError: Unexpected token 上面代码的写法会报错,因为JavaScript引擎会将{x}理解成一个代码块,从而发生语法错误。//正确的写法let x;({x} = {x:1});x //1
2.3 字符串的解构
字符串可以解构赋值。因为字符串被转换成一个类似数组的对象。
const [a,b] = 'yes';a //'y'b //'e'c //'s'
2.4 函数参数的解构
function move({x = 0,y = 0} = {}){ return [x, y];}move({x: 3, y:8}); // [3, 8]move({x: 3}); // [3, 0]move({}) // [0, 0]move() // [0, 0]上面代码中,函数move的参数是一个对象,通过对这个对象进行解构,得到变量x 和 y 的值。如果解构失败, x 和 y 等于认值。注意下面的写法:function move({x, y} = {x: 0, y: 0}){ return [x, y];}move({x: 3,y: 8}) //[3,8]move({x:3}) //[3, undefined]move({}) //[undefined,undefined]move() //[0, 0]上面的代码是为函数move的参数指定默认值,而不是为变量x 和 y 指定默认值,所以得到与前一种写法不同的结果。
写了那么多,来总结一下在工作中的常用的例子:
- 交换变量的值
let x = 1; let y = 2; [x, y] = [y, x]; x //2 y //1 上面的代码交换变量x 和 y 的值。
- 从函数返回多个值
函数返回多个值,只能将它们放在数组或对象里返回。 有了解构赋值,取出这些值就非常方便了。 //返回一个数组 function example(){ return [1,2,3]; } let [a, b, c] = example(); //返回一个对象 function example(0{ return { foo: 1, bar: 2 } } let { foo, bar } = example();
- 函数参数的定义
解构赋值可以将一组参数与变量名对应起来 //参数是一组有次序的值 function f([x,y,z]){....} f([1,2,3]) //参数是一组无次序的值 function f({x, y, z}){...} f({z: 3, y: 2, x: 1})
- 提取Json数据
let jsonData = { id : 42, status: 'ok', data : [77,909] }; let { id, status, data:number } = jsonData; id // 42 status // 'ok' number // [77,909]
- 输入模块的指定方法
加载模块时,指定输入哪些方法。 const { SourceMapConsumer, SourceNode } = require("source-map");
3、模板字符串
es6引入模板字符串(``),进行字符串的拼接,对于字符串中嵌入变量,使用${}。
var str = '这是字符串变量';$('#body').append(`liyong,${str},good`);es5字符串查找: 只有indexOf()方法,可以用来确定一个字符串是否包含里一个字符串。返回子字符串的位置(如果没有找到该子字符串,则返回-1) var d = 'this is a line'; d.indexOf('is') //2es6提供了一种查找子字符串的方法:includes() -includes(): 返回布尔值,表示是否找到了参数字符串。 var d = 'this is a line'; d.includes('is') //true
4、数组的知识
4.1数组的扩展运算符
//将一个数组转化为用逗号分割的参数序列 console.log([1,...[2,3,4],5]) //[1,2,3,4,5]
4.2、复制数组
数组是复合的数据类型,直接复制的话,只是复制的指向底层数据结构的指针,而不是克隆了一个新的数组。
es5中数组的复制; const a1 = [1,2]; const a2 = a1; a2[0] = 2; a1; //[2,2] 上面代码,a2不是a1的克隆,而是指向同一个数据的另一个指针。修改a2,会直接导致a1的变化。 另外一种es5的方法: const a1 = [1,2]; const a2 = a1.concat(); a2[0] = 2; a1; //[1,2] 上面代码中,a1会返回原数组的克隆,再修改a2就不会对a1产生影响。 es6扩展运算符的复制数组的写法: const a1 = [1,2]; const a2 = [...a1]; a2[0] = 2; a1; //[1,2]
4.3、合并数组
数组合并的写法:
//es5 const b1 = [3,4,5] [1,2].concat(b1) //[1,2,3,4,5] //es6 [1,2, ...b1] //[1,2,3,4,5]
4.4、Array.from()
将一组类似数组的对象(一组Dom节点)转为真正的数组
let arrayLike = { '0':'li', '1':'yong', length:2 } //es5的写法 let arr = [].slice.call(arrayLike); //['li','yong'] let arr = Array.prototype.slice.call(arrayLike);//['li','yong'] //es6的写法 let arr = Array.from(arrayLike); //['li','yong']
4.5、Array.of()
用于将一组值,转换为数组。
Array.of(3,11,8) //[3,11,8]
4.6、数组实例的find()和findIndex()
数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有的数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined.
[1,4,-5,10].find((n) => n < 0) //-5 以上代码找出数组中第一个小于0的成员
4.7、数组实例的includes()
与字符串的includes方法类似,Array.prototype.includes方法返回一个布尔值。
[1,2,3].includes(2); //true [1,2,3].includes(4); //false
4.8、数组实例的entries(),keys(),values()
keys()是对键名的遍历、values()是对键值的遍历、entries()是对键值对的遍历
let arr = ['li','yong']; for(let [index,value] of arr.entries()){ console.log(`索引 ${index} : 键值 ${value}`)}//索引 0 : 键值 li//索引 1 : 键值 yong
4.9、数组的遍历方法
- forEach:会自动省略为空的数组元素
let arr = ['li','yong','good','']; arr.forEach((value,index)=>console.log(`索引${index} : 键值${value}`))//索引0 : 键值li// 索引1 : 键值yong// 索引2 : 键值good
- filter:创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有的元素。 filter()不会对空数组进行检测 filter()不会改变原始数组
let arr = ['li','yong','good']; arr.filter(x=>{if(x == 'li'){return console.log(`筛选的是:${x}`)}})
4.10、数组转换字符串
- join()方法
let arr = ['li','yong','good']; console.log(arr.join('|')) //li|yong|good
- toString()方法
let arr = ['li','yong','good']; console.log(arr.toString()) //li,yong,good
5、对象的扩展
5.1、对象的属性和方法的简写
const name = 'li'; const age = 00; const o = {name, age,method(){ console.log(this.name) }} o.method() //'li'
5.2、Object.is()
es5 比较两个值是否相等,相等运算符(==)和严格相等运算符(===)。前者的不足是,会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。
es6 提出"Same-value-equality"(同值相等)。Object.is就是比较两个值是否严格相等。Object.is(true,1) //false Object.is(true,true) //true Object.is(+0,-0) //false Object.is(NaN,NaN) //true
5.3、Object.assign()
Object.assign方法用于对象的 合并,将源对象(source)的所有的可枚举属性,复制到目标对象(target)。
const target = {a: 1}; const source1 = {b: 2}; const source2 = {c: 3}; Object.assign(target, source1, source2) target //{a:1, b:2, c:3}
5.4、Object.assign()
- 为对象添加属性 const bbbb = {name:'li'}; Object.assign(bbbb,{age:18,height:00}) bbbb; //{ // age:18 // height:0 // name:"li" // }
-
为对象添加方法
直接将两个函数放在大括号中,再使用assign方法添加到目标对象中。 const bbbb = {name:'li'}; Object.assign(bbbb,{getName(){ return this.name; }, getAge(){ return this.age; }
})
5.5、Object.keys(),Object.values(),Object.entries(),
返回一个数组
Object.entries的用途是遍历对象的属性。const obj = {name:'li',age:00 }; for( let [key,value] of Object.entries(obj) ){ console.log(`属性${JSON.stringify(key)}:属性值${value}`) } //属性"name":属性值li //属性"age":属性值00