Skip to content

各种手写源码

技术面试如果足够有诚意就让人拿出笔记本当场写,随便查,你才能看到面试者在真实的写代码的时候是什么状态,什么思路。就看面试者去哪里查,怎么查,你就能得到比让人默写 API 要有价值得多的信息

——尤雨溪

防抖

节流

new

Object.create

深拷贝

instanceof

call、apply、bind

柯里化

发布订阅

AJAX

flat

async/await 实现

实现一个双向数据绑定

Array.isArray 实现

promise 实现

防抖

文章可见防抖与节流

饭都

javascript
function debounce(func, wait, flag) {
    let timer = null;
    return function (...args) {
        clearTimeout(timer);
        if (!timer && flag) {
            func.apply(this, args)
        } else {
            timer = setTimeout(() => {
            	func.apply(this, args)
        	}, wait)   
        }
    }
}

节流

javascript
function throttle(func, wait) {
    let pre,
        timer = null;
    if (Date.now() - pre > wait) {
        clearTimeout(timer);
        timer = null;
        pre = Date.now()
        func.apply(this, args)
    } else if (!timer) {
        timer = setTimeout(() => {
            func.apply(this, args)
        }, wait)
    }
}

new

文章可看new 做了什么

ES5 版本:

javascript
function new2(Constructor, ...args) {
  let obj = Object.create(null);
  obj.__proto__ = Constructor.prototype;
  let result = Constructor.apply(obj, args);
  return typeof result === 'object' ? result : obj;
}

ES3 版本:

javascript
function new3() {
    let obj = {},
    Constructor = Array.prototype.shift.call(arguments)
    var F = function(){}
    F.prototype = Constructor.prototype
    obj = new F()
    var result = Constructor.apply(obj, arguments)
    returb typeof result === 'object' ? result : obj
}

Object.create

javascript
function create(obj) {
    function F() {}
    F.prototype = obj;
    return new F()
}

深拷贝

文章可看拷贝的密码

javascript
function deepClone(source, storage = new WeakMap()) {
    // 针对基本数据类型
    if (typeof source !== 'object' || source === null) {
        return source
    }
    // 是否是日期
    if (source.constructor === Date) {
        return new Date(source)
    }
    // 是否是正则
    if (source.constructor === RegExp) {
        return new RegExp(source)
    }
    // 是否是数组
    let target = source instanceof Array ? [] : {}
    // 循环引用 返回存储的引用数据
    if (storage.has(source)) return storage.get(source)
    // 开辟存储空间设置临时存储值
    storage.set(source, target)
    // 是否包含 Symbol 类型
    let isSymbol = Object.getOwnPropertySymbols(source)
    // 包含 Symbol 类型
    if (isSymbol.length) {
        isSymbol.forEach((item) => {
            if (typeof source[item] === 'object') {
                target[item] = deepClone(source[item], storage);
                return
            }
            target[item] = source[item]
        })
    }
    // 不包含 Symbol
    for(let key in source) {
        if (source.hasOwnProperty(key)) {
            target[key] = typeof source[key] === 'object' ? deepClone(sourcep[key], storage) : source[key]
        }
    }
    return target;
}

instanceof

文章可看instanceof——找祖籍

javascript
function myInstanceof(left, right) {
    if (typeof left !== 'object' || left === null) return false;
    let proto = Object.getPrototypeOf(left);
    while (true) {
        if (proto == null) return false;
        if (proto == right.prototype) return true;
        proto = Object.getPrototypeOf(proto);
    }
}

call、apply、bind

call

javascript
function myCall(context = window, args) {
    if (this === Function.prototype) {
        return undefined;
    }
    let fn = Symbol()
    context[fn] = this;
    let result = context[fn](...args)
    delete context[fn];
    return result
}

apply

javascript
function myApply(context = window, args) {
    if (this === Function.prototype){
        return undefined
    }
    let fn = Symbol()
    context[fn] = this;
    let result;
    if (Array.isArray(arg)){
        result = context[fn](...args)
    } else {
        result = context[fn]()
    }
    delete context[fn]
    return result
}

bind

javascript
function myBind(context, ...args1) {
    if (this === Function.prototype) {
        return new TypeError('Error')
    }
    const _this = this;
    return function F(...args2) {
        if (this instance F) {
            return new _this(...args1, ...args2)
        }
        return _this.apply(context, args1.concat(args2))
    }
}

柯里化

ES5实现:

javascript
function curry(fn) {
  return function curriedFn() {
    var _args = Array.prototype.slice.call(arguments);
    if (_args.length < fn.length) {
      return function () {
        var _args2 = Array.prototype.slice.call(arguments);
        return curriedFn.apply(null, _args.concat(_args2));
      };
    }

    return fn.apply(null, _args);
  };
}

ES6实现:

javascript
function curry(fn) {
      // 获取函数的参数数量
    const argLength = fn.length;
    // 定义递归的柯里化函数
    function inner(...args) {
       	// 如果参数的数量满足要求,调用原函数
        if (args.length >= argLength) {
            return fn(...args)
        } else {
            // 否则返回一个新函数,继续接收参数
            return function (...args2) {
                return inner(...args, ...args2)
            }
        }
    }
    return inner;
}

或者(看你那种好理解)

javascript
function curry(fn) {
  return function curriedFn(...args) {
    if (args.length < fn.length) {
      return function () {
        return curriedFn(...args.concat(Array.from(arguments)));
      };
    }

    return fn(...args);
  };
}

发布订阅

javascript
class eventEmitter {
    constructor() {
        this.event = {}
    }
    
    on(type, callback) {
        if (!this.event[type]) {
            this.event[type] = [callback]
        } else {
            this.event[type].push(callback)
        }
    }
    
    off(type, callback) {
        if (!this.event[type]) {
            return
        }
        this.event[type] = this.event[type].filter((item) => {
            return item !== callback
        })
    }
    
    emit(type, ...args) {
        if (!this.event[type]) {
            return
        }
        this.event[type].forEach((callback) => {
            callback.apply(this, args)
        })
    }
    
    once(type, callback) {
        function f() {
            callback()
            this.off(type, f)
        }
        this.on(type, f)
    }
}

示例:

javascript
const eventEmitter = new EventEmitter();

// 订阅事件
eventEmitter.on("公众号", (name) => {
    console.log(`我订阅了${name}`)
})

// 发布事件
eventEmitter.emit('公众号''随朱波流')

AJAX

async JavaScript and XML(JSON)

javascript
var xhr = new XMLHttpRequest();

xhr.open('GET', '/xxx', true)
// false 异步
// true 同步

xhr.setRequestHeader("Content-Type", "application/json")

xhr.onreadystatechange = function () {
    if (xhr.readyState !== 4) return;
    if (xhr.status === 200 || xhr.status === 304) {
       	console.log('响应数据:', xhr.responseText);
    } else {
        console.error('请求失败,状态码:' + xhr.status);
    }
}

 xhr.send();

304:协商缓存重定向

flat

即数组扁平化

javascript
let ary = [1, [2, [3, [4, 5]]], 6];// -> [1, 2, 3, 4, 5, 6]

普通递归

javascript
let result = [];
function flat(arr) {
    for (let i = 0; i < arr.length; i++) {
        let item = arr[i]
        if (Array.isArray(item)) {
            flat(item)
        } else {
            result.push(item)
        }
    }
}

迭代:for of

for of 是迭代器

javascript
function wrap() {
    let ret = []
    return function flat(arr) {
        for (let item of arr) {
            if (item.constructor === Array) {
                ret.concat(flat(item))
            } else {
                ret.push(item)
            }
        }
        return ret
    }
}

console.log(wrap()(ary))

方法二:some

Array.prototype.some():找到符合条件的元素返回 true,否则则返回 false

javascript
const flatten = function(arr){
    while(arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr)
    }
    return arr
}

console.log(flatten(arr))

方法三:reduce

javascript
const flatten = arr = arr.reduce((acc, cur) => {
    return Array.isArray(acc) ? [...acc, ...flatten(cur)] : [...acc, cur]
}, [])

写原型上

javascript
let ary = [1, [2, [3, [4, 5]]], 6];
Array.prototype.flatten = function(depth) {
   let result = []
   for (let i = 0; i < this.length; i++) {
       let item = this[i];
       if (Array.isArray(item) && depth > 0) {
           result = [...result, ...this[i].flatten(depth -1)]
       } else {
           result.push(item)
       }
   } 
   return result
}

PS:数组原型上已经有 flat,所以改个名字

参考资料