es6语法

es6语法

let和const命令

let

  1. let声明的变量,只在let命令所在的代码块内有效
{let a = 10;var b = 20;
}
console.log(a); //a is not defined
console.log(b); //20

2.不存在遍历提升现象

var命令会发生变量提升现象,即变量可以在声明之前使用,值为undefined

let声明的变量一定要在声明后使用,否则会报错

//var的情况
console.log(c);//输出undefined
var c = 30;//let的情况
console.log(c);// 报错ReferenceError
let c = 30;

3.不允许重复声明

let在相同作用域内,重复声明同一个变量会报错

let c = 10;
let c = 30;
console.log(c); //报错function func(arg) {let arg; // 报错
}

4.暂时性死区

只要块级作用域内存在let命令,它所声明的变量就“绑定”这个区域,不再受外部的影响

if (true) {// TDZ开始tmp = 'abc'; // ReferenceErrorconsole.log(tmp); // ReferenceErrorlet tmp; // TDZ结束console.log(tmp); // undefinedtmp = 123;console.log(tmp); // 123
}

较隐蔽的暂时性死区

//function bar(x = y, y = 2) {// return [x, y];
//}//bar(); // 报错
function bar(x = 2, y = x) {return [x, y];
}
bar(); // [2, 2]

块级作用域

为什么需要块级作用域?

原因一:内层变量可能会覆盖外层变量

function foo(a){console.log(a);if(1===2){var a = 'hello ';}
}
var a = 10;
foo(a);

原因二:用来计数的循环遍历泄露为全局变量

var arr = []
for(var i = 0; i < 10; i++){arr[i] = function(){return i;}
}
console.log(arr[5]());//10

变量i只用来控制循环,但是循环结束后,它并没有消失,用于变量提升,泄露成了全局变量

解决循环计数问题

//解决方式一:使用闭包
var arr = []
for(var i = 0; i < 10; i++){arr[i] = (function(n){return function(){return n;}})(i)
}
//解决方式二:使用let声明ivar arr = []
for(let i = 0; i < 10; i++){arr[i] = function () {return i;}
}

const

声明一个只读的常量。一旦声明,常量的值就不能改变

const a = 10;
a = 20;//报错const b; //报错

const其本质并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动

const foo = {};// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

默认情况下建议使用const,当知道变量值需要被修改时再改为let即可

模板字符串

传统的 JavaScript 语言,输出模板通常是这样写的

let name = "Kimi";
let greeting = "Hello, " + name + "!";
console.log(greeting); // 输出: Hello, Kimi!

上面的这种写法相当繁琐不方便,ES6引入了模板字符串解决这个问题

let name = "Kimi";
let greeting = `Hello, ${name}!`;
console.log(greeting); // 输出: Hello, Kimi!

解构赋值

数组解构

  • 将数组的单元值快速批量赋值给一系列变量
<script>// 普通的数组let arr = [1, 2, 3]// 批量声明变量 a b c // 同时将数组单元值 1 2 3 依次赋值给变量 a b clet [a, b, c] = arrconsole.log(a); // 1console.log(b); // 2console.log(c); // 3
</script>
  • 数组解构细节
<script>// const pc = ['海尔', '联想', '小米', '方正'];// [hr, lx, mi, fz] = pc// console.log(hr, lx, mi, fz);// function getValue() {//   return [100, 60]// }// [max, min] = getValue()// console.log(max, min);// const pc = ['海尔', '联想', '小米', '方正']// const [hr, lx, mi, fz] = ['海尔', '联想', '小米', '方正']// console.log(hr)// console.log(lx)// console.log(mi)// console.log(fz)// // 请将最大值和最小值函数返回值解构 max 和min 两个变量// function getValue() {//   return [100, 60]// }// const [max, min] = getValue()// console.log(max)// console.log(min)// 1. 变量多, 单元值少 , undefined// const [a, b, c, d] = [1, 2, 3]// console.log(a) // 1// console.log(b) // 2// console.log(c) // 3// console.log(d) // undefined// 2. 变量少, 单元值多// const [a, b] = [1, 2, 3]// console.log(a) // 1// console.log(b) // 2// 3.  剩余参数 变量少, 单元值多// const [a, b, ...c] = [1, 2, 3, 4]// console.log(a) // 1// console.log(b) // 2// console.log(c) // [3, 4]  真数组// 4.  防止 undefined 传递// const [a = 0, b = 0] = [1, 2]// const [a = 0, b = 0] = []// console.log(a) // 1// console.log(b) // 2// 5.  按需导入赋值// const [a, b, , d] = [1, 2, 3, 4]// console.log(a) // 1// console.log(b) // 2// console.log(d) // 4// const arr = [1, 2, [3, 4]]// console.log(arr[0])  // 1// console.log(arr[1])  // 2// console.log(arr[2])  // [3,4]// console.log(arr[2][0])  // 3// 多维数组解构// const arr = [1, 2, [3, 4]]// const [a, b, c] = [1, 2, [3, 4]]// console.log(a) // 1// console.log(b) // 2// console.log(c) // [3,4]const [a, b, [c, d]] = [1, 2, [3, 4]]console.log(a) // 1console.log(b) // 2console.log(c) // 3console.log(d) // 4</script>

注意: js 前面必须加分号情况

在这里插入图片描述

1.立即执行函数

在这里插入图片描述

2.数组解构

在这里插入图片描述

对象解构

  • 将对象属性和方法快速批量赋值给一系列变量
<script>// 普通对象const user = {name: '小明',age: 18};//以前用法//console.log(obj.name)// 批量声明变量 name age// 同时将数组单元值 小明  18 依次赋值给变量 name  ageconst {name, age} = user//解构之后name、age就可以直接用了,不需要对象.属性来使用了console.log(name) // 小明console.log(age) // 18
</script>

多级对象解构:

 <script>// const pig = {//   name: '佩奇',//   family: {//     mother: '猪妈妈',//     father: '猪爸爸',//     sister: '乔治'//   },//   age: 6// }// // 多级对象解构// const { name, family: { mother, father, sister } } = pig// console.log(name)// console.log(mother)// console.log(father)// console.log(sister)const person = [{name: '佩奇',family: {mother: '猪妈妈',father: '猪爸爸',sister: '乔治'},age: 6}]const [{ name, family: { mother, father, sister } }] = personconsole.log(name)console.log(mother)console.log(father)console.log(sister)</script>

函数参数的解构

function add([x, y]){return x + y;
}add([1, 2]); // 3

使用默认值:

function addCart(n,num=0){return n+num;
}
addCart(10);//10
addCart(10,20); //30

用途:

1.从函数返回多个值

函数只能返回一个值,若要返回多个值,只能放在数组或对象中返回

// 返回一个数组function example() {return [1, 2, 3];
}
let [a, b, c] = example();// 返回一个对象function example() {return {foo: 1,bar: 2};
}
let { foo, bar } = example();

2.函数参数的定义

解构赋值可以方便地将一组参数与变量名对应起来

// 参数是一组有次序的值
// function f([x, y, z]) { 
//     console.log(x,y,z);//1 2 3
//  }
// f([1, 2, 3]);// 参数是一组无次序的值
function f({x, y, z}) {console.log(x,y,z);//1 2 3
}
f({z: 3, y: 2, x: 1});

3.提取JSON数据

解构赋值对提取 JSON 对象中的数据,尤其有用

let jsonData = {id: 42,status: "OK",data: [867, 5309]
};let { id, status, data: number } = jsonData;
//对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者
console.log(id, status, number);
// 42, "OK", [867, 5309]

4.输入模块的指定方法

加载模块时,往往需要指定输入哪些方法→解构赋值使得输入语句非常清晰。

const {ajax} = require('xxx')ajax()

函数扩展

函数参数

默认值

ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法

function log(x,y){y = y || 'world';console.log(x,y);
}
log('hello');//hello world
log('hello','china') //hello china
log('hello','')//hello world

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x, y = 'World') {console.log(x, y);
}log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
默认的表达式可以是一个函数*
function getVal(val) {return val + 5;
}
function add2(a, b = getVal(5)) {return a + b;
}
console.log(add2(10));
练习
// 写法一
function m1({x = 0, y = 0} = {}) {return [x, y];
}// 写法二
function m2({x, y} = { x: 0, y: 0 }) {return [x, y];
}

以上两种写法的区别:都对函数的参数设定了默认值;区别:写法一函数参数默认值为空对象,但设置了对象解构赋值的默认值;写法二函数参数默认值为一个具体属性的对象,但没有设置对象解构赋值的默认值

// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]// x 和 y 都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]// x 有值,y 无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]

剩余参数

ES6引入了rest参数(形式为…变量名),用于获取函数的多余参数,主要是为了解决arguments的问题。

按照ES5的写法,如果不确定用户传几个参数,我们肯定是使用arguments(动态参数)伪数组来接收

let book = {title: '学前端',name: 'zcy',age: 18
}function pick(obj) {let result = {};for (let i = 1; i < arguments.length; i++) {result[arguments[i]] = obj[arguments[i]];}return result;
}
let bookData = pick(book, 'title', 'name', 'age');
console.log(bookData);

但在es6中,我们可以获取一个真正的数组。

let book = {title: '学前端',name: 'zcy',age: 18
}function pick(obj, ...args) {console.log(args);  //['title', 'name', 'age']let result = {}; // 定义一个空对象for (let i = 0; i < args.length; i++) {//把对象中的属性值拿过来,给空对象(如果该对象没有这个属性,那么就添加一个)result[args[i]] = obj[args[i]];}return result;
}
let bookData = pick(book, 'title', 'name', 'age');
console.log(bookData);

展开运算符

将一个数组(或对象)分割,并将数组的各个项作为分离的参数传给函数。其实就是把数组或对象拆开

应用场景:

1、比如我要获取数组的最大值,以前我们会使用apply

const arr = [123, 545, 34, 234, 5];
console.log(Math.max.apply(null, arr));

但是现在我们可以这样写

const arr = [123, 545, 34, 234, 5];
console.log(Math.max(...arr));

2、其实也可以把对象里的东西拆开

let obj1 = {x:100, y:200};
let obj2 = {a:1,...obj1,b:2
}
console.log(obj2);  //{a: 1, x: 100, y: 200, b: 2}

箭头函数

基本用法

在ES5中这样定义函数

let f = function(v){return v;
}

在ES6允许使用箭头=>定义函数

let f = v=>v;
//等同于
let f = function(v){return v;
}// 有一个参数
let add = value => value;// 有两个参数
let add = (value,value2) => value + value2;let add = (value1,value2)=>{return value1 + value2;
} 
// 无参数
let fn = () => "hello world";let doThing = () => {}
//如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
let getId = id => ({id: id,name: 'mjj'}) //注意
let obj = getId(1);
对象中的箭头函数

如一个Person对象,里面有eat方法:

let person = {name: "jack",// 以前:eat: function (food) {console.log(this.name + "在吃" + food);},// 箭头函数版:eat2: food => console.log(person.name + "在吃" + food),// 这里拿不到this// 简写版:eat3(food){console.log(this.name + "在吃" + food);}
}
箭头函数this指向

在这里插入图片描述

  • 箭头函数中并不存在this!!!,内部this只能通过查找作用域链来确定
  • 箭头函数默认帮我们绑定外层this的值,所有在箭头函数中this值和外层this是一样的
  • 箭头函数的this引用的就是最近作用域的this
<body><script>// 以前this的指向:  谁调用的这个函数,this 就指向谁// console.log(this)  // window// // 普通函数// function fn() {//   console.log(this)  // window// }// window.fn()// // 对象方法里面的this// const obj = {//   name: 'andy',//   sayHi: function () {//     console.log(this)  // obj//   }// }// obj.sayHi()// 2. 箭头函数的this  是上一层作用域的this 指向// const fn = () => {//   console.log(this)  // window// }// fn()// 对象方法箭头函数 this// const obj = {//   uname: 'pink老师',//   sayHi: () => {//     console.log(this)  // this 指向谁? window//   }// }// obj.sayHi()const obj = {uname: 'pink老师',sayHi: function () {console.log(this)  // objlet i = 10const count = () => {console.log(this)  // obj }count()}}obj.sayHi()</script>
</body>
let PageHandler = {id:123,init:function(){document.addEventListener('click',function(event) {this.doSomeThings(event.type);},false);},doSomeThings:function(type){console.log(`事件类型:${type},当前id:${this.id}`);}
}
PageHandler.init();//解决this指向问题
let PageHandler = {id: 123,init: function () {// 使用bind来改变内部函数this的指向document.addEventListener('click', function (event) {this.doSomeThings(event.type);}.bind(this), false);},doSomeThings: function (type) {console.log(`事件类型:${type},当前id:${this.id}`);}
}
PageHandler.init();let PageHandler = {id: 123,init: function () {// 箭头函数没有this的指向,箭头函数内部的this值只能通过查找作用域链来确定// 如果箭头函数被一个非箭头函数所包括,那么this的值与该函数的所属对象相等,否 则是全局的window对象document.addEventListener('click', (event) => {console.log(this);this.doSomeThings(event.type);}, false);},doSomeThings: function (type) {console.log(`事件类型:${type},当前id:${this.id}`);}
}
PageHandler.init();
箭头函数参数

箭头函数中没有 arguments,只能使用 ... 动态获取实参

<body><script>// 1. 利用箭头函数来求和const getSum = (...arr) => {let sum = 0for (let i = 0; i < arr.length; i++) {sum += arr[i]}return sum}const result = getSum(2, 3, 4)console.log(result) // 9</script>
</body>
箭头函数的作用
  • 使表达更加简洁

     代码解读
    复制代码const isEven = n => n % 2 == 0;
    const square = n => n * n;
    
  • 简化回调函数

     代码解读
    复制代码// 正常函数写法
    [1,2,3].map(function (x) {return x * x;
    });// 箭头函数写法
    [1,2,3].map(x => x * x);
    

注意:

1.箭头函数它不是一个对象,可认为是一个表达式或语法条

2.箭头不能使用使用new关键字来实例化对象

 代码解读
复制代码let Person = ()=>{}
let p1 = new Person();// Person is not a constructor

##对象扩展

基本用法

const name = '张三';
const age = 19;
const person = {name, //等同于name:nameage,// 方法也可以简写sayName() {console.log(this.name);}
}
person.sayName();

这种写法用于函数的返回值,将会非常方便。

function getPoint() {const x = 1;const y = 10;return {x, y};
}getPoint()
// {x:1, y:10}
对象扩展运算符
const [a, ...b] = [1, 2, 3];
a // 1
b // [2, 3]

...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。

 代码解读
复制代码let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

数组扩展

扩展运算符-替代apply()方法

由于扩展运算符可以展开数组,所以不再需要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);

例子1:应用Math.max()方法,简化求出一个数组最大元素

// ES5 的写法
Math.max.apply(null, [14, 3, 77])// ES6 的写法
Math.max(...[14, 3, 77])// 等同于
Math.max(14, 3, 77);

例子2

// ES5 的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);// ES6 的写法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);

from()将伪数组转换为真数组

1.将arguments转换为一个真数组

function add() {let arr = Array.from(arguments);console.log(arr);  //[1, 2, 3]
}
add(1, 2, 3);

2.比如ul里面的好多li的数组集合,转换为真正的数组

<body><ul><li>1</li><li>2</li><li>3</li></ul><script>//  Array.from(lis) 把伪数组转换为真数组const lis = document.querySelectorAll('ul li')// console.log(lis)// lis.pop() 报错const liss = Array.from(lis)liss.pop()console.log(liss)</script>
</body>

也可直接用展开运算符

console.log([...lis]);  // [li, li, li, li]

3.from()还可以有第二个参数(可选),是一个映射函数→允许你在将每个元素添加到新数组时对其进行处理。

语法:

Array.from(arrayLike[, mapFn[, thisArg]])
//arrayLike:一个类似数组的对象
//mapFn(可选):一个映射函数,用于在将元素添加到新数组时对其进行处理
//thisArg(可选):执行映射函数时使用的 this 值

示例:

// 使用字符串创建数组
let str='hello'
// 字符串是一个可迭代对象,因为它具有length属性,且可以通过索引访问每个字符
// Array.from()遍历字符串的每个字符→从索引0-索引4→每个字符("H", "e", "l", "l", "o")被依次添加到新数组
let arr=Array.from(str)
console.log(arr);//输出:['h', 'e', 'l', 'l', 'o']// 使用映射函数对每个元素进行处理
//这个映射函数被调用时,当前元素作为第一个参数,当前元素的索引作为第二个参数
let squares = Array.from({length: 5}, (v, i) => i + 1);
console.log(squares); // 输出: [1, 2, 3, 4, 5]// 使用映射函数对数组的每个元素进行平方处理
let arr = [1, 2, 3, 4, 5];
let squared = Array.from(arr, x => x * x);
console.log(squared); // 输出: [1, 4, 9, 16, 25]

of()将任意数据类型转换为数组

console.log(Array.of('2', [2, 3], { 'a': 1 }, undefined));
//['2', Array(2), {…}, undefined]

find()findiIndex()

1.find()是找出数组中符合条件的第一个数组成员

const array = [1, 2, 3, 4, 5];
const found = array.find(element => element > 3);
console.log(found); // 输出: 4

2.findexIndex()是找出数组中符合条件的第一个数组成员的索引

const array = [1, 2, 3, 4, 5];const index = array.findIndex(element => element > 3);
console.log(index); // 输出: 3

数组中的keys()values()entries()遍历

它们都返回一个遍历器对象,可以用for…of循环进行遍历

  • keys() 用于获取数组索引的遍历
  • values() 用于获取数组值的遍历
  • entries() 用于获取数组键/值对的遍历
const array = ['a', 'b', 'c'];// 遍历键
for (let key of array.keys()) {console.log(key); // 输出: 0 然后是 1 然后是 2
}// 遍历值
for (let value of array.values()) {console.log(value); // 输出: 'a' 然后是 'b' 然后是 'c'
}// 遍历键/值对
for (let entry of array.entries()) {console.log(entry); // 输出: [0, 'a'] 然后是 [1, 'b'] 然后是 [2, 'c']
}

includes()判断某个元素是否在数组中

用于判断一个数组是否包含一个特定的值,根据情况,它能够返回 truefalse

const array = [1, 2, 3, 4, 5];// 检查数组是否包含值 3
console.log(array.includes(3)); // 输出: true// 检查数组是否包含值 6
console.log(array.includes(6)); // 输出: false// 使用 fromIndex 选项
console.log(array.includes(2, 3)); // 输出: false,因为在索引 3 之后找不到值 2

Symbol类型

  • 唯一性:每个通过 Symbol() 创建的符号都是唯一的,即便描述(description)相同,创建的符号也不同。
  • 不可变Symbol 值是不可改变的。
  • 不可枚举:使用 Symbol 定义的属性不会出现在对象的 for...inObject.keys()、或 JSON.stringify() 等操作中。
<script>// 原始数据类型Symbol,它表示独一无二的值// 最大用途:用来定义对象的私有变量const name=Symbol('name')const name2=Symbol('name')console.log(name===name2);//falselet s1=Symbol('s1')console.log(s1);//Symbol(s1)let obj={[s1]:'学前端'}// obj[s1]='学前端'// console.log(obj);//{Symbol(s1): '学前端'}// console.log(obj[s1]);//学前端// 获取Symbol声明的属性let m=Object.getOwnPropertySymbols(obj)console.log(m[0]);//Symbol(s1)let s=Reflect.ownKeys(obj)console.log(s[0]);</script>

Set和Map数据结构

set

<script>// 集合:表示无重复值的有序列表let set =new Set()console.log(set);//Set(0) {size: 0}// 添加元素set.add(2)set.add(4)set.add('4')set.add(['hello','hi','fine'])// 删除元素set.delete(2)// 校验某个值是否在set中console.log( set.has(4));console.log( set.size);// 将set转换成数组let set2=new Set([1,2,3,5,6,3])console.log(set2);//Set(5) {1, 2, 3, 5, 6}// 扩展运算符let arr=[...set2]console.log(arr);//[1, 2, 3, 5, 6]// 1.set中对象的引用无法被释放// let set3=new Set(),obj={}// set3.add(obj)// // 释放当前的资源// obj=null// console.log(set3);// 2.通过弱引用可被释放let set4=new WeakSet(),obj={}set4.add(obj)// 释放当前资源obj=nullconsole.log(set4);
</script>

map

<script>// Map类型是键值对的有序列表,键和值是任意类型// let map=new Map()// map.set('name','张三')// map.set('age',20)// console.log(map.get('name'));// console.log(map.delete('name'));// console.log(map.clear());// map.set(['a',[1,2,3],'hello'])// console.log(map);
</script>

proxy和Reflect

proxy

监听对象属性

在之前,若希望监听一个对象的相关操作,可通过Object.defineProperty 的存储属性来进行监听,它必须去深度遍历对象里的每一个属性

<script>const obj={name:'why',age:18,height:1.88}// 需求:监听对象属性的所有操作// 监听属性的操作// 1.只针对一个属性// let _name=obj.name// Object.defineProperty(obj,'name',{//   set: function(newValue){//     console.log("监听:给name设置了新值:",newValue);//     _name=newValue//   },//   get: function(){//     console.log("监听:获取name的值");//     return _name//   }// })// 2.监听所有的属性:遍历所有的属性,对每一个属性使用definePropertyconst keys=Object.keys(obj)for(const key of keys){let value=obj[key]Object.defineProperty(obj,key,{set: function(newValue){console.log(`监听:给${key}设置了新值`,newValue);value=newValue},get: function(){console.log(`监听:获取${key}的值`);return value}})}console.log(obj.name);console.log(obj.age);obj.name='zcy'obj.age=21</script>

缺点:

1.其设计初衷既不是为了去监听一个完整的对象

2.目前我们只能用它监听属性的设置和获取,不能监听增加和删除

在ES6中,新增了一个Proxy类,就是为了我们监听另外一个对象而生的

先创建一个代理对象,之后对该对象的所有操作,都通过代理对象完成,代理对象可监听我们想要原对象进行哪些操作

 <script>const obj={name:'why',age:18,height:1.88}// 1.创建一个代理对象const proxy = new Proxy(obj,{set:function(target,key,newValue){console.log(`监听:监听${key}的设置值:`,newValue);target[key]=newValue},get:function(target,key){console.log(`监听:监听${key}的获取`);return target[key]}})// 对obj的所有操作,应该去操作objProxyconsole.log(proxy.name);//whyproxy.name='zcy'console.log(proxy.name);//zcy// 新增proxy.address='广州'</script>
其它捕获器的监听

在这里插入图片描述

代码实例:

 <script>const obj={name:'why',age:18,height:1.88}// 1.创建一个代理对象const proxy = new Proxy(obj,{set:function(target,key,newValue){console.log(`监听:监听${key}的设置值:`,newValue);target[key]=newValue},get:function(target,key){console.log(`监听:监听${key}的获取`);return target[key]},deleteProperty:function(target,key){const deleted = delete target[key]console.log(`监听:监听删除${key}属性,删除操作结果:${deleted}`);return delete target[key]},has:function(target,key){console.log(`监听:监听in判断${key}属性`);return key in target}})// deletedelete proxy.name;// hasconsole.log("age"in proxy);</script>

Reflect

提供许多操作JavaScript对象的方法,有点像Object中操作对象的方法

已经有Object,那为啥还需要Reflect嘞?

1.由于早期ECMA规范并未考虑到对象本身的操作如何设计更加规范,故将这些API放在Object身上

2.Object作为一个构造函数,这些操作实际上放到它身上并不太合适

3.ES6中新增Reflect,让这些操作集中到Reflext身上

reflect和Object的区别之一

<script>"use strict"const obj={name:'acy',age:18}Object.defineProperty(obj,'name',{configurable:false})// 1.用以前的方式进行操作// delete obj.name// if(obj.name){//   console.log('name删除成功');// }else{//   console.log('name没有删除成功');// }// 2.Reflectif(Reflect.deleteProperty(obj,'name')){console.log('name删除成功');}else{console.log('name没有删除成功');}</script>

Reflect和Proxy共同完成代理

<script>obj={name:'zcy',age:19}const proxy=new Proxy(obj,{set:function(target,key,newValue,receiver){// target[key]=newValue// 代理对象的目的:不再直接操作原对象// 1.好处一:代理对象的目的:不再直接操作原对象// 2.好处二:Reflect.set有返回布尔值,可以判断本次操作是否成功console.log(receiver);const isSuccess=Reflect.set(target,key,newValue)if(!isSuccess){throw new Error(`set ${key} failure`)}},get:function(target,key,receiver){}})console.log(obj);</script>

receiver的作用:

<script>obj={_name:'zcy',set name(newValue){console.log("this",this);this._name=newValue},get name(){return this._name}}const proxy=new Proxy(obj,{set:function(target,key,newValue,receiver){// target[key]=newValue// 代理对象的目的:不再直接操作原对象// 1.好处一:代理对象的目的:不再直接操作原对象// 2.好处二:Reflect.set有返回布尔值,可以判断本次操作是否成功// 3.好处三:receiver就是我们外面的proxy对象;Reflect.set、get的最后一个参数,可以决定对象访问器setter/getter的this指向// console.log(receiver);console.log('proxy设置方法被调用');const isSuccess=Reflect.set(target,key,newValue,receiver)if(!isSuccess){throw new Error(`set ${key} failure`)}},get:function(target,key,receiver){return Reflect.get(target,key,receiver)}})// 操作代理对象proxy.name='sss'
</script>

迭代器Iterator

  • 迭代器(Iterator)是一种允许你逐个访问一个数据集合中元素的接口。迭代器模式是一种设计模式,主要用于遍历一个聚合对象,如数组,或者某些类似数组的对象(例如 NodeList
  • 一个数据结构只要具有Symbol.iterator属性,就说明有interator接口,就可以被for...of遍历
<script>// Iterator// 是一种新的遍历机制,两个核心// 1.迭代器是一个接口,能快捷的访问数据,通过Symbol.iterator来创建迭代器 通过迭代器的next()获取迭代之后的结果// 2.迭代器是用于遍历数据结构的指针(数据库的游标)// 使用迭代const items=['one','two','three']// 1.创建新的迭代器const ite=items[Symbol.iterator]()console.log(ite.next());//{value: 'one', done: false} done→如果为false表示遍历继续,如果为true,表示遍历完成console.log(ite.next());//{value: 'two', done: false}console.log(ite.next());//{value: 'three', done: false}
</script>

生成器Generator

Generator函数的写法:function* fn() {yield},yield相当于return

Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象

yield

当每次调用next方法,内部指针就从函数头部或上次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止

即Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行

遍历器对象next方法的运行逻辑:

(1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值
(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式
(3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值
(4)如果该函数没有return语句,则返回的对象的value属性值为undefined

function* fn() {console.log('start');yield 2;yield 'hello world';return 'end'
}
let gen = fn();
console.log(gen.next());  //{value: 2, done: false}
console.log(gen.next()); //{value: 'hello world', done: false}
console.log(gen.next());  //{value: 'end', done: true}
console.log(gen.next()); //{value: undefined, done: true}

Generator 函数可以不用yield表达式,此时会变成一个单纯的暂缓执行函数:

function* f() {console.log('执行了!')
}var generator = f();setTimeout(function () {generator.next()
}, 2000);

上面代码中,函数f如果是普通函数,在为变量generator赋值时就会执行。

但是,函数f是一个 Generator 函数,就变成只有调用next方法时,函数f才会执行

Generator和Iterator接口的关系

任何一个对象,只要有Symbol.iterator方法(iterator接口),就可以调用该方法生成一个遍历器(迭代器)对象;

若没有,可给当前对象赋值一个interator接口,再进行遍历

 // 使用场景:为不具备Interator接口的对象提供了遍历操作function* ObjectEntries(obj){// 获取对象所有属性名保存到数组[name,age]const propKeys=Object.keys(obj)for (const propKey of propKeys){yield[propKey,obj[propKey]]}}const obj={name:'acy',age:20}// 给当前对象赋值一个interator接口obj[Symbol.iterator]=ObjectEntriesconsole.log(obj);for(let [key,value] of ObjectEntries(obj)){console.log(`${key},${value}`);}

next方法的参数

yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
如果不传参, 第二次不带参数,那么y = 2 * undefined = NaN,z = undefined / 3 = NaN,第三次不带参数,那么z = undefined, 返回5 + NaN + undefined = NaN
如果传参,那么第二次参数传给上一个yield也就是y = 2 * 12 = 24, value: 24 /3 = 8,第三次参数传给上一个yield也就是z = 13, value: 5 + 24 + 13 = 42

function* foo(x) {var y = 2 * (yield (x + 1));var z = yield (y / 3);return (x + y + z);
}var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}  第二次不带参数,那么y=2*undefined=NaN,z=undefined/3 =NaN
a.next() // Object{value:NaN, done:true}   第三次不带参数,那么z=undefined, 返回5+NaN+undefined=NaNvar b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false } //第二次参数传给上一个yield也就是y=2*12=24,value:24/3=8
b.next(13) // { value:42, done:true } //第三次参数传给上一个yield也就是z=13,value:5+24+13=42

示例代码:

<script>// 1.generator函数 可以通过yield关键字,将函数挂起,为改变执行流程提供了可能,也为异步变成提供方案// 2.它与普通函数的区别// 2.1function后面 函数名之前有个*// 2.2只能在函数内部使用yield表达式,让函数挂起function* func(a){console.log('one');yield 2;console.log('two');yield 3;console.log('end');}// console.log(func());// 返回一个遍历器对象 可以调用next方法// let fn=func()// console.log(fn.next());// console.log(fn.next());// console.log(fn.next());/* 总结:generator函数是分段执行的yield语句是暂停执行next()恢复执行*/function* add(){console.log('start');// x 不是yield '2' 的返回值,它是next()调用 恢复当前yield()执行传入的实参let x = yield '2'console.log('one:'+x);let y = yield '3'console.log('two:'+y);return x+y}const fn = add()console.log(fn.next());//{value: '2', done: false}console.log(fn.next(20));//{value: '3', done: false}console.log(fn.next(30));//{value: 50, done: true}
</script>

Promise对象

基础认识

  • 表示(管理)一个异步操作最终完成(或失败)及其结果值
  • 通过学习Promise,可以知道成功和失败状态,可以关联对应处理函数,了解 axios 内部运作的原理

在这里插入图片描述

Promise三种状态

每个 Promise 对象必定处于以下三种状态之一:

  1. 待定(pending):初始状态,既没有被兑现,也没有被拒绝
  2. 已兑现(fulfilled):操作成功完成
  3. 已拒绝(rejected):操作失败
    在这里插入图片描述
<body><script>/*** 目标:认识Promise状态*/// 1. 创建Promise对象(pending-待定状态)const p = new Promise((resolve, reject) => {// Promise对象创建时,这里的代码都会执行了// 2. 执行异步代码setTimeout(() => {// resolve() => 'fulfilled状态-已兑现' => then()resolve('模拟AJAX请求-成功结果')// reject() => 'rejected状态-已拒绝' => catch()reject(new Error('模拟AJAX请求-失败结果'))}, 2000)})console.log(p)// 3. 获取结果p.then(result => {console.log(result)}).catch(error => {console.log(error)})</script>
</body>

resolvereject

1.resolve : 将Promise对象的状态从 Pending(进行中) 变为 Fulfilled(已成功)

2.reject : 将Promise对象的状态从 Pending(进行中) 变为 Rejected(已失败)

3.resolvereject 都可以传入任意类型的值作为实参,表示 Promise 对象成功(Fulfilled)和失败(Rejected)的值

Promise封装XHR对象

<body><p class="my-p"></p><script>/*** 目标:使用Promise管理XHR请求省份列表*  1. 创建Promise对象*  2. 执行XHR异步代码,获取省份列表*  3. 关联成功或失败函数,做后续处理*/// 1. 创建Promise对象const p = new Promise((resolve, reject) => {// 2. 执行XHR异步代码,获取省份列表const xhr = new XMLHttpRequest()xhr.open('GET', 'http://hmajax.itheima.net/api/province')xhr.addEventListener('loadend', () => {// xhr如何判断响应成功还是失败的?// 2xx开头的都是成功响应状态码if (xhr.status >= 200 && xhr.status < 300) {resolve(JSON.parse(xhr.response))} else {reject(new Error(xhr.response))}})xhr.send()})// 3. 关联成功或失败函数,做后续处理p.then(result => {console.log(result)document.querySelector('.my-p').innerHTML = result.list.join('<br>')}).catch(error => {// 错误对象要用console.dir详细打印!!!console.dir(error)// 服务器返回错误提示消息,插入到p标签显示document.querySelector('.my-p').innerHTML = error.message})</script>
</body>

Promise链式调用

基本用法

依靠 then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束

可解决回调函数嵌套问题

在这里插入图片描述

<body><script>/*** 目标:掌握Promise的链式调用* 需求:把省市的嵌套结构,改成链式调用的线性结构*/// 1. 创建Promise对象-模拟请求省份名字const p = new Promise((resolve, reject) => {setTimeout(() => {resolve('北京市')}, 2000)})// 2. 获取省份名字const p2 = p.then(result => {console.log(result)// 3. 创建Promise对象-模拟请求城市名字// return Promise对象最终状态和结果,影响到新的Promise对象return new Promise((resolve, reject) => {setTimeout(() => {resolve(result + '--- 北京')}, 2000)})})// 4. 获取城市名字p2.then(result => {console.log(result)})// then()原地的结果是一个新的Promise对象console.log(p2 === p)</script>
</body>
解决回调函数地狱问题

每个 Promise 对象中管理一个异步任务,用 then 返回 Promise 对象,串联起来

在这里插入图片描述

<body><form><span>省份:</span><select><option class="province"></option></select><span>城市:</span><select><option class="city"></option></select><span>地区:</span><select><option class="area"></option></select></form><script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script><script>/*** 目标:把回调函数嵌套代码,改成Promise链式调用结构* 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中*/let pname = ''// 1. 得到-获取省份Promise对象axios({url: 'http://hmajax.itheima.net/api/province'}).then(result => {pname = result.data.list[0]document.querySelector('.province').innerHTML = pname// 2. 得到-获取城市Promise对象return axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})}).then(result => {const cname = result.data.list[0]document.querySelector('.city').innerHTML = cname// 3. 得到-获取地区Promise对象return axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})}).then(result => {console.log(result)const areaName = result.data.list[0]document.querySelector('.area').innerHTML = areaName})</script>
</body>

Promise.all 静态方法

合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑

在这里插入图片描述

<script>/*** 目标:掌握Promise的all方法作用,和使用场景* 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并* 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看* code:* 北京-110100* 上海-310100* 广州-440100* 深圳-440300*/// 1. 请求城市天气,得到Promise对象const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100' } })const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100' } })const gzPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100' } })const szPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300' } })// 2. 使用Promise.all,合并多个Promise对象const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])p.then(result => {// 注意:结果数组顺序和合并时顺序是一致console.log(result)const htmlStr = result.map(item => {return `<li>${item.data.data.area} --- ${item.data.data.weather}</li>`}).join('')document.querySelector('.my-ul').innerHTML = htmlStr}).catch(error => {console.dir(error)})</script>

async 函数

基本用法

  • 在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
  • await替代 then 方法来提取 Promise 对象成功状态的结果
  • async/await使得异步代码看起来像同步代码,再也没有回调函数。但是改变不了JS单线程、异步的本质。(异步代码同步化)
<body><form><span>省份:</span><select><option class="province"></option></select><span>城市:</span><select><option class="city"></option></select><span>地区:</span><select><option class="area"></option></select></form><script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script><script>/*** 目标:掌握async和await语法,解决回调函数地狱* 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值* 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)*/// 1. 定义async修饰函数async function getData() {// 2. await等待Promise对象成功的结果const pObj = await axios({url: 'http://hmajax.itheima.net/api/province'})const pname = pObj.data.list[0]const cObj = await axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})const cname = cObj.data.list[0]const aObj = await axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})const areaName = aObj.data.list[0]document.querySelector('.province').innerHTML = pnamedocument.querySelector('.city').innerHTML = cnamedocument.querySelector('.area').innerHTML = areaName}getData()</script>
</body>

捕获错误

用 try catch 捕获同步流程的错误

<script>/*** 目标:async和await_错误捕获*/async function getData() {// 1. try包裹可能产生错误的代码try {const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' })const pname = pObj.data.list[0]const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } })const cname = cObj.data.list[0]const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } })const areaName = aObj.data.list[0]document.querySelector('.province').innerHTML = pnamedocument.querySelector('.city').innerHTML = cnamedocument.querySelector('.area').innerHTML = areaName} catch (error) {// 2. 接着调用catch块,接收错误信息// 如果try里某行代码报错后,try中剩余的代码不会执行了console.dir(error)}}getData()</script>

Class

基本用法

ES6中的class其实就类似于ES5中的构造函数
比如下面两个写法是等价的:

<script>// es5构造函数// function Person(name,age){//   this.name=name//   this.age=age// }// // 给它一些共用的方法// Person.prototype.satName=function(){//   return this.name// }// let p1=new Person('zcy',18)// console.log(p1);// es6class Person{// 实例化的时候会被立即调用constructor(name,age){this.name=namethis.age=age}// 给它赋值方法// sayName(){//   return this.name// }// sayAge(){//   return this.age// }}// 通过Object.assign()一次性向类中添加多个方法Object.assign(Person.prototype, {sayName(){return this.name},sayAge(){return this.age}})let p1=new Person('zcy',18)console.log(p1);</script>

类的继承

1.Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法

2super关键字调用父类中的构造函数,改变父类中的this指向,让父类中的this指向子类

3.子类在继承父类的方法后,可以重写父类的方法

<script>// 使用关键字class Animal{constructor(name,age){this.name=namethis.age=age}sayName(){return this.name}sayAge(){return this.age}}// 创建一个Dogclass Dog extends Animal{constructor(name,age,color){super(name,age)// Animal.call(this,name,age)this.color=color}sayColor(){return `${this.name}${this.age}岁了,它的颜色是${this.color}`}// 重写父类的方法sayName(){// super相当于Animal.prototypereturn this.name+super.sayAge()+this.color}}let d=new Dog('小白',10,'red')console.log(d.sayAge());console.log(d.sayName());</script>

Module 模块化

es6模块主要功能由两个命令构成:exportimport

export用于规定模块的对外接口

import用于输入其它模块提供的功能

export命令

一个模块就是一个独立文件。如果想从在不读取模块内部的某个变量,就必须使用export关键字输出该变量

//module/index.js
export const name = 'zhangsan ';
export const age = 18;
export const color = 'red ';
export const sayName = function() {console.log(fristName);
}//也可以这样
const name = 'zhangsan ';
const age = 18;
const color = 'red ';
const sayName = function() {console.log(fristName);
}
export {name,age,color,sayName}

import命令

使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块

//main.js
import {name,age,color,sayName,fn} from './modules/index.js';

如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名

import * as obj from './modules/index.js';
console.log(obj);

export default 命令

使用export default命令为模块指定默认输出

//export-default.js
export default function(){console.log('foo');
}//或者写成
function foo() {console.log('foo');
}export default foo;

在其它模块加载该模块时,import命令可以为该匿名函数指定任意名字

//import-default.js
import customName from './export-default.js'
customNmae();//foo

如果想在一条import语句中,同事输入默认方法和其他接口,可以写成下面这样

import customName,{add} from 'export-default.js'
//export-default.js
export default function(){console.log('foo');
}export function add(){console.log('add')
}

export default也可以用来输出类

// MyClass.js
export default class Person{ ... }// main.js
import Person from 'MyClass';
let o = new Person();

Decorator

介绍

Decorator(装饰器)是一种设计模式,允许用户在不改变原类和使用继承的情况下,动态地扩展类属性和类方法

这里定义一个士兵,这时候他什么装备都没有

class soldier{ 
}

定义一个得到 AK 装备的函数,即装饰器

function strong(target){target.AK = true
}

使用该装饰器对士兵进行增强

@strong
class soldier{
}

这时候士兵就有武器了

soldier.AK // true

通过上述代码可得知Decorator的优点:

1.代码可读性变强,装饰器命名相当于一个注释

2.在不改变原有代码情况下,对原来功能进行扩展

用法

类的装饰

当对类本身进行装饰的时候,能够接受一个参数,即类本身

将装饰器行为进行分解,大家能够有个更深入的了解

@decorator
class A {}// 等同于class A {}
A = decorator(A) || A;

下面@testable就是一个装饰器,target就是传入的类,即MyTestableClass,实现了为类添加静态属性

@testable
class MyTestableClass {// ...
}function testable(target) {target.isTestable = true;
}MyTestableClass.isTestable // true

如果想要传递参数,可以在装饰器外层再封装一层函数

function testable(isTestable) {return function(target) {target.isTestable = isTestable;}
}@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true@testable(false)
class MyClass {}
MyClass.isTestable // false
类属性的装饰

当对类属性进行装饰的时候,能够接受三个参数:

  • 类的原型对象
  • 需要装饰的属性名
  • 装饰属性名的描述对象

首先定义一个readonly装饰器

function readonly(target, name, descriptor){descriptor.writable = false; // 将可写属性设为falsereturn descriptor;
}

使用readonly装饰类的name方法

class Person {@readonlyname() { return `${this.first} ${this.last}` }
}

相当于以下调用

readonly(Person.prototype, 'name', descriptor);

如果一个方法有多个装饰器,就像洋葱一样,先从外到内进入,再由内到外执行

function dec(id){console.log('evaluated', id);return (target, property, descriptor) =>console.log('executed', id);
}class Example {@dec(1)@dec(2)method(){}
}
// evaluated 1
// evaluated 2
// executed 2
// executed 1

外层装饰器@dec(1)先进入,但是内层装饰器@dec(2)先执行

注意:

装饰器不能用于修饰函数,因为函数存在变量声明情况

var counter = 0;var add = function () {counter++;
};@add
function foo() {
}

编译阶段,变成下面

var counter;
var add;@add
function foo() {
}counter = 0;add = function () {counter++;
};

意图是执行后counter等于 1,但是实际上结果是counter等于 0

使用场景

基于Decorator强大的作用,我们能够完成各种场景的需求,下面简单列举几种:

使用react-redux的时候,如果写成下面这种形式,既不雅观也很麻烦

class MyReactComponent extends React.Component {}export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);

通过装饰器就变得简洁多了

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}

mixins,也可以写成装饰器,让使用更为简洁了

function mixins(...list) {return function (target) {Object.assign(target.prototype, ...list);};
}// 使用
const Foo = {foo() { console.log('foo') }
};@mixins(Foo)
class MyClass {}let obj = new MyClass();
obj.foo() // "foo"

下面再讲讲core-decorators.js几个常见的装饰器

1.@antobind

autobind装饰器使得方法中的this对象,绑定原始对象

import { autobind } from 'core-decorators';class Person {@autobindgetPerson() {return this;}
}let person = new Person();
let getPerson = person.getPerson;getPerson() === person;
// true

2.@readonly

readonly装饰器使得属性或方法不可写

import { readonly } from 'core-decorators';class Meal {@readonlyentree = 'steak';
}var dinner = new Meal();
dinner.entree = 'salmon';
// Cannot assign to read only property 'entree' of [object Object]

3.@deprecate

deprecatedeprecated装饰器在控制台显示一条警告,表示该方法将废除

import { deprecate } from 'core-decorators';class Person {@deprecatefacepalm() {}@deprecate('功能废除了')facepalmHard() {}
}let person = new Person();person.facepalm();
// DEPRECATION Person#facepalm: This function will be removed in future versions.person.facepalmHard();
// DEPRECATION Person#facepalmHard: 功能废除了

再由内到外执行

function dec(id){console.log('evaluated', id);return (target, property, descriptor) =>console.log('executed', id);
}class Example {@dec(1)@dec(2)method(){}
}
// evaluated 1
// evaluated 2
// executed 2
// executed 1

外层装饰器@dec(1)先进入,但是内层装饰器@dec(2)先执行

注意:

装饰器不能用于修饰函数,因为函数存在变量声明情况

var counter = 0;var add = function () {counter++;
};@add
function foo() {
}

编译阶段,变成下面

var counter;
var add;@add
function foo() {
}counter = 0;add = function () {counter++;
};

意图是执行后counter等于 1,但是实际上结果是counter等于 0

使用场景

基于Decorator强大的作用,我们能够完成各种场景的需求,下面简单列举几种:

使用react-redux的时候,如果写成下面这种形式,既不雅观也很麻烦

class MyReactComponent extends React.Component {}export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);

通过装饰器就变得简洁多了

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}

mixins,也可以写成装饰器,让使用更为简洁了

function mixins(...list) {return function (target) {Object.assign(target.prototype, ...list);};
}// 使用
const Foo = {foo() { console.log('foo') }
};@mixins(Foo)
class MyClass {}let obj = new MyClass();
obj.foo() // "foo"

下面再讲讲core-decorators.js几个常见的装饰器

1.@antobind

autobind装饰器使得方法中的this对象,绑定原始对象

import { autobind } from 'core-decorators';class Person {@autobindgetPerson() {return this;}
}let person = new Person();
let getPerson = person.getPerson;getPerson() === person;
// true

2.@readonly

readonly装饰器使得属性或方法不可写

import { readonly } from 'core-decorators';class Meal {@readonlyentree = 'steak';
}var dinner = new Meal();
dinner.entree = 'salmon';
// Cannot assign to read only property 'entree' of [object Object]

3.@deprecate

deprecatedeprecated装饰器在控制台显示一条警告,表示该方法将废除

import { deprecate } from 'core-decorators';class Person {@deprecatefacepalm() {}@deprecate('功能废除了')facepalmHard() {}
}let person = new Person();person.facepalm();
// DEPRECATION Person#facepalm: This function will be removed in future versions.person.facepalmHard();
// DEPRECATION Person#facepalmHard: 功能废除了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/437830.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Cpp::STL—vector类的模拟实现(11)

文章目录 前言一、各函数接口总览二、默认成员函数vector();vector(size_t n, const T& val T( ));template< class InputIterator> vector(InputIterator first, InputIterator last);vector(const vector<T>& v);vector<T>& operator(const v…

Oracle exadata存储节点更换内存操作及报错处理

1.报错信息 在进行Oracle exadata巡检时&#xff0c;发现cell节点有一根内存报错&#xff0c;报错信息如下&#xff1a; 报错内存位置为&#xff1a;CPU1 P1/D2槽位 报错内存信息&#xff1a; 根据报错信息确认内存PN号、大小等息&#xff0c;并将信息反馈公司&#xff0c;及…

【java数据结构】顺序表

【java数据结构】顺序表 一、了解List接口二、顺序表2.1 线性表2.2 顺序表2.2.1 顺序表接口的实现给数组增加新元素判断数组数据是否为满在 pos 位置新增元素判定是否包含某个元素查找某个元素对应的位置获取 pos 位置的元素给 pos 位置的元素设为 value删除第一次出现的关键字…

数据结构:将复杂的现实问题简化为计算机可以理解和处理的形式

整句话的总体意义是&#xff0c;**数据结构是用于将现实世界中的实体和关系抽象为数学模型&#xff0c;并在计算机中表示和实现的关键工具**。它不仅包括如何存储数据&#xff0c;还包括对这些数据的操作&#xff0c;能够有效支持计算机程序的运行。通过这一过程&#xff0c;数…

语言模型发展史

四个阶段 第一阶段&#xff1a;基于规则和统计的语言模型 由人工设计特征并使用统计方法对固定长度的文本窗口序列进行建模分析&#xff0c;这种建模方式也被称为N-gram语言模型。 优点&#xff1a; 1&#xff09;采用极大似然估计, 参数易训练 2&#xff09;完全包含了前n-…

Spring(学习笔记)

<context:annotation-config/>是 Spring 配置文件中的一个标签&#xff0c;用于开启注解配置功能。这个标签可以让 Spring 容器识别并处理使用注解定义的 bean。例如&#xff0c;可以使用 Autowired 注解自动装配 bean&#xff0c;或者使用 Component 注解将类标记为 bea…

虚拟机三种网络模式详解

在电脑里开一台虚拟机&#xff0c;是再常见不过的操作了。无论是用虚拟机玩只有旧版本系统能运行的游戏&#xff0c;还是用来学习Linux、跑跑应用程序都是很好的。而这其中&#xff0c;虚拟机网络是绝对绕不过去的。本篇文章通俗易懂的介绍了常见的虚拟网络提供的三种网络链接模…

鸿蒙OpenHarmony

开源鸿蒙系统编译指南 Ubuntu编译环境配置第一步&#xff1a;Shell 改 Bash第二步&#xff1a;安装Git和安装pip3工具第三步&#xff1a;远程仓配置第四步&#xff1a;拉取代码第五步&#xff1a;安装编译环境第六步&#xff1a;本地编译源码 Windows开发环境配置第一步&#x…

dubbo微服务

一.启动nacos和redis 1.虚拟机查看是否开启nacos和redis docker ps2.查看是否安装nacos和redis docker ps -a3.启动nacos和redis docker start nacos docker start redis-6379 docker ps二.创建三个idea的maven项目 1.第一个项目dubboapidemo 2.1.1向pom.xml里添加依赖 …

x-cmd pkg | qrencode - 命令行生成二维码,小白也能轻松上手!

目录 简介首次用户功能特点竞品和相关项目进一步阅读 简介 qrencode 是一个用于生成二维码的命令行工具。它可以将文本、URL、电话号码等信息转换为二维码图像。生成的二维码图像可以保存为图片文件&#xff0c;方便在电子文档、网页、移动应用等各种场景中使用。 它支持的二维…

深入理解 Solidity 中的支付与转账:安全高效的资金管理攻略

在 Solidity 中&#xff0c;支付和转账是非常常见的操作&#xff0c;尤其是在涉及资金的合约中&#xff0c;比如拍卖、众筹、托管等。Solidity 提供了几种不同的方式来处理 Ether 转账&#xff0c;包括 transfer、send 和 call&#xff0c;每种方式的安全性、灵活性和复杂度各有…

SKD4(note上)

微软提供了图形的界面API&#xff0c;叫GDI 如果你想画某个窗口&#xff0c;你必须拿到此窗口的HDC #include <windows.h> #include<tchar.h> #include <stdio.h> #include <strsafe.h> #include <string>/*鼠标消息 * 键盘消息 * Onkeydown * …

STM32 软件触发ADC采集

0.91寸OLED屏幕大小的音频频谱&#xff0c;炫酷&#xff01; STM32另一个很少人知道的的功能——时钟监测 晶振与软件的关系&#xff08;深度理解&#xff09; STM32单片机一种另类的IO初始化方法 ADC是一个十分重要的功能&#xff0c;几乎任何一款单片机都会包含这个功能&a…

阿里云 SAE Web:百毫秒高弹性的实时事件中心的架构和挑战

作者&#xff1a;胡志广(独鳌) 背景 Serverless 应用引擎 SAE 事件中心主要面向早期的 SAE 控制台只有针对于应用维度的事件&#xff0c;这个事件是 K8s 原生的事件&#xff0c;其实绝大多数的用户并不会关心&#xff0c;同时也可能看不懂。而事件中心&#xff0c;是希望能够…

JS进阶 3——深入面向对象、原型

JS 进阶3——深入面向对象、原型 1.编程思想 面向过程&#xff1a;分析出解决问题的过程&#xff0c;然后用函数将这些步骤一步步封装起来面向对象&#xff1a;将事物分为一个个对象&#xff0c;然后对象之间分工合作 2.构造函数&#xff1a;封装性、面向对象 构造函数方法存…

linux学习--第七天(多路复用IO)

多路复用IO -阻塞IO与非阻塞IO -IO模型 IO的本质时基于操作系统接口来控制底层的硬件之间数据传输&#xff0c;并且在操作系统中实现了多种不同的IO方式&#xff08;模型&#xff09;比较常见的有下列三种&#xff1a; 1.阻塞型IO模型 2.非阻塞型IO模型 3.多路复用IO模型 -阻…

开源项目 - 交通工具检测 yolo v3 物体检测 单车检测 车辆检测 飞机检测 火车检测 船只检测

开源项目 - 交通工具检测 yolo v3 物体检测 单车检测 车辆检测 飞机检测 火车检测 船只检测 开源项目地址&#xff1a;https://gitcode.net/EricLee/yolo_v3 示例&#xff1a;

【C++】多态(下)

个人主页~ 多态&#xff08;上&#xff09;~ 多态 四、多态的原理1、虚表的存储位置2、多态的原理3、动态绑定和静态绑定 五、单继承和多继承关系的虚函数表1、单继承中的虚函数表2、多继承中的虚函数表 六、多态中的一些小tips 四、多态的原理 1、虚表的存储位置 class A {…

开放式耳机哪个品牌好?分享几款不错的开放式蓝牙耳机

相信很多人戴入耳式耳机时间一久&#xff0c;就不是很舒服。经常会有闷热、不透气的感觉&#xff0c;甚至有的朋友会因为佩戴入耳式耳机滋生细菌&#xff0c;导致最后炎症的发生。总之&#xff0c;入耳式耳机真的不适合长时间佩戴&#xff0c;而且佩戴的场景也有很多限制。 那…

一文了解构建工具——Maven与Gradle的区别

目录 一、Maven和Gradle是什么&#xff1f; 构建工具介绍 Maven介绍 Gradle介绍 二、使用时的区别&#xff1a; 1、新建项目 Maven&#xff1a; Gradle&#xff1a; 2、配置项目 Maven&#xff1a; Gradle&#xff1a; 3、构建项目——生成项目的jar包 Gradle&…