zoukankan      html  css  js  c++  java
  • 本周的学习总结

    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是动态时

  • 相关阅读:
    事务传播机制,搞懂。
    洛谷 P1553 数字反转(升级版) 题解
    洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is Here 题解
    洛谷 P1055 ISBN号码 题解
    洛谷 P2141 珠心算测验 题解
    洛谷 P1047 校门外的树 题解
    洛谷 P1980 计数问题 题解
    洛谷 P1008 三连击 题解
    HDU 1013 题解
    HDU 1012 题解
  • 原文地址:https://www.cnblogs.com/wangxi01/p/12807744.html
Copyright © 2011-2022 走看看