工具函数实现原理和细节(一)

542 阅读3分钟

最近刷一些笔试题,巩固一下基础,做一个笔记,随着做题进度,也会持续更新,欢迎指正

1、实现一个深拷贝

function copyObj(obj) {
  let cloneObj
  //当输入数据为简单数据类型时直接复制
  if (obj && typeof obj !== 'object') {
    cloneObj = obj
  }
  //当输入数据为对象或数组时
  else if (obj && typeof obj === 'object') {
    //检测输入数据是数组还是对象
    cloneObj = Array.isArray(obj) ? [] : {}
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        if (obj[key] && typeof obj[key] === 'object') {
          //若当前元素类型为对象时,递归调用
          cloneObj[key] = copyObj(obj[key])
        }
        //若当前元素类型为基本数据类型
        else {
          cloneObj[key] = obj[key]
        }
      }
    }
  }
  return cloneObj
}

2、简单的发布订阅模式 和观察者模式

  • 发布订阅者模式
class EventClass {
  _events = {}
  on = (key, callBack) => {
    if (!this._events[key]) {
      this._events[key] = []
    }
    this._events[key].push(callBack)
  }
  emit = (key, ...args) => {
    this._events[key].forEach((callBack) => {
      callBack(...args)
    })
  }
  off(key, callBack) {
    this._events[key] = this._events[key].map((item) => {
      return item !== callBack
    })
  }
  once(key, callback) {
    const once = (...args) => {
      callback(...args)
      this.off(key, once)
    }
    this.on(key, once)
  }
}
  • 观察者模式
// 目标者类
class Subject {
    constructor() {
        // 观察者列表
        this.observers = [];
    }
    // 添加
    add(observer) {
        this.observers.push(observer);
    }
    // 删除
    remove(observer) {
        let idx = this.observers.findIndex(item => item === observer);
        idx > -1 && this.observers.splice(idx, 1);
    }
    // 通知
    notify() {
        for (let observer of this.observers) {
            observer.update();
        }
    }
}

// 观察者类
class Observer {
    constructor(name) {
        this.name = name;
    }
    // 目标对象更新时触发的回调
    update() {
        console.log(`目标者通知我更新了,我是:${this.name}`);
    }
}

// 实例化目标者
let subject = new Subject();

// 实例化两个观察者
let obs1 = new Observer('前端开发者');
let obs2 = new Observer('后端开发者');

// 向目标者添加观察者
subject.add(obs1);
subject.add(obs2);

// 目标者通知更新
subject.notify();
// 输出:
// 目标者通知我更新了,我是前端开发者
// 目标者通知我更新了,我是后端开发者

3、单例模式

// 手写一个单例模式
class Single {
  oneSingle = ''
  static installSingle = () => {
    if (!this.oneSingle) {
      this.oneSingle = new Single()
      return this.oneSingle
    }
    return this.oneSingle
  }
}

4、工厂模式-传入参数即可创建实例

// 工厂模式-传入参数即可创建实例
function Employee(name) {
  this.name = name;
  this.say = function () {
    console.log('I am employee ' + name);
  };
}

function EmployeeFactory() {
  this.create = function (name) {
    return new Employee(name);
  };
}

let persons = [];
const employeeFactory = new EmployeeFactory();
persons.push(employeeFactory.create('jake'));
persons.push(employeeFactory.create('rabin'));
persons.forEach(item => {
  item.say();
});
// I am employee jake
// I am employee rabin
function Vendor(name) {
  this.name = name;
  this.say = function () {
    console.log('I am Vendor ' + name);
  };
}

function vendorFun() {
  return function (name) {
    return new Vendor(name);
  };
}

const vendorFactory = vendorFun();
let vendors = [];
vendors.push(vendorFactory('jake'));
vendors.push(vendorFactory('rabin'));
vendors.forEach(item => {
  item.say();
});
// I am Vendor jake
// I am Vendor rabin

5、装饰器的用法

  • AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等。把这些功能抽离出来之后, 再通过“动态织入”的方式掺入业务逻辑模块中。
  • AOP的好处首先是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便地复用日志统计等功能模块。
  • 在 JavaScript 中实现 AOP,都是指把一个函数“动态织入”到另外一个函数之中
// 写了一个业务代码,扩展当前的业务代码
function say(a,b){
    console.log('say',a,b);
}
// 给某个方法 添加一个方法在他执行之前调用
Function.prototype.before = function (callback) {
    return (...args)=>{ // 剩余运算符, 箭头函数没有this(向上找) 也没有arguments
        callback();
        this(...args); // 展开运算符  apply的用法
    }
}
let beforeSay = say.before(function(){
    console.log('before say')
})
beforeSay('hello','world');
  • 装饰者(decorator)模式能够在不改变对象自身的基础上,动态的给某个对象添加额外的职责,不会影响原有接口的功能。
class Person {
  name: string;
  constructor(name:string) {
    this.name = name;
  }
  @fun(false)
  static say() {
    return "hello" + this.name
  }
}
function fun(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("target",target);
    console.log("propertyKey",propertyKey);
    descriptor.enumerable = value
    console.log(descriptor);
  }
}
const p = new Person("zhangsan")
// 打印的结果
// target [Function: Person] { say: [Function] }
// propertyKey say
// {
//   value: [Function],
//   writable: true,
//   enumerable: false,
//   configurable: true
// }

6、手写instanceof 的实现

// [1,2,3] instanceof Array ---- true
// L instanceof R
// 变量R的原型 存在于 变量L的原型链上
function instance_of(L,R){    
    // 验证如果为基本数据类型,就直接返回false
    const baseType = ['string', 'number','boolean','undefined','symbol']
    if(baseType.includes(typeof(L))) { return false }
    
    let RP  = R.prototype;  //取 R 的显示原型
    L = L.__proto__;       //取 L 的隐式原型
    while(true){           // 无线循环的写法(也可以使 for(;;) )
        if(L === null){    //找到最顶层
            return false;
        }
        if(L === RP){       //严格相等
            return true;
        }
        L = L.__proto__;  //没找到继续向上一层原型链查找
    }
}