常见 JavaScript 设计模式 — 原来这么简单

设计模式

设计模式总共有 23 种,但在前端领域其实没必要全部都去学习,毕竟大部分的设计模式是在 JavaScript 中占的比重并不是那么大,本文会列举出一些 JavaScript 常见的、容易被忽视的设计模式,不过还是有必要先简单了解一下设计模式相关的概念.

设计模式是什么?

先举个形象的例子,比如现在正在考试而且恰好在考数学,实际上每道数学题目都对应着一种或多种解决公式(如和三角形相关的勾股定理),而这些解决公式是经过数学家研究、推导、总结好的,我们只需要把 题目已有公式 对应上就很容易解决问题,而 设计模式 也是如此,只不过是它是相对于 软件设计领域 而言的.

设计模式(Design pattern) 是一套被反复使用、经过分类、代码设计经验的总结,简单来说设计模式就是为了解决 软件设计领域 不同场景下相应问题的 解决方案.

设计原则(SOLID)

SOLID 实际上指的是五个基本原则,但在前端领域涉及到最多的是仍然是前面两条:

  • 单一功能原则(Single Responsibility Principle)
  • 开放封闭原则(Opened Closed Principle)
  • 里式替换原则(Liskov Substitution Principle)
  • 接口隔离原则(Interface Segregation Principle)
  • 依赖反转原则(Dependency Inversion Principle)

设计模式的类型

主要分为三个类型:

  • 创建型

    • 主要用于解耦 对象的实例化 过程,即用于创建对象,如对象实例化
    • 本文主要包含:简单工厂模式、抽象工厂模式、单例模式、原型模式
  • 行为型

    • 主要用于优化不同 对象接口 间的结构关系,如把 对象 结合在一起形成一个更大的结构
    • 本文主要包含:装饰器模式、适配器模式、代理模式
  • 结构型

    • 主要用于定义 对象 如何交互、划分责任、设计算法
    • 本文主要包含:策略模式、状态模式、观察者模式、发布订阅模式、迭代器模式

    创建型设计模式

    设计模式的核心是区分逻辑中的 可变部分不变部分,并使它们进行分离,从而达到使变化的部分易扩展、不变的部分稳定.

    工厂模式

    简单工厂模式

    核心就是创建一个对象,这里的 可变部分参数不变部分共有属性.

举例:通过不同职级的员工创建员工相关信息,需要包含 name、age、position、job 等信息.

实现方式一:

  • 核心就是 可变部分 默认 参数化

    function Staff(name, age, position, job) {
      this.name = name;
      this.age = age;
      this.position = position;
      this.job = job;
    }
    
    const developer = new Staff('zs', 18, 'develoment', ['写 bug', '改 bug', '摸鱼']);
    const productManager = new Staff('ls', 30, 'manager', ['提需求', '改需求', '面向 PPT 开发']);

    实现方式二:

  • 实际上在实现方式一中的 job 部分是和 position 是相互关联的,可以认为 job 部分是 不变的,因此可以根据 position 内容的内容来自动匹配 job

    function Staff(name, age, position, job) {
      this.name = name;
      this.age = age;
      this.position = position;
      this.job = job;
    }
    
    function StaffFactory(name, age, position){
      let job = []
      switch (position) {
          case 'develoment':
              job = ['写 bug', '改 bug', '摸鱼'];
              break;
          case 'manager':
              job = ['提需求', '改需求', '面向 PPT 开发'];
              break;
          ...
      }
    
      return new Staff(name, age, position, job);
    }
    
    const developer = StaffFactory('zs', 18, 'developer');
    const productManager = StaffFactory('ls', 30, 'manager');

    抽象工厂模式

    这个模式最显眼的就是 抽象 两个字了,在如 Java 语言当中存在所谓的 抽象类,这个抽象类里面的所有属性和方法都没有具体实现,只有单纯的定义,而继承这个抽象类的子类必须要实现其对应的抽象属性和抽象方法.

JavaScript 中没有这样的直接定义,不过根据上面的描述其实我们可以把它映射到 typescript 中的 interface 接口,理解到这其实让我联想到了 vue.js 中的 自定义渲染器,预留的自定义渲染器的各个方法目的就是实现跨平台的渲染方式

// 文件位置:packages\runtime-core\src\renderer.ts
export function createRenderer<
  HostNode = RendererNode,
  HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
  return baseCreateRenderer<HostNode, HostElement>(options)
}

// 文件位置:packages\runtime-core\src\renderer.ts
// RendererOptions 就是一个 Interface 接口
export interface RendererOptions<
  HostNode = RendererNode,
  HostElement = RendererElement
> {
  patchProp(
    el: HostElement,
    key: string,
    prevValue: any,
    nextValue: any,
    isSVG?: boolean,
    prevChildren?: VNode<HostNode, HostElement>[],
    parentComponent?: ComponentInternalInstance | null,
    parentSuspense?: SuspenseBoundary | null,
    unmountChildren?: UnmountChildrenFn
  ): void
  insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void
  remove(el: HostNode): void
  createElement(
    type: string,
    isSVG?: boolean,
    isCustomizedBuiltIn?: string,
    vnodeProps?: (VNodeProps & { [key: string]: any }) | null
  ): HostElement
  createText(text: string): HostNode
  createComment(text: string): HostNode
  setText(node: HostNode, text: string): void
  setElementText(node: HostElement, text: string): void
  parentNode(node: HostNode): HostElement | null
  nextSibling(node: HostNode): HostNode | null
  querySelector?(selector: string): HostElement | null
  setScopeId?(el: HostElement, id: string): void
  cloneNode?(node: HostNode): HostNode
  insertStaticContent?(
    content: string,
    parent: HostElement,
    anchor: HostNode | null,
    isSVG: boolean,
    start?: HostNode | null,
    end?: HostNode | null
  ): [HostNode, HostNode]
}

接下来我们将以上的 typescript 的形式转变成 JavaScript 形式的抽象模式:

// 抽象 Render 类
class Renderer {
  patchProp(
    el,
    key,
    prevValue,
    nextValue,
    isSVG,
    prevChildren,
    parentComponent,
    parentSuspense,
    unmountChildren
  ) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  insert(el, parent, anchor) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  remove(el) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  createElement(type, isSVG, isCustomizedBuiltIn, vnodeProps) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  createText(text) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  createComment(text) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  setText(node, text) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  setElementText(node, text) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  parentNode(node) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  nextSibling(node) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  querySelector(selector) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  setScopeId(el, id) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  cloneNode(node) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
  insertStaticContent(content, parent, anchor, isSVG, start, end) {
    throw Error('抽象工厂方法不能直接使用,你需要将我重写!!!');
  }
}

// 具体渲染函数的实现
class createRenderer extends Renderer{
    // 待实现的渲染器方法
    ...
}

单例模式

核心就是通过多次 new 操作进行实例化时,能够保证创建 实例对象唯一性.

vuex 中的单例模式

其实,vuex 中就使用到了 单例模式,代码本身比较简单,当 install 方法被多次调用时,就会得到一个错误信息,并不会多次向 Vue 中混入 vuex 中自定义的内容:

image.png

image.png

实现一个单例模式

这里举个封装 localStorage 方法的例子,并提供给外部对应的创建方法,如下:

let storageInstance = null;

class Storage {
    getItem(key) {
        let value = localStorage.getItem(key);
        try {
            return JSON.parse(value);
        } catch (error) {
            return value;
        }
    }

    setItem(key, value) {
        try {
            localStorage.setItem(JSON.stringify(value));
        } catch (error) {
            // do something
            console.error(error);
        }
    }
}

// 单例模式
export default function createStorage(){
    if(!storageInstance){
        storageInstance = new Storage();
    }
    return storageInstance;
}

原型模式

JavaScript 中原型模式是很常见的,JavaScript 中实现的 继承 或者叫 委托 也许更合适,因为它不等同于如 Java 等语言中的继承,毕竟 JavaScript继承 是基于原型(prototype)来实现.

class Person {
    say() {
        console.log(`hello, my name is ${this.name}!`);
    }

    eat(foodName) {
        console.log(`eating ${foodName}`);
    }
}

class Student extends Person {
    constructor(name) {
        super();
        this.name = name;
    }
}

const zs = new Student('zs');
const ls = new Student('ls');

console.log(zs.say === ls.say);// Java 中是不相等的, JavaScript 中是相等的
console.log(zs.eat === ls.eat);// Java 中是不相等的, JavaScript 中是相等的

vue2 中的原型模式

文件位置:\src\core\instance\lifecycle.js

image.png

结构型设计模式

装饰器模式

核心是在不改变原 对象/方法 的基础上,通过对其进行包装拓展,使原有 对象/方法 可以满足更复杂的需求.

装饰器本质

装饰器模式本质上就是 函数的传参和调用,通过函数为已有 对象/方法 进行扩展,而不用修改原对象/方法,满足 开放封闭原则.

通过配置 babel 通过将 test.js 转为为 bable_test.js 用来查看装饰器的本质:

babel.config.json

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "current"
        }
      }
    ]
  ],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", { "loose": true }]
  ]
}

test.js

// 定义装饰器
function decoratorTest(target) {
  console.log(target);
}

// 使用装饰器,装饰 Person 类
@decoratorTest
class Person {
  say() {}
  eat() {}
}

执行 babel test.js --out-file babel_test.js 命令是生成 babel_test.js

"use strict";

var _class;

function decoratorTest(target) {
  console.log(target);
}

let Person = decoratorTest(_class = class Person {
  say() {}

  eat() {}

}) || _class;

React 中的装饰器模式 —— HOC 高阶组件

高阶组件 是参数为 组件,返回值为新组件的 函数,在 ReactHOC 通常用于复用组件公共逻辑.

// TodoList 组件
class TodoList extends React.Component {}

// HOC 函数
function WrapContainer(Comp) {
  return (
    <div style={{ border: "1px solid red", padding: 10 }}>
      <Comp title="todo" />
    </div>
  );
}

// HOC 装饰 TodoList 组件,为 TodoList 组件包裹红色边框
const newTodoList = WrapContainer(TodoList);

适配器模式

适配器模式本质就是 让原本不兼容的功能能够生效,避免大规模修改代码,对外提供统一使用.

Axios 中的适配器

通过观察 Axios 的目录结构,很容就发现其使用了适配器模式:

image.png

其实 Axios 中的 adapters 主要目的是根据当前运行时环境,向外返回对应的适配器 adapter,而这个适配器要做的其实就是兼容 web 浏览器环境和 node 环境的 http 请求,保证对外暴露的仍然是统一的 API 接口

image.png

代理模式

代理模式顾名思义就是 不能直接访问目标对象,需要通过代理器来实现访问,通常是为了提升性能、保证安全等.

事件代理

事件代理是很常见的性能优化手段之一,react 的事件机制也采用了事件代理的方式(篇幅有限可自行了解),这里演示简单的 JavaScript 事件代理:

<div id="container">
  <p>this number is 1</p>
  <p>this number is 2</p>
  <p>this number is 3</p>
  <p>this number is 4</p>
  <p>this number is 5</p>
</div>

<script>
  const container = document.querySelector("#container");
  container.addEventListener("click", function (e) {
    alert(e.target.textContent);
  });
</script>

Vue 中的代理 Proxy

Vue.js 3 中通过 Proxy 实现了对数据的代理,任何读取、设置的操作都会被 代理对象handlers 拦截到,从而实现 Vue 中的 tracktrigger

image.png
image.png

行为型设计模式

策略模式

策略模式实际上就是定义一系列的算法,将单个功能封装起来,并且对扩展开放.

举个例子

假如我们需要为某个游乐场的门票价格做差异化询价,主要人员类型分为 儿童、成年人、老年人 三种,其对应的门票折扣为 8折、9折、8.5折

if-else 代码一把梭

缺点:无论哪种人员类型的折扣变动,都需要修改 finalPrice 函数,不符合对 对修改封闭

function finalPrice(type, price) {
  if (type === "child") {
    // do other thing
    return price * 0.8;
  }

  if (type === "adult") {
    // do other thing
    return price * 0.9;
  }

  if (type === "aged") {
    // do other thing
    return price * 0.85;
  }
}

单一功能封装

缺点:若人员类型增加妇女类型,仍然需要修改 finalPrice 函数,且不符合 对扩展开放

function childPrice(price) {
  // do other thing
  return price * 0.8;
}

function adultPrice(price) {
  // do other thing
  return price * 0.9;
}

function agedPrice(price) {
  // do other thing
  return price * 0.85;
}

function finalPrice(type, price) {
  if (type === "child") {
    return childPrice(price);
  }

  if (type === "adult") {
    return adultPrice(price);
  }

  if (type === "aged") {
    return agedPrice(price);
  }
}

创建映射关系

通过映射关系,很好的将 finalPrice 和 具体的计算逻辑进行分离,在需要扩展类型时,只需要修改 priceTypeMap 对象而不用修改对外暴露的 finalPrice 函数.

const priceTypeMap = {
  child: function (price) {
    // do other thing
    return price * 0.8;
  },
  adult: function (price) {
    // do other thing
    return price * 0.9;
  },
  aged: function (price) {
    // do other thing
    return price * 0.85;
  },
};

function finalPrice(type, price) {
    return priceTypeMap[type](price);
}

状态模式

状态模式允许一个对象在其内部状态发生改变时,能够改变原本的行为.

举例子

假如现在我们需要设计一个售票机器,主要出售 巴士、火车、飞机票等,价格分别为 50、150、1000,并且能够根据剩余票数决定是否能够继续购买.

通过策略模式实现核心代码逻辑

有了上面的 策略模式 的思想,立马就可以设计出如下的代码:

缺点:没有根据剩余票数决定是否可以继续售卖,主要原因就在于抽离的 ticketTypeMapTicketMachine 之间的状态没有关联

const ticketTypeMap = {
  bus() {
    // do other thing
    return 50;
  },
  train() {
    // do other thing
    return 150;
  },
  plane() {
    // do other thing
    return 1000;
  },
};

class TicketMachine {
  constructor() {
    // 剩余票数
    this.remain = {
      bus: 100,
      train: 150,
      plane: 200,
    };
  }

  selling(type) {
    return ticketTypeMap[type]();
  }
}

关联对象状态 — 函数传参

通过函数传参的方式将对象传递给目标函数,让目标函数通过该对象访问和修改对象内部的状态.

const ticketTypeMap = {
  bus(remain) {
    if (remain.bus <= 0) return Error("抱歉,巴士票已售完");
    remain.bus--;
    return 50;
  },
  train(remain) {
    if (remain.train <= 0) return Error("抱歉,火车票已售完");
    remain.train--;
    return 150;
  },
  plane(remain) {
    if (remain.plane <= 0) return Error("抱歉,飞机票已售完");
    remain.plane--;
    return 1000;
  },
};

class TicketMachine {
  constructor() {
    // 剩余票数
    this.remain = {
      bus: 100,
      train: 150,
      plane: 200,
    };
  }

  selling(type) {
    return ticketTypeMap[type](this.remain);
  }
}

关联对象状态 — 整合方法

实际上 ticketTypeMap 映射的方法和 TicketMachine 有较强的关联性,不应该单独存在,因此,可以将这个映射对象整合进 TicketMachine 当中

class TicketMachine {
  constructor() {
    // 剩余票数
    this.remain = {
      bus: 100,
      train: 150,
      plane: 200,
    };
  }

  ticketTypeMap = {
    that: this,
    bus() {
      const { remain } = this.that;
      if (remain.bus <= 0) return Error("抱歉,巴士票已售完");
      remain.bus--;
      return 50;
    },
    train() {
      const { remain } = this.that;
      if (remain.train <= 0) return Error("抱歉,火车票已售完");
      remain.train--;
      return 150;
    },
    plane() {
      const { remain } = this.that;
      if (remain.plane <= 0) return Error("抱歉,飞机票已售完");
      remain.plane--;
      return 1000;
    },
  };

  selling(type) {
    return this.ticketTypeMap[type]();
  }
}

观察者模式

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

vue 中的观察者模式

image.png

vue 中的响应式原理就使用了 观察者模式,我们简单回顾一下其工作流程:

  • compile:将模板内容编译得到对应的 render 渲染函数
  • render:渲染函数执行生成 VNode,通过 patch 函数初始化视图 view

    • Observe:负责将 data 中返回的对象进行数据劫持(getter/setter),且其中会使用 Dep 来实现 watcher 的存储,相当于 被观察者
    • Dep:在触发 getter 时执行 dep.depend() 实际上执行的是 watcher.addDep(),该方法会将当前的 dep 对象保存到 Watcher,同时将当前的 watcher 通过 dep.addSub() 添加到 Dep
    • Watcher:相当于 观察者,提供统一的 update() 方法供 Dep 调用
  • data changed:响应式数据发生变更,触发数据劫持操作 setter

    • 进而执行 dep.notify() 方法,通过循环去执行 watcher.update() 方法,即执行 queueWatcher()watcher 添加到 queue 队列中
    • 最后由 scheduler 调度器 中执行 nextTick(flushSchedulerQueue) 进行异步队列刷新操作

以上过程中,显然 ObserveWatcher 就是 被观察者观察者 ,因为 Observe 中实现了对 Watcher 的收集和监听到数据状态发生变化时通知 Watcher 更新的处理,可以认为 Dep 只是 Observe 中使用到的一个存储和派发 Watcher 的工具.

发布订阅模式

发布订阅模式有三个核心:发布者、事件中心、订阅者,且发布订阅模式中的 发布者订阅者 不能直接进行通信,必须要经过 事件中心 来统一调度.

与观察者模式的区别

实际上,发布订阅模式和观察者模式在概念上非常相似,做的事情也都一致,主要区别在于:

  • 发布订阅模式依赖于 事件中心 统一调度 发布者订阅者发布者订阅者 不直接进行通信
  • 观察者模式中的 被观察者观察者 是直接建立连接的,被观察者 需要保存 观察者 的信息,观察者 需要提供统一的 方法 供观察者进行使用

实现发布订阅模式

vue 中的 全局事件总线(Event Bus)和 node 中的 Event Emitter,甚至是浏览器中的事件注册(addEventListener)和执行,它们都属于发布订阅模式.

下面实现一个简单的发布订阅模式:

class EventEmitter {
  constructor() {
    this.handlers = {};
  }

  on(name, handle) {
    if (!this.handlers[name]) {
      this.handlers[name] = [];
    }

    this.handlers[name].push(handle);
  }

  emit(name, ...args) {
    if (this.handlers[name]) {
      this.handlers[name].forEach((handle) => {
        handle(...args);
      });
    }
  }

  off(name, handle) {
    if (this.handlers[name]) {
      this.handlers[name] = this.handlers[name].filter((h) => {
        if (handle) return h !== handle;
        return false;
      });
    }
  }

  once(name, handle) {
    const onceHandle = (...args) => {
      handle(...args);
      this.off(name, onceHandle);
    };

    this.on(name, onceHandle);
  }
}

迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示,核心目的就是 遍历.

JavaScript 中的遍历方式

  • Array :for...of、for...in、forEach、map、filter
  • Object :for...in
  • Map :for...of、forEach
  • Set :for...of、forEach

看起来很难有一种方法能够兼容以上几种数据结构的遍历方式,即不需要考虑数据结构本身就能实现遍历的目的,但我们可以基于 ES6Symbol.iterator 实现自定义迭代器.

Symbol.iterator 实现通用迭代

Symbol.iterator 为每一个对象定义了默认的迭代器,拥有该迭代器后就可以被 for...of 循环使用.

function $each(data, handle) {
  if (typeof data !== "object") throw TypeError("data should be object!");

  if (!data[Symbol.iterator]) {
    Object.prototype[Symbol.iterator] = function () {
      let i = 0;
      let keys = Reflect.ownKeys(this);
      return {
        next() {
          const done = i >= keys.length;
          return {
            value: done ? undefined : keys[i++],
            done,
          };
        },
      };
    };
  }

  for (const item of data) {
    handle(item);
  }
}

最后

大前端的各种新技术层出不穷,很容易忽视如数据结构、设计模式等基础内容,其实看很多设计模式相关的内容,很少有讲得简单易懂的,终归是没有结合现有的框架去学习到底是如何使用起来。

0 条评论
请不要发布违法违规有害信息,如发现请及时举报或反馈
还没有人评论呢,速度抢占沙发!
相关文章
  • 以前写过一个简单的发布订阅模式,现在项目中还在用,应该是没什么大问题的,简单使用可以,代码如下:module.exports = class Watcher { constructor () { ...

  • 代理模式在javascript中可以使用Proxy对象,可以更好的去控制一些对象的交互,既然谈到了Proxy,我们先简单了解一下Proxy到底是干什么的ProxyProxy是用来创建一个对象的代理,实...

  • 1、什么是发布-订阅模式 发布-订阅模式是属于经过解耦合的观察者模式。 让多个订阅者对象同时监听某一个调度中心,这个调度中心观察到发布者发生变化的时候,会通知所有的订阅者。 涉及的几个角色: 订阅者...

  • 想分享的几种设计模式目前模式:工厂模式,单例模式,适配器模式,装饰者模式,建造者模式建造者模式简介:建造者模式(builder pattern)比较简单,它属于创建型模式的一种。白话:4个部分:有个产...

  • 1.概念:FileReader 对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区即buffer)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。 2.属性...

  • JavaScript 不断升级迭代,越来越多的新特性让我们的代码写起来干净有趣,在这篇文章中我们介绍了 10 个新特性。 1. 字符串。原型.replaceAll replaceAll () 返回...

  • /** * 替换字符串,默认替换 ""。传递 regExps,一个正则表达式数组。 * * @param source 被修剪的字符串 * @param regExps 正则表达式,找到匹配的...

  • 1.1 基本概念 在某些特殊场合中,⼀个类对外提供且只提供⼀个对象时,这样的类叫做单例类,⽽设计单例类的流程和思想叫做单例设计模式。 1.2 实现流程 私有化构造方法,使用private关键字修...

  • 使用 Notification 可以在系统级别发送页面外部显示的桌面通知,即使浏览器在后台运行也可以向用户发出消息 检查权限 发送通知需要用户授权,通过只读属性 Notification.permi...

  • 常见的在线客服系统中,或者是统计代码中,粘贴一段js代码,就能引入某个插件的效果。这个是怎么实现的呢? 原理非常的简单: 对于不同的加载文件类型创建不同的节点,然后添加各自的属性,最后扔到head ...

  • // -------------------------Day04homework 大练习------------------------ #### 练习题1: * 显示用户输入内容 ...

  • 简介: 门面模式又称之为外观模式,属于结构型的设计模式。 用于为子系统中的一组接口提供一致的调用方案。门面模式定义了一个高层接口,引入门面角色之后,只需要直接与门面角色交互,降低了系统的耦合度。 适用...

  • 学习信息 学习形式:网络教学视频 学习地址:https://www.bilibili.com/video/BV1Sy4y1C7ha/?spm_id_from=333.337.search-card.a...

  • JavaScript合集 学完HTML5+CSS3的小伙伴,学习JS时,要多敲多练多想多拓展 刚开始入门JS的时候,我们不需要纠结那么多,有些需要先记住,后面会慢慢明白为什么是这样的 JS基础部分 我...

  • new Promise() 的时候,传一个 executor 给 Promise. let promise = new Promise(function(resolve, reject) { //...

  • 一般做业务开发,不太容易有大量使用设计模式的场景。这里总结一下在业务开发中使用较为频繁的设计模式。当然语言为Java,基于Spring框架。1 策略模式(Strategy Pattern)一个类的行为...

  • this的指向问题 全局作用域 在JS中,全局的变量和函数附着在global对象上,全局对象在浏览器环境下是window对象。 在全局作用域中,this指向全局对象window。 console....

  • 简介: 代理模式,是结构型的设计模式。用于为其它对象提供一种代理以控制对这个对象的访问。 目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象,并且可以在不改变目标对象的情况下添加一些额外的...

  • JavaScript入门系列目录 JavaScript入门①-基础知识筑基 JavaScript入门②-函数(1)基础{浅出} JavaScript入门③-函数(2)原理{深入}执行上下文 Java...

  • 01、JavaScript基础知识 JavaScript(缩写:JS)是一种具有面向对象能力的、解释型的程序语言,基于对象和事件驱动,具有相对安全性的客户端脚本语言。JavaScript是一门完备的 ...