神族九帝's blog 神族九帝's blog
首页
网盘 (opens new window)
线报 (opens new window)
商城 (opens new window)
  • 复习指导
  • HTML
  • CSS
  • JavaScript
  • 设计模式
  • 浏览器
  • 手写系列
  • Vue
  • Webpack
  • Http
  • 前端优化
  • 项目
  • 面试真题
  • 算法
  • 精选文章
  • 八股文
  • 前端工程化
  • 基础篇
  • 进阶篇
  • 高级篇
  • 计算机基础
  • 高频考点
  • 精简题
  • 综合问题
  • 复习题
  • vue
  • vue2源码学习
  • 剖析vuejs内部运行机制
  • TypeScript 入门实战笔记
  • vue3源码学习
  • 2周刷完100道前端优质面试真题
  • npm发包
  • 重学node
  • 前端性能优化方法与实战
  • webpack原理与实战
  • webGl
  • 前端优化
  • Web3
  • 更多
  • 网站
  • 资源
  • Vue资源
  • 收藏的一些API
  • 未来要做的事
  • 宝塔面板+青龙面板
  • 安卓手机当服务器使用
  • 京东自动评价代码
  • 搭建x-ui免流服务器(已失效)
  • 海外联盟
  • 好玩的docker
  • 导航
GitHub (opens new window)

神族九帝,永不言弃

首页
网盘 (opens new window)
线报 (opens new window)
商城 (opens new window)
  • 复习指导
  • HTML
  • CSS
  • JavaScript
  • 设计模式
  • 浏览器
  • 手写系列
  • Vue
  • Webpack
  • Http
  • 前端优化
  • 项目
  • 面试真题
  • 算法
  • 精选文章
  • 八股文
  • 前端工程化
  • 基础篇
  • 进阶篇
  • 高级篇
  • 计算机基础
  • 高频考点
  • 精简题
  • 综合问题
  • 复习题
  • vue
  • vue2源码学习
  • 剖析vuejs内部运行机制
  • TypeScript 入门实战笔记
  • vue3源码学习
  • 2周刷完100道前端优质面试真题
  • npm发包
  • 重学node
  • 前端性能优化方法与实战
  • webpack原理与实战
  • webGl
  • 前端优化
  • Web3
  • 更多
  • 网站
  • 资源
  • Vue资源
  • 收藏的一些API
  • 未来要做的事
  • 宝塔面板+青龙面板
  • 安卓手机当服务器使用
  • 京东自动评价代码
  • 搭建x-ui免流服务器(已失效)
  • 海外联盟
  • 好玩的docker
  • 导航
GitHub (opens new window)
  • 复习指导

  • HTML

  • CSS

  • JavaScript

  • 设计模式

  • 浏览器

  • 手写系列

    • 继承
    • call,apply,bind
      • apply,call,bind 原理
        • 判断数据类型
        • 类数组借用方法
        • 获取数组的最大值/最小值
        • 继承
      • 手写 new
      • 手写 call 和 apply
      • 手写 bind
      • 手写 instanceof
      • 手写 debounce
      • 手写 throllte
    • 手写promise
    • 手写基础js
    • 手写排序
    • 并行限制的Promise
  • Vue

  • Webpack

  • Http

  • 前端优化

  • 项目

  • 面试真题

  • 算法

  • 精选文章

  • 八股文

  • 前端工程化

  • 面试题
  • 手写系列
wu529778790
2021-09-06

call,apply,bind

20210906173631

# apply,call,bind 原理

call、apply 和 bind 是挂在 Function 对象上的三个方法,调用这三个方法的必须是一个函数

都可以改变函数 func 的 this 指向

call 和 apply 的区别在于,传参的写法不同:apply 的第 2 个参数为数组; call 则是从第 2 个至第 N 个都是给 func 的传参

bind 虽然改变了 func 的 this 指向,但不是马上执行,而这两个(call、apply)是在改变了函数的 this 指向之后立马执行

let a = {
  name: "jack",
  getName: function(msg) {
    return msg + this.name;
  },
};
let b = {
  name: "lily",
};
console.log(a.getName("hello~")); // hello~jack
console.log(a.getName.call(b, "hi~")); // hi~lily
console.log(a.getName.apply(b, ["hi~"])); // hi~lily
let name = a.getName.bind(b, "hello~");
console.log(name()); // hello~lily

# 判断数据类型

function getType(obj) {
  let type = typeof obj;
  if (type !== "object") {
    return type;
  }
  return Object.prototype.toString.call(obj).replace(/^$/, "$1");
}

# 类数组借用方法

arguments 这种类数组

var arrayLike = {
  0: "java",
  1: "script",
  length: 2,
};
Array.prototype.push.call(arrayLike, "jack", "lily");
console.log(typeof arrayLike); // 'object'
console.log(arrayLike);
// {0: "java", 1: "script", 2: "jack", 3: "lily", length: 4}

# 获取数组的最大值/最小值

apply 直接传递数组作为调用方法的参数,也可以减少一步展开数组

let arr = [13, 6, 10, 11, 16];
const max = Math.max.apply(Math, arr);
const min = Math.min.apply(Math, arr);
console.log(max); // 16
console.log(min); // 6

# 继承

function Parent() {
  this.name = "parent";
}

Parent.prototype.getName = function() {
  return this.name;
};

function Child() {
  Parent.call(this);
  this.type = "child";
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

# 手写 new

new 被调用后大致做了哪几件事情:

  1. 让实例可以访问到私有属性
  2. 让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
  3. 构造函数返回的最后结果是引用数据类型
function MyNew(ctor, ...args) {
  if (typeof ctor !== "function") throw " ctor must be a function";
  let obj = Object.create(ctor.prototype);
  // let obj = new Object();
  // obj.prototype = Object.create(ctor.prototype);
  let res = ctor.call(obj, ...args);
  let isObject = typeof res === "object" && res !== null;
  let isFunction = typeof res === "function";
  return isObject || isFunction ? res : obj;
}

# 手写 call 和 apply

实现 call 和 apply 的关键就在 eval 这行代码。其中显示了用 context 这个临时变量来指定上下文,然后还是通过执行 eval 来执行 context.fn 这个函数,最后返回 result

Function.prototype.call = function(context, ...args) {
  let context = context || window;
  context.fn = this;
  let result = context.fn(...args);
  delete context.fn;
  return result;
};

Function.prototype.apply = function(context, args) {
  let context = context || window;
  context.fn = this;
  let result = context.fn(...args);
  delete context.fn;
  return result;
};

# 手写 bind

要注意这两个方法(call 和 apply)和 bind 的区别就在于,这两个方法是直接返回执行结果,而 bind 方法是返回一个函数,因此这里直接用 eval 执行得到结果

实现 bind 的核心在于返回的时候需要返回一个函数,故这里的 fbound 需要返回,但是在返回的过程中原型链对象上的属性不能丢失。因此这里需要用 Object.create 方法,将 this.prototype 上面的属性挂到 fbound 的原型上面,最后再返回 fbound

Function.prototype.bind = function(context, ...args) {
  if (typeof this !== "function") {
    throw new Error("this must be a function");
  }
  var that = this;
  var fbound = function() {
    that.call(context, ...args, arguments);
  };
  if (this.prototype) {
    fbound.prototype = Object.create(this.prototype);
  }
  return fbound;
};

# 手写 instanceof

function myInstanceof(left, right) {
  //基本数据类型直接返回false
  if (typeof left !== "object" || left === null) return false;
  //getProtypeOf是Object对象自带的一个方法,能够拿到参数的原型对象
  let proto = Object.getPrototypeOf(left);
  while (true) {
    //查找到尽头,还没找到
    if (proto == null) return false;
    //找到相同的原型对象
    if (proto == right.prototype) return true;
    proto = Object.getPrototypeof(proto);
  }
}

# 手写 debounce

https://blog.shenzjd.com/pages/3c209d1a362c4/ (opens new window)

function debounce(cb, delay) {
  var timer;
  return function() {
    clearTimeout(timer);
    timer = setTimeout(() => {
      cb(...arguments);
    }, delay);
  };
}

# 手写 throllte

function throttle(cb, delay) {
  var timer;
  return function() {
    if (timer) return;
    timer = setTimeout(() => {
      cb();
      timer = null;
      // clearTimeout(timer)
    }, delay);
  };
}
编辑 (opens new window)
上次更新: 2022/05/24, 14:27:28
继承
手写promise

← 继承 手写promise→

最近更新
01
好玩的docker
07-04
02
我的第一个NFT
07-03
03
海外迅雷
06-01
更多文章>
Power by vuepress | Copyright © 2015-2023 神族九帝
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×