1. 什么是面向对象 #

1.1 抽象(Abstraction) #

interface IStorage {
    save(key: any, value: any): void;
    read(key: any): void;
}
class LocalStorage implements IStorage {
    save(key: any, value: any) {
        localStorage.setItem(key, value);
    }
    read(key: any) {
        return localStorage.getItem(key);
    }
}
class User {
    constructor(public name: string, public storage: IStorage) {

    }
    save() {
        this.storage.save('userInfo', JSON.stringify(this));
    }
    read() {
        return this.storage.read('userInfo');
    }
}
let user = new User('张三', new LocalStorage());
user.save();
user.read();

1.2 继承 #

export { };
class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    eat() {
        console.log(`${this.name} eat`)
    }
}
let animal = new Animal('动物');
animal.eat();

class Dog extends Animal {
    age: number;
    constructor(name: string, age: number) {
        super(name);
        this.age = age;
    }
    speak() {
        console.log(`${this.name} is barking!`);
    }
}
let dog = new Dog('🐶', 5);
dog.eat();
dog.speak();

过度使用继承或者说继承层次过深会导致代码可读性、可维护性变差 子类和父类高度耦合,修改父类的代码,会直接影响到子类

1.3 封装 #

1control 2control

class Animal {
    public name: string;
    protected age: number;
    private weight: number;
    constructor(name: string, age: number, weight: number) {
        this.name = name;
        this.age = age;
        this.weight = weight;
    }
}
class Person extends Animal {
    private money: number;
    constructor(name: string, age: number, weight: number, money: number) {
        super(name, age, weight);
        this.money = money;
    }
    getName() {
        console.log(this.name);
    }
    getAge() {
        console.log(this.age);
    }
    getWeight() {
        console.log(this.weight);
    }
    getMoney() {
        console.log(this.money);
    }
}
let p = new Person('zfpx', 9, 100, 100);
console.log(p.name);
console.log(p.age);
console.log(p.weight);

1.4 多态 #

class Animal {
    public name: string;
    protected age: number;
    private weight: number;
    constructor(name: string, age: number, weight: number) {
        this.name = name;
        this.age = age;
        this.weight = weight;
    }
    speak() {
        throw new Error('此方法必须由子类实现!');
    }
}
class Person extends Animal {
    private money: number;
    constructor(name: string, age: number, weight: number, money: number) {
        super(name, age, weight);
        this.money = money;
    }
    getName() {
        console.log(this.name);
    }
    getAge() {
        console.log(this.age);
    }
    getMoney() {
        console.log(this.money);
    }
    speak() {
        console.log('你好!');
    }

}
class Dog extends Animal {
    constructor(name: string, age: number, weight: number) {
        super(name, age, weight);
    }
    speak() {
        console.log('汪汪汪!');
    }
}
let p = new Person('zfpx', 10, 10, 10);
p.speak();
let d = new Dog('zfpx', 10, 10);
d.speak();

2. 设计原则 #

2.1 什么是设计? #

2.2 SOLID五大设计原则 #

首字母 指代 概念
S 单一职责原则 单一功能原则认为对象应该仅具有一种单一功能的概念
O 开放封闭原则 开闭原则认为软件体应该是对于扩展开放的,但是对于修改封闭的的概念
L 里氏替换原则 里氏替换原则认为程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的的概念
I 接口隔离原则 接口隔离原则认为多个特定客户端接口要好于一个宽泛用途的接口的概念
D 依赖反转原则 依赖反转原则认为一个方法应该遵从依赖于抽象而不是一个实例的概念,依赖注入是该原则的一种实现方式。

2.2.1 O 开放封闭原则 #

class Customer {
    constructor(public rank: string) { }
}
class Product {
    constructor(public name: string, public price: number) {

    }
    cost(customer: Customer) {
        switch (customer.rank) {
            case 'member':
                return this.price * .8;
            case 'vip':
                return this.price * .6;
            default:
                return this.price;
        }
    }
}
let p1 = new Product('笔记本电脑', 1000);
let member = new Customer('member');
let vip = new Customer('vip');
let guest = new Customer('guest');
console.log(p1.cost(member));
console.log(p1.cost(vip));
console.log(p1.cost(guest));
class Customer {
+    constructor(public rank: string, public discount: number = 1) { }
+    getDiscount() {
+        return this.discount;
+    }
}
class Product {
    constructor(public name: string, public price: number) {

    }
    cost(customer: Customer) {
-        /*  switch (customer.rank) {
-             case 'member':
-                 return this.price * .8;
-             case 'vip':
-                 return this.price * .6;
-             default:
-                 return this.price;
-         } */
+        return this.price * customer.getDiscount();
    }
}
+let p1 = new Product('笔记本电脑', 1000);
+let member = new Customer('member', .8);
+let vip = new Customer('vip', .6);
let guest = new Customer('guest');
console.log(p1.cost(member));
console.log(p1.cost(vip));
console.log(p1.cost(guest));
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
let instance: AxiosInstance = axios.create();
instance.interceptors.request.use((config: AxiosRequestConfig) => {
    config.url = 'http://localhost:8080' + config.url;
    return config;
});

instance.interceptors.response.use(response => {
    if (response.status !== 200 || response.data.code != 0) {
        return Promise.reject(response);
    } else {
        return response.data.data;
    }
})
/**
 * {code:0,data:{id:1,name:'zhufeng'}}
 */
instance({
    url: '/api/users'
}).then(result => {
    console.log(result);
}, error => {
    console.error(error);
});

2.2.2 S 单一职责原则 #

product

class Product {
    public name: string;
-    public categoryName: string;
-    public categoryIcon: string;
+     public category:Category;
}
+class Category {
+    public name: string;
+    public icon: string;
+}

2.2.3 L 里氏替换原则 #

Substitution

abstract class AbstractDrink {
    abstract getName(): string;
}
class CocaCola extends AbstractDrink {
    getName(): string {
        return '可乐';
    }
}
class Sprite extends AbstractDrink {
    getName(): string {
        return '雪碧';
    }
}
class Fanta extends AbstractDrink {
    getName(): string {
        return '芬达';
    }
}
class Customer {
    drink(drink: AbstractDrink) {
        console.log('喝' + drink.getName());
    }
}
let customer = new Customer();
let cocaCola = new CocaCola();
let sprite = new Sprite();
let fanta = new Fanta();
customer.drink(cocaCola);
customer.drink(sprite);
customer.drink(fanta);
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
    render() {
        return (
            <div>App </div>
        )
    }
}
let element = React.createElement(App);
ReactDOM.render(element, document.getElementById('root'));
abstract class AbstractDrink {
    abstract getName(): any;
}
class CocaCola extends AbstractDrink {
    getName(): any {
        return 100;
    }
}

2.2.4 D 依赖倒置原则 #

Inversion

abstract class GirlFriend {
    public age: number;
    public height: number;
    public abstract cook(): void;
}
class LinZhiLing extends GirlFriend {
    public cook(): void {

    }
}
class HanMeiMei extends GirlFriend {
    public cook(): void {

    }
}
class SingleDog {
    constructor(public girlFriend: GirlFriend) {

    }
}
let s1 = new SingleDog(new LinZhiLing());
let s2 = new SingleDog(new HanMeiMei());
import { createStore } from 'redux';
let store = createStore(state => state);
export interface Action<T = any> {
    type: T
}
export interface AnyAction extends Action {
    // Allows any extra properties to be defined in an action.
    [extraProps: string]: any
}
let action: AnyAction = { type: 'increment', payload: 5 }
store.dispatch(action);

2.2.5 I 接口隔离原则 #

interface IUserManager {
    updateUserInfo(): void;
    updatePassword(): void;
}
interface IProductManager {
    updateProduct(): void;
    updatePrice(): void;
}

play

interface Running {
    run(): void;
}
interface Flying {
    fly(): void;
}
interface Swimming {
    swim(): void;
}
class Automobile implements Running, Flying, Swimming {
    run() { }
    fly() { }
    swim() { }
}

2.3 迪米特法则 #

Demeter

class Salesman {
    constructor(public name: string) {

    }
    sale() {
        console.log(this.name + ' 销售中....');
    }
}
class SaleManager {
    private salesmen: Array<Salesman> = [new Salesman('张三'), new Salesman('李四')];
    sale() {
        this.salesmen.forEach(salesman => salesman.sale());
    }
}
class CEO {
    private saleManager: SaleManager = new SaleManager();
    sale() {
        this.saleManager.sale();
    }
}
let ceo = new CEO();
ceo.sale();

2.4 合成复用原则 #

2.4.1 类的关系 #

xinyuanjieyi

composite

2.4.2 合成复用原则 #

class Cooker {
    cook() {

    }
}
class Person {
    private cooker: Cooker = new Cooker();
    cook() {
        this.cooker.cook();
    }
}

2.5 总结 #

2.6 如何写出好代码? #

3. 23种设计模式 #

3.1 创建型 #

3.2 结构型模式 #

3.3 行为型 #

4. 工厂模式 #

4.1 简单工厂模式 #

simplefatory2

4.1.2 代码 #

abstract class Coffee {
    constructor(public name: string) {

    }
}
class AmericanoCoffee extends Coffee {
    constructor(public name: string) {
        super(name);
    }
}
class LatteCoffee extends Coffee {
    constructor(public name: string) {
        super(name);
    }
}
class CappuccinoCoffee extends Coffee {
    constructor(public name: string) {
        super(name);
    }
}

class Café {
    static order(name: string) {
        switch (name) {
            case 'Americano':
                return new AmericanoCoffee('美式咖啡');
            case 'Latte':
                return new LatteCoffee('拿铁咖啡');
            case 'Cappuccino':
                return new LatteCoffee('卡布奇诺');
            default:
                return null;
        }
    }
}
console.log(Café.order('Americano'));
console.log(Café.order('Latte'));
console.log(Café.order('Cappuccino'));

4.1.3 缺点 #

4.1.3 前端应用场景 #

4.1.3.1 jQuery #
class jQuery{
    constructor(selector){
        let elements = Array.from(document.querySelectorAll(selector));
        let length = elements?elements.length:0;
        for(let i=0;i<length;i++){
            this[i]=elements[i];
        }
        this.length = length;
    }
    html(html){
        if(html){
           this[0].innerHTML=html;
        }else{
          return this[0].innerHTML;
        }
    }
}
window.$ = function(selector){
   return new jQuery(selector);
}
4.1.3.2 React #
export function createElement(type, config, children) {
    return ReactElement(
        type,
        key,
        ref,
        self,
        source,
        ReactCurrentOwner.current,
        props,
    );
}

4.2 工厂方法模式 #

4.2.1 类图 #

factorymethod

4.2.2 代码 #

export { }
abstract class Coffee {
    constructor(public name: string) {

    }
}
abstract class Factory {
    abstract createCoffee(): Coffee;
}
class AmericanoCoffee extends Coffee {
    constructor(public name: string) {
        super(name);
    }
}

class AmericanoCoffeeFactory extends Factory {
    createCoffee() {
        return new AmericanoCoffee('美式咖啡')
    }
}

class LatteCoffee extends Coffee {
    constructor(public name: string) {
        super(name);
    }
}
class LatteCoffeeFactory extends Factory {
    createCoffee() {
        return new LatteCoffee('拿铁咖啡')
    }
}
class CappuccinoCoffee extends Coffee {
    constructor(public name: string) {
        super(name);
    }
}
class CappuccinoFactory extends Factory {
    createCoffee() {
        return new CappuccinoCoffee('卡布奇诺')
    }
}
class Café {
    static order(name: string) {
        switch (name) {
            case 'Americano':
                return new AmericanoCoffeeFactory().createCoffee();
            case 'Latte':
                return new LatteCoffeeFactory().createCoffee();
            case 'Cappuccino':
                return new CappuccinoFactory().createCoffee();
            default:
                return null;
        }
    }
}
console.log(Café.order('Americano'));
console.log(Café.order('Latte'));
console.log(Café.order('Cappuccino'));

4.2.3 应用场景 #

4.2.3.1 React #

4.3 抽象工厂模式 #

4.3.1 组成角色 #

4.3.1 类图 #

abstractfactory3

abstractfactory2

4.3.2 代码 #

export { };
abstract class AmericanoCoffee { }
abstract class LatteCoffee { }
abstract class CappuccinoCoffee { }

class StarbucksAmericanoCoffee extends AmericanoCoffee { }
class StarbucksLatteCoffee extends LatteCoffee { }
class StarbucksCappuccinoCoffee extends CappuccinoCoffee { }

class LuckinAmericanoCoffee extends AmericanoCoffee { }
class LuckinLatteCoffee extends LatteCoffee { }
class LuckinCappuccinoCoffee extends CappuccinoCoffee { }

abstract class CafeFactory {
    abstract createAmericanoCoffee(): AmericanoCoffee;
    abstract createLatteCoffee(): LatteCoffee;
    abstract createCappuccinoCoffee(): CappuccinoCoffee;
}
class StarbucksCafeFactory extends CafeFactory {
    createAmericanoCoffee() {
        return new StarbucksAmericanoCoffee();
    }
    createLatteCoffee() {
        return new StarbucksLatteCoffee();
    }
    createCappuccinoCoffee() {
        return new StarbucksCappuccinoCoffee();
    }
}
class LuckinCafeFactory extends CafeFactory {
    createAmericanoCoffee() {
        return new LuckinAmericanoCoffee();
    }
    createLatteCoffee() {
        return new LuckinLatteCoffee();
    }
    createCappuccinoCoffee() {
        return new LuckinCappuccinoCoffee();
    }
}

let starbucksCafeFactory = new StarbucksCafeFactory();
console.log(starbucksCafeFactory.createAmericanoCoffee());
console.log(starbucksCafeFactory.createCappuccinoCoffee());
console.log(starbucksCafeFactory.createLatteCoffee());

let luckinCafeFactory = new LuckinCafeFactory();
console.log(luckinCafeFactory.createAmericanoCoffee());
console.log(luckinCafeFactory.createCappuccinoCoffee());
console.log(luckinCafeFactory.createLatteCoffee());

5.单例模式 #

5.1 类图 #

singleobject

5.2 代码 #

5.2.1 单例模式 #
export { };
class Window {
    private static instance: Window;
    private constructor() { }
    static getInstance() {
        if (!Window.instance) {
            Window.instance = new Window();
        }
        return Window.instance;
    }
}
//new Window();
var w1 = Window.getInstance();
var w2 = Window.getInstance();
console.log(w1 === w2);
5.2.2 ES5单例模式 #
interface Window {
    hello: any
}
function Window() { }
Window.prototype.hello = function () {
    console.log('hello');
}
Window.getInstance = (function () {
    let window: Window;
    return function () {
        if (!window)
            window = new (Window as any)();
        return window;
    }
})();
let window = Window.getInstance();
window.hello();
5.2.3 透明单例 #
let Window = (function () {
    let window: Window;
    let Window = function (this: Window) {
        if (window) {
            return window;
        } else {
            return (window = this);
        }
    }
    Window.prototype.hello = function () {
        console.log('hello');
    }
    return Window;
})();

let window1 = new (Window as any)();
let window2 = new (Window as any)();
window1.hello();
console.log(window1 === window2)
5.2.4 单例与构建分离 #
export { }
interface Window {
    hello: any
}
function Window() {
}
Window.prototype.hello = function () {
    console.log('hello');
}

let createInstance = (function () {
    let instance: Window;
    return function () {
        if (!instance) {
            instance = new (Window as any)();
        }
        return instance;
    }
})();

let window1 = createInstance();
let window2 = createInstance();
window1.hello();
console.log(window1 === window2)
5.2.5 封装变化 #
export { }
function Window() {

}
Window.prototype.hello = function () {
    console.log('hello');
}

let createInstance = function (Constructor: any) {
    let instance: any;
    return function (this: any) {
        if (!instance) {
            Constructor.apply(this, arguments);
            Object.setPrototypeOf(this, Constructor.prototype)
            instance = this;
        }
        return instance;
    }
};
let CreateWindow: any = createInstance(Window);
let window1 = new CreateWindow();
let window2 = new CreateWindow();
window1.hello();
console.log(window1 === window2)

5.3 场景 #

5.3.2 commonjs #
(function(modules) {
  // webpack的启动函数
  //模块的缓存
  var installedModules = {};
  //定义在浏览器中使用的require方法
  function __webpack_require__(moduleId) {
    //检查模块是否在缓存中
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    //创建一个新的模块并且放到模块的缓存中
    var module = (installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    });

    //执行模块函数
    modules[moduleId].call(
      module.exports,
      module,
      module.exports,
      __webpack_require__
    );

    //把模块设置为已经加载
    module.l = true;

    //返回模块的导出对象
    return module.exports;
  }
}
5.3.2 jQuery #
if(window.jQuery!=null){
  return window.jQuery;
}else{
    //init~~~~~~~
}
5.3.3 模态窗口 #
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <button id="show-button">显示模态窗口</button>
    <button id="hide-button">隐藏模态窗口</button>
    <script>
        class Login {
            constructor() {
                this.element = document.createElement('div');
                this.element.innerHTML = (
                    `
            用户名 <input type="text"/>
            <button>登录</button>
            `
                );
                this.element.style.cssText = 'width: 100px; height: 100px; position: absolute; left: 50%; top: 50%; display: block;';
                document.body.appendChild(this.element);
            }
            show() {
                this.element.style.display = 'block';
            }
            hide() {
                this.element.style.display = 'none';
            }
        }
        Login.getInstance = (function () {
            let instance;
            return function () {
                if (!instance) {
                    instance = new Login();
                }
                return instance;
            }
        })();

        document.getElementById('show-button').addEventListener('click', function (event) {
            Login.getInstance().show();
        });
        document.getElementById('hide-button').addEventListener('click', function (event) {
            Login.getInstance().hide();
        });
    </script>
</body>

</html>
5.3.4 store #
function createStore(reducer: any) {
    let state: any;
    let listeners: any[] = [];
    function getState() {
        return state;
    }
    function dispatch(action: any) {
        state = reducer(state, action);
        listeners.forEach(l => l());
    }
    function subscribe(listener: any) {
        listeners.push(listener);
        return () => {
            listeners = listeners.filter(item => item != listener);
            console.log(listeners);
        }
    }
    dispatch({});
    return {
        getState,
        dispatch,
        subscribe
    }
}
let store = createStore((state: any, action: any) => state);
5.3.5 缓存 #
let express = require('express');
let fs = require('fs');
let cache: Record<any, any> = {};
let app = express();
app.get('/user/:id', function (req: any, res: any) {
    let id = req.params.id;
    let user = cache.get(id);
    if (user) {
        res.json(user);
    } else {
        fs.readFile(`./users/${id}.json`, 'utf8', function (err: any, data: any) {
            let user = JSON.parse(data);
            cache.put(id, user);
            res.json(user);
        });
    }
});
app.listen(3000);

6. 适配器模式 #

myadapor

6.1 类图 #

poweradaptor4

myadapters

6.2 代码 #

class Socket {
    output() {
        return '输出220V';
    }
}

abstract class Power {
    abstract charge(): string;
}
class PowerAdapter extends Power {
    constructor(public socket: Socket) {
        super();
    }
    //转换后的接口和转换前不一样
    charge() {
        return this.socket.output() + ' 经过转换 输出24V';
    }
}
let powerAdapter = new PowerAdapter(new Socket());
console.log(powerAdapter.charge());

6.3 场景 #

6.2.1 axios #
//let axios = require('axios');
let url = require('url');
function axios(config: any): any {
    let adaptor = getDefaultAdapter();
    return adaptor(config);
}
axios({
    method: 'GET',
    url: 'http://localhost:8080/api/user?id=1'
}).then(function (response: any) {
    console.log(response);
}, function (error: any) {
    console.log(error);
})

function xhr(config: any) {
    return new Promise(function (resolve, reject) {
        var request = new XMLHttpRequest();
        request.open(config.method, config.url, true);
        request.onreadystatechange = function () {
            if (request.readyState == 4) {
                if (request.status == 200) {
                    resolve(request.response);
                } else {
                    reject('请求失败');
                }
            }
        }
    })
}
function http(config: any) {
    let http = require('http');
    let urlObject = url.parse(config.url);
    return new Promise(function (resolve, reject) {
        const options = {
            hostname: urlObject.hostname,
            port: urlObject.port,
            path: urlObject.pathname,
            method: config.method
        };
        var req = http.request(options, function (res: any) {
            let chunks: any[] = [];
            res.on('data', (chunk: any) => {
                chunks.push(chunk);
            });
            res.on('end', () => {
                resolve(Buffer.concat(chunks).toString());
            });
        });
        req.on('error', (err: any) => {
            reject(err);
        });
        req.end();
    })
}
function getDefaultAdapter(): any {
    var adapter;
    if (typeof XMLHttpRequest !== 'undefined') {
        adapter = xhr;
    } else if (typeof process !== 'undefined') {
        adapter = http;
    }
    return adapter;
}

server.js

let express = require('express');
let app = express();
app.get('/api/user', (req, res) => {
    res.json({ id: req.query.id, name: 'zhufeng' });
});
app.listen(8080);
6.2.2 toAxiosAdapter #
function toAxiosAdapter(options: any) {
    return axios({
        url: options.url,
        method: options.type
    }).then(options.success)
        .catch(options.error)
}


$.ajax = function (options: any) {
    return toAxiosAdapter(options)
}

$.ajax({
    url: '/api/user',
    type: 'GET',
    success: function (data: any) {
        console.log(data)
    },
    error: function (err: any) {
        console.error(err);
    }
})
6.2.3 promisify #
let fs = require('fs');
var Bluebird = require("bluebird");
let readFile = Bluebird.promisify(fs.readFile);

(async function () {
    let content = await readFile('./1.txt', 'utf8');
    console.log(content);
})()
function promisify(readFile: any) {
    return function (filename: any, encoding: any) {
        return new Promise(function (resolve, reject) {
            readFile(filename, encoding, function (err: any, data: any) {
                if (err)
                    reject(err);
                else
                    resolve(data);
            })
        });
    }
}
6.2.4 computed #
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <script src="https://cdn.bootcss.com/vue/2.5.17/vue.js"></script>
    <title>vue</title>
</head>
<body>
<div id="root">
<p>{{name}}</p>
<p>{{upperName}}</p>
</div>    
<script>
let vm=new Vue({
    el: '#root',
    data: {
        name:'zfpx'
    },
    computed: {
        upperName() {
            return this.name.toUpperCase();
        }
    }
});
</script>
</body>
</html>
6.2.5 tree #
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <select id="users"></select>
    <script>
        let tree = [{
            name: '父亲',
            key: '1',
            children: [
                {
                    name: '儿子',
                    key: '1-1',
                    children: [
                        {
                            name: '孙子',
                            key: '1-1-1'
                        }
                    ]
                }
            ]
        }]
        function flattenAdapter(tree, flattenArray) {
            tree.forEach((item) => {
                if (item.children) {
                    flattenAdapter(item.children, flattenArray)
                }
                flattenArray.push({ name: item.name, key: item.key })
            })
            return flattenArray
        }
        let array = [];
        flattenAdapter(tree, array);
        array.reverse();
        let users = document.getElementById('users');
        let options = array.map(item => `<option value="${item.key}">${item.name}</option>`).join('');
        users.innerHTML = options;
    </script>
</body>
</html>
6.2.6 Sequelize #
//cnpm i sequelize sqlite3 -S
const { Sequelize, Model, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');

class User extends Model { }
User.init({
    username: DataTypes.STRING
}, { sequelize, modelName: 'user' });

sequelize.sync()
    .then(() => User.create({
        username: 'zhufeng'
    }))
    .then(result => {
        console.log(result.toJSON());
    });

7.装饰器模式 #

7.1 类图 #

shapedecorator

7.2 代码 #


abstract class Shape {
    abstract draw(): void;
}
class Circle extends Shape {
    draw() {
        console.log('绘制圆形');
    }
}
class Rectangle extends Shape {
    draw() {
        console.log('绘制矩形');
    }
}

abstract class ColorfulShape extends Shape {
    public constructor(public shape: Shape) {
        super();
    }
    abstract draw(): void;
}

class RedColorfulShape extends ColorfulShape {
    draw() {
        this.shape.draw();
        console.log('把边框涂成红色');
    }
}
class GreenColorfulShape extends ColorfulShape {
    draw() {
        this.shape.draw();
        console.log('把边框涂成绿色');
    }
}

let circle = new Circle();
let redColorfulShape = new RedColorfulShape(circle);
redColorfulShape.draw();

let rectangle = new Rectangle();
let greenColorfulShape = new GreenColorfulShape(rectangle);
greenColorfulShape.draw();

7.3 应用场景 #

7.3.1 装饰器 #
7.3.1.1 类装饰器 #

decorator

export { }
namespace decorator {
    interface Animal {
        swings: string;
        fly: any
    }
    function flyable(target: any) {
        console.log(target);

        target.prototype.swings = 2;
        target.prototype.fly = function () {
            console.log('I can fly');
        }
    }
    @flyable
    class Animal {
        constructor() { }
    }
    let animal: Animal = new Animal();
    console.log(animal.swings);
    animal.fly();
}

decorator_factory

namespace decorator_factory {
    interface Animal {
        swings: string;
        fly: any
    }
    function flyable(swings: number) {
        return function flyable(target: any) {
            console.log(target);

            target.prototype.swings = swings;
            target.prototype.fly = function () {
                console.log('I can fly');
            }
        }
    }

    @flyable(2)
    class Animal {
        constructor() { }
    }
    let animal: Animal = new Animal();
    console.log(animal.swings);
    animal.fly();
}
7.3.1.2 属性装饰器 #
namespace property_namespace {
    //实例属性target是类的原型对象,key是属性名称
    function instancePropertyDecorator(target: any, key: string) {
    }
    //类属性target是的构造函数
    function classPropertyDecorator(target: any, key: string) {
    }
    //实例方法装饰器target是原型对象,key方法名,descriptor是方法描述符
    function instanceMethodDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
    }
    //类方法装饰器target是类的构造函数
    function classMethodDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
    }
    class Person {
        @instancePropertyDecorator
        instanceProperty: string;
        @classPropertyDecorator
        public static classProperty: string;
        @instanceMethodDecorator
        instanceMethod() {
            console.log('instanceMethod');
        }
        @classMethodDecorator
        classMethod() {
            console.log('classMethod');
        }
    }
}
7.3.1.3 core-decorator #
let { readonly } = require('core-decorators');
function deprecate(msg: string, options: any) {
    return function (target: any, attr: any, descriptor: any) {
        //DEPRECATION Calculator#add: This function will be removed in future versions.
        let oldVal = descriptor.value;
        descriptor.value = function (...args: any[]) {
            let message = msg ? msg : `DEPRECATION ${target.constructor.name}#${attr}: This function will be removed in future versions.`;
            let see = options && options.url ? `see ${options.url}` : ``;
            console.warn(message + '\r\n' + see);
            return oldVal(...args);
        }
    }
}
class Calculator {
    @deprecate('stop using this', { url: 'http://www.baidu.com' })
    add(a: number, b: number) {
        return a + b;
    }
}
let calculator = new Calculator();
calculator.add(1, 2);
7.3.2 AOP概念 #

springaop

apoprogram

7.3.3 埋点 #
7.3.3.1 项目配置 #
  1. 创建项目
create-react-app zhufeng_tract
yarn add customize-cra react-app-rewired --dev
  1. config-overrides.js

    const {
     override,
     addDecoratorsLegacy,
    } = require("customize-cra");
    module.exports = override(
     addDecoratorsLegacy(),
    );
    
  2. jsconfig.json

    {
     "compilerOptions": {
         "experimentalDecorators": true
     }
    }
    
7.3.3.2 index.js #
import React from 'react';
import { render } from 'react-dom';
import { before, after } from './track';

class App extends React.Component {
    @before(() => console.log('点击方法执行前'))
    onClickBeforeButton() {
        console.log('beforeClick');
    }

    @after(() => console.log('点击方法执行后'))
    onClickAfterButton() {
        console.log('afterClick');
    }

    @after(() => fetch('/api/report'))
    onClickAjaxButton() {
        console.log('ajaxClick');
    }

    render() {
        return (
            <div>
                <button onClick={this.onClickBeforeButton}>beforeClick</button>
                <button onClick={this.onClickAfterButton}>afterClick</button>
                <button onClick={this.onClickAjaxButton}>ajaxClick</button>
            </div>
        )
    }
}
render(<App />, document.getElementById('root'));
7.3.3.3 track.js #
export const before = function (beforeFn) {
    return function (target, methodName, descriptor) {
        let oldMethod = descriptor.value;
        descriptor.value = function () {
            beforeFn.apply(this, arguments);
            return oldMethod.apply(this, arguments);
        }
    }
}

export const after = function (afterFn) {
    return function (target, methodName, descriptor) {
        let oldMethod = descriptor.value;
        descriptor.value = function () {
            oldMethod.apply(this, arguments);
            afterFn.apply(this, arguments);
        }
    }
}
7.3.4 表单校验 #
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>用户注册</title>
</head>

<body>
    <form action="">
        用户名<input type="text" name="username" id="username">
        密码<input type="text" name="password" id="password">
        <button id="submit-btn">注册</button>
    </form>
    <script>
        Function.prototype.before = function (beforeFn) {
            let _this = this;
            return function () {
                let ret = beforeFn.apply(this, arguments);
                if (ret)
                    _this.apply(this, arguments);
            }
        }
        function submit() {
            alert('提交表单');
        }
        submit = submit.before(function () {
            let username = document.getElementById('username').value;
            if (username.length < 6) {
                return alert('用户名不能少于6位');
            }
            return true;
        });
        submit = submit.before(function () {
            let username = document.getElementById('username').value;
            if (!username) {
                return alert('用户名不能为空');
            }
            return true;
        });
        document.getElementById('submit-btn').addEventListener('click', submit);
    </script>
</body>
</html>

8.代理模式 #

8.1 类图 #

proxystar

8.2 代码 #

abstract class Star {
    abstract answerPhone(): void;
}

class Angelababy extends Star {
    public available: boolean = true;
    answerPhone(): void {
        console.log('你好,我是Angelababy.');
    }
}
class AngelababyAgent extends Star {
    constructor(private angelababy: Angelababy) {
        super();
    }
    answerPhone(): void {
        console.log('你好,我是Angelababy的经纪人.');
        if (this.angelababy.available) {
            this.angelababy.answerPhone();
        }
    }
}
let angelababyAgent = new AngelababyAgent(new Angelababy());
angelababyAgent.answerPhone();

8.3 场景 #

8.3.1 事件委托代理 #

captureevent

<body>
    <ul id="list">
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
<script>
  let list = document.querySelector('#list');
  list.addEventListener('click',event=>{
       alert(event.target.innerHTML);
  });     
</script>    
</body>
8.3.2 虚拟代理(图片预加载) #
8.3.2.1 app.js #
let express=require('express');
let path=require('path')
let app=express();
app.get('/images/loading.gif',function (req,res) {
    res.sendFile(path.join(__dirname,req.path));
});
app.get('/images/:name',function (req,res) {
    setTimeout(() => {
        res.sendFile(path.join(__dirname,req.path));
    }, 2000);
});
app.get('/',function (req,res) {
    res.sendFile(path.resolve('index.html'));
});
app.listen(8080);
8.3.2.2 index.html #
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .bg-container {
            width: 600px;
            height: 400px;
            margin: 100px auto;
        }

        .bg-container #bg-image {
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <div id="background">
        <button data-src="/images/bg1.jpg">背景1</button>
        <button data-src="/images/bg2.jpg">背景2</button>
    </div>
    <div class="bg-container">
        <img id="bg-image" src="/images/bg1.jpg" />
    </div>
    <script>
        let container = document.querySelector('#background');

        class BackgroundImage {
            constructor() {
                this.bgImage = document.querySelector('#bg-image');
            }
            setSrc(src) {
                this.bgImage.src = src;
            }
        }
        class LoadingBackgroundImage { 
             static LOADING_URL= `/images/loading.gif`;
            constructor() {
                this.backgroundImage = new BackgroundImage();
            }
            setSrc(src) {
                this.backgroundImage.setSrc(LoadingBackgroundImage.LOADING_URL);
                let img = new Image();
                img.onload = () => {
                    this.backgroundImage.setSrc(src);
                }
                img.src = src;
            }
        }
        let loadingBackgroundImage = new LoadingBackgroundImage();
        container.addEventListener('click', function (event) {
            let src = event.target.dataset.src;
            loadingBackgroundImage.setSrc(src + '?ts=' + Date.now());
        });
    </script>
</body>

</html>
8.3.3 虚拟代理(图片懒加载) #
8.3.3.1 index.html #
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Lazy-Load</title>
    <style>
        .image {
            width: 300px;
            height: 200px;
            background-color: #CCC;
        }

        .image img {
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <div class="image-container">
        <div class="image">
            <img data-src="/images/bg1.jpg">
        </div>
        <div class="image">
            <img data-src="/images/bg2.jpg">
        </div>
        <div class="image">
            <img data-src="/images/bg1.jpg">
        </div>
        <div class="image">
            <img data-src="/images/bg2.jpg">
        </div>
        <div class="image">
            <img data-src="/images/bg1.jpg">
        </div>
        <div class="image">
            <img data-src="/images/bg2.jpg">
        </div>
        <div class="image">
            <img data-src="/images/bg1.jpg">
        </div>
        <div class="image">
            <img data-src="/images/bg2.jpg">
        </div>
        <div class="image">
            <img data-src="/images/bg1.jpg">
        </div>
        <div class="image">
            <img data-src="/images/bg2.jpg">
        </div>
    </div>
</body>
<script>
    const imgs = document.getElementsByTagName('img');
    const clientHeight = window.innerHeight || document.documentElement.clientHeight;
    let loadedIndex = 0;
    function lazyload() {
        for (let i = loadedIndex; i < imgs.length; i++) {
            if (clientHeight - imgs[i].getBoundingClientRect().top > 0) {
                imgs[i].src = imgs[i].dataset.src;
                loadedIndex = i + 1;
            }
        }
    }
    lazyload();
    window.addEventListener('scroll', lazyload, false);
</script>
</html>
8.3.4 缓存代理 #
const factorial = function f(num) {
    if (num === 1) {
        return 1;
    } else {
        return (num * f(num - 1));
    }
}

const proxy = function (fn) {
    const cache = {};  // 缓存对象
    return function (num) {
        if (num in cache) {
            return cache[num];   // 使用缓存代理
        }
        return cache[num] = fn.call(this, num);
    }
}

const proxyFactorial = proxy(factorial);
console.log(proxyFactorial(5));
console.log(proxyFactorial(5));
console.log(proxyFactorial(5));
let count = 0;
function fib(n) {
    count++;
    return n <= 2 ? 1 : fib(n - 1) + fib(n - 2);
}
var result = fib(10);
console.log(result, count);//55 110
let count = 0;
const fibWithCache = (function () {
    let cache = {};
    function fib(n) {
        count++;
        if (cache[n]) {
            return cache[n];
        }
        let result = n <= 2 ? 1 : fib(n - 1) + fib(n - 2);
        cache[n] = result;
        return result;
    }
    return fib;
})();
var result = fibWithCache(10);
console.log(result, count);//55 17
8.3.5 防抖代理 #
8.3.5.1 节流 #
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #container {
            width: 200px;
            height: 400px;
            border: 1px solid red;
            overflow: auto;
        }

        #container .content {
            height: 4000px;
        }
    </style>
</head>

<body>
    <div id="container">
        <div class="content"></div>
    </div>
    <script>
        function throttle(callback, interval) {
            let last;
            return function () {
                let context = this;
                let args = arguments;
                let now = Date.now();
                if (last) {
                    if (now - last >= interval) {
                        last = now;
                        callback.apply(context, args);
                    }
                } else {
                    callback.apply(context, args);
                    last = now;
                }

            }
        }
        let lastTime = Date.now();
        const throttle_scroll = throttle(() => {
            console.log('触发了滚动事件', (Date.now() - lastTime) / 1000);
        }, 1000);
        document.getElementById('container').addEventListener('scroll', throttle_scroll);
    </script>
</body>

</html>
8.3.5.2 防抖 #
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #container {
            width: 200px;
            height: 400px;
            border: 1px solid red;
            overflow: auto;
        }

        #container .content {
            height: 4000px;
        }
    </style>
</head>

<body>
    <div id="container">
        <div class="content"></div>
    </div>
    <script>
        function throttle(callback, delay) {
            let timer;
            return function () {
                let context = this;
                let args = arguments;
                if (timer)
                    clearTimeout(timer);
                timer = setTimeout(() => {
                    callback.apply(context, args);
                }, delay);
            }
        }
        let lastTime = Date.now();
        const throttle_scroll = throttle(() => {
            console.log('触发了滚动事件', (Date.now() - lastTime) / 1000);
        }, 1000);
        document.getElementById('container').addEventListener('scroll', throttle_scroll);
    </script>
</body>

</html>
8.3.5.3 未防抖 #
<body>
    <ul id="todos">
    </ul>
<script>
    let todos = document.querySelector('#todos');
    window.onload = function(){
        fetch('/todos').then(res=>res.json()).then(response=>{
            todos.innerHTML = response.map(item=>`<li "><input value="${item.id}" type="checkbox" ${item.completed?"checked":""}/>${item.text}</li>`).join('');
        });
    }
    function toggle(id){
       fetch(`/toggle/${id}`).then(res=>res.json()).then(response=>{
            console.log('response',response);
        });
    }
    todos.addEventListener('click',function(event){
        let checkbox = event.target;
        let id = checkbox.value;
        toggle(id);
    });
</script>
</body>

app.js

let express=require('express');
let app=express();
app.use(express.static(__dirname));
let todos=[
    {id: 1,text: 'a',completed: false},
    {id: 2,text: 'b',completed: false},
    {id: 3,text: 'c',completed: false},
];
app.get('/todos',function (req,res) {
    res.json(todos);
});
app.get('/toggle/:id',function (req,res) {
    let id=req.params.id;
    todos = todos.map(item => {
        if (item.id==id) {
            item.completed=!item.completed;
        }
        return item;
    });
    res.json({code:0});
});
app.listen(8080);
8.3.5.4 防抖 #

todos.html

<body>
    <ul id="todos">
    </ul>
    <script>
    let todos = document.querySelector('#todos');
    window.onload = function(){
        fetch('/todos').then(res=>res.json()).then(response=>{
            todos.innerHTML = response.map(item=>`<li "><input value="${item.id}" type="checkbox" ${item.completed?"checked":""}/>${item.text}</li>`).join('');
        });
    }
    function toggle(id){
       fetch(`/toggle/${id}`).then(res=>res.json()).then(response=>{
            console.log('response',response);
        });
    }
    let LazyToggle = (function(id){
        let ids = [];
        let timer;
        return function(id){
            ids.push(id);
            if(timer){
               clearTimeout(timer);
            }
            timer = setTimeout(function(){
                toggle(ids.join(','));
                ids = null;
                clearTimeout(timer);
                timer = null;
            },2000);
        }
    })();
    todos.addEventListener('click',function(event){
        let checkbox = event.target;
        let id = checkbox.value;
        LazyToggle(id);
    });
</script>

app.js

app.get('/toggle/:ids',function (req,res) {
    let ids=req.params.ids;
    ids=ids.split(',').map(item=>parseInt(item));
    todos = todos.map(item => {
        if (ids.includes(item.id)) {
            item.completed=!item.completed;
        }
        return item;
    });
    res.json({code:0});
});
8.3.6 代理跨域 #
8.3.6.1 正向代理和反向代理 #

positiveproxy

fanproxy

proxy-server.js

const http = require('http');
const httpProxy = require('http-proxy');
//创建一个代理服务
const proxy = httpProxy.createProxyServer();
//创建http服务器并监听8888端口
let server = http.createServer(function (req, res) {
    //将用户的请求转发到本地9999端口上
    proxy.web(req, res, {
        target: 'http://127.0.0.1:9999'
    });
    //监听代理服务错误
    proxy.on('error', function (err) {
        console.log(err);
    });
});
server.listen(8888, '0.0.0.0');

real-server.js

const http = require('http');
let server = http.createServer(function (req, res) {
    res.end('9999');
});
server.listen(9999, '0.0.0.0');
8.3.6.2 代理跨域 #
otherWindow.postMessage(message, targetOrigin, [transfer]);
window.addEventListener("message", receiveMessage, false);

origin.js

let express=require('express');
let app=express();
app.use(express.static(__dirname));
app.listen(3000);

target.js

let express = require('express');
let app = express();
let bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(__dirname));
let users = [];
app.post('/register', function (req, res) {
    let body = req.body;
    let target = body.target;
    let callback = body.callback;
    let username = body.username;
    let password = body.password;
    let user = { username, password };
    let id = users.length == 0 ? 1 : users[users.length - 1].id + 1;
    user.id = id;
    users.push(user);
    res.status(302);
    res.header('Location', `${target}?callback=${callback}&args=${id}`);
    res.end();
});
app.listen(4000);

reg.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script type="text/javascript">
        window.addEventListener('message', function (event) {
            console.log(event.data);

            if (event.data.receiveId) {
                alert('用户ID=' + event.data.receiveId);
            }
        })
    </script>
    <iframe name="proxyIframe" id="proxyIframe" frameborder="0"></iframe>
    <form action="http://localhost:4000/register" method="POST" target="proxyIframe">
        <input type="hidden" name="callback" value="receiveId">
        <input type="hidden" name="target" value="http://localhost:3000/target.html">
        用户名<input type="text" name="username" />
        密码<input type="text" name="password" />
        <input type="submit" value="提交">
    </form>
</body>

</html>

target.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        window.onload = function () {
            var query = location.search.substr(1).split('&');
            let callback, args;
            for (let i = 0, len = query.length; i < len; i++) {
                let item = query[i].split('=');
                if (item[0] == 'callback') {
                    callback = item[1];
                } else if (item[0] == 'args') {
                    args = item[1];
                }
            }
            try {
                window.parent.postMessage({ [callback]: args }, '*');
            } catch (error) {
                console.log(error);
            }
        }
    </script>
</body>

</html>
8.3.7 $.proxy #
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>jquery proxy</title>
</head>

<body>
    <button id="btn">点我变红</button>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script>
        let btn = document.getElementById('btn');
        btn.addEventListener('click', function () {
            setTimeout($.proxy((function () {
                $(this).css('color', 'red');
            }), this), 1000);
        });    
    </script>
</body>

</html>
function proxy(fn, context) {
    return function () {
       return fn.call(context, arguments);
    }
}
8.3.8 Proxy #
let wang={
    name: 'wanglaoshi',
    age: 29,
    height:165
}
let wangMama=new Proxy(wang,{
    get(target,key) {
        if (key == 'age') {
            return wang.age-1;
        } else if (key == 'height') {
            return wang.height-5;
        }
        return target[key];
    },
    set(target,key,val) {
        if (key == 'boyfriend') {
            let boyfriend=val;
            if (boyfriend.age>40) {
                throw new Error('太老');
            } else if (boyfriend.salary<20000) {
                throw new Error('太穷');
            } else {
                target[key]=val;
                return true;
            }
        }
    }
});
console.log(wangMama.age);
console.log(wangMama.height);
wangMama.boyfriend={
    age: 41,
    salary:3000
}
8.3.9 Vue2和Vue3 #
8.3.10 #

9.观察者模式 #

9.1 类图 #

teacherobserver

9.2 代码 #

abstract class Student {
    constructor(public teacher: Teacher) { }
    public abstract update();
}
class Xueba extends Student {
    public update() {
        console.log(this.teacher.getState() + ',学霸抬头举手');
    }
}
class Xuezha extends Student {
    public update() {
        console.log(this.teacher.getState() + ',学渣低头祈祷');
    }
}

class Teacher {
    private students: Student[] = new Array<Student>();
    public state: string = '老师讲课'
    getState() {
        return this.state;
    }
    public askQuestion() {
        this.state = '老师提问';
        this.notifyAllStudents();
    }
    attach(student: Student) {
        this.students.push(student);
    }
    notifyAllStudents() {
        this.students.forEach(student => student.update());
    }
}
let teacher = new Teacher();
teacher.attach(new Xueba(teacher));
teacher.attach(new Xueza(teacher));
teacher.askQuestion();

9.3 场景 #

9.3.1 DOM事件绑定 #

原生

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>观察者模式</title>
</head>

<body>
    <button id="btn">click</button>
    <script>
        let btn = document.getElementById('btn');
        const handler1 = () => { console.log(1); }
        const handler2 = () => { console.log(2); }
        const handler3 = () => { console.log(3); }
        btn.addEventListener('click', handler1);
        btn.addEventListener('click',handler2);
        btn.addEventListener('click', handler3);
    </script>
</body>

</html>

jquery

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>观察者模式</title>
</head>

<body>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <button id="btn">click</button>
    <script>
        const handler1 = () => { console.log(1); }
        const handler2 = () => { console.log(2); }
        const handler3 = () => { console.log(3); }
        $('#btn').on('clickMe', handler1);
        $('#btn').on('clickMe', handler2);
        $('#btn').on('clickMe', handler3);
        $('#btn').trigger('clickMe');
        console.log('=================================');
        $('#btn').off('clickMe', handler2);
        $('#btn').trigger('clickMe');
    </script>
</body>

</html>
9.3.2 Promise #
class Promise {
    private callbacks: Array<Function> = []
    constructor(fn) {
        let resolve = () => {
            this.callbacks.forEach(callback => callback())
        };
        fn(resolve);
    }
    then(callback) {
        this.callbacks.push(callback);
    }
}
let promise = new Promise(function (resolve) {
    setTimeout(function () {
        resolve(100);
    }, 1000);
});
promise.then(() => console.log(1));
promise.then(() => console.log(2));
9.3.3 callbacks #
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script>
        let callbacks = $.Callbacks();

        let a1 = () => alert(1);
        let a2 = () => alert(2);
        let a3 = () => alert(13);
        callbacks.add(a1);
        callbacks.add(a2);
        callbacks.add(a3);
        callbacks.remove(a3);
        callbacks.fire();
    </script>
</body>
</html>

Callbacks

function Callbacks() {
    let observers = [];
    function add(observer) {
        observers.push(observer);
    }
    function remove(observer) {
        let index = observers.indexOf(observer);
        if (index != -1)
            observers.splice(index, 1);
    }
    function fire() {
        observers.forEach(observer => observer());
    }
    return {
        add,
        remove,
        fire
    }
}
9.3.4 EventEmitter #

自定义事件

const EventEmitter = require('events');
let subject = new EventEmitter();
subject.on('click', function (name) {
    console.log(1, name);
});
subject.on('click', function (name) {
    console.log(2, name);
});
subject.emit('click', 'zhufeng');

events.js

class EventEmitter{
    constructor() {
        this._events={};
    }
    on(type,listener) {
        let listeners=this._events[type];
        if (listeners) {
            listeners.push(listener);
        } else {
            this._events[type]=[listener];
        }
    }
    emit(type) {
        let listeners=this._events[type];
        let args=Array.from(arguments).slice(1);
        listeners.forEach(listener => listener(...args));
    }
}
module.exports = EventEmitter;
9.3.5 流 #
let fs = require('fs');
let rs = fs.createReadStream('./1.txt', { highWaterMark: 3 });
rs.on('data', function (data) {
    console.log(data.toString());
});
rs.on('end', function () {
    console.log('end');
});
9.3.6 http服务器 #
let http = require('http');
let server = http.createServer();
server.on('request', (req, res) => {
    res.end('zhufeng');
});
server.listen(3000);
9.3.7 生命周期函数 #

reactlifecycles

vuelifecycles

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="root"></div>
    <script>
        let container = document.getElementById('root');
        class Component {
            state = { number: 0 }
            componentWillMount() {
                console.log('componentWillMount');
            }

            componentDidMount() {
                console.log('componentDidMount');
            }
            shouldComponentUpdate() {
                console.log('shouldComponentUpdate');
                return true;
            }
            componentWillUpdate() {
                console.log('componentWillUpdate');
            }

            componentDidUpdate() {
                console.log('componentDidUpdate');
            }
            setState(newState) {
                this.state = { ...this.state, ...newState }
                if (!this.shouldComponentUpdate || (this.shouldComponentUpdate && this.shouldComponentUpdate())) {
                    if (this.componentWillUpdate)
                        this.componentWillUpdate();
                    let content = this.render();
                    container.innerHTML = content;
                    if (this.componentDidUpdate)
                        this.componentDidUpdate();
                }
            }
            render() {
                console.log('render');
                return this.state.number;
            }
        }
        render(Component, document.getElementById('root'));
        function render(Component, container) {

            let component = new Component();
            if (component.componentWillMount)
                component.componentWillMount();
            let content = component.render();
            container.innerHTML = content;
            if (component.componentDidMount)
                component.componentDidMount();

            setTimeout(() => {
                component.setState({ number: 1 });
            }, 3000);
        }

    </script>
</body>

</html>
9.3.8 EventBus #

EventBus.js

import Vue from 'vue';  
export default new Vue(); 

组件A

import EventBus from './EventBus';  
EventBus.$on("customEvent", name => {
    console.log(name);
})

组件B

import EventBus from './EventBus';  
EventBus.$emit("customEvent", 'zhufeng')
<body>
    <script src="https://cdn.bootcss.com/vue/2.5.17/vue.js"></script>
    <script>
        let EventBus = new Vue();
        EventBus.$on("customEvent", name => {
            console.log(name);
        })
        EventBus.$emit("customEvent", 'zhufeng')
    </script>
</body>
9.3.9 Vue响应式 #

vuereactive

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue</title>
</head>

<body>
    <div id="name"></div>
    <div id="age"></div>
    <script>
        let name = document.getElementById('name');
        let age = document.getElementById('age');
        class Dep {
            subs = []
            addSub(sub) {
                this.subs.push(sub);
            }
            notify() {
                this.subs.forEach((sub) => sub())
            }
        }
        function observe(target) {
            Object.keys(target).forEach((key) => {
                let val = target[key];
                const dep = new Dep();
                if (key == 'name') {
                    name.innerHTML = val;
                    dep.addSub(() => { name.innerHTML = val });
                } else if (key == 'age') {
                    age.innerHTML = val;
                    dep.addSub(() => { age.innerHTML = val });
                }
                Object.defineProperty(target, key, {
                    get: function () {
                        return val;
                    },
                    set: function (value) {
                        val = value;
                        dep.notify();
                    }
                });
            })
        }

        let obj = { name: '名称', age: '年龄' };
        observe(obj);
        setTimeout(() => {
            obj.name = '新名称';
        }, 3000);
        setTimeout(() => {
            obj.age = '新年龄';
        }, 6000);
    </script>
</body>
</html>
9.3.9 redux #
export default function createStore(reducer,preloadedState,enhancer) {
    if (enhancer) {
        return enhancer(createStore)(reducer,preloadedState);
    }
    let state=preloadedState;
    let listeners=[];
    function getState() {
        return state;
    }
    function subscribe(listener) {
        listeners.push(listener);
        return function () {
            const index=listeners.indexOf(listener);
            listeners.splice(index,1);
        }
    }
    function dispatch(action) {
        state=reducer(state,action);
        listeners.forEach(listener=>listener())
    }
    dispatch({type:'@@redux/INIT'});
    return {
        dispatch,
        subscribe,
        getState
    }
}

9.4 发布订阅模式 #

9.4.1 区别 #

subscribepattern

9.4.2 类图 #

publishsubscribe

9.4.3 代码 #
class Agency {
    _topics = {}
    subscribe(topic, listener) {
        let listeners = this._topics[topic];
        if (listeners) {
            listeners.push(listener);
        } else {
            this._topics[topic] = [listener];
        }
    }
    publish(topic, ...args) {
        let listeners = this._topics[topic] || [];
        listeners.forEach(listener => listener(...args));
    }
}
class Landlord {
    constructor(public agent: Agency) { }
    lend(topic, area, money) {
        this.agent.publish(topic, area, money);
    }
}

class Tenant {
    constructor(public agent: Agency, public name: string) { }
    order(topic) {
        this.agent.subscribe(topic, (area, money) => {
            console.log(this.name, `${area}平米, ${money}元`);
        });
    }
}
let agent = new Agency();
let rich = new Tenant(agent, '大款');
let poor = new Tenant(agent, '北漂');
let landlord = new Landlord(agent);
rich.order('豪宅');
poor.order('单间');
landlord.lend('豪宅', 10000, 1000000);
landlord.lend('单间', 10, 1000);
9.4.4 Redis发布订阅 #

redissubscribe

redispublishsubscribe

SUBSCRIBE channel_a
PUBLISH channel_a zhufeng
let redis = require('redis');
let client1 = redis.createClient(6379, '127.0.0.1');
let client2 = redis.createClient(6379, '127.0.0.1');

client1.subscribe('channel_a');
client1.subscribe('channel_b');
client1.on('message', (channel, message) => {
    console.log('client1', channel, message);
    client1.unsubscribe('channel_a');
});
client2.publish('channel_a', 'a_hello');
client2.publish('channel_b', 'b_hello');

setTimeout(() => {
    client2.publish('channel_a', 'a_world');
    client2.publish('channel_b', 'b_world');
}, 3000);

10. 外观模式 #

xiaomicontrol

facadepattern

10.1 计算器 #

facadepattern2

class Sum {
    sum(a, b) {
        return a + b;
    }
}
class Minus {
    minus(a, b) {
        return a - b;
    }
}
class Multiply {
    multiply(a, b) {
        return a * b;
    }
}
class Calculator {
    sumObj
    minusObj
    multiplyObj
    constructor() {
        this.sumObj = new Sum();
        this.minusObj = new Minus();
        this.multiplyObj = new Multiply();
    }
    sum(...args) {
        return this.sumObj.sum(...args);
    }
    minus(...args) {
        return this.minusObj.minus(...args);
    }
    multiply(...args) {
        return this.multiplyObj.multiply(...args);
    }
}
let calculator = new Calculator();
console.log(calculator.sum(1, 2));
console.log(calculator.minus(1, 2));
console.log(calculator.multiply(1, 2));

10.2 计算机 #

computer2


class CPU {
    startup() { console.log('打开CPU'); }
    shutdown() { console.log('关闭CPU'); }
}
class Memory {
    startup() { console.log('打开内存'); }
    shutdown() { console.log('关闭内存'); }
}
class Disk {
    startup() { console.log('打开硬盘'); }
    shutdown() { console.log('关闭硬盘'); }
}
class Computer {
    cpu;
    memory;
    disk;
    constructor() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.disk = new Disk();
    }
    startup() {
        this.cpu.startup();
        this.memory.startup();
        this.disk.startup();
    }
    shutdown() {
        this.cpu.shutdown();
        this.memory.shutdown();
        this.disk.shutdown();
    }
}
let computer = new Computer();
computer.startup();
computer.shutdown();

10.3 压缩 #

export { }
var zlib = require('zlib');
var fs = require('fs');
let path = require('path');
function open(input) {
    let ext = path.extname(input);
    switch (ext) {
        case '.gz':
            return unZip(input);
        case '.rar':
            return unRar(input);
        case '.7z':
            return un7z(input);
        default:
            break;
    }
}
function unZip(src) {
    var gunzip = zlib.createGunzip();
    var inputStream = fs.createReadStream(src);
    var outputStream = fs.createWriteStream(src.slice(0, -3));
    console.log('outputStream');

    inputStream.pipe(gunzip).pipe(outputStream);
}
function unRar(src) {
    console.log('Rar解压后的', src);
}
function un7z(src) {
    console.log('7z解压后的', src);
}
open('./source.txt.gz');

function zip(src) {
    var gzip = zlib.createGzip();//创建压缩流
    var inputStream = fs.createReadStream(src);
    var outputStream = fs.createWriteStream(src+'.gz');
    inputStream.pipe(gzip).pipe(outputStream);
}
zip('source.txt');

10.4 redux #

10.5 函数重载 #

10.5.1 参数重载 #

sum.ts

function sum(a: number, b: string);
function sum(a: string, b: number);
function sum(a: any, b: any) {
    return a + b;
}
10.5.2 react createElement #

-react create-element

export function createElement (
  context: Component,
  tag: any,
  data: any,
  children: any,
  normalizationType: any,
  alwaysNormalize: boolean
): VNode | Array<VNode> {
  if (Array.isArray(data) || isPrimitive(data)) {
    normalizationType = children
    children = data
    data = undefined
  }
  if (isTrue(alwaysNormalize)) {
    normalizationType = ALWAYS_NORMALIZE
  }
  return _createElement(context, tag, data, children, normalizationType)
}
10.5.3 vue createElement #

-vue create-element

  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    if (__DEV__) {
      if (Object.freeze) {
        Object.freeze(childArray);
      }
    }
    props.children = childArray;
  }
10.5.4 buffer #

buffer.js

Buffer.prototype.slice = function slice(start, end) {
  const srcLength = this.length;
  end = end !== undefined ? srcLength: srcLength;
  const newLength = end > start ? end - start : 0;
  return new FastBuffer(this.buffer, this.byteOffset + start, newLength);
};
10.5.5 createStore #
export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }
}
10.5.6 axios #
function getDefaultAdapter() {
  var adapter;
  if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    adapter = require('./adapters/http');
  } else if (typeof XMLHttpRequest !== 'undefined') {
    adapter = require('./adapters/xhr');
  }
  return adapter;
}
10.5.7 polyfill #
jQuery.removeEvent = document.removeEventListener ?
    function( elem, type, handle ) {
        if ( elem.removeEventListener ) {
            elem.removeEventListener( type, handle, false );
        }
    } :
    function( elem, type, handle ) {
        if ( elem.detachEvent ) {
            elem.detachEvent( "on" + type, handle );
        }
    };

11. 迭代器模式 #

11.1 类图 #

iterator

11.2 代码 #

function createIterator(arr) {
    let index=0;
    return {
        next() {
            return index<arr.length?
                {value: arr[index++],done: false}:
                {done:true}
        }
    }
}
let it=createIterator([1,2]);
console.log(it.next());
console.log(it.next());
console.log(it.next());

11.3 场景 #

11.3.1 forEach #

11.3.2 each #

<body>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script>

    $.each(['1', '2', '3'], function (index, item) {
        console.log(index, item);
    })
    $.each({ name: 'zhufeng', age: 10 }, function (index, item) {
        console.log(index, item);
    })
    function each(obj, callback) {
        if (Array.isArray(obj)) {
            let length = obj.length;
            for (let i = 0; i < length; i++) {
                if (callback.call(obj[i], i, obj[i]) === false) {
                    break;
                }
            }
        } else {
            for (let i in obj) {
                if (callback.call(obj[i], i, obj[i]) === false) {
                    break;
                }
            }
        }

        return obj;
    }

    </script>

</body>

11.3.3 Iterator #

Array[Symbol.iterator] = function () {
    let index = 0;
    return {
        next: () => {
            return index < this.length ?
                { value: this[index++], done: false } :
                { done: true }
        }
    }
}
let arr = [1, 2];
let it = arr[Symbol.iterator]();
console.log(it.next());
console.log(it.next());
console.log(it.next());

11.3.4 yield* #

let generator = function* () {
    yield 1;
    yield* [2, 3];
    yield 4;
};

var iterator = generator();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

11.3.5 二叉树遍历 #

varorder

class Tree {
    constructor(public left, public value, public right) {
    }
}
//['A', 'B', 'D','E', 'C', 'F','G']
function* leftOrder(tree) {
    if (tree) {
        yield tree.value;
        yield* leftOrder(tree.left);
        yield* leftOrder(tree.right);
    }
}

//['D', 'B', 'E','A', 'F', 'C','G']
function* inOrder(tree) {
    if (tree) {
        yield* inOrder(tree.left);
        yield tree.value;
        yield* inOrder(tree.right);
    }
}
//['D', 'E', 'B','F', 'G', 'C','A']
function* rightOrder(tree) {
    if (tree) {
        yield* rightOrder(tree.left);
        yield* rightOrder(tree.right);
        yield tree.value;
    }
}
function make(array) {
    if (array.length === 1) return new Tree(null, array[0], null);
    return new Tree(make(array[0]), array[1], make(array[2]));
}
let tree = make([[['D'], 'B', ['E']], 'A', [['F'], 'C', ['G']]]);
var result: any[] = [];
for (let node of rightOrder(tree)) {
    result.push(node);
}
console.log(result);

"downlevelIteration": true, / Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. /