005ES6基础知识

1. let和const

1.1 let命令

  • let声明的变量与var声明的方式一样,只是let声明的变量的作用范围仅限于变量声明的作用域内有效,在作用域外访问会产生错误。
  • 大家都知道在for循环中使用var声明循环变量因子会产生一些意想不到的结果,例如
let btns = document.querySelectorAll('.li');
for(var i = 0;i < btns.length;i++){
	btns[i].addEventListener('click',() => {
		console.log(i); // 输出的是btns.length,而并非是当前需要的i 
	});
}

而let声明的变量却不同,每次循环都是一个新的变量i,而上次i的计算值由JavaScript引擎内部记住。另外需要说明的是for循环中设置循环变量因子那部分是一个父作用域,而循环体是一个单独的子作用域。

  • var命令具有预解析机制,即将var声明的变量提升到当前所在的作用域最前面,但不会执行赋值。ES6中的let命令并没有被赋予预解析机制,官方给出的理由是,预解析变量提升弄混了程序的顺序性,是代码的逻辑产生的混乱。这也就意味着使用let声明变量,不能在声明语句前进行使用,会报错而非undefined。
  • 前面也提到了let命令声明的变量的使用范围仅限于变量声明的作用域内。另外若区块中存在let或const命令,在区块中提前使用会报错,也会屏蔽区块外的同名变量带来的影响。总之就是,使用let命令之前,该变量都是不可用的。这种现象叫暂时性死区,即声明前的那部分区块。有些死区比较隐蔽,例如
function fn (x = y,y = 2){
	console.log(x, y); // 此时会报错,因为默认值y,而y还未声明就使用
}
  • let不允许在相同的作用域内重复声明同名变量,无论是否存在均使用let命令。

1.2 块作用域

在前面也曾提到了JavaScript中的作用域,ES5中只规定了全局作用域和函数作用域,但这就会产生很多不合理的场景,所以ES6添加了块级作用域。
在ES6中,let和const命令命名的变量均属于块作用域,当然大括号{}内的代码均是属于块作用域,并且允许块作用域任意嵌套,内层块作用域允许定义外层作用域的同名变量。
块级作用域内允许声明函数,类似let,对当前块作用域之外没有影响,但是这样就改变了块级作用域内声明的函数处理规则,会对老代码产生影响,故允许浏览器不遵守,选择自己的方式,具体为支持ES6的浏览器需要遵守允许块级作用域内声明函数、函数声明类似于var即提升到全局作用域或函数作用域头部、同时函数声明还会提升到块作用域的头部,其他环境浏览器依旧将块级作用域内声明的函数当做let处理。为了谨慎起见,切记不要在块作用域内声明函数,即使非得到使用,也要使用函数表达式而非函数声明语句,且声明一定要在大括号内否则报错。

1.3 const命令

const命令声明的变量,只能被读取不能被更改,也就是说变量声明的同时需要赋值,一旦赋值,就不能再做更改;
声明时不赋值会报错,且同一个作用域内不允许被重复声明同名变量;
const声明的变量也没有变量提升功能,只能在声明后使用;
但是需要说明的是:const声明的变量不能被改变,本质上并不是变量的值不能改变,学过c++等语言的程序员大多都知道指针这个东西,const类似于指针的概念,只是不允许改变变量指向的那个内存地址值,而并不限制变量内存地址指向的内存地址值内的数据值是否被更改。这里需要再次说明的是,对于像数值、字符串或布尔类型的数据,变量内存地址中存储的就是这些具体的值;而对于符合类型的数据,主要针对是对象和函数类型,变量内存地址保存的只是数据的一个指针。
对象冻结Object.freeze方法,是可以冻结对象的,严格模式下对象声明时就必须一次性初始化完成。

1.4 顶层对象属性

顶层对象在浏览器中执行window,在node中指向global对象,而在ES5中顶层对象的属性和全局变量是等价的。但这就带来了几个很大问题,⾸先是没法在编译时就报出变量未声明的错误,只有运⾏时才能知道(因为全局变量可能是顶层对象的属性创造的,⽽属性的创造是动态的);其次,程序员很容易不知不觉地就创建了全局变量(⽐如打字出错);最后,顶层对象的属性是到处可以读写的,这⾮常不利于模块化编程。另⼀⽅⾯,window 对象有实体含义,指的是浏览器的窗⼝对象,顶层对象是⼀个有实体含义的对象,也是不合适的。
ES6,规定为了兼容,var和function声明的与之前不变,即依旧是顶层对象的属性,而使用let、const、class声明的全局变量,不属于顶层对象属性。
ES5中的顶层对象本身也在不同的环境中不同,很是麻烦,下边提供两个方法获取顶层对象。

// ⽅法⼀
(typeof window !== 'undefined'? window: 
(typeof process === 'object' && typeof require === 'function' &&typeof global === 'object') ? global: this);
// ⽅法⼆
var getGlobal = function () {
if (typeof self !== 'undefined') { return self; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
throw new Error('unable to locate global object');
};

2. 变量的解构

ES6中允许按照一定模式,从数组和对象中提取值,并对变量赋值,这从操作称之为解构。

2.1 数组解构

let [a,b,c] = [1,2,3]; // 两边对象的变量和值需要具有相同的方括号嵌套深度;
当然如果想忽略一些值,需要左边用逗号用于代替,let[,c] = [1,2,3];
或想将剩下的放在一个新的数组中,let[a,…arr] = [1,2,3];
结构失败变量的值就是undefined;
变量数量大于值数量会解构失败,值数量大于变量数量就会自动自动忽略多余的值;
解构中允许指定默认值,let[a =1] = [],此时不会结构失败,除了undefined其他都会覆盖默认值;

2.2 对象解构

let {foo,bar} = {foo:1,bar:2};
对象解构与数组解构不同之处在于,数组元素是按次序决定变量取值的,而对象没有次序,是按照key关键字和属性名一致决定的,也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。
与数组一样,皆有也可用于嵌套解构的对象;
对象的解构也可指定默认值,同样除了undefined均能覆盖;

2.3 字符串解构

对于字符串的解构类似于将字符串转换成了类似数组的对象。
let [a.b,c,d,e] = ‘hello’;
类似数组的对象还有length属性,因此还可对这个属性解构赋值,let[length:len] = ‘hello’;此时len就是5

2.4 数值和布尔值解构

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

2.5 函数参数解构

函数的参数也可以使⽤解构赋值。

2.6 圆括号问题

解构虽然简单,但是解析起来并不容易,一个式子到底是模式还是表达式,必须得等到解析到等号才能知道,当若模式中出现圆括号就会产生歧义,故不建议在模式中放置圆括号。
变量声明语句不能使用圆括号;函数参数也属于变量声明,不能带圆括号;赋值语句的模式不能带括号([a])= [5];
能使用圆括号的只有赋值非模式[(a)] =[5]。

2.7 解构用途

交换变量;从函数中返回多个值;函数参数定义;提取json数据;函数参数的默认值;遍历map结构;输入模块的指定方法;

3. 字符串扩展

// 字符串遍历器接口,ES6为字符串添加了遍历器接口,使得字符串可以被for...of循环遍历;
for(let char of 'bar'){
	console.log(char);
}

// ES5中字符串对象提供charAt方法,返回字符串给定位置的字符,此方法却不能识别大于0xFFFF字符;
// ES6提供的at()方法却可以
'abc'.at(0);

// 传统上只有indexOf方法可以确定一个字符串是否包含另一个字符串,
// ES6又提供了三个方法includes,startsWith,endsWith,第二个参数表示开始位置,前两者是从哪个位置开始,后一者是倒数第几位向前
'hello'.includes('l');// 返回布尔值,表示是否找到参数字符串
'hello'.startsWith('l');// 返回布尔值,表示参数字符串是否在原字符串头部
'hello'.endsWith('l'); // 返回布尔值,表示参数字符串是否在原字符串的尾部

// repeat方法将返回一个新字符串,表示将原字符串赋值几遍
'o'.repeat(3);//参数只能是有限正整数,0--1为0,NaN为0,字符串转成数字

// ES2017引入了字符串补全长度的功能,padStart()头部补全,padEnd()尾部补全,
//若字符串已大于或等于指定长度,返回原字符串,若补全字符串长度和现有字符串长度之和大于等于指定长度,截取补全字符串;
//补全字符串省略会用空格代替。常见用途有数值补全指定位数,提示字符串格式。
'o'.padStart(5,'a'); //aaaao
'o'.padEnd(5,'a'); // oaaaa

// 传统上字符串都是用单引号或双引号拼接字符串,
//而ES6提供了反引号,不仅可以使用${变量}添加动态数据,还可以跨行。
// 大括号内可放入任意js表达式,既可以运算也可引用对象属性,还能调用函数。
// 也可嵌套。
`
1
${bar}
`;

4. 数值扩展

4.1 基本方法扩展

Number.isFinite(num/Infinity);//返回布尔值,用来判断是否为有限数值,无限数值或非数字类型皆为false
Number.isNaN(num/NaN);//返回布尔值,用来判断NaN,只要括号内传入的值计算结果不为NaN皆是false
// 需要注意的是,ES5也提供了全局方法isFinite和isNaN,上边提到的这两个方法是ES6提供了,
//不同于ES5提供的方法,Number.isFinite方法对非数值一律返回false,Number.isNaN方法只有对NaN才会返回true,
//ES5中的这两个方法会先调用Number()方法将非数值转成数值,在进行判断。

Number.parseInt();Number.parseFloat();
// 这两个方法跟之前的parseInt()和parseFloat()一样,只是移植到了Number对象而已,甚至方法内存地址都一样。

Number.isInteger();//用来判断数值是否为整数,因js内部对整数和浮点数是同样的存储方法,故3和3.0被视为是同一个值

Number.EPSILON;// 表示是一个极小的常量,是1与大于1的最小浮点数之间的差值。

Number.isSafeInteger(); // 用来判断一个整数是否在精确范围内,-2^53 到2^53之间,即Number.MAX_SAFE_INTEGER到Number.MIN_SAFE_INTEGER

4.2 math对象新方法

// Math对象新增了17个与数学相关的静态方法
Math.trunc(); // 用于去除一个数的小数部分,针对非数值的数据先转成数据再处理,NaN/空值/字母字符串/undefined处理结果为NaN

Math.sign();// 用来判断数字为正值,负值或零,非数值先转成数字再判断,返回+1/-1/0/-0/NaN

Math.cbrt();// 用来计算数字的立方根,非数值先转成数值再计算

Math.clz32();// 用于返回整数的32位二进制表示

Math.imul();// 返回两个以32位带符号的整数相乘的结果

Math.fround();// 返回数值的单精度浮点形式
Math.hypot();// 返回所有参数数值的平方和的平方根
Math.expm1();Math.log1p();Math.log10();Math.log2(); // 对数运算
Math.sinh();Math.cosh();Math.tanh();Math.asinh();Math.acosh();Math.atanh(); // 用于三角函数运算
Math.singnbit();// 用来判断值的正负,只要是负数或-0都返回true
** 表示指数运算符,等价于 a ** =3 等价于 a = a*a*a;

5. 函数扩展

// 函数参数默认值,当函数调用是没有传入该参数时,默认使用默认值;
// 使用参数默认值时函数不能与同名参数,允许function(x,x,y){},不允许function(x,x,y=1){}
// 参数默认值并不是传值的,而是每次重新计算默认值的表达式的值;
// 默认值参数一般放置函数参数尾部,否则无法省略;函数参数的length并不包含默认值参数
function fn1(x = 1){}

// rest参数是用来获取函数调用时参数多余函数声明时函数参数个数,是一个数组;
// arguments是对象,伪数组,若要使用要么通过Array原型转成数组,要么let arr = [...arr];
// rest参数必须为函数声明参数列表的最后一个
function fn2(x,y,...rest){}
fn2(1,2,3,4);

// 从ES5开始,函数内部可以设定为严格模式,ES6在使用了默认参数,解构赋值或扩展运算符,就不能使用严格模式了

// 函数的name属性返回函数的函数名
foo.name;

// 箭头函数,箭头函数内部也可嵌套箭头函数
let f1 = v => {};
let f2 = v => v; // 返回v,大括号被解释为代码块,({a:1,b:2})返回对象。
let f3 = () => {};
// 箭头函数注意点:函数体内this指向定义时所在的对象,而非使用时所在的对象,指向固定;
// 不能作为构造函数,即不能说过你new命令;
// 不能使用arguments对象,此对象在箭头函数内不存在,可用rest参数代替
// 不能使用yield命令,即意味着不能用作Generator函数

// 双冒号运算符,左边是对象,右边是函数,用这这种方式绑定函数的this指向,取代了call/apply/bind显示绑定
foo::bar /*等价于*/bar.bind(foo);
foo::bar(...arguments)/*等价于*/bar.apply(foo,arguments);
// 左边为空,右边是一个对象的方法,表示将该方法绑定到该对象上;

  • 尾调用
    尾调用优化,就是函数的最后一步是调用另一个函数,尾调用不一定出现在函数尾部,只要是最后一步操作即可;
    函数的调用会在内存中形成一个调用记录,若A调用B,A上边还会形成B到的调用记录,只有在B调用结束后,结果返回A后,B的调用记录才会结束;而在尾调用中,B函数是A函数的最后一步,A上边不需要保存B的调用记录,直接用B的调用记录取代A的调用记录即可,从而达到了节省内存。
    ES6规定,所有ECMAScript的实现都必须部署尾调用优化,就不会出现栈溢出;
    需要注意的是尾调用优化只有在严格模式下才会生效;正常模式下arguments返回函数参数,caller返回当前函数的调用函数,因尾调用优化时,函数的调用栈会被改写,也就失去了这两个变量。
    正常模式下完成,完成尾调用优化,尾递归之所以会出现溢出,是因为调用栈过多,用循环替代递归就避免了溢出,使多层嵌套成了一层;

6. 数组扩展

// 扩展运算符,用于复制数组,合并数组,与解构赋值结合,字符串,将Iterator接口对象转成真正数组
...[1,2,3]; // 将一个数组转为用逗号隔开的参数序列

6.1 Array扩展方法

// Array.from()方法用于将类似数组的对象和可遍历的对象转成真正的数组。
// 在实际应用中,类似数组对象并不是凭空用对象造一个,而是DOM返回的nodeList集合,以及函数内部的arguments对象;
// 类似数组的对象本质特征只有一点,就是有length属性;
// 参数为真正数组时,会返回个一模一样的真正数组;
// 扩展运算符其实调用的是遍历器接口,但若对象没有部署这个结构,则无法使用扩展运算符完成转换;
Array.from(lis);

// Array.of()方法将一组值转成数组,当参数个数为1个时,实际是指数组的长度;
// 参数个数不少于两个时,才起到目的作用;无参时为空数组;
Array.of(1,2,3);

6.3 数组实例的扩展方法

// copyWithin方法,将指定位置的成员复制到其他位置,然后返回当前数组
[1,2,3,4,5].copyWithin(0,3); // 从三号位的开始到最后的元素复制到从0号位置上
[1,2,3,4,5].copyWithin(0,3,4); // 将三号位元素赋值到零号位上
...

// find方法找到第一个符合条件的数组成员,参数为一个回调函数,返回布尔值
[1,2,3].find((item,index,arr) => item > 2);
// findIndex方法与find类似,返回第一个符合条件的元素的下标索引
[1,2,3].findIndex((item,index,arr) => item > 2);

// fill用于使用定值填充一个数组
[a,a,a].fill(1); // 用1填充数组的每一位
[a,a,a].fill(1,2,3); // 用1填充数组的第2位置

for(let index of [1,2].keys()){} // 遍历数组的键
for(let val of [1,2].values()){} // 遍历数组的值
for(let vk of [1,2].entries()){} // 遍历数组的键值

[1,2].includes(1); // 判断数组中是否包含某个值,返回布尔值,indexOf方法不含有返回-1,且为严格相等判断

6.4 数组空位

数组空位,表示该位置的值为空,什么都没,不等价与undefined。
ES5中规定,对空位的处理大多是忽略空位,其中forEach/filter/every/some等方法会跳过空位,map方法会跳过但会保留这个值,join和toString会将空位当做undefined处理。
ES6规定空位为undefined。

7. 对象扩展

7.1 属性简洁表示法

// ES6允许直接将变量或函数作为对象的属性和方法
let a = 1;
let fun = () => {};
let obj = {a,fun};

// 属性名表达式
obj.foo = 1;/*等价于*/ obj['foo'] = 1;

// 属性简洁表示和属性名表达式不能同时使用
let a = 1;
let obj = {[a]}; // 错误
let obj = {[a]:1};  // 正确

// 属性名表达式如果是一个对象会自动将对象转成字符串[object object]

// 方法的name属性返回函数名
{method:()=>{}}.method.name; // method
{
	get getm:() => {},
	set setm:() => {}
}.getm.name/setm.name; // 会报错

7.2 Object对象新增方法

// Object.is()方法,弥补了ES5中的==运算符自动转换数据类型和===运算符NaN不等于自身和+0等于-0的情况,
// 此方法用来比较两个值是否严格相等
Object.is('foo','foo');
Object.is(+0,-0); // false
Object.is(NaN,NaN); // true

// Object.assign()用于对象合并,浅拷贝,若值为对象,拷贝到的是索引;同名属性会替换而非添加;数组看做对象;
// 只要一个参数直接返回该目标对象,不是对象的转成对象后再合并;undefined和null无法转对象会报错;
// 若非对象在非第一个参数位置也不会报错
// 常见应用场景:为对象添加属性,为对象添加方法;克隆对象,合并多个对象;为属性指定默认值;
Object.assign({a:1},{b:2}); // 第一个对象为目标对象,将后边的对象中的数据复制到目标对象中

// Object.setPrototypeOf()⽤来设置⼀个对象的 prototype 对象,返回参数对象本身
Object.setPrototypeOf(object, prototype);

// Object.getPrototypeOf()⽤于读取⼀个对象的原型对象。
Object.getPrototypeOf(obj);

7.3 属性的遍历

// for...in
for(let key in obj){}

// Object.keys(obj),返回一个数组,不含symbol属性
Object.keys(obj);

// Object.getOwnPropertyNames(obj),返回包含对象自身的所有属性的键名数组,不含symbol属性
Object.getOwnPropertyNames(obj);

// Object.getOwnPropertySymbols(obj),返回包含对象自身的所有symbol属性键名
 Object.getOwnPropertySymbols(obj);

// Reflect.ownKeys(obj)返回⼀个数组,包含对象⾃身的所有键名,
 Reflect.ownKeys(obj);

7.4 super关键字

this关键字总是指向函数所在的当前对象,ES6中又添加了super关键字,用于指向当前对象的原型对象,super关键字表示原型对象时,只能用于对象的方法中,用在其他地方都是错误的。

8.Next

异步同步问题,promise和async/await。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值