ECMAScript新特性

ECMAScript

  • JavaScript的语言本身
  • JavaScript是ECMAScript的扩展语言
  • JavaScript能够在浏览器环境中操作dom和bom,在node环境中可以做读写文件的操作
  • ECMAScript只提供最基本的语法
    只停留在语言层面【约定代码如何编写,如何定义变量函数、循环、分支等语句】,不能完成业务中功能性开发

web环境: JavaScript = ECMAScript + Web Apis( dom + bom )
node环境:JavaScript = ECMAScript + Node Apis( fs + net + etc. )

ES2015 ( ES6 )

解决原有语法不足:let,const提供的块级作用域
增强原有语法:解构,展开,参数默认值,模板字符串
全新增加的对象,全新增加的方法:Promise对象,proxy方法
全新的数据类型 Symbol; 全新的数据结构set,map

let与块级作用域

先声明变量,才能使用变量,不能进行状态提升(如val可以先使用,再声明,可以进行状态提升)
块级作用域中定义的成员,外部不能访问到

const

只读,常量,不能修改(不能重新指向一个新的内存地址,可以修改常量中的属性成员),不能重新赋值(会改变内存指向)

最佳实践:不用var,主用const,辅用let (对于一些需要修改的值)

解构赋值

可以解构数组、对象,并可以对解构出来的属性赋默认值
数组解构:根据数组中的位置解构
对象解构:根据键名去解构,也可以对键名进行重命名

模板字符串

模板字符串可支持换行, 模板字符串变量用${}包裹

模板字符串标签函数

// 使用console.log作为模板字符串的标签
const str = console.log`hello world`;  // 输出结果:['hello world'] 
// 这里输出数组的原因是 
// 按照模板字符串中有嵌入的表达式,按照表达式分割过后那些静态的内容分割成的数组

function myTagFunc (strings, name, gender) {
	// 这里strings是模板字符串中静态字符串经过动态变量分割的数组
	// name,gender是动态变量
	return strings[0] + name + strings[1] + gender + strings[2];
}
const name = 'tom';
const gender = 18;
const result = myTagFunc`hey, ${name} is a ${gender}`;
console.log(result);  //输出结果:hey, tom is a 18

字符串的扩展方法

includes()

字符串中是否包含

startsWith()

字符串开头是否是

endsWith()

字符串结尾是否是

const message = 'Error: foo is not defined';
message.includes('foo');  // true
message.startsWith('Error');  // true
message.endsWith('.');   // false

参数默认值

带有默认值的参数需要在函数入参的最后

剩余参数

// 对于函数未知个数的参数,以前都是使用arguments对象去接收,arguments对象实际上是一个伪数组
function foo() {
	console.log(arguments); // 输出结果:[Arguments] {'0': 1, '1': 2, '2': 3, '3': 4}
}
function foo(...args) {
	// ES2015新增 ...操作 ,只能放在形参的最后一位,且只能用一次
	console.log(args);  // 输出结果: [1, 2, 3, 4]
}
foo(1,2,3,4);

展开数组


const arr = ['1', '2', '3'];

// ES2015之前用法
console.log.apply(console, arr);   // 输出结果: 1 2 3
// apply 设置this指向,this指向console,展开数组是arr  

// ES2015新增加的...
console.log(...arr);  // 输出结果: 1 2 3

箭头函数

箭头函数中没有this的机制,所以不会改变this指向

对象字面量增强

const bar = 'bar';
const obj = {
	foo: 123,
	bar,  // 这里如果键名和变量名一致时,可以这样写,等同于 bar: bar
	method () {
		console.log(this);  // 当前作用域 也就是obj
	},  // 等同于 method: function () {}

	[bar]: 123,  // 计算属性名,可以直接在动态键名外用中括号包裹
}

对象扩展方法

Object.assign(target, source1, source2, …)

一般用这个方法进行对象复制

将多个源对象中的属性复制到一个目标对象中

const source1 = {
	a: 123,
	b: 123,
}
const target = {
	a: 456,
	c: 456,
}

const result = Object.assign(target, source1);  // assign后面的对象属性覆盖第一个对象属性
												// { a: 123, c: 456, b: 123 }
console.log(result === target); // true

Object.is(值1, 值2)

判断两个值是否相等

console.log(0 == false);  // true
console.log(0 === false);  // false
console.log(+0 === -0);  // true
console.log(NaN === NaN);  // false
console.log(Object.is(+0, -0));  // false
console.log(Object.is(NaN, NaN));  // true

proxy 代理对象

ES5提供的 Object.defineProperty 监视属性中的对象读写。vue3以前的版本都是通过它实现数据响应,从而完成双向数据绑定

Object.defineProperty 只能监视属性读取和写入;proxy能监视到更多对象操作(如delete操作,对对象中方法的调用)
Object.defineProperty 监视数组只能重写数组的操作方法(大致上通过自定义方法去覆盖掉原本数组上的push,pop,shift等方法,以此劫持对这个方法的调用过程);proxy监视数组可以根据对数组的操作推算出来数组变化地方索引,内容等
proxy是以非侵入的方式监管了对象的读写

const person = {
	name: 'name',
	age: 20,
}

// Object.defineProperty
Object.defineProperty(person, 'name', {
    get() {
        return person._name
    },
    set(value) {
        person._name = value
    },
})

Object.defineProperty(person, 'age', {
    get() {
        return person._age
    },
    set(value) {
        person._age = value
    },
})

// new Proxy(代理对象,代理处理对象);
const personProxy =new Proxy(person, {
	// get 方法监视属性的访问
    get(target, property) {
    	// target 是 代理的目标对象  property 是 我们想要访问的属性名称
		// 判断 property在target中是否存在
        return property in target ? target[property] : 'default';
    },

	// set 方法监视 对象中设置属性的过程
    set (target, property, value) {
    	// target 是 代理的目标对象  
    	// property 是 我们要写入的属性名称  
    	// value 我们要写入的属性值
        if (property === 'age') {
            if (!Number.isInteger(value)) {
                throw new TypeError(`${value} is not an int`);
            }
        }
        target[property] = value;
    }
});

personProxy.age = '111';  // 会报错
personProxy.gender = true;

console.log(personProxy.name);  // zce
console.log(personProxy.xxx);   // default
console.log(personProxy.age);
console.log(personProxy.gender);
delete personProxy.age  // 可以删除掉  这个是操作符

Reflect

统一的对象操作API
属于一个静态类,不能通过new方式构建实例对象,只能调用静态类中的静态方法,如:Reflect.get()
Reflect内部封装了一系列对对象的底层操作,有13个静态方法
Reflect成员方法就是Proxy处理对象的默认实现

统一提供一套用于操作对象的API (之前操作对象时有可能使用object上的方法,也有可能用delete、in这种操作符)

const obj = {
	name: 'eee',
	age: 18,
}
// 在没用Reflect时,对对象进行操作时,可能是对象本身的操作方法,可能是操作符
'name' in obj;
delete obj['age'];
Object.keys(obj);

// 使用Reflect时
Reflect.has(obj, 'name');
Reflect.delectProperty(obj, 'age');
Reflect.OwnKeys(obj);

Promise

解决了传统异步编程中回调函数嵌套过深的问题

class类

独立定义类型

// 以前 想要定义 Person 的类型时,需要定义一个Person的函数作为这个类型的构造函数
function Person (name) {
	// 可以通过this去访问当前的实例对象
	this.name = name;
}
// 如果我们需要在这个类型所有的实例之间去共享一些成员
// 可以借助函数对象的prototype原型去实现
Person.prototype.say = function () {
	console.log(this.name);
}

// class 类
class Person {
	// 构造器
	constructor (name) {
		this.name = name;
	}
	say () {
		console.log(this.name);
	},
}

// 还是通过new关键字 创建Person 类型的实例
const p = new Person('tom');

静态方法

实例方法:通过这个类型构造的实例对象去调用
静态方法:直接通过类型本身去调用

关键词 static

class Person {
	// 构造器
	constructor (name) {
		this.name = name;
	}
	say () {
		console.log(this.name);
	},
	static of (name) {
		return new Person(name);
	}
}
// 这里调用Person类中的of方法时,不需要通过new关键字去创建实例对象
// 可以直接用一下方式调用
const temp = Person.of('tom');
temp.say();

类的继承

关键词 extends

class Person {
	// 构造器
	constructor (name) {
		this.name = name;
	}
	say () {
		console.log(this.name);
	},
}

class Student extends Person {
	constructor (name, num) {
		// super对象始终指向父类,调用它就是调用父类中的构造函数
		super(name);
		this.num = num;
	}
	hello () {
		super.say();
		console.log(this.num);
	}
}
const s = new Student('jack', 100);
s.hello();  // jack 100

set数据结构

常见的是为数组元素去重

// Set是类型,通过new Set去构造实例对象
const s = new Set();
s.add(1).add(2).add(3).add(2);  // Set { 1, 2, 3}
// add方法返回集合对象本身,如果add添加已有的值时,s会将其过滤掉
s.size;  // 获取集合长度
s.has(100);  // 这个集合中是否存在某个值
s.delete(3);  // 删除这个集合中指定的值
s.clear();   // 清除这个集合中的全部内容

const arr = [1,1,2,2,3,3,4];
const result = new Set(arr);       // Set { 1, 2, 3, 4 }
const result = [...new Set(arr)];  // [1, 2, 3, 4]

Map数据结构

Map可以用任意类型的数据作为键,而对象的键只能用字符串

Symbol

一种全新的原始数据类型
最主要的作用是为对象添加独一无二的属性名

const cache = {};

// a.js
cache['a_foo'] = Math.random();

// b.js
cache['b_foo'] = '123';
// 以前都是约定好每个文件中设置变量名如何设置,不会重复

const s = Symbol();
// 每次调用Symbol都是独一无二的,不会重复
const obj = { [Symbol()]: 123, [Symbol()]: 456 };  // { [Symbol()]: 123, [Symbol()]: 456 }

// 如果想要复用同一个Symbol值,Symbol提供了for方法,是静态方法
const s1 = Symbol.for('foo');
const s2 = Symbol.for('foo');
console.log(s1 === s2);  // true

const obj1 = {};
console.log(obj1.toString());  // [object Object]  这里叫做对象的 toString 标签

const obj2 = {
	[Symbol.toStringTag]: 'XObject'
};
console.log(obj2.toString());  // [object XObject]  这里叫做对象的 toString 标签
Object.getOwnPropertySymbols(obj); // 获取的全是键名为Symbol值的属性

BigInt

用于存放更长的数字,ES2019中

for…of…

for…in…适合遍历对象,for…of… 适合遍历数组,伪数组

// forEach循环无法终止循环
// forEach想要终止循环,需要借助 array.some() 或者 array.every()
for (const item of array) {
    break; // 可以终止循环
}

// 伪数组也可以用for...of...
for (const item of arguments) {

}
// Map也可以用for... of ... 

Iterable 可迭代接口

无论什么复杂的数据结构都是实现了统一的接口Iterable所以才能被for…of…循环;
用for…of…的前提就是 实现Iterable接口

// 实现可迭代接口 Iterable

// 实现对象的iterator方法后,对象就可以用for...of...遍历了
// 整个对象 叫Iterable; 带有next方法的叫iterator; next方法里面的叫iteratationResult
const obj = {
	store: ['foo', 'bar', 'baz'],
	[Symbol.iterator]: function () {
		let index = 0;
		const self = this;
		return {
			next: function () {
				const result = {
					value: self.store[index],
					done: index > self.store.length,
				}
				index++;
				// return 返回对象是 迭代结果接口
				return result
			}
		}
	}
}

迭代器模式

迭代器的意义:对外提供统一遍历接口,让外部不用关心数据内部结构是什么

生成器 generator

生成器函数最大的特点:惰性执行
用生成器是为了 避免异步编程中回调函数嵌套过深,从而提供更好的异步编程解决方案,详情在异步编程源码那块详细描述

生成器函数的应用:发号器,实现对象的iterator方法

// 发号器
function * createIsAdd () {
	let i = 0;
	while (true) {
		yield i++;
	}
}
const isAdd = createIsAdd();
console.log(isAdd.next().value);  // 1
console.log(isAdd.next().value);  // 2
console.log(isAdd.next().value);  // 3
console.log(isAdd.next().value);  // 4

ES Modules

语言层面的模块化标准

ES2016

数组实例对象的includes方法

const arr = ['foo', 1, 2, 'fjf'];
console.log(arr.includes('foo'));

指数运算符

// 2的10次方

// 以前用法
Math.pow(2,10);

//指数运算符
2 ** 10

ES2017

Object.values(对象)

ES2015中的Object.keys返回键名组成的数组,而Object.values返回键值组成的数组

const obj = {
	age: '18',
	name: '张三',
}
console.log(Object.values(obj));  // ['18', '张三']

Object.entries(对象)

以数组的形式返回对象中所有的键值对

const obj = {
	age: '18',
	name: '张三',
}
console.log(Object.entries(obj));  // [['age', '18'], [name, '张三']]

// 可以先将对象转化为 以数组形式返回对象所有的键值对
// 然后用for...of...去遍历
for ( const [key, value] of Object.entries(obj)) {
	console.log(key, value);   // age 18
}

Object.getOwnPropertyDescriptors(对象)

获取对象当中属性的完整信息
ES2015过后可以给对象定义get或者set属性,这种属性是不能通过Object.assign方法去完全复制的,
配合ES2015对对象定义get,set使用

const p1 = {
	firstName: 'jintian',
	lastName: 'tianqi',
	get fullName () {
		return this.firstName + this.lastName;
	}
}
const description = Object.getOwnPropertyDescriptors(p1);

字符串的原型方法 padStart

字符串填充方法,用给定的字符串去填充目标字符串的开始位置

字符串的原型方法 padEnd

字符串填充方法,用给定的字符串去填充目标字符串的结束位置

const msg = '今天是个好日子';
msg.padEnd(16, '-');  // 如果msg字符串没有16位时,从尾部缺少几位用几个'-'填充

在函数参数中添加尾逗号

function get_1(age, name, ) {
}

async / await

解决了异步回调函数嵌套过深的问题,使得代码更加简洁易读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值