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={};
复制代码
解构赋值
模板字符串
定义:使用反引号来界定的字符串字面量。
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 }
复制代码
相关操作方法:
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
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)
}
复制代码