• 本周的学习总结


    RESTful的来龙去脉

    • level 0
    • level 1
    • level 2
    • level 3

    level 0 - 面向服务员

    假设我们去麦当劳,想去买个汉堡包,首先和服务员说要一个汉堡包,然后等待喊订单号为123456的客户可以取餐了就行:

    //input 
    {
        "addOrder": {
            "orderName": "hamburger"
        }
    }
    
    //output
    {
        "orderId": "123456"
    }
    

    如果这时候,我们有一张会员卡,我们想先查询一下余额,这时我们应该询问一下:

    //input 
    {
        "queryBalance": {
            "cardId": "886333"
        }
    }
    
    //output
    {
        "balance": "0"
    }
    

    发现卡里没钱,汉堡没得吃了,于是我们取消订单

    {
        "deleteOrder": {
            "orderId": "123456"
        }
    }
    

    level 1 - 面向资源

    还是上面那个例子,小麦当劳店扩张了,单单靠一个服务员肯定不够了,需要多个人来负责,有的人专门负责订单相关的事情:

    /orders
    
    {
        "addOrder": {
            "orderName": "hamburger"
        }
    }
    
    {
        "deleteOrder": {
            "orderId": "123456"
        }
    }
    

    有的人专门负责钱包相关的事情:

    /cards
    
    {
        "queryBalance": {
            "cardId": "886333"
        }
    }
    

    Level 2 - 打上标签

    服务的流程还可以继续优化,因为负责订单的人每次都需要去看是新增订单还是删除订单,很不方便,于是我们规定:

    • 所有新增资源的请求,都在请求上面写上大大的POST,表示这是一笔新增资源的请求,
    • 其他种类的请求,比如查询类的,用GET表示,
    • 删除类的,用DELETE表示,
    • 修改类的,分为2种:
      1.如果这个修改,无论发送多少次,最后一次修改后的资源,总是和第一次修改后的一样,我们使用PUT,
      2.如果这个修改,每次修改都会让这个资源和前一次的不一样,我们使用PATCH或者POST

    于是,上面变成了这样:

    POST /orders
    
    {
        "orderName": "hamburger"
    }
    
    GET /cards/886333
    
    DELETE /orders/123456
    

    level 3 - 完美服务

    忽然有一天,一个客户抱怨说,他下了单,不知道去哪里取消,一个服务员回复说,你不会看我们的宣传单吗?上面写着:DELETE /orders/{orderId},
    顾客反问道,谁会去看那个啊,服务员不服,又说道,你瞎吗... ,然后,两人就打了起来。
    有了上面的教训,我们可以继续优化,客户下了单之后,不仅给他们返回订单的编号,还给顾客返回所有可以对这个订单做的操作,于是变成了下面这样:

    //input
    POST /orders
    
    {
        "orderName": "hamburger"
    }
    
    //output
    {
        "orderId": "123456",
        "link": {
            "rel": "cancel",
            "url": "/order/123456"
        }
    }
    

    这次返回时多了一项link信息,里面包含了一个rel属性和url属性,relrelationship的意思,这里的关系是cancelurl则告诉你如何执行这个cancel操作,接着你就可以这样子来取消订单啦,

    DELETE /orders/123456
    

    上面讲的Level0 ~ Level3,来自Leonard Richardson提出的Richardson Maturity Model
    rustful

    typescript

    原始数据类型

    定义变量时,使用冒号在后面加上它的类型。

    let isDone: boolean = false;
    let decLiteral: number = 6;
    let myName: string = 'Tom';
    //声明一个void类型的变量没有什么用,因为你只能将它赋值为`undefined`和`null`
    let unusable: void = undefined;
    let u: undefined = undefined;
    let n: null = null;
    

    任意值

    1.在任意值上访问任何属性都是允许的

    let anyThing: any = 'hello';
    console.log(anyThing.myName);
    console.log(anyThing.myName.firstName);
    

    2.变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型

    let something;
    something = 'seven';
    something = 7;
    
    something.setName('Tom');
    

    类型推论

    如果没有明确的指定类型,那么TypeScript会依照类型推论(Type Inference)的规则推断出一个类型。

    let myFavoriteNumber = 'seven';
    myFavoriteNumber = 7;
    
    //等价于
    let myFavoriteNumber: string = 'seven';
    myFavoriteNumber = 7;
    

    联合类型

    联合类型(Union Types)表示取值可以为多种类型中的一种。

    let myFavoriteNumber: string | number;
    myFavoriteNumber = 'seven';
    myFavoriteNumber = 7;
    

    TypeScript不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:

    function getLength(something: string | number): number {
        return something.length;
    }
    
    // index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
    //   Property 'length' does not exist on type 'number'.
    

    对象的类型——接口

    TypeScript中,我们使用接口(Interfaces)来定义对象的类型。

    interface Person {
        name: string;
        age: number;
    }
    
    let tom: Person = {
        name: 'Tom',
        age: 25
    };
    

    接口一般首字母大写。有的编程语言中会建议接口的名称加上I前缀。

    注意

    • 赋值的时候,变量的形状必须和接口的形状保持一致,多一些或少一些都不行
    • 可选属性
    interface Person {
        name: string;
        age?: number;
    }
    
    let tom: Person = {
        name: 'Tom'
    };
    
    • 任意属性
    interface Person {
        name: string;
        age?: number;
        [propName: string]: any;
    }
    
    let tom: Person = {
        name: 'Tom',
        gender: 'male'
    };
    
    • 只读属性
    interface Person {
        readonly id: number;
        name: string;
        age?: number;
        [propName: string]: any;
    }
    
    let tom: Person = {
        id: 89757,
        name: 'Tom',
        gender: 'male'
    };
    
    tom.id = 9527;
    
    // index.ts(14,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.
    

    数组的类型

    1.「类型 + 方括号」表示法

    let fibonacci: number[] = [1, '1', 2, 3, 5];
    
    // Type 'string' is not assignable to type 'number'.
    

    2.数组泛型

    let fibonacci: Array<number> = [1, 1, 2, 3, 5];
    

    3.用接口表示数组

    interface NumberArray {
        [index: number]: number;
    }
    let fibonacci: NumberArray = [1, 1, 2, 3, 5];
    

    4.类数组

    function sum() {
        let args: number[] = arguments;
    }
    
    // Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
    
    function sum() {
        let args: {
            [index: number]: number;
            length: number;
            callee: Function;
        } = arguments;
    }
    

    事实上常用的类数组都有自己的接口定义,如IArguments, NodeList, HTMLCollection

    function sum() {
        let args: IArguments = arguments;
    }
    

    函数的类型

    参数定义自己的类型,冒号后面定义返回值

    function sum(x: number, y: number): number {
        return x + y;
    }
    

    观察者模式与订阅发布模式

    很久以前,我去网上寻找vue响应式原理时,第一次知道了Object.defineProperty和这两种设计模式,那会我不懂什么是设计模式,最近在看一些关于设计模式的知识,
    重新看见这两个名词,感觉自己略微懂了,防止忘记,记录一下。

    它们其实非常相似,区别在于观察者模式是发布者直接接触订阅者,而发布订阅模式,发布者和订阅者通过中间平台间接接触

    生活中的观察者模式

    周一刚上班,前端开发李雷就被产品经理韩梅梅拉进了一个钉钉群——“员工管理系统需求第99次变更群”。这个群里不仅有李雷,还有后端开发 A,测试同学 B。三位技术同学看到这简单直白的群名便立刻做好了接受变更的准备、打算撸起袖子开始干了。此时韩梅梅却说:“别急,这个需求有问题,我需要和业务方再确认一下,大家先各忙各的吧”。这种情况下三位技术同学不必立刻投入工作,但他们都已经做好了本周需要做一个新需求的准备,时刻等待着产品经理的号召。

    一天过去了,两天过去了。周三下午,韩梅梅终于和业务方确认了所有的需求细节,于是在“员工管理系统需求第99次变更群”里大吼一声:“需求文档来了!”,随后甩出了"需求文档.zip"文件,同时@所有人。三位技术同学听到熟悉的“有人@我”提示音,立刻点开群进行群消息和群文件查收,随后根据群消息和群文件提供的需求信息,投入到了各自的开发里。上述这个过程,就是一个典型的观察者模式

    观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。

    在我们上文这个钉钉群里,一个需求信息对象对应了多个观察者(技术同学),当需求信息对象的状态发生变化(从无到有)时,产品经理通知了群里的所有同学,以便这些同学接收信息进而开展工作:角色划分 --> 状态变化 --> 发布者通知到订阅者,这就是观察者模式的“套路”。

    在实践中理解定义

    结合我们上面的分析,现在大家知道,在观察者模式里,至少应该有两个关键角色是一定要出现的——发布者订阅者。用面向对象的方式表达的话,那就是要有两个类。

    首先我们来看这个代表发布者的类,我们给它起名叫Publisher。这个类应该具备哪些“基本技能”呢?大家回忆一下上文中的韩梅梅,韩梅梅的基本操作是什么?首先是拉群(增加订阅者),然后是@所有人(通知订阅者),这俩是最明显的了。此外作为群主&产品经理,韩梅梅还具有踢走项目组成员(移除订阅者)的能力。OK,产品经理发布者类的三个基本能力齐了,下面我们开始写代码:

    // 定义发布者类
    class Publisher {
      constructor() {
        this.observers = []
        console.log('Publisher created')
      }
      // 增加订阅者
      add(observer) {
        console.log('Publisher.add invoked')
        this.observers.push(observer)
      }
      // 移除订阅者
      remove(observer) {
        console.log('Publisher.remove invoked')
        this.observers.forEach((item, i) => {
          if (item === observer) {
            this.observers.splice(i, 1)
          }
        })
      }
      // 通知所有订阅者
      notify() {
        console.log('Publisher.notify invoked')
        this.observers.forEach((observer) => {
          observer.update(this)
        })
      }
    }
    

    ok,搞定了发布者,我们一起来想想订阅者能干啥——其实订阅者的能力非常简单,作为被动的一方,它的行为只有两个——被通知、去执行(本质上是接受发布者的调用,这步我们在Publisher中已经做掉了)。既然我们在Publisher中做的是方法调用,那么我们在订阅者类里要做的就是方法的定义:

    // 定义订阅者类
    class Observer {
        constructor() {
            console.log('Observer created')
        }
    
        update() {
            console.log('Observer.update invoked')
        }
    }
    

    最后,一段合并的代码:

    观察者模式

    class Publisher {
      constructor() {
        this.observers = [];
      }
      addObserver(observer) {
        this.observers.push(observer);
      }
      removeObserver(observer) {
        this.observers.forEach((item, index) => {
          if (item === observer) this.observers.splice(index, 1);
        });
      }
      notifyObersers() {
        this.observers.forEach((item) => {
          item.update(this);
        });
      }
    }
    class Observer {
      update(publisher) {
        // 更新操作
      }
    }
    // 在上面两个类中扩展出具体的类
    class PMPublisher extends Publisher {
      constructor() {
        super();
        this.state = null;
        this.observers = [];
      }
      getState() {
        return this.state;
      }
      setState(state) {
        this.state = state;
        this.notifyObersers();
      }
    }
    class PMObserver extends Observer {
      constructor(id) {
        super();
        this.id = id;
        this.state = null;
      }
      update(publisher) {
        this.state = publisher.getState();
        this.work();
      }
      work() {
        console.log(`员工 ${this.id} 接收状态 ${this.state},开始干活啦~`);
      }
    }
    // 测试一下
    const xiaoqiang = new PMPublisher(),
    xiaoting = new PMObserver("#01"),
    xiaoyong = new PMObserver("#02"),
    xiaolong = new PMObserver("#03");
    xiaoqiang.addObserver(xiaoting);
    xiaoqiang.addObserver(xiaoyong);
    xiaoqiang.addObserver(xiaolong);
    xiaoqiang.setState("开会");
    

    发布——订阅模式

    class EventEmitter {
      constructor() {
        this.handlers = {};
      }
    
      // 注册事件监听器
      on(event, callback) {
        if (!this.handlers[event]) this.handlers[event] = [];
        this.handlers[event].push(callback);
      }
    
      // 移除某个事件回调队列里的指定回调函数
      off(event, callback) {
        const callbacks = this.handlers[event],
          index = callbacks.indexOf(callback);
        if (index !== -1) {
          callbacks.splice(index, 1);
        }
      }
    
      // 触发目标事件
      emit(event, ...params) {
        if (this.handlers[event]) {
          this.handlers[event].forEach((callback) => {
            callback(...params);
          });
        }
      }
    
      // 为事件注册单次监听器
      once(event, callback) {
        // 对回调函数进行包装,使其执行完毕自动被移除
        const wrapper = (...params) => {
          callback(...params);
          this.off(event, wrapper);
        };
        this.on(event, wrapper);
      }
    }
    

    简化拷贝写法:给全局JSON对象挂载一个deep

    简化拷贝写法

    解构的属性的key是动态时

    解构的属性的key是动态时
    解构的属性的key是动态时

  • 相关阅读:
    题解报告——垃圾陷阱
    后缀自动机
    计算几何之凸包
    平衡树——treap
    图论--最小费用最大流(MCMF)
    很重要的吐槽!
    图论--网络流初步(最大流,增广路)
    字符串--Trie树(字典树)
    图论--Tarjan求强联通分量
    数据结构--堆
  • 原文地址:https://www.cnblogs.com/wangxi01/p/12807744.html
Copyright © 2020-2023  润新知