ECMAScript新特性介绍

·  阅读 16

ECMAScript与javascript

  • ECMAScript是javascript的标准化规范;
  • javascript是扩展ECMAScript的语言;
  • ECMAScript提供了基本语法;

es新特性

可分为以下方面:

  • 解决之前语法上的个别问题和不足;
  • 对语法的增强;
  • 新的对象、方法、功能;
  • 新的数据类型、数据结构;

let与块级作用域

  • 作用域:某个成员起作用的范围;
  • 作用域可分为:全局作用域、函数作用域、块级作用域。
// let 声明的成员只会在所声明的块中生效

if (true) {
	let foo = 123;
    console.log(foo);// 123
}
console.log(foo);// foo is not defined

// let 阻止了变量声明提升现象 

if (true) {
	var bar = 123;
}
console.log(bar);// 123
复制代码

const常量

const特点及示例:

// const常量声明过后不允许重新赋值
const name='123';
// name='456';// TypeError: Assignment to constant variable.

// const常量声明同时要赋值
const age;// SyntaxError: Missing initializer in const declaration
age=18;

// 对于引用数据类型的常量,可以修改引用地址指向的成员属性
const obj={};
obj.name='789';

obj={};
复制代码

解构赋值

传送门:juejin.cn/post/701582…

模板字符串

定义:使用反引号来界定的字符串字面量。

const str = `this is a string`;
console.log(str);// this is a string
复制代码

特点

  • 允许字符串换行书写;
const str2 = `this is a string,
hello
`;
console.log(str2);/** 
this is a string,
hello
*/
复制代码
  • 通过 ${ } 插入表达式,表达式的执行结果将会输出到对应位置;
const name = 'lisi'
const msg = `hei, ${name} --- ${4 + 2} ---- ${Math.random()}`
console.log(msg);// hei, lisi --- 6 ---- 0.6673511270440893
复制代码

标签化的模板字符串

模板字符串的标签就是一个特殊的函数,使用这个标签就是调用这个函数,模板字符串中的文本和表达式的值将作为参数传给这个函数。“标签化的模板字符串”的值就是这个函数的返回值。

反引号可以充当开头和末尾的圆括号使得函数被调用。

const str3 = console.log`hello world`; // [ 'hello world' ]

const name2 = 'tom'
const gender = false
function myTagFunc(strings, name, gender) {
	// console.log(strings, name, gender)
	// return '123'
	const sex = gender ? "man" : "woman";
	return strings[0] + name + strings[1] + sex + strings[2];
}
const result = myTagFunc`hey, ${name2} is a ${gender}.`;
console.log(result);// hey, tom is a woman.
复制代码

参数默认值与剩余参数

参数默认值

注意: 这里指的是函数的默认值;

  • 函数形参未赋值默认undefined
function foo(enable) {
	console.log(enable); //undefined
}

foo();
复制代码
  • 函数形参默认赋值,未传递实参,显示默认值;传递实参,显示传递的值
function foo(enable=true) {
	console.log(enable); //true
}
// 未传递实参
foo();

function foo(enable=true) {
	console.log(enable); //false
}
// 传递实参
foo(false);
复制代码
  • 多个形参,后面参数的默认值可以使用前面参数的值来定义;
function foo(num,enable=num*2) {
	console.log(num); //3
	console.log(enable); //6
}

foo(3);
复制代码

剩余参数

  • 剩余参数使用...运算符。
  • 可以收集剩下的传入参数,只能写在最后面;
function foo(first, ...args) {
	console.log(args);//[ 2, 3, 4 ]
}
foo(1, 2, 3, 4);
复制代码

展开数组

// 展开数组参数

const arr = ['foo', 'bar', 'baz']

// 一个一个调用
console.log(
  arr[0],
  arr[1],
  arr[2],
);// foo bar baz

// 使用apply
console.log.apply(console, arr);//foo bar baz

// 使用展开运算符
console.log(...arr);//foo bar baz
复制代码

箭头函数

  • 普通函数使用function声明
function inc (number) {
  return number + 1
}
复制代码
  • 箭头函数写法:
// 箭头函数最简方式
const inc = n => n + 1

const inc = (n) =>{ return n + 1}
复制代码

箭头函数特点:

  • 函数体只有一条语句,自动return
const inc = n => n + 1
复制代码
  • 函数体多条语句,返回值仍需 return
// 函数体多条语句,返回值仍需 return
const inc = (n, m) => {
  console.log('inc invoked')
  return n + 1
}

console.log(inc(100))
复制代码

使用场景:回调函数

const arr = [1, 2, 3, 4, 5, 6, 7]

arr.filter(function (item) {
  return item % 2
})

// 回调函数
arr.filter(i => i % 2)
复制代码

箭头函数与其他函数的区别:

  • 没有prototype属性,不能作为新类的构造函数;
  • 从定义自己的环境继承this的值,没有自己的this;
const person = {
  name: 'tom',
  sayHi: function () {
    console.log(this.name);// tom
  },
  sayHi2:  () =>{
    console.log(this.name);// undefined
  }
}

person.sayHi();
person.sayHi2();
复制代码

对象字面量属性简写

const bar = '345'

const obj = {
  foo: 123,
  // bar: bar
  // 属性名与变量名相同,可以省略 : bar
  bar,
  // method1: function () {
  //   console.log('method111')
  // }
  // 方法可以省略 : function
  method1 () {
    console.log('method111')
    // 这种方法就是普通的函数,同样影响 this 指向。
    console.log(this)
  },
  // Math.random(): 123 // 不允许
  // 通过 [] 让表达式的结果作为属性名
  [bar]: 123
}

// obj[Math.random()] = 123

console.log(obj)
obj.method1()
复制代码

Proxy

Proxy类是js中最强大的元编程特性,可以修改js对象的基础行为。让我们能够自己实现基础操作,创建具有普通对象无法企及能力的代理对象。

简单使用:

const person = {
	name: "zce",
	age: 20,
};

/**
 * @person {object} 目标对象
 * @handlers {object} 处理器对象
 */
const personProxy = new Proxy(
	person,
	(handlers = {
		/**
		 * 监视属性读取
		 * @param {*} target 目标对象
		 * @param {*} property 属性
		 */
		get(target, property) {
			return property in target ? target[property] : "default";
			console.log(target, property);
			return 100;
		},
		/**
		 * 监视属性设置
		 * @param {*} target 目标对象
		 * @param {*} property 属性
		 * @param {*} value 要设置的值
		 */
		set(target, property, value) {
			console.log(target, property, value);
			if (property === "age") {
				if (!Number.isInteger(value)) {
					throw new TypeError(`${value} is not an int`);
				}
			}
			target[property] = value;
		},
	})
);

console.log(personProxy);//{ name: 'zce', age: 20 }
personProxy.age = 100;
personProxy.gender = true;
console.log(personProxy);// { name: 'zce', age: 100, gender: true }
复制代码

相关操作方法:

image.png Proxy 对比 Object.defineProperty()

  • Proxy 可以监视读写以外的操作
const person = {
  name: 'zce',
  age: 20
}

const personProxy = new Proxy(person, {
  // 删除的时候执行
  deleteProperty (target, property) {
    console.log('delete', property)
    delete target[property]
  }
})

delete personProxy.age
console.log(person)// { name: 'zce' }
复制代码
  • Proxy 可以很方便的监视数组操作
const list = [];

const listProxy = new Proxy(list, {
	set(target, property, value) {
		console.log("set", property, value);
		target[property] = value;
		return true;
	},
});

listProxy.push(100)
listProxy.push(100)
console.log(list);//[ 100, 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)
复制代码

Reflect

  • 统一的对象操作 API;内部封装了一系列对对象的底层操作。
  • Reflect函数一对一映射了Proxy的处理器方法;
const obj = {
  name: '123',
  age: 18
}

const proxy = new Proxy(obj, {
  get (target, property) {
    return Reflect.get(target, property)
  }
})

console.log(proxy.name)

// 普通对象的方法操作
console.log('name' in obj);
console.log(delete obj['age']);
console.log(Object.keys(obj))


// Reflect API操作
console.log(Reflect.has(obj,'name'));
console.log(Reflect.deleteProperty(obj,'age'));
console.log(Reflect.ownKeys(obj));
复制代码

Reflect API

image.png

Promise

class

class是一个语法糖,class中属性方法写当前对象的原型上。

// class 关键词

// 普通函数当前实例

function Person(name) {
	this.name = name;
}

// 把方法写在Person的原型上
Person.prototype.say = function () {
	console.log(`hi, my name is ${this.name}`);
};

class People {
  constructor(name){
    this.name=name
  }
  // 把方法写在原型上
  say(){
    console.log(`hi, my name is ${this.name}`);//hi, my name is 123
  }
}

new People('123').say()
复制代码

static 方法

使用static关键字创建静态方法,不用new实例化即可调用;

class People {
	constructor(name) {
		this.name = name;
	}
	// 把方法写在原型上
	say() {
		console.log(`hi, my name is ${this.name}`); //hi, my name is 123
	}

	static addName(name) {
		return new People(name);
	}
}

const p = People.addName("me");
p.say(); // hi, my name is me
复制代码

class的extends 继承

class People {
	constructor(name) {
		this.name = name;
	}
	// 把方法写在原型上
	say() {
		console.log(`hi, my name is ${this.name}`); //hi, my name is 123
	}

	static addName(name) {
		return new People(name);
	}
}

class Stu extends People{
  constructor(name,number){
    // 指向父类,调用父类的构造函数
    super(name);
  }

  hello(){
    super.say() // 调用父类成员
    console.log(`my school number is ${this.number}`)
  }
}

const s = new Stu('jack', '100')
s.hello()
复制代码

Set类

Set是一个集合,与数组类似。不同的是:集合没有索引,也不允许重复。

const s = new Set();
// 想集合中添加数据
s.add(1).add(2).add(3).add(4).add(2);
console.log(s); //Set(4) { 1, 2, 3, 4 }
// 遍历Set 集合
s.forEach((i) => console.log(i));

for (let i of s) {
	console.log(i);
}
// 获取集合的长度
console.log(s.size);
// 判断当前集合有无该值
console.log(s.has(100))
// 从集合中删除
console.log(s.delete(3))
console.log(s)
// 清空集合
s.clear()
console.log(s)
复制代码

应用场景:

  • 数组去重
const arr = [1, 2, 1, 3, 4, 1]

// const result = Array.from(new Set(arr))
const result = [...new Set(arr)]

console.log(result)
复制代码

弱引用版本 WeakSet

弱引用版本 WeakSet 差异就是 Set 中会对所使用到的数据产生引用 即便这个数据在外面被消耗,但是由于 Set 引用了这个数据,所以依然不会回收 而 WeakSet 的特点就是不会产生引用, 一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。

Map类

表示一组被称为键的值,每个键都关联着另一个值。

// const obj = {}
// obj[true] = 'value'
// obj[123] = 'value'
// obj[{ a: 1 }] = 'value'

// console.log(Object.keys(obj))
// console.log(obj['[object Object]'])

const m = new Map()

const tom = { name: 'tom' }

m.set(tom, 90)

console.log(m)

console.log(m.get(tom))

// m.has()
// m.delete()
// m.clear()

m.forEach((value, key) => {
  console.log(value, key)
})
复制代码

弱引用版本 WeakMap 差异就是 Map 中会对所使用到的数据产生引用 即便这个数据在外面被消耗,但是由于 Map 引用了这个数据,所以依然不会回收 而 WeakMap 的特点就是不会产生引用, 一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。

Symbol 数据类型

是es6新增的一种原始数据类型,用作非字符串的属性名。表示一种独一无二的符号。

const s = Symbol();
console.log(s)
console.log(typeof s);//symbol

// 两个 Symbol 永远不会相等
console.log(
  Symbol() === Symbol()
);//false

// Symbol 描述文本
console.log(Symbol('foo'))
console.log(Symbol('bar'))
console.log(Symbol('baz'))

// 使用 Symbol 为对象添加用不重复的键
const obj = {
  [Symbol()]: 123
}
obj[Symbol()] = '123'
obj[Symbol()] = '456'
console.log(obj)

// Symbol 模拟实现私有成员
const name = Symbol()
const person = {
  [name]: 'lisi',
  say () {
    console.log(this[name])
  }
}

// 由于无法创建出一样的 Symbol 值,
// 所以无法直接访问到 person 中的「私有」成员
// person[Symbol()]
person.say()
复制代码

迭代器(Iterator)

Symbol.iterator是一个符号值,可作为方法名,让对象变得可迭代。

js中可迭代对象的迭代器方法使用符号Symbol.iterator作为名字。

const set = new Set(['foo', 'bar', 'baz']);

const iterator = set[Symbol.iterator]();

console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

while(true){
  const current = iterator.next();
  if (current.done) {
    break // 迭代已经结束了,没必要继续了
  }
  console.log(current.value)
}
复制代码

实现可迭代对象(Iterable)

const obj = {
	[Symbol.iterator]: function () {
		return {
			next: function () {
				return {
					value: "hello",
					done: true,
				};
			},
		};
	},
};

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 result;
			},
		};
	},
};

for (const item of obj) {
	console.log("循环体",item);
}
复制代码

生成器

使用function * 关键定义

function * foo () {
  console.log('123')
  return 100
}

const result = foo()
console.log(result.next());//{ value: 100, done: true }

function * fn () {
  console.log('1111')
  yield 100
  console.log('2222')
  yield 200
  console.log('3333')
  yield 300
}

const generator = fn()

console.log(generator.next()) // 第一次调用,函数体开始执行,遇到第一个 yield 暂停
console.log(generator.next()) // 第二次调用,从暂停位置继续,直到遇到下一个 yield 再次暂停
console.log(generator.next()) // 。。。
console.log(generator.next()) // 第四次调用,已经没有需要执行的内容了,所以直接得到 undefined
复制代码

应用:

  • 使用 Generator 函数实现 iterator 方法
const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '外语'],
  work: ['喝茶'],
  [Symbol.iterator]: function * () {
    const all = [...this.life, ...this.learn, ...this.work]
    for (const item of all) {
      yield item
    }
  }
}

for (const item of todos) {
  console.log(item)
}
复制代码
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改