NodeJs Decorator裝飾器

裝飾器的做用

裝飾器(Decorator)是一個函數,用來修改類的行爲。這是ES7的一個提案,目前Babel轉碼器已經支持。api

console.log('start decorate.js');
//對類的裝飾 爲類添加一個靜態屬性
@addCountry
class tank_1 {
  // ...
}
//
function addCountry(target) {
  target.country = "Chinese";
}

console.log(tank_1.country);

//對類的實例裝飾 添加實例屬性 經過目標類的prototype對象操做

@addLevel
class tank_2 {

}

function addLevel(target){
  target.prototype.Level=10;
}
console.log(new tank_2().Level)


//若是以爲一個參數不夠用,能夠在修飾器外面再封裝一層函數。
@addTankType("輕坦")
class tank_3{

}
function addTankType(tankType){
return function(target){
  target.tankType=tankType
}
}
console.log(tank_3.tankType);

//對類中的方法的修飾
/* 修飾器函數readonly一共能夠接受三個參數。 function readonly(target, name, descriptor){ // descriptor對象原來的值以下 // { // value: specifiedFunction, // enumerable: false, // configurable: true, // writable: true // }; descriptor.writable = false; return descriptor; } readonly(Person.prototype, 'name', descriptor); // 相似於 Object.defineProperty(Person.prototype, 'name', descriptor); 修飾器第一個參數是類的原型對象,上例是Person.prototype,修飾器的本意是要「修飾」類的實例,可是這個時候實例還沒生成,因此只能去修飾原型(這不一樣於類的修飾,那種狀況時target參數指的是類自己);第二個參數是所要修飾的屬性名,第三個參數是該屬性的描述對象。 */
class tank_4{

  @shoot
  shoot(a,b){
    console.log("shoot"+a+" "+b)
  }
}

function shoot(target,name,descriptor){
  //descriptor.value:表示tank_4對象的方法shoot(a,b)
  var oldValue = descriptor.value;

  console.log(descriptor.value);
  descriptor.value = function() {
    console.log(`Calling ${name} with`, arguments);
    return oldValue.apply(this, arguments);
  };
  return descriptor;
}

new tank_4().shoot(1,2);



複製代碼

apply的做用

apply方法能劫持另一個對象的方法,繼承另一個對象的屬性


function Person(name,age){   //定義一個類,人類 
    this.name=name;     //名字 
    this.age=age;       //年齡 
    this.sayhello=function(){alert("hello")};
} 
function Print(){            //顯示類的屬性 
    this.funcName="Print"; 
    this.show=function(){      
        var msg=[];
        for(var key in this){ 
            if(typeof(this[key])!="function"){
                msg.push([key,":",this[key]].join(""));
            }
        } 
        alert(msg.join(" "));
    };
} 
function Student(name,age,grade,school){    //學生類 
    Person.apply(this,arguments);
    Print.apply(this,arguments);
    this.grade=grade;                //年級 
    this.school=school;                 //學校 
} 
var p1=new Person("jake",10);
p1.sayhello();
var s1=new Student("tom",13,6,"清華小學");
s1.show();
s1.sayhello();
alert(s1.funcName);

複製代碼

學生類原本不具有任何方法,可是在Person.apply(this,arguments)後,數組

他就具有了Person類的sayhello方法和全部屬性。app

在Print.apply(this,arguments)後就自動獲得了show()方法koa

裝飾器Route

const Router = require('koa-router')
const {resolve} = require('path')
const _ = require('lodash')
const glob = require('glob')
const R = require('ramda')
const Joi = require('joi');

const symbolPrefix = Symbol('prefix')
const routerMap = new Map()

const isArray = c => _.isArray(c)
  ? c
  : [c]

import Msg from "../utils/msg"
import url from 'url';
import mongoose from "mongoose";

const clientModel = mongoose.model("client")

export class Route {
  constructor(app, apiPath) {
    this.app = app
    this.apiPath = apiPath
    this.router = new Router()
  }

  init() {
    //獲取路由文件 require("");
    glob.sync(resolve(this.apiPath, './**/*.js')).forEach(require)

    //獲取校驗參數文件
    glob.sync(resolve(resolve(__dirname, '../check_param'), './**/*.js')).forEach(require)

    for (let [conf, controller] of routerMap) {
      console.log(controller)
      const controllers = isArray(controller)
      let prefixPath = conf.target.symbolPrefix;
      if (prefixPath)
        prefixPath = normalizePath(prefixPath)
      const routerPath = prefixPath + conf.path
      this.router[conf.method](routerPath, ...controllers) //對應的路由地址 找到對應的方法
    }

    this.app.use(this.router.routes())
    this.app.use(this.router.allowedMethods())
  }
}

const normalizePath = path => path.startsWith('/')
  ? path
  : `/${path}`
//對function的裝飾 
//conf :{method:"post",path:"/getUser"} 
//target:class User, key:爲方法名getUser descriptor:class User的keyfunction 具體實現
const router = conf => (target, key, descriptor) => {
  conf.path = normalizePath(conf.path)
  console.log(conf);
  routerMap.set({
    target: target,
    method: conf.method,
    path: conf.path
  }, target[key])
}
//對類的裝飾 用於初始化路由
export const ctrl = path => target => (target.prototype.symbolPrefix = path)
/* const ctrl=function(path){ return function(target){ target.prototype.symbolPrefix=path; } }*/
//對方法的裝飾 用戶初始化路由
export const get = path => router({method: 'get', path: path})
/* const get_1 =function(path){ return function (target, key, descriptor) { path = normalizePath(path) console.log(conf); routerMap.set({target: target,method: "get",path: path}, target[key]);//<路由類,以及路由類對應的方法> } }*/
export const post = path => router({method: 'post', path: path})

const decorate = (target, key, descriptor, middleware) => {
  //let [target, key, descriptor] = args

  target[key] = isArray(target[key])

  target[key].unshift(middleware)
  //console.log(descriptor);
  // return descriptor
}

//const convert = middleware => (...args) => decorate(args, middleware)
//對方法的裝飾 用於添加中間件
const convert = function(middleware) {
  return function(target, key, descriptor) {
    //decorate(args, middleware)
    target[key] = isArray(target[key])

    target[key].unshift(middleware)
  }
}

export const required=function(rules){
  return function(middleware){
    return function(target,key,descriptor){
    target[key] = isArray(target[key])
    target[key].unshift(middleware)
    return descriptor
    }
  }
}
export const check = rules => convert(async (ctx, next) => {
  let errors = []
  if (!Array.isArray(rules)) {
    console.error("@check rules必須爲數組類型");
  }
  if (rules.length < 3) {
    console.error("@check 必須傳遞三個參數");
  }
  let param_position = rules[0];
  if(param_position!="body"&&param_position!="query"){
    console.error("param_position should be \"body\" or query");
  }
  //console.log(param_position);
  let check_file = rules[1];
  //console.log(check_file);
  let check_func = rules[2];
  //console.log(check_func);
  let content = ctx.request[param_position];
  //console.log(content);

  // console.log(check_func);
  let schema = null;
  try {
    schema = require(resolve(__dirname, '../check_param/' + check_file))[check_func];
  } catch (error) {
    console.error("@check 參數傳遞有誤");
    console.error(error);
  }
  //console.log(schema);
  const result = Joi.validate(content, schema);
  //console.log(result.error);
  if (result.error) {

    ctx.body = new Msg().paramError(result.error.details[0].message);
    return;
  }

  await next()
})

export const auth=()=>convert(async(ctx,next)=>{
  let decoded=ctx.state.decoded;
  let {client_id}=decoded;
  let _client=await clientModel.findOne({client_id:client_id}).lean();
  if(_client&&_client.admin_type==0){
  //沒有權限訪問
  ctx.body=new Msg().send_403();
  return;
  }
  await next();
})

複製代碼
相關文章
相關標籤/搜索