手写代码

213 阅读4分钟

juejin.cn/post/684490…

懒加载 & 预加载

lilywei739.github.io/2017/02/06/… www.geekjc.com/post/58d94d…

防抖和节流

juejin.cn/post/684490… juejin.cn/post/684490…

深拷贝

数组去重、数组乱序

继承(ES5/ES6)

实现promise

实现promise.all

实现promise.retry

将一个同步callback包装成promise形式

写一个函数,可以控制最大并发数

jsonp的实现

eventEmitter(emit,on,off,once)

实现new

实现数组flat、filter等方法

lazyMan

函数currying

如何在ES5环境下实现const

实现instanceof

instanceof

juejin.cn/post/697144…

encodeURIComponent & decodeURIComponent 用法

image.png

xhr & ajax

JUEJIN.CN/POST/684490…

怎么用原生JS写一个GET请求呢?如下代码,只需3行:

let xhr = new XMLHttpRequest();
xhr.open("GET", "/list");
xhr.send();

image.png

怎么用原生JS写一个POST请求呢?如下图所示:

image.png

排序算法

instanceof

instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。

function instanceof(left, right) {
    // 获得类型的原型
    let prototype = right.prototype
    // 获得对象的原型
    left = left.__proto__
    // 判断对象的类型是否等于类型的原型
    while (true) {
    	if (left === null)
    		return false
    	if (prototype === left)
    		return true
    	left = left.__proto__
    }
}

手写call、apply、bind

call

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

apply

Function.prototype.myApply = function(context) {
  context = context || window
  let fn = Symbol()
  context[fn] = this
  let result
  if (arguments[1]) {
    result = context[fn](...arguments[1])
  } else {
    result = context[fn]()
  }
  delete context[fn]
  return result
}

bind

Function.prototype.myBind = function (context) {
  var _this = this
  var args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

实现Storage类,使得该对象为单例,基于localStorage进行封装。实现方法 setItem(key,value) 和 getItem(key)?

静态方法版:
class Storage {
  static create() {
    if (!Storage.instance) {
      Storage.instance = new Storage();
    }
    return Storage.instance;
  }
  setItem(key, value) {
    return localStorage.setItem(key, value);
  }
  getItem(key, value) {
    return localStorage.getItem(key, value);
  }
}

闭包版:
function Storage() { }
Storage.prototype.setItem = function (key, value) {
  return localStorage.setItem(key, value);
}
Storage.prototype.getItem = function (key) {
  return localStorage.getItem(key, value);
}
const createStorage = (function () {
  let instance;
  return function () {
    if (!instance) {
      instance = new Storage();
    }
    return instance
  }
})()

如何判断一个变量是否是数组?

  • arr instanceof Array
  • Array.prototype.isPrototypeOf(arr)
  • Array.isArray(arr)
  • Object.prototype.toString.call(arr) === '[object Array]'
  • arr.constructor === Array

类数组转为数组的方式有哪些?

  • [].slice.call(arguments)
  • Array.from(arguments)
  • [...arguments]

Symbol.iterator

//obj就是可遍历的,因为它遵循了Iterator标准,且包含[Symbol.iterator]方法,方法函数也符合标准的Iterator接口规范。
//obj.[Symbol.iterator]() 就是Iterator遍历器
let obj = {
  data: [ 'hello', 'world' ],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++],
            done: false
          };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};

如何在 JS 中“深冻结”对象?

如果咱们想要确保对象被深冻结,就必须创建一个递归函数来冻结对象类型的每个属性:

浅冻结

let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
Object.freeze(person); 
person.profession.name = "doctor";
console.log(person); //output { name: 'Leonardo', profession: { name: 'doctor' } }

深冻结

function deepFreeze(object) {
    let propNames = Object.getOwnPropertyNames(object);
    for (let name of propNames) {
        let value = object[name];
        object[name] = value && typeof value === "object" ?
            deepFreeze(value) : value;
    }
    return Object.freeze(object);
}
let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
deepFreeze(person);
person.profession.name = "doctor"; // TypeError: Cannot assign to read only property 'name' of object

手写ajax

  • (1)创建XMLHttpRequest对象,也就是创建一个异步调用对象
  • (2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息
  • (3)设置响应HTTP请求状态变化的函数
  • (4)发送HTTP请求
  • (5)获取异步调用返回的数据
  • (6)使用JavaScript和DOM实现局部刷新

sleep函数

github.com/Advanced-Fr…

如何判断一个对象是不是空对象?

Object.keys(obj).length === 0

浅克隆 & 深克隆 浅拷贝 & 深拷贝

浅克隆:

function shallowClone(obj) {
  let cloneObj = {};
  for (let i in obj) {
    cloneObj[i] = obj[i];
  }
  return cloneObj;
}

深克隆

  • 考虑基础类型
  • 引用类型
    • RegExp、Date、函数 不是 JSON 安全的
    • 会丢失 constructor,所有的构造函数都指向 Object
    • 破解循环引用
function deepCopy(obj) {
  if (typeof obj === 'object') { // null -> object?
    var result = obj.constructor === Array ? [] : {};
    for (var i in obj) {
      result[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i];
    }
  } else {
    var result = obj;
  }
  return result;
}

能不能实现图片懒加载?

juejin.cn/post/684490…

写个简单Loader

loader 就是一个 node 模块,它输出了一个函数。当某种资源需要用这个 loader 转换时,这个函数会被调用。并且,这个函数可以通过提供给它的 this 上下文访问 Loader API 。 reverse-txt-loader

// 定义
module.exports = function(src) {
 //src是原文件内容(abcde),下面对内容进行处理,这里是反转
 var result = src.split('').reverse().join('');
 //返回JavaScript源码,必须是String或者Buffer
 return `module.exports = '${result}'`;
}
//使用
{
test: /\.txt$/,
use: [{
'./path/reverse-txt-loader'
}]
},

使用ES5实现一个继承?

所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。

只调用了一次超类构造函数,效率更高。避免在SuberType.prototype上面创建不必要的、多余的属性,与其同时,原型链还能保持不变。

因此寄生组合继承是引用类型最理性的继承范式。

function SuperType(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.getName = function () {
    return this.name;
}

function SuberType(name, age) {
    SuperType.call(this, name);
    this.age = age;
}
//寄生组合式继承
SuberType.prototype = Object.create(SuperType.prototype);
SuberType.prototype.constructor = SuberType;

SuberType.prototype.getAge = function () {
    return this.age;
}

let girl = new SuberType('Yvette', 18);
girl.getName();

如何实现 Promise.all ?

要实现 Promise.all,首先我们需要知道 Promise.all 的功能:

  • 如果传入的参数是一个空的可迭代对象,那么此promise对象回调完成(resolve),只有此情况,是同步执行的,其它都是异步返回的。
  • 如果传入的参数不包含任何 promise,则返回一个异步完成.
  • promises 中所有的promise都“完成”时或参数中不包含 promise 时回调完成。
  • 如果参数中有一个promise失败,那么Promise.all返回的promise对象失败
  • 在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组
Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let index = 0;
        let result = [];
        if (promises.length === 0) {
            resolve(result);
        } else {
            function processValue(i, data) {
                result[i] = data;
                if (++index === promises.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                //promises[i] 可能是普通值
                Promise.resolve(promises[i]).then((data) => {
                    processValue(i, data);
                }, (err) => {
                    reject(err);
                    return;
                });
            }
        }
    });
}

如何实现 Promise.prototype.finally ?

不管成功还是失败,都会走到finally中,并且finally之后,还可以继续then。并且会将值原封不动的传递给后面的then.

Promise.prototype.finally = function (callback) {
    return this.then((value) => {
        return Promise.resolve(callback()).then(() => {
            return value;
        });
    }, (err) => {
        return Promise.resolve(callback()).then(() => {
            throw err;
        });
    });
}