ECMAScript是什么
ECMAScript(ES),也是一门脚本语言,通常把它看作为JavaScript(JS)的标准化规范,实际上JS是ES的扩展语言,因为ES只提供了最基本的语法(如定义变量,for循环等),JS实现了ES语言的标准,并且又做了扩展,使得我们可以在浏览器环境中操作BOM和DOM,在Node环境中,可以去读写文件这种操作
浏览器环境中
这是在浏览器环境中,js包含es和浏览器提供的API,这些API又包含DOM和BOM
Node环境中
这是在node环境中,js包含es和node提供的API,这些API又包含fs(用于操作系统中的文件),net(用于底层的网络通信),etc.
所以ES就是JS语言本身
ES版本
2015年开始,ES保持每年一个版本的迭代,所以现在变得越来越高级,便捷
目前的所有版本
从2009年发布的5版本到其中的ES2015,经历了6年时间才被完全标准化,这6年时间,也是web发展的黄金时间,所以这个版本包含了很多颠覆式的新功能,由于ES2015迭代的时间太长,发布的内容过多,所以从之后的版本开始,es的发布就变得更加频繁,也符合当下前端发展小步快跑的现状。
自从ES2015开始,ES2015开始按照发行年份命名,不再按照版本号命名,由于这个决定是在2015年才开始,所以很多人也把ES2015叫做ES6。
目前市面上主流的运行环境,也都纷纷跟进,也都开始逐步支持这些最新的特性,所以对于我们这些开发者而言,学习这些新特性很有必要。
ES2015
也就是ES6,新时代ES的版本标准,相比之前变化较大。目前很多人又将ES6泛指ES5.1后所有的新标准,比如我们在很多资料中看到“使用ES6的async和await”,实际上呢async函数是ES2017中制定的标准,所以以后逛论坛得注意分辨,它说的ES6是泛指还是特指。
ES2015规范链接
这里就整理ES5.1之后的,比较重要的且值得了解的新特性,这些变化可以分为四大类
- 解决原有语法上的问题与不足(let,const等)
- 对原有语法进行增强,使其变得更为便捷易用(解构,模版字符串等)
- 全新的对象,全新的的方法,全新的功能(Promise等)
- 全新的数据类型和数据结构(symbol,set,map等)
let与块级作用域
作用域:代码中某个成员能够起作用的范围。在ES2015之前,只有全局作用域和函数作用域,ES2015中又新增块级作用域。
块:指的就是代码中,用一对{ }所包裹起来的范围,例如if,for语句中的{},都会产生我们所说的块。以前块是没有独立作用域的。
if(true) {
var aa = 'aaa';
let bb = 'bbb';
}
console.log(aa) // => 'aaa'
console.log(bb) // => 'ReferenceError: bb is not defined'
let与var还有一个区别是,let没有变量提升
// 变量提升
console.log(a) // => undefined,说明在我们打印的时候,a就已经被声明了,只是还没赋值,这种现象就叫变量提升
var a = 123
console.log(b) // => Uncaught ReferenceError: Cannot access 'b' before initialization
let b = 123
其实let,const是有变量提升的,var在提升后的初始化阶段会赋值undefined,let与const不会赋值,所以此时访问没有任何赋值的变量时,会抛出错误,称为暂时性死区
JS变量提升与时间死区
const
const:用来声明一个只读的常量。
它的特点就是在let的特点上多了一个只读,只读的意思是声明后不允许再被修改
const a = 123
a = 456 // => Uncaught TypeError: Assignment to constant variable.
const b = {name: 123}
b.name = 456
console.log(b) // => {name: 456}
注意,不能被修改,只是说我们用const声明过后,不允许重新指向一个新的内存地址,并不是说不能修改常量中的属性成员。所以b.name修改成功
最佳实践:不用var,主用const,配合let
数组的解构
// 普通方式获得数组成员
const arr = ['aa', 'bb', 'cc']
let a = arr[0];
let b = arr[1];
let c = arr[2];
console.log(a, b, c)
// 解构
let [a, b, c] = arr
console.log(a, b, c) // => aa bb cc
// 只获取第一个
let [a] = arr
console.log(a) // => aa
// 只获取第三个,把变量名称删除,要保留逗号
let [, , c] = arr
console.log(c) // => cc
// 只获取后两个,用...表示,这种用法只能放在最后
let [a, ...rest] = arr
console.log(rest) // => ["bb", "cc"]
// 使用默认值
let [a, b, c, more = 'dd'] = arr // 没有第四项,本来是undefined
console.log(a, b, c, more) // => aa bb cc dd
对象的解构
// 与数组的解构类似
const obj = {name: 123, age: 18}
let {name, sex = "female"} = obj
console.log(name, sex) // => 123 "female"
// 变量重命名
const obj = {name: 123, age: 18}
let {name : objName} = obj
console.log(objName) // => 123
模版字符串
const name = 'Tom';
let str1 = 'hey,' + name // 传统方式
let str2 = `hey,${name}` // 反引号方式
// ${}中不仅可以加入变量,任何规范的js语句都可以
function getAge(n) {
return n + 1;
}
let str3 = `hey ${name}, i am ${getAge(10)} years old`
新方式还有一个好处是支持换行,以前要是想在字符串中换行需要\n
带标签的模版字符串
const gender = 1;
function tag(string, gender) {
console.log(string)
gender = gender === 1 ? '男' : '女';
return string[0] + string[1] + gender;
}
let result = tag`我的性别是${gender}`
console.log(result) // => 我的性别是男
字符串的扩展方法
const message = 'hello, i love you';
message.startsWith('hello') // => true 是否以hello开头
message.endsWith('he') // => false 是否以he结束
message.includes('love') // => true 是否包含love
函数参数默认值
// 以前赋默认值
function f1 (value) {
// value = value || 100; // 其实这样是错的,如果传进去false ,也变成 内容是100了
value = value === undefined ? 100 : value; // 这样最标准
return '内容是' + value;
}
console.log(f1()) // => 内容是100
// 新方式
function f1 (value = 100) {
return '内容是' + value;
}
console.log(f1()) // => 内容是100
剩余参数
// 传统方式
function f1 () {
console.log(arguments) // 伪数组 => 1,2,3,4
}
f1(1,2,3,4)
// ES2015
function f1 (a, ...args) {
console.log(a) // => 1
console.log(args) // => [2,3,4] 正常数组
}
f1(1,2,3,4)
展开数组参数
const arr = [1,2,3]
// 传统方式
console.log(arr[0], arr[1], arr[2]) // => 1 2 3
// 如果参数不固定
console.log.apply(this, arr) // => 1 2 3
// ES2015
console.log(...arr) // => 1 2 3
箭头函数
function add(a, b) {
return a + b
}
// 箭头函数 更简短易读
const add = (a, b) => a + b
箭头函数与this
const obj = {
name: 'Tom',
sayHi1: function () {
console.log(this)
},
sayHi2: () => {
console.log(this)
}
}
obj.sayHi1() // this指向person
obj.sayHi2() // this指向window
- 普通函数,谁调用了这个普通函数,this 指向谁。是在执行时绑定。
- 箭头函数,this指向定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。
比如这里箭头函数本身所在的作用域为全局作用域(对象的这个花括号没有作用域),而全局作用域的this指向window。
如果你是懒人,只需要记住这一句话就好了:不管在什么情况下,箭头函数的this跟外层function的this一致,外层function的this指向谁,箭头函数的this就指向谁,如果外层不是function则指向window。
对象字面量增强
const name = 'Tom';
const obj = {
// name: name 以前必须要接冒号,现在变量名相同可省略
name,
// sayHi: function f () { 函数也是,可精简为以下方式
// console.log(this)
// },
sayHi () { // 此方式和上面的function一样,不是箭头函数
console.log(this)
}
['ha' + 'ha']: 123 // 计算属性名,不用再像以前一样 obj['ha' + 'ha'] = 123
}
Object.assign
将多个源对象中的属性,复制到一个目标对象中
如果有相同的属性,源对象的属性会覆盖掉目标对象的属性
const source1 = {
a: 123,
b: 123
}
const target = {
a: 456,
c: 456
}
const result = Object.assign(target, source1) // 第一个入参是目标对象,后续都是源对象
console.log(target) // => {a: 123, b: 456, c: 456}
console.log(result === target) // => true 返回值就是目标对象
经常用Object.assign({}, obj) 来复制一个一模一样的对象
Object.is
比较两值是否相等
console.log(
// 0 == false // => true
// 0 === false // => false
// +0 === -0 // => true
// NaN === NaN // => false
// Object.is(+0, -0) // => false
// Object.is(NaN, NaN) // => true
)
Proxy
为对象设置访问代理器的(代理器就想像成门卫,进去干任何事都要经过他)
const person = {
name: 'zce',
age: 20
}
const personProxy = new Proxy(person, {
// 监视属性读取
get (target, property) {
return property in target ? target[property] : 'default'
// console.log(target, property) // => 目标对象,属性名
// return 100
},
// 监视属性设置
set (target, property, value) {
if (property === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError(`${value} is not an int`)
}
}
target[property] = value
// console.log(target, property, value) // => 目标对象,属性名,值
}
})
person.age = 100 // 可以更改person,但是不会经过set方法
personProxy.age = 100 // 可以更改person,也会经过set方法
Proxy VS Object.defineproperty
defineproperty只能监视对象的读取和写入
proxy能监视到更多的对象操作,比如delete,调用一个函数,都能监视到
proxy支持数组的监视
const list = []
const listProxy = new Proxy(list, {
set (target, property, value) {
console.log('set', property, value) // => [], 0, 100
target[property] = value
return true // 表示设置成功
}
})
listProxy.push(100)
proxy是以非侵入的方式监管了对象的读写
const person = {}
Object.defineProperty(person, 'name', {
get () {
console.log('name 被访问')
return person._name
},
set (value) {
console.log('name 被设置')
person._name = value
}
})
Object.defineProperty(person, 'age', {
get () {
console.log('age 被访问')
return person._age
},
set (value) {
console.log('age 被设置')
person._age = value
}
})
person.name = 'jack'
console.log(person.name)
// Proxy 方式更为合理 不需要对每个属性都绑定
const person2 = {
name: 'zce',
age: 20
}
const personProxy = new Proxy(person2, {
get (target, property) {
console.log('get', property)
return target[property]
},
set (target, property, value) {
console.log('set', property, value)
target[property] = value
}
})
personProxy.name = 'jack'
console.log(personProxy.name)
Reflect
Reflect最大的意义在于统一对象的操作
const obj = {
name: 'zce',
age: 18
}
console.log('name' in obj) // 判断是否有某个属性
console.log(delete obj['age']) // 删除某个属性
console.log(Object.keys(obj)) // 枚举属性的key
// 由上可以看出,对于对象的操作,既有操作符,又有对象的某个方法,并不统一
// Reflect最大的意义在于统一对象的操作
console.log(Reflect.has(obj, 'name')) // 判断是否有某个属性
console.log(Reflect.deleteProperty(obj, 'age')) // 删除某个属性
console.log(Reflect.ownKeys(obj)) // 枚举属性的key
class类
// 传统的创建类的方式
function Person(name) { // 构造函数
this.name = name;
}
Person.prototype.sayHi = function () { // 原型
console.log(`my name is ${this.name}`);
}
// class
class Person {
constructor(name) { // 构造函数
this.name = name;
}
sayHi() { // 实例方法
console.log(`my name is ${this.name}`);
}
static create (name) { // 静态方法
return new Person(name)
}
}
const p = new Person('Eric');
const p2 = Person.create('Eric);
类的继承
class Person {
constructor (name) {
this.name = name
}
say () {
console.log(`hi, my name is ${this.name}`)
}
}
class Student extends Person {
constructor (name, number) {
super(name) // 执行父类构造函数,必须有
//子类构造器中,this是由 super()调用所产生的(即所谓「父类的this」)。在super()调用之前,你是不能在子类构造器中使用this的,如果访问之,会产生ReferenceError
this.number = number
}
hello () {
super.say() // 调用父类方法
console.log(`my school number is ${this.number}`)
}
}
const s = new Student('jack', '100')
s.hello()
s.say()
网友评论