前言
美国大选又来了,比二人转好看多了,有一段听biden说他儿子在伊拉克咋的咋的,然后川川一直在打断biden,我就听biden一直念叨my son, my son的,正在跑步的我以为开始飙脏话了~不知道这次debate我川会说出多少个China来~那么今天我们就结合川川来用一下js中的decorator来实现一个川普的Class,并对这个class的方法 做一下debounce 和 throttle。
什么是decorator(装饰器)
顾名思义,就是用来装饰你输入的东西的(这个东西可以是类,可以是类的属性),其实可以理解成就是一个wrapper(not rapper)。
在ES6之前没有Class
的概念,所以装饰器没啥作用,在ES6提出之后呢,你会发现比方说我们有多个类需要一些共享的行为之后呢,这个装饰器就会让你的代码更优雅一些。最早接触decorator其实应该是在ts当中,后来有一次面试被问到了装饰器的问题。万万没想到js中也有。
使用方法
因为现在浏览器中还是无法使用decorator的语法,还处于stage2貌似,所以需要加一个babel的插件,在webpack的js rules中加入这个插件@babel/plugin-proposal-decorators
并安装。
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins:[['@babel/plugin-proposal-decorators', { legacy: true }]]
}
}
}
然后就可以愉快的玩耍了~
简单理解decorator其实就是一个方法,将输入的东西进行装饰。如下面的代码:
@decorator
class Trump{
// Trmup是被修饰的类
@decorator
Shouting(){console.log('China!')}
// Shouting是被修饰的方法
}
// decorator就是修饰器
function decorator(target, key, descriptor){
}
使用很简单,就是加上@decoratorName
以后就能够对下面的东西进行修饰。
注意,装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。
---本句话复制自阮一峰的ECMAScript6入门
修饰类
// Trump是被修饰的类
@decorator
class Trump{
shouting(){console.log(`make ${this.country} great again`)}
}
function decorator(target, key, descriptor){
// 添加静态属性
target.identification = 'Chinese'
// 为实例添加属性
target.prototype.country = 'China'
}
console.log(Trump.identification)
// 输出Chinese
let trump = new Trump()
trump.shouting()
// 输出make China great again
console.log(trump.country)
//输出China
- 当修饰器修饰类的时候三个参数
target
,key
,descriptor
后面两个都是undefine,只有第一个参数target是class自己,所以我们先往类上添加了一个静态属性indentification
。 - 又因为
Trump.prototype === trump.__proto__
, 所以使用target.prototype
往实例trump
的原型链上添加了属性country
- 实例的方法
shouting
调用了this.country
,最终输出"make China great again" 。thank you president Trump!
修饰类中的属性/方法
下面我们再次使用trump类,其中有三个方法,分别为shouting
, repeating
, throttle
,
repeating
方法做个debounce,频率太快就闭嘴yielding
方法做个截流,每秒只允许说一次- 其中我们让
shouting
方法不可更改
class Trump{
@readonly
shouting(){console.log(`sleepy joe`)}
@debounce(2000)
// 如果2s内重复说的话就禁言
repeating(){console.log('China!')}
@throttle(1000)
// 频率太快的只允许1s说一次
yielding(){console.log(`Fake news!`)}
}
function readonly(target, key, descriptor){
descriptor.writable = false
descriptor.enumerable = true
descriptor.configurable = true
return descriptor
}
function debounce(time){
return function(target, key, descriptor) {
let debounceId = true
let func = descriptor.value
descriptor.value = () => {
clearTimeout(debounceId)
debounceId = setTimeout(() => {
func.call(this)
}, time)
}
}
}
function throttle(time){
return function(target, key, descriptor) {
let throttleId = null
let func = descriptor.value
descriptor.value = () => {
if(throttleId) return
throttleId = setTimeout(() => {
func.call(this)
throttleId = null
}, time)
}
}
}
let trump = new Trump()
setInterval(() => {
// debounce
trump.repeating()
}, 200)
// 频率太快,没有打印China
setInterval(() => {
// throttle
trump.yielding() // fake news
}, 200)
// 每1秒才打印一次
trump.shouting = () => {
console.log('sleepy Trump')
}
// 无法改变shouting的value
// Uncaught TypeError: Cannot assign to read only property 'shouting' of object '#<Trump>'
与装饰类不同,当我们装饰类中的属性或者方法的时候,装饰器的三个参数全部有值,
target
实际上是类的原型对象,也就是Trump.prototype,装饰器的本意是要“装饰”类的实例,但是这个时候实例还没生成,所以只能去装饰原型(这不同于类的装饰,那种情况时target参数指的是类本身)
---本句话复制自阮一峰的ECMASript6。key
是方法名字,descriptor
实际上就是Object.defineProperty(Trump.prototype, "shouting", descriptor)
中的descriptor。
Final
decorator能让你的代码更加整洁,并且能够让类在他们之间共用一些逻辑~妙哉妙哉