1、let和const命令
+、let声明的变量只在let所在的代码块有效,即有块级作用域,不同于var;
+、let定义的变量不存在变量提升问题,必须先声明后使用;
+、不能在同一作用域内重复声明变量,这与ES5的var不同,故不能在函数作用域下直接重新声明函数的参数;
+、块级作用域可以任意嵌套,内存可以访问外层,外层不可以访问内层,内层可以重新定义外层的同名变量;
+、ES5严格模式规定函数必须在顶层作用域和函数内声明,其他情况(比如if代码块、循环代码块)的声明都会报错,但在ES6中可以;
+、const命令和let命令不同之处在于const用于定义常量,并在声明变量时必须同时赋值,如const PI=3.14,不能写成const PI;PI=3.14;
+、let和const命令声明的全局变量不属于全局对象的属性,var命令和function命令声明的全局变量,依旧是全局对象的属性。另外还有import和class命令,故在ES6中共有6种声明变量的方法。
2、变量的解构赋值
+、数组的解构赋值基本用法是[a,b,c]=[1,2,3],然后a=1,b=2,c=3;
+、解构分完全解构和不完全解构,不完全解构有两种情况:左边少于右边和右边少于
左边,右边少于左边时,没有对应值的变量赋值为undefined。解构要求等号右边是可
遍历的结构,如数组,非空对象等;
+、解构赋值可以指定默认值,如[a=1,b=2]=[],默认值生效的条件是右边对应位置上
的值是不是严格等于undefined,如[a=1,b=2]=[undefined,null],此时a=1,b=null;
+、以上是数组的解构赋值,对象也有解构赋值,基本用法为{foo,bar}={foo:’a’,bar:’b’};
+、数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,
变量必须与属性同名,才能取到正确的值,如果变量名与属性名不一致,必须写成下面
这样{foo:fo}={foo:’a’},此时就有fo=’a’ 也就是说,对象的解构赋值的内部机制,是先找
到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者;
f+、要将一个已经声明的变量用于解构赋值,必须非常小心。如var x;{x}={x=1};这种写法
会报错,因为JS引擎会将{x}理解成一个代码块从而引发语法错误,只有不将大括号写
在行首才可避免,正确的写法是({x}={x=1});
+、对象的解构赋值可以很方便的将现有对象的方法赋值到某个变量,如:
let {log,sin,cost}=Math;
+、字符串的解构赋值,const [a,b]=’he’,此时a=’h’,b=’e’,字符串被转换成了一个类似
数组的对象,类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值;
+、数值和布尔值的解构赋值,当等号右边是数值和布尔值则会先转化为对象,如:let
{toString:s}=123;s===Number.prototype.toString为true,解构赋值的规则是,只要等号右边的值不是对象,就先将其转为对象。由于undefined和null无法转为对象,所以对它
们进行解构赋值,都会报错;
+、函数参数也可解构赋值并可指定默认值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函数时,传入的对象会覆盖函数定义时指定的默认值,该默认值只在调用
函数时不传参数起作用;
+、圆括号问题。不能使用圆括号的情况:1 变量声明语句中不能带有圆括号,2 函数
参数中,模式不能带有圆括号,3 赋值语句中,不能将整个模式,或嵌套模式中的一层,
放在圆括号之中。可以使用圆括号的情况:赋值语句的非模式部分,[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确 [(parseInt.prop)] = [3]; // 正确
3、字符串的扩展
+、 JS允许用\uxxxx形式表示一个字符,这种表示只限于\u0000-\uFFFF之间的字符,超过该范围的字符必须用两个双字节形式表达,ES6对这一点做了改进,当超过该范围时,只要将码点放入大括号即可,如”\u{20BB7}”可输出”吉”;
+、 JS内部,字符以UTF-16格式储存,每个字符固定为2字节,对于大于0xFFFF的字符,JS会认为是两个字符,普通的charCodeAt方法和charAt方法无法正常工作,ES6提供了codePointAt方法和fromCodePoint方法以及at方法可解决这个问题,codePointAt函数返回码点的十进制形式;
+、 ES6给字符串添加了Iterator遍历器接口,使得字符串可以被for…of循环遍历,这个遍历器最大的优点是可以识别大于0xFFFF的码点;
+、 ES6提供了除indexOf()方法之外的三个方法:includes(),startsWith(),endsWith();
+、 ES6提供了repeat()方法,返回一个新字符串,表示将原字符串重复n次,参数是小数时会向下取整,不能为负或Infinity,是字符串时会先转为数字;
+、 ES7提供了padStart(),padEnd()方法,用于补全字符串长度,共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串;
+、 模板字符串,模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量,模板字符串中嵌入变量,需要将变量名写在${}之中,大括号内部可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性
4、正则表示式的扩展
+、 字符串对象共有四个方法可使用正则表达式:match(),replace(),search(),split(),ES6将这4个方法,在语言内部全部调用RegExp的实例方法,从而做到所有与正则相关的方法,全都定义在RegExp对象上;
+、 添加了u修饰符和y修饰符,ES5只有i,g,m;
+、 添加了sticky属性表示是否设置了y修饰符,增加flags属性返回修饰符,ES5的source属性是返回正则表达式的正文;
+、 ES5不支持后行断言和后行否定断言,ES7中加入了;
5、数值的扩展
+、 ES6提供了二进制和八进制数值的新写法,分别用前缀0b(0B)和0o(0O)表示,ES5的严格模式中八进制不再允许使用0表示,将0b和0o前缀的字符串数值转为十进制,要使用Number方法;
+、 提供了Number.isFinite()和Number.isNaN()两个方法,用来检查Infinite和NaN这两个特殊值,与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,非数值一律返回false;
+、 ES6将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变,Number.isInteger()用来判断一个值是否为整数。需要注意的是,在JavaScript内部,整数和浮点数是同样的储存方法,所以3和3.0被视为同一个值,ES6在Number对象上面,新增一个极小的常量Number.EPSILON,引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围;
+、 JavaScript能够准确表示的整数范围在-253到253之间(不含两个端点),超过这个范围,无法精确表示这个值,Number.isSafeInteger()则是用来判断一个整数是否落在这个范围之内;
+、 ES6在Math对象上新增了17个与数学相关的方法。所有这些方法都是静态方法,只能在Math对象上调用。
6、数组的扩展
+、 Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。值得提醒的是,扩展运算符(...)也可以将某些数据结构转为数组,扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。Array.from方法则是还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组;
+、 Array.of方法用于将一组值,转换为数组,Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3],Array.of(3).length // 1;
+、 数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置
(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组;
+、 数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调
函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined,findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1,fill方法使用给定值,填充一个数组;
+、 ES6提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返
回一个遍历器对象(详见《Iterator》一章),可以用for...of循环进行遍历,唯一的
区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历;
+、 Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与
字符串的includes方法类似。该方法属于ES7,但Babel转码器已经支持,没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值,indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相当运算符(===)进行判断,这会导致对NaN的误判;
+、 关于数组空位:ES6则是明确将空位转为undefined;
7、函数的扩展
+、 ES6允许为函数的参数设置默认值,即直接写在参数定义的后面,参数变量是默认声明的,所以不能用let或const再次声明;
+、 指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真,如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。(function (a = 0, b, c) {}).length // 0 (function (a, b = 1, c) {}).length // 1
+、 ES6引入rest参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。注意,rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错,函数的length属性,不包括rest参数;
+、 扩展运算符(spread)是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列,由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了;
+、 箭头函数,ES6允许使用“箭头”(=>)定义函数,如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分,如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回,由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。箭头函数有几个使用注意点。(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。(4)不可以使用yield命令,因此箭头函数不能用作Generator函数;
+、 尾调用优化
8、对象的扩展
+、 ES6允许直接写入变量和函数,作为对象的属性和方法var foo = 'bar';var baz =
{foo};baz // {foo: "bar"}// 等同于var baz = {foo: foo};
var o = {
method() {
return "Hello!";
}
};
// 等同于
var o = {
method: function() {
return "Hello!";
}
};
+、 ES6允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内let propKey = 'foo';let obj = { [propKey]: true, ['a' + 'bc']: 123};
+、 Object.is()用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。不同之处只有两个:一是+0不等于-0,二是NaN等于自身
+、 Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用,Object.assign可以用来处理数组,但是会把数组视为对象。用途:1为对象添加属性和方法2克隆对象3合并多个对象4为属性指定默认值
+、 可枚举性。ES5有三个操作会忽略enumerable为false的属性,for...in 循环:只遍历对象自身的和继承的可枚举的属性,Object.keys():返回对象自身的所有可枚举的属性的键名,JSON.stringify():只串行化对象自身的可枚举的属性,ES6新增了两个操作,Object.assign():只拷贝对象自身的可枚举的属性Reflect.enumerate():返回所有for...in循环会遍历的属性。操作中引入继承的属性会让问题复杂化,大多数时候,我们只关心对象自身的属性。所以,尽量不要用for...in循环,而用Object.keys()代替
+、 ES6新增结构Set和Map,Set类似于数组,但是成员的值都是唯一的,没有重复的值.Set函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化. Set结构的实例有以下属性。Set.prototype.constructor:构造函数,默认就是Set函数。Set.prototype.size:返回Set实例的成员总数。add(value):添加某个值,返回Set结构本身。delete(value):删除某个值,返回一个布尔值,表示删除是否成功。has(value):返回一个布尔值,表示该值是否为Set的成员。clear():清除所有成员,没有返回值。keys():返回一个键名的遍历器values():返回一个键值的遍历器entries():返回一个键值对的遍历器forEach():使用回调函数遍历每个成员。。Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键,可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
9、模块化
+、 ES6模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西。自动采用严格模式,不管你有没有在模块头部加上"use strict";
+、 模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能;
+、 export用法总结:1输出变量:export var m=1;或者var m=1;export {m} 2输出函数:export function f(){}或者function(){} export {f} 可以使用as来指定输出别名
+、 import用法总结:1、import命令接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块对外接口的名称相同。如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。注意,import命令具有提升效果,会提升到整个模块的头部,首先执行,2、如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起export { es6 as default } from './someModule';// 等同import { es6 } from './someModule';export default es6;上面代码中,export和import语句可以结合在一起,写成一行。但是从可读性考虑,不建议采用这种写法,而应该采用标准写法,3、除了指定加载某个输出值,还可以使用整体加载,即用星号()指定一个对象,所有输出值都加载在这个对象上面。这时一般会使用as将()重命名;
+、 export default命令:使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载,该命令可以让用户自由命名要加载的变量和函数。用法和export相似,只是第二种用法不能加大括号,且使用import导入默认值时也不能加大括号;
+、 模块的继承:先导入要继承的模块,再将其输出就OK,其中可以加一下自己的模块,export * from 'circle';
10、Class
+、 基本语法:class Point{ constructor(x,y){this.x=x;this.y=y} toString(){}} 必须有contructor函数,定义的方法是直接定义在原型上的。类的数据类型就是函数,类本身就指向构造函数。类的内部所有定义的方法,都是不可枚举的,这一点与ES5的行为不一致
+、 与函数一样,Class也可以使用表达式的形式定义。
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
上面代码使用表达式定义了一个类。需要注意的是,这个类的名字是MyClass而不是Me,Me只在Class的内部代码可用,指代当前类
+、 继承:extends。子类必须在contructor函数中调用super函数来创建this,super这个关键字,有两种用法,含义不同。
(1)作为函数调用时(即super(...args)),super代表父类的构造函数。
(2)作为对象调用时(即super.prop或super.method()),super代表父类。注意,此时super即可以引用父类实例的属性和方法,也可以引用父类的静态方法。
-
、 Class的静态方法,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
-
、 Class的静态属性和实例属性:类的实例属性可以用等式,写入类的定义之中,不用写在类的constructor方法里面,类的静态属性只要在上面的实例属性写法前面,加上static关键字就可以了
+、 new.target属性 new是从构造函数生成实例的命令。ES6为new命令引入了一个new.target属性,(在构造函数中)返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。
11、编程规范
+、 使用let代替var;
+、 静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号;
+、 使用数组成员对变量赋值时,优先使用解构赋值,函数的参数如果是对象的成员,优先使用解构赋值,如果函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于以后添加返回值,以及更改返回值的顺序。
+、 单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法,如果对象的属性名是动态的,可以在创造对象的时候,使用属性表达式定义。
+、 使用扩展运算符(...)拷贝数组,使用Array.from方法,将类似数组的对象转为数组
+、 那些需要使用函数表达式的场合,尽量用箭头函数代替。因为这样更简洁,而且绑定了this。简单的、单行的、不会复用的函数,建议采用箭头函数。如果函数体较为复杂,行数较多,还是应该采用传统的函数写法。所有配置项都应该集中在一个对象,放在最后一个参数,布尔值不可以直接作为参数
+、 不要在函数体内使用arguments变量,使用rest运算符(...)代替。因为rest运算符显式表明你想要获取参数,而且arguments是一个类似数组的对象,而rest运算符可以提供一个真正的数组,使用默认值语法设置函数参数的默认值
+、 总是用Class,取代需要prototype的操作,使用extends实现继承,因为这样更简单,不会有破坏instanceof运算的危险,使用import取代require,使用export取代module.exports,如果模块只有一个输出值,就使用export default,如果模块有多个输出值,就不使用export default,不要export default与普通的export同时使用
网友评论