let const 和var 的区别
var的使用:
1)使用var存在无意义的变量提升
2)如果某个变量的值不希望被修改,无法进行设置
3)可以重复进行声明操作
4)全局变量会自动成为顶级对象的属性
let的使用:
1)无法重复声明
2)没有变量的提升
3)全局变量不会成为顶级对象的属性
4)会形成一个块级作用域,使用let声明的变量只能在块级作用域内有效
5)使用let声明的变量会形成一个暂时性死区(也就是说这个块级作用域内有了let定义变量,
这个变量就跟这个作用域块形成了一个绑定,不会再受外界的影响)
const的使用:
1)通过const声明的是常量,不是变量
2)一旦变量被声明,他的值就不能被修改(内存中的地址不能被修改)
3)同样不存在变量的声明提升,同样存在暂时性死区,同样不能重复声明
注意:
ES5 只有两种声明变量的方法:var命令和function命令。
ES6 除了添加let和const命令,还添加了另外两种声明变量的方法:import命令和class命令。所以,ES6 一共有 6 种声明变量的方法。
解构赋值的用法(数组,对象)
数组的结构赋值:
let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值
如果解构不成功,值就是undefined
对象的结构赋值:
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;
而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
数组字符串新增的方法(常用)
数组新增方法:
1)Array.from():方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
2)Array.of():将一组值,转换为数组
3)数组实例的find():数组实例的find方法,用于找出第一个符合条件的数组成员。(返回的是值)
4)findIndex():数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1(返回的是索引位置)
字符串新增方法:
1)传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。
includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
2) repeat()
repeat方法返回一个新字符串,表示将原字符串重复n次。
3)在ES2017中引入了字符串自动补全的功能如果某个字符串不够指定长度,会在头部或尾部补全。
padStart()用于头部补全,padEnd()用于尾部补全。
4)ES2019对字符串实例新增了trimStart()和trimEnd()这两个方法它们的行为与trim()一致,
trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。
对象新增的方法
属性的遍历
ES6 一共有 5 种方法可以遍历对象的属性。
(1)for...in
for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
(2)Object.keys(obj)
Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。
(5)Reflect.ownKeys(obj)
Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。
首先遍历所有数值键,按照数值升序排列。
其次遍历所有字符串键,按照加入时间升序排列。
最后遍历所有 Symbol 键,按照加入时间升序排列。
新增基本数据类型-Symbol
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,
一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,
可以保证不会与其他属性名产生冲突。
let s = Symbol();
typeof s
// "symbol"
Set和Map数据结构
Set数据结构
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成 Set 数据结构。
Map数据结构:
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
如果你需要“键值对”的数据结构,Map 比 Object 更合适。
Proxy
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,
因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,
用在这里表示由它来“代理”某些操作,可以译为“代理器”。
var proxy = new Proxy(target, handler);
Iterator(遍历器)和for of
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费
Generator函数的使用方法
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。
返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
形式上,Generator 函数是一个普通函数,但是有两个特征。
一是,function关键字与函数名之间有一个星号;
二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
上面代码定义了一个 Generator 函数helloWorldGenerator,它内部有两个yield表达式(hello和world),即该函数有三个状态:hello,world 和 return 语句(结束执行)。
然后,Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。
不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,
也就是前面介绍的遍历器对象(Iterator Object)。
async函数
ES2017 标准引入了 async 函数,使得异步操作变得更加方便。
async 函数是什么?一句话,它就是 Generator 函数的语法糖。
async函数是对Generator函数的改进:
1)内置执行器
2)更好的语义
3)更广的实用性
4)返回值是一个Promise对象
async函数必须与await进行配套使用否则就会报错
Class的基本用法
class其实是对function的简化 class的声明:
class Point {
constructor() {
// ...
}
toString() {
// ...
}
toValue() {
// ...
}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
};
Class的继承
直接使用extend关键字:
class Point {
}
class ColorPoint extends Point {
}
继承:
如果子类和父类完全一样就使用extend完全继承
如果子类想要拥有父类的属性,同时还需要拥有自己的父类就需要使用super关键字,
对于方法而言,如果没有同名的方法直接调用即可,但是如果想使用同名方法需要调用super关键字
class A {
constructor() {
this.x = 1;
}
print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
m() {
super.print();
}
}
let b = new B();
b.m() // 2
Module 的语法
常用的导入导出
模块功能主要由两个命令构成:
export和import。
export命令用于规定模块的对外接口,
import命令用于输入其他模块提供的功能。
export default{}
为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。
异步遍历器
前面介绍过,for...of循环用于遍历同步的 Iterator 接口。新引入的for await...of循环,则是用于遍历异步的 Iterator 接口。
async function f() {
for await (const x of createAsyncIterable(['a', 'b'])) {
console.log(x);
}
}
// a
// b
箭头函数和普通函数的区别,箭头函数可以使用new吗?
箭头函数是普通函数的简写,可以更优雅的定义一个函数,和普通函数相比,有以下几点差异:
1、函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
2、不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
3、不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
4、不可以使用 new 命令,因为:
没有自己的 this,无法调用 call,apply。
没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 __proto__