说在前面
本文是自己在阅读阮一峰老师的<<ECMAScript 6 入门>>后,自己的一些总结和归纳。 不是十分的全面,但基本包括的常用的。还有一些我没涉及到的,麻烦大家自行去下方连接了解学习
如果有错误或者哪里有些不对的地方,或者觉得解释不是很清楚的,还请大家指出来
附上连接ECMAScript 6 入门
变量
-
var 全局变量 变量提升
-
let 块级变量 不存在变量提升 具有暂时性死区(let声明的变量跟这个块绑定,如果在这个块内,没有定义先使用就会报错(即使外部定义的有,也不能使用),如图),具有块级作用域。
-
TDZ 暂时性死区:在代码块内,使用
let
命令声明变量之前,该变量都是不可用的var num = 10 if(1) { console.log(num); // 结果报错,原因 TDZ let num = 20; }
-
const 声明常量 ,声明必须赋值 ,具有块级作用域
if(true) { const a = 10; // 声明必须赋值,否则报错 if(true) { // console.log(a); 结果报错,原因 TDZ const a = 20; console.log(a); } console.log(a); } console.log(a); //结果20 10 报错
const对于复杂数据类型
复杂数据类型里面的值是可以更改的, 但是不能重新赋值
const arr = [1,2,3]
arr[0] = 4;
console.log(arr) // [ 4, 2, 3 ]
arr = [7,8,9];
console.log(arr) // 报错
let const var 三者区别
var | let | const |
---|---|---|
函数级作用域 | 块级作用域 | 块级作用域 |
变量提升 | 不存在变量提升 | 不存在变量提升 |
值可更改 | 值可更改 | 值不可更改 |
暂时性死区 | 暂时性死区 |
const 要比 let 关键字效率高
解构赋值
数组解构
let arr = [1,2,3]
let [a,b,c,d=5,e] = arr; // d这样写是设置默认值
console.log(a,b,c,d,e) //1 2 3 5 undefined
解构成功了就是对应的值 不成功就是undefined
对象解构
// 顺序不重要 重要的是变量名字
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
// 若和属性名不同则为undefined 同样可以设置默认值
let { baz,bbb='ccc' } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
bbb // ccc
起别名
// 匹配属性:别名名称
let { bar: barname, foo } = { foo: 'aaa', bar: 'bbb' };
console.log(barname) // aaa
Symbol
ES6新引入的原始数据类型 第七种
原来6种:undefined
、null
、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
生成
let s = Symbol(); // 不用new
// 传递参数(字符串) 是它的描述信息
let s1 = Symbol('foo');
s1 // Symbol(foo)
//如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。
const obj = {
toString() {
return 'abc';
}
};
const sym = Symbol(obj);
sym // Symbol(abc)
Symbol.prototype.description(ES9)
const sym = Symbol('foo');
sym.description // "foo"
作为属性名的 Symbol
每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符或者是key
注意,Symbol 值作为对象属性名时,不能用点运算符。
也可以作为常量,利用值不相等
const mySymbol = Symbol();
const a = {};
a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"
点运算符后面总是字符串,所以不会读取mySymbol
作为标识名所指代的那个值,导致a
的属性名实际上是一个字符串,而不是一个 Symbol 值。
在对象中
在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。
let s = Symbol();
let obj = {
[s]: function (arg) { ... }
};
obj[s](123);
// 增强写法
let obj = {
[s](arg) { ... }
};
如果s
不放在方括号中,该属性的键名就是字符串s
,而不是s
所代表的那个 Symbol 值。
属性名的编译
Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...in
、for...of
循环中,也不会被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回。
// 可以通过Object.getOwnPropertySymbols()方法获取 返回数组
const obj = {};
let a = Symbol('a');let b = Symbol('b');
obj[a] = 'Hello';obj[b] = 'World';
const objectSymbols = Object.getOwnPropertySymbols(obj);
objectSymbols// [Symbol(a), Symbol(b)]
//Reflect.ownKeys()方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
let obj = {
[Symbol('my_key')]: 1,
enum: 2,
nonEnum: 3
};
Reflect.ownKeys(obj)// ["enum", "nonEnum", Symbol(my_key)]
Symbol.for(),Symbol.keyFor()
Symbol('') // 全局注册
Symbol.for() // 对全局注册的重新利用
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2 // true
// 你调用Symbol.for("cat")30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")30 次,会返回 30 个不同的 Symbol 值。
Symbol.keyFor()
方法返回一个已登记的 Symbol 类型值的key
。
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined
// 变量s2属于未登记的 Symbol 值,所以返回undefined。
函数的扩展
-
可以设置默认参数(可以结合解构使用)
-
增加了 length 属性(返回没有指定默认值的参数个数)
(function (a) {}).length // 1 (function (a = 5) {}).length // 0 (function (a, b, c = 5) {}).length // 2 // 注意位置 (function (a = 0, b, c) {}).length // 0 (function (a, b = 1, c) {}).length // 1
-
rest参数
// 示例代码 function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(2, 5, 3) // 10 // 函数的length属性,不包括 rest 参数。
-
name 属性 返回 函数名称(ES6。ES5返回空)
-
尾调用:指某个函数的最后一步是调用另一个函数
-
尾递归:尾调用自身,就称为尾递归
-
柯里化(currying),意思是将多参数的函数转换成单参数的形式。
箭头函数(重点)
var f = () => 5; // 没有或者只有一个参数可以不加()
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
// 如果多余一条语句需要用{}包裹且加上return
// 如果要返回一个对象 需要加上()
let getTempItem = id => ({ id: id, name: "Temp" });
使用注意点
(1)函数体内的this
对象,是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new
命令,否则会抛出一个错误。
(3)不可以使用arguments
对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield
命令,因此箭头函数不能用作 Generator 函数。
一道关于箭头函数的面试题
var a = 100;
var obj = {
age:20,
say:()=>{
alter(this.age)
}
}
obj.say() // 100 对象没有作用域 this指向全局。若不定义则为undefined
扩展运算符
console.log(...[1, 2, 3])
// 1 2 3
//扩展运算符后面还可以放置表达式。
const arr = [
...(x > 0 ? ['a'] : []),
'b',
];
// 只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。
扩展运算符应用
// 代替apply方法
// ES5 的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);
// ES6的写法
function f(x, y, z) {
// ...
}
let args = [0, 1, 2];
f(...args);
// 复制数组
const a2 = [...a1];
// 合并数组
const a3 = [...a2,...a1]
还可以解构字符串
可以把伪数组转换成真正的数组
补充,什么是伪数组
1、伪数组是一个对象 2、这个对象必须要有length属性 3、如果这个对象的length不为0,那么必须要有按照下标存储的数据
如何判断数据是不是伪数组:
1、首先他是个对象 2、是对象,有length属性 3、有length,值必须是number类型 4、length值是number类型,并且值不为0,这个对象还得按照下标存储数据
Array扩展方法
构造函数的方法:Array.from()
// 可以把伪/类数组变成数组
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
// 第二个参数可以对元素进行处理,再放回
let arr2 = Array.from(arrayLike, item=> item*2);
Array.of
Array.of(3, 11, 8) // [3,11,8]
// 把一组值转换成数组
实例方法:find()
[1, 4, -5, 10].find((n) => n < 0)
// -5
查找第一个满足条件的对象
接收函数作为参数, 如果存在返回对应的对象,不存在返回undefined
实例方法:findlndex()
用法与上面相同,返回的是第一个满足条件的成员位置,如果没有找到,返回 -1
实例方法:includes()
表示某个数组是否包含给定的值,返回布尔值
[1,2,3].includes(2) // true
对象的扩展
属性方法的简洁表示
// 当键值一样的时候可以简写
return {
a: a,
// 等同于
a
}
// 方法
const o = {
method() {
return "Hello!";
}
};
// 等同于
const o = {
method: function() {
return "Hello!";
}
};
方法的name属性
同函数
对象的扩展运算符(ES8)
可以看解构
链判断运算符 (ES10)
读取对象内部的某个属性,判断该对象是否存在
三种用法
obj?.prop
// 对象属性obj?.[expr]
// 同上func?.(...args)
// 函数或对象方法的调用
例子
// 下面是判断对象方法是否存在,如果存在就立即执行的例子。
iterator.return?.()
Null 判断运算符(es10)
只有运算符左侧的值为null
或undefined
时,才会返回右侧的值。
const headerText = response.settings.headerText ?? 'Hello, world!';
需要更多自行百度,这个目前暂不支持
对象的新增方法
Object.assign() 对象的合并
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
// 第一个参数是目标对象,后面的参数都是源对象。
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
注意,如果有同名,则后者覆盖前者。 是浅拷贝!!!
若只有一个则直接返回本身
Object.keys(),Object.values(),Object.entries()
返回 键,值,键值数组(把每个键值作为一个数组返回)
Object.fromEntries()
object.entries()的逆操作
模板字符串
模板字符串不用拼接来显示变量名字
let name = '张三'
let sayHello = `Hello ,my name is ${name}` // 没声明报错
console.log(sayHello) //Hello ,my name is 张三
可以换行
let ul =
`
<ul>
<li>first</li>
<li>second</li>
</ul>
`
console.log(ul)
/*
<ul>
<li>first</li>
<li>second</li>
</ul>
*/
可以调用函数
function fn() {
return "Hello World";
}
`foo ${fn()} bar` // 没声明报错
// foo Hello World bar
标签模板
alter`hello` 等同于 alter("hello")
String的扩展方法
startwith 和 endwith
判断是否以。。开头 或者结尾 返回布尔值
实例方法 repeat()
参数为整数, 将字符串重复几次输出
Set数据结构
利用set 可以做数据去重
Set 实例的属性和方法
//Set 结构的实例有以下属性。
Set.prototype.constructor://构造函数,默认就是Set函数。
Set.prototype.size://返回Set实例的成员总数。
//Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。
Set.prototype.add(value)://添加某个值,返回 Set 结构本身。
Set.prototype.delete(value)://删除某个值,返回一个布尔值,表示删除是否成功。
Set.prototype.has(value)://返回一个布尔值,表示该值是否为Set的成员。
Set.prototype.clear()://清除所有成员,没有返回值。
遍历Set
Set.prototype.keys()
:返回键名的遍历器Set.prototype.values()
:返回键值的遍历器Set.prototype.entries()
:返回键值对的遍历器Set.prototype.forEach()
:使用回调函数遍历每个成员
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
set.forEach((value, key) => console.log(key + ' : ' + value))
// "red", "red"
// "green", "green"
// "blue", "blue"
WeakSet
WeakSet 结构与 Set 类似,但是有区别
- WeakSet 的成员只能是对象,而不能是其他类型的值。
- WeakSet 中的对象都是弱引用
- 需要自行百度
Map
键值对,原本键只可以用 字符串,现在不局限于 字符串
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content') // 设置 参数 对象,内容
m.get(o) // "content" 获取
m.has(o) // true 判断是否含有
m.delete(o) // true 删除
m.has(o) // false
// 同样也可以接收数组
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size // 2 返回成员数
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author“
map.clear() // 清空
map.size // 0
// 若一键多值则覆盖
遍历方法
Map.prototype.keys()
:返回键名的遍历器。Map.prototype.values()
:返回键值的遍历器。Map.prototype.entries()
:返回所有成员的遍历器。Map.prototype.forEach()
:遍历 Map 的所有成员。
跟set 基本相同 同样支持扩展运算符
WeakMap
类似weakSet和set关系 特点
- WeakMap
只接受对象作为键名(
null`除外),不接受其他类型的值作为键名。 WeakMap
的键名所指向的对象,不计入垃圾回收机制。
Proxy 代理
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
var proxy = new Proxy(target, handler);
// target参数表示所要拦截的目标对象,
// handler参数也是一个对象,用来定制拦截行为。
Proxy 支持的拦截操作
- get(target, propKey, receiver):拦截对象属性的读取,比如
proxy.foo
和proxy['foo']
。 - set(target, propKey, value, receiver):拦截对象属性的设置,比如
proxy.foo = v
或proxy['foo'] = v
,返回一个布尔值。 - has(target, propKey):拦截
propKey in proxy
的操作,返回一个布尔值。 - deleteProperty(target, propKey):拦截
delete proxy[propKey]
的操作,返回一个布尔值。 - ownKeys(target):拦截
Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in
循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性。 - getOwnPropertyDescriptor(target, propKey):拦截
Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象。 - defineProperty(target, propKey, propDesc):拦截
Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值。 - preventExtensions(target):拦截
Object.preventExtensions(proxy)
,返回一个布尔值。 - getPrototypeOf(target):拦截
Object.getPrototypeOf(proxy)
,返回一个对象。 - isExtensible(target):拦截
Object.isExtensible(proxy)
,返回一个布尔值。 - setPrototypeOf(target, proto):拦截
Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 - apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如
proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。 - construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如
new proxy(...args)
。
目前只看几个个人觉得常用的
推荐直接点击这个点击这个
Get 属性读取
三个参数,依次为目标对象、属性名和 proxy 实例本身:可选(严格地说,是操作行为所针对的对象),示例代码如下
var person = {
name: "张三"
};
var proxy = new Proxy(person, {
get: function(target, propKey) {
if (propKey in target) {
return target[propKey];
} else {
throw new ReferenceError("Prop name \"" + propKey + "\" does not exist."); // 没有这层返回undefined
}
}
});
proxy.name // "张三"
proxy.age // 抛出一个错误
get方法可以继承
如果想更多了解,可以点击这个
Set 属性设置
四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
示例代码
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 对于满足条件的 age 属性以及其他属性,直接保存
obj[prop] = value;
}
};
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错
如果想更多了解,可以点击这个
Class 的基本语法
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
} // 方法之间不需要加,
toString() { // 不用加function
return '(' + this.x + ', ' + this.y + ')';
}
doStuff() {
console.log('stuff');
}
}
// 可以直接new
var b = new Point();
b.doStuff() // "stuff"
类的数据类型就是函数,类本身就指向构造函数。
class Point {
// ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true 构造函数指向类本身
类的所有方法都定义在类的prototype
属性上面
在类的实例上面调用方法,其实就是调用原型上的方法。
class B {}
let b = new B();
b.constructor === B.prototype.constructor // true
通过Object.assign
方法可以很方便地一次向类添加多个方法。
Object.assign(Point.prototype, {
toString(){},
toValue(){}
});
// 原因 类的方法都定义在prototype对象上面
constructor 方法
默认方法,new时自动调用,类里必须含有,没有定义生成空的方法
class Point {
}
// 等同于
class Point {
constructor() {}
}
实例的属性除非显式定义在其本身(即定义在this
对象上),否则都是定义在原型上(即定义在class
上)。
//定义类
class Point {
constructor(x, y) {
this.x = x; // 如果是无参, 例如 x= 0 则可以携程下面的
this.y = y; //
}
// x = 0; 不用再写constructor 调用不变
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
取值函数(getter)和存值函数(setter)
学过java之类的应该好理解 就是个重载
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
let inst = new MyClass();
inst.prop = 123;
// setter: 123
inst.prop
// 'getter'
##属性表达式
即类的属性名,可以采用表达式。
let methodName = 'getArea';
class Square {
constructor(length) {
// ...
}
[methodName]() {
// ...
}
}
Class 表达式
const MyClass = class Me {
getClassName() {
return Me.name; // 只存在内部,相当于this 且和类名相同
}
};
// 如果不用 相当于
const MyClass = class { /* ... */ };
注意点
-
默认严格模式
严格模式主要有以下限制。
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能使用
with
语句 - 不能对只读属性赋值,否则报错
- 不能使用前缀 0 表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 不能删除变量
delete prop
,会报错,只能删除属性delete global[prop]
eval
不会在它的外层作用域引入变量eval
和arguments
不能被重新赋值arguments
不会自动反映函数参数的变化- 不能使用
arguments.callee
- 不能使用
arguments.caller
- 禁止
this
指向全局对象 - 不能使用
fn.caller
和fn.arguments
获取函数调用的堆栈 - 增加了保留字(比如
protected
、static
和interface
)
-
类不存在变量提升
-
name属性,返回类名
-
Generator 方法 ,方法前加* 就是一个 Generator 函数。
-
this 默认指向实例
静态属性等
不做过多介绍,需要自行看书 这里
Model
导入/加载 import
// 从fs模块加载 3 个方法,其他方法不加载
// 其他模块导出后才可以导入
import { stat, exists, readFile } from 'fs';
// 可以用as 作为重命名
import { lastName as name } from './profile.js';
// 接口 不允许改写
// 整体加载 *号
import * as circle from './circle';
这种加载称为“编译时加载”或者静态加载
导出 export
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };// 写法不唯一
// 也可以导出类和函数 不做列举
默认导出 export default
即导入这个模块就是导入这个,只可以存在一个
// export-default.js
export default function () {
console.log('foo');
}
// import-default.js
import customName from './export-default';
customName(); // 'foo'
特别注意
export
命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
// 报错 原因,因为没有提供对外的接口
export 1;
// 报错 同上
var m = 1;
export m;
// 报错 同上
function f() {}
export f;
// 正确写法
export var m = 1;
var m = 1;
export {m};
export function f() {};
function f() {}
export {f};
export 与 import 的复合写法
export { foo, bar } from 'my_module';
// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar };
模块继承
需要自行 点击这里
Promise对象
作用:解决异步编程
基本用法
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功条件 */){
resolve(value);
} else {
reject(error);
}
});
Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。resolve
改为成功状态,把结果作为参数传出。reject
改为失败状态,把错误作为参数传出
常用方法
then,catch,finally
promise.then(function(posts) {
// 操作执行成功代码
}).catch(function(error) {
// 操作执行失败代码
console.log('发生错误!', error);
}).finally((fina)=>{
// 无论成功失败都要执行的代码 ES8引入
});
想要深入了解或者了解其他方法请点击这里