Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

手写系列:call、apply、bind、函数柯里化 #3

Open
wangmeijian opened this issue Dec 31, 2020 · 0 comments
Open

手写系列:call、apply、bind、函数柯里化 #3

wangmeijian opened this issue Dec 31, 2020 · 0 comments

Comments

@wangmeijian
Copy link
Owner

wangmeijian commented Dec 31, 2020

少废话,show my code

call

原理都在注释里了

// 不覆盖原生call方法,起个别名叫myCall,接收this上下文context和参数params
Function.prototype.myCall = function (context, ...params) {
  // context必须是个对象并且不能为null,默认为window
  const _this = typeof context === "object" ? context || window : window;
  // 为了避免和原有属性冲突,定义一个Symbol类型的属性
  const key = Symbol();
  // call方法的目的是改变函数的this指向,函数的this指向它的调用者,也就是说我们的目标是改变函数的调用者。
  // 下面的this就是函数本身,给_this增加一个名为[key]的方法指向this,就能用_this来调用this了
  _this[key] = this;
  const result = _this[key](...params);
  // 获取函数执行结果后,删除以上添加的属性
  delete _this[key];
  return result;
};

apply

和call的区别在于第二个参数

Function.prototype.myApply = function (context, params) {
  return this.myCall(context, ...params);
};

bind

和call的区别在于不立即执行,返回一个函数即可

Function.prototype.myBind = function (context, ...params) {
  const _this = this;
  // 返回的函数也能接收参数,但是是放在params后面
  return function (...args) {
    return _this.myCall(context, ...[...params, ...args]);
  };
};

函数柯里化

函数柯里化,举例,有如下函数

function test(a, b, c, d, e) {
  console.log(a + b + c + d + e);
}

有一个curry转换函数对test函数进行一些转换

function curry(){
  // todo
}
const transformTest = curry(test, ...args)

转换之后,原本一次性传过去的参数现在可以分步传参

// 使得
test(1,2,3,4,5)
// 等同于
transformTest(1)(2)(3)(4)(5)
// 或者
transformTest(1, 2)(3)(4, 5)
// 又或者
transformTest(1, 2, 3, 4)(5)

curry函数应该怎么写?

function curry(fn, ...args) {
  // 判断参数个数是不是等于原函数参数个数
  // 如果是,直接返回调用结果
  if ([...args].length >= fn.length) {
    return fn(...args);
  } else {
    // 如果不是,则返回一个函数
    return (...params) => {
      // 将前面传的全部参数传给curry,回到第一步的if判断,直到参数个数满足要求
      return curry(fn, ...args, ...params);
    };
  }
}

防抖

防抖函数最常见的应用场景是搜索框,用户的输入一般是连续的,为了减轻服务器压力,没有必要响应每个字符的搜索结果,如果用户连续输入一段关键字后等待一定时间,没有再次输入,就可以发起搜索请求,这样就少了很多请求。

function debounce(fn, defer) {
  let timer;
  return (...args) => {
    const context = this;
    // timer有值说明在defer时间内,多次触发了该函数,清空定时器,重新计时
    timer && clearTimeout(timer);
    timer = setTimeout(() => {
      fn.call(context, ...args);
      timer = null;
    }, defer);
  }
}

立即执行的防抖函数

function debounce(fn, defer,immediate) {
  let timer;
  return (...args) => {
    const context = this;
    // timer有值说明在defer时间内,多次触发了该函数,清空定时器,重新计时
    timer && clearTimeout(timer);
    const callNow = immediate && !timer;
    timer = setTimeout(() => {
      fn.call(context, ...args);
      timer = null;
    }, defer);
    if(callNow){
      fn.call(context, ...args)
    }
  }
}

节流

节流,让回调函数在一定时间内只执行一次,适用于比较耗费性能的操作,如DOM操作。

function throttle(fn, defer) {
  let time = new Date();
  return () => {
    // 两次时间差大于defer,就执行一次
    if(new Date() - time > defer){
      fn();
      time = new Date();
    }
  }
}

new

new操作符可以实例化一个对象,new一个对象的时候,会调用对象的构造函数,实例化出来的结果,它的__proto__指向构造函数的prototype。如果构造函数有返回值,直接返回这个值。

// 因无法实现new那样的操作符,用函数代替
function myNew(Constructor, ...arguments) {
  const obj = new Object();
  const res = Constructor.call(obj, ...arguments);
  obj.__proto__ = Constructor.prototype;
  return typeof res === 'object' ? res : obj;
}

Promise

这个需要另起一篇:Promise是如何实现异步编程的?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant