手动实现代码

214 阅读3分钟

手写代码实现

防抖和节流

防抖

function debounce(func, ms = 500) {
  let timer;
  return function (...args) {
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      func.apply(this, args);
    }, ms);
  };
}

节流

function throttle(fn, delay) {
    // 记录上一次函数触发的时间
    var lastTime = 0;
    return function() {
        // 记录当前函数触发的时间
        var nowTime = Date.now();
        if (nowTime - lastTime > delay) {
        // 修正this指向问题
            fn.call(this);
        // 同步时间
          lastTime = nowTime;
        }
    }
}

手动实现new

github.com/mqyqingfeng…

JS里面new的使用方法

//构造函数
function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.height = 1.88
Person.prototype.eat = function () {
  console.log(this.name + ' eat')
}

var john = new Person('john', 18)
console.log(john.name,john.age,john.height)//john 18 1.88
john.eat()//john eat

手动实现new第一版代码

//第一版代码
function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.height = 1.88
Person.prototype.eat = function () {
  console.log(this.name + ' eat')
}
//new_ 函数为我们实现的new方法
function new_(){
  var obj = new Object()
  var Constructor = [].shift.call(arguments)//取出第一个参数顺便去掉arguments第一个参数
  obj.__proto__ = Constructor.prototype//让实例的__proto__指向构造函数的prototype
  Constructor.apply(obj,arguments)//apply和call的区别就是传入参数的区别,apply传入数组,所以这里要用apply
  return obj
}

var owen = new_(Person,'owen',20)
console.log(owen.name,owen.age,owen.height)
owen.eat()

但是函数如果有返回值情况会有一点变化

1.返回一个对象

//构造函数 返回一个对象
function Person(name, age) {
  this.name = name
  this.age = age
  return {
    name: name,
    age: age
  }
}
Person.prototype.height = 1.88
Person.prototype.eat = function () {
  console.log(this.name + ' eat')
}

var john = new Person('john', 18)
console.log(john.name,john.age,john.height)//john 18 undefined
john.eat()//TypeError: john.eat is not a function

如果返回的是对象的话,实例只能访问到返回对象中的属性

2.返回一个基本数据类型

//构造函数 返回的是一个基本数据类型
function Person(name, age) {
  this.name = name
  this.age = age
  return 'hello'
}
Person.prototype.height = 1.88
Person.prototype.eat = function () {
  console.log(this.name + ' eat')
}

var john = new Person('john', 18)
console.log(john.name,john.age,john.height)//john 18 1.88
john.eat()//john eat

如果返回一个基本数据类型,则会忽略这个返回值,依然返回创建的实例对象

所以重写new方法还需要考虑返回值的类型

// 第二版的代码
function new_() {

    var obj = new Object(),

    Constructor = [].shift.call(arguments);

    obj.__proto__ = Constructor.prototype;

    var ret = Constructor.apply(obj, arguments);

    return typeof ret === 'object' ? ret : obj;

};

手动实现call / apply / bind

call

//手动实现call

Function.prototype.call2 = function(context,...args){
  if(typeof context !== object || typeof context !== function){
	context = Object(context)
  }
  var result,
      key = Symbol('key')
  context[key] = this
  result = context[key](...args)
  delete context[key]
  return result
}

apply

//手动实现apply
//apply的参数是用数组传递
Function.prototype.apply2 = function(context,args){
  if(typeof context !== object || typeof context !== function){
	context = Object(context)
  }
  var result,
      key = Symbol('key')
  context[key] = this
  result = context[key](...args)
  delete context[key]
  return result
}

bind

无参数传递

//先写一个没有参数传递的版本
Function.prototype.bind2 = function (context) {
  var _this = this
  return function () {//bind返回的是一个函数
    //this.call(context) 这里的this是调用这个返回函数的this,不一定是实例对象
   _this.call(context) 
  }
}

测试一下

var obj = {
  a: 100,
  b: 200
}

function add() {
  return this.a + this.b
}

var fn = add.bind2(obj)
console.log(fn())//但这里输出的的undifined 为什么呢? 
//因为fn对应的是bind2里返回的函数


function () {
   
   _this.call(context)
   
}
//这个函数是没有返回值的,所以返回undifined

再测试一下

var obj = {
  a: 100,
  b: 200
}

function add() {
  console.log(this.a+this.b)
}

var fn = add.bind2(obj)
console.log(fn())//输出300    因为我们这是在函数里直接打印所以打印出300

有参数传递

Function.prototype.bind2 = function (context) {
  var _this = this
  var outerArgs = Array.prototype.slice.call(arguments, 1)
  return function () {
    var innerArgs = Array.prototype.slice.call(arguments)
    _this.apply(context, outerArgs.concat(innerArgs))
  }
}

测试一下

var obj = {
  a: 100,
  b: 200
}

function add(c, d) {
  console.log(this.a + this.b + c + d)
}

var fn = add.bind2(obj, 1)//函数科里化
//第二次传参
fn(1)// 302  

作为构造函数时

github.com/mqyqingfeng…

bind返回的函数如果作为构造函数,搭配new关键字出现的话,我们的绑定this就需要“被忽略”。
Function.prototype.bind2 = function (context) {
  var _this = this
  var fNOP = function(){}
  var outerArgs = Array.prototype.slice.call(arguments, 1)
  var fBound = function(){
    var innerArgs = Array.prototype.slice.call(arguments)
    //为什么这里可以判断 `this instanceof fBound` 是因为fBound返回的时候这个函数还没有执行到内部 
    _this.apply(this instanceof fBound ? this :context, outerArgs.concat(innerArgs))
  }
  fNOP.prototype = this.prototype;
  fBound.prototype = new fNOP();
  return fBound;
}

Promise

class Promise1 {
  // 我们调用的时候是传入((resolve,reject)=>{

  // })
  constructor(executor) {
    this.status = "pending"
    this.value = undefined
    this.reason = undefined
    //成功存放的数组
    this.onResolvedCallbacks = []
    // 失败存放的数组
    this.onRejectedCallbacks = []
    let resolve = value => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.value = value;
        // 一旦resolve执行,调用成功数组的函数
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    };
    let reject = reason => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.reason = reason;
        // 一旦resolve执行,调用成功数组的函数
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };
    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled, onRejected) {
    if (this.status == 'fulfilled') {
      onFulfilled(this.value)
    } else if (this.status == 'rejected') {
      onRejected(this.reason)
    } else if (this.status == 'pending') {
      this.onResolvedCallbacks.push(() => {
        onFulfilled(this.value);
      })
    }
  }
}
var p1 = new Promise1((resolve, reject) => {
  setTimeout(()=>{
    resolve(1)
  },1000)
}).then(value => {
  console.log(value)
}, reason => {
  console.log(value)
})