let const
1.不能重复声明变量
//不能在函数内重新声明参数
function f3 (a1){
let a1;
}
// ** 不报错 **
function f4 (a2){
{
let a2
}
}
2.const 声明时,必须赋值;否则报错
变量的解构赋值
1.在ES6中,直接从数组和对象中取值,按照对应位置,赋值给变量的操作。
如果解构不成功,变量的值就等于 undefined 。
let [a, b, ..c] = [1];
console.log(a, b, c); // 1, undefined, [] 使用剩余符...为空数组
let [a] = []; // a => undefined
let [a, b] = [1]; // a => 1 , b => undefined
**两边模式不同,报错。**如下
let [a] = 1;
let [a] = false;
let [a] = NaN;
let [a] = undefined;
let [a] = null;
let [a] = {};
指定解构的默认值: 右边模式对应的值,必须严格等于 undefined ,默认值才能生效,而 null 不严格等于 undefined 。
let [a = 1] = [undefined]; // a => 1
let [a = 1] = [null]; // a => null
let {a=1} = {a:undefined}; // a => 1
let {a=1} = {a:null}; // a => null
2.对象解构赋值,若 变量名 和 属性名 不一致,则需要修改名称。
let {a:b} = {a:1, c:2};
// error: a is not defined
// b => 1
let {a:b=3} = {a:4}; // b = >4
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
上面代码中, a 是匹配的模式, b 才是变量。真正被赋值的是变量 b ,而不是模式 a 。
3.解构赋值的规则是, 只要等号右边的值不是对象或数组,就先将其转为对象 。由于 undefined 和 null 无法转为对象 ,所以对它们进行解构赋值,都会报错。
// 数值和布尔值的包装对象都有toString属性
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
4.函数参数的解构赋值
function fun ({a=0, b=0} = {}){
return [a, b];
}
fun ({a:1, b:2}); // [1, 2]
fun ({a:1}); // [1, 0]
fun ({}); // [0, 0]
fun (); // [0, 0]
function fun ({a, b} = {a:0, b:0}){
return [a, b];
}
fun ({a:1, b:2}); // [1, 2]
fun ({a:1}); // [1, undefined]
fun ({}); // [undefined, undefined]
fun (); // [0, 0]
字符串的拓展
1.includes(),startsWith(),endsWith()
includes('xxx',n) :返回 布尔值 ,表示 是否找到参数字符串 。
startsWith('xxx',n) :返回 布尔值 ,表示参数字符串是否在原字符串的 头部 。
endsWith('xxx',n) :返回 布尔值 ,表示参数字符串是否在原字符串的 尾部 。
endsWith 是针对前 n 个字符,而其他两个是针对从第 n 个位置直到结束。
2.repeat():repeat 方法返回一个新字符串,表示将原字符串重复 n 次。
- 参数为 小数 ,则取整
- 参数为 负数 或 Infinity ,则报错
- 参数为 0到-1的小数 或 NaN ,则取0 例如:'ab'.repeat(-0.5); //''
- 参数为 字符串 ,则转成 数字
数值的拓展
1.Number.isFinite(), Number.isNaN()
Number.isFinite() 用于检查一个数值是否是有限的,即不是 Infinity ,若参数不是 Number 类型,则一律返回 false 。
Number.isNaN() 用于检查是否是 NaN ,若参数不是 NaN ,则一律返回 false 。
与传统全局的 isFinite() 和 isNaN() 方法的区别,传统的这两个方法,是先将参数转换成 数值 ,再判断。 而ES6新增的这两个方法则只对 数值 有效, Number.isFinite() 对于 非数值 一律返回 false , Number.isNaN() 只有对于 NaN 才返回 true ,其他一律返回 false 。
isFinite(25); // true
isFinite("25"); // true
Number.isFinite(25); // true
Number.isFinite("25"); // false
isNaN(NaN); // true
isNaN("NaN"); // true
Number.isNaN(NaN); // true
Number.isNaN("NaN"); // false
2.Number.parseInt(), Number.parseFloat() 这两个方法与全局方法 parseInt() 和 parseFloat() 一致,目的是逐步 减少全局性的方法 ,让 语言更模块化 。
3.Number.isInteger():用来判断一个数值是否是整数,若参数不是数值,则返回 false
Number.isInteger(10); // true
Number.isInteger(10.0); // true
Number.isInteger(10.1); // false
Math对象的拓展
1.Math.trunc:用来去除小数的小数部分, 返回整数部分 。
- 若参数为 非数值 ,则 先转为数值 。
- 若参数为 空值 或 无法截取整数的值 ,则返回 NaN
2.Math.sign():判断一个数是 正数 、 负数 还 是零 ,对于非数值,会先转成 数值 。
- 参数为正数, 返回 +1
- 参数为负数, 返回 -1
- 参数为0, 返回 0
- 参数为-0, 返回 -0
- 参数为其他值, 返回 NaN
函数的拓展
1.使用参数默认值时,参数名不能相同:
function f (a, a, b){ ... }; // 不报错
function f (a, a, b = 1){ ... }; // 报错
2.尾参数定义默认值:
function f (a=1,b){
return [a, b];
}
f(); // [1, undefined]
f(2); // [2, undefined]
f(,2); // 报错
f(undefined, 2); // [1, 2]
function f (a, b=1, c){
return [a, b, c];
}
f(); // [undefined, 1, undefined]
f(1); // [1,1,undefined]
f(1, ,2); // 报错
f(1,undefined,2); // [1,1,2]
在给参数传递默认值时,传入 undefined 会触发默认值,传入 null 不会触发。
3.rest 参数
函数的 length 属性不包含 rest 参数。
函数的length属性:length 属性将返回,没有指定默认值的参数数量,并且rest参数不计入 length 属性。
function f1 (a){...};
function f2 (a=1){...};
function f3 (a, b=2){...};
function f4 (...a){...};
function f5 (a,b,...c){...};
f1.length; // 1
f2.length; // 0
f3.length; // 1
f4.length; // 0
f5.length; // 2
rest 参数只能放在最后一个,否则报错:
拓展运算符的运用
1.复制数组(深拷贝)
let a1 = [1, 2];
let a2 = [...a1];
// let [...a2] = a1; // 作用相同
a2[0] = 3;
console.log(a1,a2); // [1,2] [3,2]
2.合并数组(浅拷贝)
3.与解构赋值结合
与解构赋值结合生成数组,但是使用拓展运算符需要放到参数最后一个,否则报错。
数组的扩展
1.flat(),flatMap()
flat() 用于将数组一维化,返回一个新数组,不影响原数组。
默认一次只一维化一层数组,若需多层,则传入一个整数参数指定层数。
若要一维化所有层的数组,则传入 Infinity 作为参数。
[1, 2, [2,3]].flat(); // [1,2,2,3]
[1,2,[3,[4,[5,6]]]].flat(3); // [1,2,3,4,5,6]
[1,2,[3,[4,[5,6]]]].flat('Infinity'); // [1,2,3,4,5,6]
flatMap() 是将原数组每个对象先执行一个函数,在对返回值组成的数组执行 flat() 方法,返回一个新数组,不改变原数组。
flatMap() 只能展开一层。
[2, 3, 4].flatMap((x) => [x, x * 2]);
// [2, 4, 3, 6, 4, 8]
对象的扩展
1.属性名表达式????搞不懂是啥意思???有人给解释下吗???
let a = 'hi leo';
let b = {
[a]: true,
['a'+'bc']: 123,
['my' + 'fun'] (){
return 'hi';
}
};
// b.a => undefined ; b.abc => 123 ; b.myfun() => 'hi'
// b[a] => 'hi leo' ; b['abc'] => 123 ; b['myfun'] => 'hi'
2.Object.is() 用于比较两个值是否严格相等,在ES5时候只要使用 相等运算符 ( == )和 严格相等运算符 ( === )就可以做比较,但是它们都有缺点,前者会 自动转换数据类型 ,后者的 NaN 不等于自身,以及 +0 等于 -0
Object.is('a','a'); // true
Object.is({}, {}); // false
// ES5
+0 === -0 ; // true
NaN === NaN; // false
// ES6
Object.is(+0,-0); // false
Object.is(NaN,NaN); // true
3.Object.assign() 实现的是浅拷贝。 由于 undefined 或 NaN 无法转成对象,所以做为参数会报错。
Object.assign(undefined) // 报错
Object.assign(NaN); // 报错
Set
1.数组去重:
// 方法1
[...new Set([1,2,3,4,4,4])]; // [1,2,3,4]
// 方法2
Array.from(new Set([1,2,3,4,4,4])); // [1,2,3,4]
2.由于 Set 结构 没有键名只有键值 ,所以 keys() 和 values() 是返回结果相同。
map
1.传入数组作为参数, 指定键值对的数组 。
let a = new Map([
['name','leo'],
['age',18]
])
2.同样的值 的两个实例,在 Map 结构中被视为两个键。
let a = new Map();
let a1 = ['aaa'];
let a2 = ['aaa'];
a.set(a1,111).set(a2,222);
a.get(a1); // 111
a.get(a2); // 222
promise
1.调用resolve或reject不会结束Promise参数函数的执行,除了return:
new Promise((resolve, reject){
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
})
// 2
// 1
new Promise((resolve, reject){
return resolve(1);
console.log(2);
})
// 1
for of
1.普通对象不能直接使用 for...of 会报错,要部署Iterator才能使用。
let a = {a:'aa',b:'bb',c:'cc'};
for (let k in a){console.log(k)}; // a b c
for (let k of a){console>log(k)}; // TypeError
类
1.ES6的 类 的所有方法都是定义在 prototype 属性上,调用类的实例的方法,其实就是调用原型上的方法。
class P {
constructor(){ ... }
toString(){ ... }
toNumber(){ ... }
}
// 等同于
P.prototyoe = {
constructor(){ ... },
toString(){ ... },
toNumber(){ ... }
}
let a = new P();
a.constructor === P.prototype.constructor; // true
2.与 ES5 一样,实例的属性除非显式定义在其本身(即定义在 this 对象上),否则都是定义在原型上(即定义在 class 上)。
class P {
constructor(x, y){
this.x = x;
this.y = y;
}
toString(){
return '(' + this.x + ', ' + this.y + ')';
}
}
var point = new Point(2, 3);
point.toString() // (2, 3)
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
// toString是原型对象的属性(因为定义在Point类上)
3.类内部方法的 this 默认指向类的实例,但单独使用该方法可能报错,因为 this 指向的问题。
class P{
leoDo(thing = 'any'){
this.print(`Leo do ${thing}`)
}
print(text){
console.log(text);
}
}
let a = new P();
let { leoDo } = a;
leoDo(); // TypeError: Cannot read property 'print' of undefined
// 问题出在 单独使用leoDo时,this指向调用的环境,
// 但是leoDo中的this是指向P类的实例,所以报错
解决方法:
1.在类里面绑定 this
class P {
constructor(){
this.name = this.name.bind(this);
}
}
2.使用箭头函数
class P{
constructor(){
this.name = (name = 'leo' )=>{
this.print(`my name is ${name}`)
}
}
}
4.静态方法包含 this 关键字,则 this 指向类,而不是实例。
class P {
static f1 (){
this.f2();
}
static f2 (){
console.log('aaa');
}
f2(){
console.log('bbb');
}
}
P.f1(); // 'aaa'
5.静态方法可以被子类继承,或者 super 对象中调用
class P{
static f1(){ return 'leo' };
}
class Q extends P { ... };
Q.f1(); // 'leo'
class R extends P {
static f2(){
return super.f1() + ',too';
}
}
R.f2(); // 'leo , too'
6.子类必须在 constructor() 调用 super() 否则报错 ,并且只有 super 方法才能调用父类实例,还有就是, 父类的静态方法,子类也可以继承到 。
7.super关键字
-
1.当函数调用,代表父类的构造函数,但必须执行一次。
class P {... }; class R extends P { constructor(){ super(); } } -
当对象调用,指向原型对象,在静态方法中指向父类。
class P { f (){ return 2 }; } class R extends P { constructor (){ super(); console.log(super.f()); // 2 } } let a = new R()
模块
1.**export 暴露的必须是接口,不能是值。 **
// 错误
export 1; // 报错
let a = 1;
export a; // 报错
// 正确
export let a = 1; // 正确
let a = 1;
export {a}; // 正确
let a = 1;
export { a as b}; // 正确
// 错误
function f(){...};
export f;
// 正确
export function f () {...};
function f(){...};
export {f};
2.import 不能直接修改输入变量的值,因为输入变量只读只是个接口,但是如果是个对象,可以修改它的属性。
// 错误
import {a} from './f.js';
a = {}; // 报错
// 正确
a.foo = 'leo'; // 不报错
3.当一个模块暴露多个方法和变量,引用时可以用 * 整体加载。但是,不允许运行时改变:
import * as obj from '/a.js';
// 不允许
obj.a = 'leo';
obj.b = function(){...};
4.export defualt 其实是输出一个名字叫 default 的变量,所以后面不能跟变量赋值语句。export default 命令的本质是将后面的值,赋给 default 变量,所以可以直接将一个值写在 export default 之后。
// 正确
export let a= 1;
let a = 1;
export defualt a;
// 错误
export default let a = 1;
// 正确
export detault 1;
##es7 1.Array.prototype.includes()方法
includes 相比 indexOf 更准确, includes 认为两个 NaN 相等,而 indexOf 不会。
let a = [1, NaN, 3];
a.indexOf(NaN); // -1
a.includes(NaN); // true
另外在判断 +0 与 -0 时, includes 和 indexOf 的返回相同。
[1, +0, 3, 4].includes(-0); // true
[1, +0, 3, 4].indexOf(-0); // 1
2.async await
- await 命令放在 try...catch 代码块中,防止Promise返回 rejected 。
- 若多个 await 后面的异步操作不存在继发关系,最好让他们同时执行。
- await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。
3.Object.values 如果参数不是对象,则返回空数组:
Object.values(10); // []
Object.values(true); // []
##es9 1.拓展运算符的解构赋值,不能复制继承自原型对象的属性。
let o1 = { a: 1 };
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3; // { b: 2 }
o3.a; // undefined
若参数是 null 或 undefined 则忽略且不报错。
let a = { ...null, ...undefined }; // 不报错
若有取值函数 get 则会执行
// 不会打印 因为f属性只是定义 而不没执行
let a = {
...a1,
get f(){console.log(1)}
}
// 会打印 因为f执行了
let a = {
...a1,
...{
get f(){console.log(1)}
}
}
补充
1.ES5/6对数组空位的处理
数组的空位不是 undefined ,而是没有任何值,数组的 undefined 也是有值。
0 in [undefined,undefined,undefined] // true
0 in [,,,] // false
prop in object: 如果指定的属性在指定的对象或其原型链中,则in 运算符返回true。
-
prop:一个字符串类型或者 symbol 类型的属性名或者数组索引(非symbol类型将会强制转为字符串)。
-
objectName:检查它(或其原型链)是否包含具有指定名称的属性的对象
ES5对空位的处理:
- forEach() , filter() , reduce() , every() 和 some() 都会跳过空位。
- map() 会跳过空位,但会保留这个值。
- join() 和 toString() 会将空位视为 undefined ,而 undefined 和 null 会被处理成空字符串
[,'a'].forEach((x,i) => console.log(i)); // 1
['a',,'b'].filter(x => true); // ['a','b']
[,'a'].every(x => x==='a'); // true
[1,,2].reduce((x,y) => x+y); // 3
[,'a'].some(x => x !== 'a'); // false
[,'a'].map(x => 1); // [,1]
[,'a',undefined,null].join('#'); // "#a##"
[,'a',undefined,null].toString(); // ",a,,"
ES6对空位的处理: 将空位视为正常值,转成 undefined 。
Array.from(['a',,'b']);// [ "a", undefined, "b" ]
[...['a',,'b']]; // [ "a", undefined, "b" ]
//copyWithin() 会连空位一起拷贝。
[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]
//fill()会将空位视为正常的数组位置。
new Array(3).fill('a') // ["a","a","a"]
//for...of循环也会遍历空位。
let arr = [, ,];
for (let i of arr) {
console.log(1);
} // 1 1
entries() 、 keys() 、 values() 、 find() 和 findIndex() 会将空位处理成 undefined 。
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]
[...[,'a'].keys()] // [0,1]
[...[,'a'].values()] // [undefined,"a"]
[,'a'].find(x => true) // undefined
[,'a'].findIndex(x => true) // 0