装饰器(Decorator)是一种与类相关的语法,用来注释或修改类和类方法。很多面向对象语言都有装饰器这种语法,值得注意的是,js 并不是严格意义上的面向对象语言,称它是一种 “基于对象的语言” 应该更合适。在 es5 中,并没有太多对于装饰器的需求,但在 es6 中,随着 class 作为一种语法糖的出现,装饰器的便利性得到了体现,逐渐变得流行。
装饰器实质是上就是一段功能函数,放在类和类方法的定义前面,但不同于普通函数的是,它的执行是在代码的编译阶段,而普通函数的执行需要等到代码运行时,所以装饰器的实质是在对编程语言进行编程,属于元编程的一种思想。
//使用示意 @decorator_function class ClassName{ ... }
目前装饰器提案处在 Stage 2[2],node 以及各浏览器均不支持装饰器的语法,所以我们需要使用 babel 插件 @babel/plugin-proposal-decorators
来转换代码。
如果使用 webpack 来打包项目,通过简单配置 webpack 就可以使项目支持装饰器语法。
首先下载 webpack
yarn add -D webpack webpack-cli
下载babel依赖
yarn add -D babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-decorators
创建webpack.config.js
//webpack.config.js var path = require('path') module.exports = { mode: "development", entry: { main: "./index.js" }, output: { path: path.resolve(__dirname, "./build"), filename: "build.js" }, module: { rules: [ { test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader' } } ] } }
配置babel支持装饰器语法
//.babelrc { "presets": [ "@babel/preset-env" ], "plugins": [ [ "@babel/plugin-proposal-decorators", { "legacy": true } ] ] }
在这里为了便于演示,简单修改package.json部分字段
{ "scripts": { "build": "webpack", "start": "node ./build/build.js" }, }
接下来就可以在代码中使用装饰器语法了!
PS
如果使用VSCode做编辑器,可能会出现编辑器对装饰器语法依然报错的情况,可以在项目根目录下创建jsconfig.json
配置VSCode对语法的支持。//jsconfig.json { "compilerOptions": { "experimentalDecorators":true, }, "exclude": [ "node_modules", "dist/*" ] }
装饰器 @
后必然需要接一个函数,这个函数需要带一个参数,也就是被装饰的类。
需要注意的是@
后的函数名带不带括号,带括号表示对函数的调用,调用的返回值不一定是函数
@fn class MyClass{ message = 'hello' } function fn(target){ //在类上加入静态属性 target.foo = 'bar' } //注意是类的静态属性 console.log(MyClass.foo) //bar
// 注意decorator后有没有括号,@后一定是一个函数 @fn2(20) class MyClass{ message = 'hello' } function fn2(value){ //返回一个函数 return function(target){ target.count = value } } console.log(MyClass.count) //20
@fn3 class MyClass{ message = 'hello' } function fn3(target){ //target.prototype target.prototype.foo = 'bar_ins' //给类的实例加入属性 } const c1 = new MyClass(); console.log(c1.foo) //bar_ins
class MyClass{ @fn4 message = 'hello' } //修饰类成员,3个参数 function fn4(target, name, descriptor){ console.log(target) //目标类的.prototype console.log(name) //被修饰的类成员的名称:message console.log(descriptor) //被修饰的类成员的描述对象 //descriptor下有一些对成员的属性描述 descriptor.writable = false //只读 descriptor.enumerable = false //不能被遍历 } const c1 = new MyClass(); c1.message = 123 //error,不允许对只读属性赋值