es6语法
let和const命令
let
- 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()
判断某个元素是否在数组中
用于判断一个数组是否包含一个特定的值,根据情况,它能够返回 true
或 false
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...in
、Object.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 对象必定处于以下三种状态之一:
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝
- 已兑现(fulfilled):操作成功完成
- 已拒绝(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>
resolve
和reject
1.
resolve
: 将Promise对象的状态从Pending(进行中)
变为Fulfilled(已成功)
2.
reject
: 将Promise对象的状态从Pending(进行中)
变为Rejected(已失败)
3.
resolve
和reject
都可以传入任意类型的值作为实参,表示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模块主要功能由两个命令构成:
export
和import
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
deprecate
或deprecated
装饰器在控制台显示一条警告,表示该方法将废除
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
deprecate
或deprecated
装饰器在控制台显示一条警告,表示该方法将废除
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: 功能废除了