一. call和apply

1. 代码完整实现

Function.prototype.mycall = function (context, ...argus) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
const fn = this
let result = null context = context || window
context.fn = fn
result = context.fn(...argus)
delete context.fn return result
} Function.prototype.myapply = function (context, ...argus) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
const fn = this
let result = null context = context || window
argus = argus && argus[0] || []
context.fn = fn
result = context.fn(...argus)
delete context.fn return result
}

2. 先来溜溜

  • 案例一
class Member {
constructor (options) {
const {name, sex, age} = options
this.name = name
this.sex = sex
this.age = age
} introduce () {
console.log(`I'm ${this.name}, ${this.age}, ${this.sex}`)
}
} const member1 = new Member({
name: 'gina',
sex: 'girl',
age: 23
}) const member2 = new Member({
name: 'gun',
sex: 'boy',
age: 24
}) member2.introduce.mycall(member1) // I'm gina, 23, girl
member2.introduce.myapply(member1) // I'm gina, 23, girl

  

  • 案例二
Math.max.myapply(null, [1,2,3,4]) // 4
Math.max.mycall(null, 1,2,3,4) // 4

  

3. 注意要点

  • 开头需要做一个类型判断:
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
  • 获取原始函数: 比如执行Math.max.mycall(null, 1,2,3,4)的时候,mycall函数内部的this指向了Math.max函数,所以我们可以通过const fn = this获取到要执行的函数,然后将该函数绑定到传入的context对象(context.fn = fn),然后再把它删除掉delete context.fn

总体来说,call和apply的实现还是比较简单的。

二. bind

1. 完整代码实现

Function.prototype.mybind = function (context, ...argus) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
const fn = this
const fBound = function (...argus2) {
return fn.apply(this instanceof fBound ? this : context, [...argus, ...argus2])
}
fBound.prototype = Object.create(this.prototype)
return fBound
}

  

2. 边溜边说

  • 案例一
const foo = {
v: 1
}; function bar() {
return this.v;
} const bindFoo = bar.mybind(foo); bindFoo() // 1

bind 函数返回的是一个可执行函数,所以return了一个函数。此刻返回的函数,按正常来说,在执行的时候,this是指向执行处的当前上下文。但该案例中, mybind 需要满足bar在执行中返回值时,this依然是指向 foo,所以我们在mybind返回的函数中需要使用fn.apply来保持上下文和执行mybind的时候一致。

  • 案例二
const foo = {
v: 1
}; function bar(name, age) {
console.log(this.v);
console.log(name);
console.log(age); } const bindFoo = bar.bind(foo, 'daisy');
bindFoo('18');
// 1
// daisy
// 18

mybind 需要做到可以接受传参,并且将参数给到bar函数,后面再执行bindFoo再传的参数,会接在之前传参的后面。所以mybind源码中使用了[...argus, ...argus2]来进行参数整合。

  • 案例三
const value = 2;

const foo = {
value: 1
}; function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
} bar.prototype.friend = 'kevin'; const bindFoo = bar.bind(foo, 'daisy'); const obj = new bindFoo('18');
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin

在执行const obj = new bindFoo('18')这一 new操作的时候,此刻this应该指向当前对象obj。所以mybindfn.apply的第一个参数,做了这样的判断this instanceof fBound ? this : context

const obj = new bindFoo('18')内部执行到this instanceof fBound ? this : context时,此刻this指向objfBound其实也就是bindFoothis instanceof fBound判断了obj是不是继承自bindFoo,也就是进行了构建函数new操作。

  • 案例4
function bar() {}

bar.prototype.value = 2

const bindFoo = bar.mybind(null);

bindFoo.prototype.value = 1;

console.log(bar.prototype.value) // 2

mybind 执行后返回的函数fBound修改prototype的时候,不应该影响到fn.prototype,两者应该是独立的。所以源码使用了fBound.prototype = Object.create(this.prototype), 而不是fBound.prototype = this.prototype

总得来说,bind的实现考虑的点还是比较多的。

参考:

https://github.com/mqyqingfeng/Blog/issues/12

最新文章

  1. 完全卸载MySQL重新安装MySQL
  2. 利用反卷积神经网络可视化CNN
  3. Hadoop总结篇之一------开篇
  4. ZPPR101-批量更改BOM组件
  5. 【linux】linux服务管理
  6. sql,插入最大值加1
  7. curl文件上传有两种方式,一种是post_fileds,一种是infile
  8. 调试UnhandledExceptionFilter
  9. 九度OJ 1372 最大子向量和(连续子数组的最大和)
  10. word2vec 入门基础(一)
  11. java selenium webdriver实战 页面元素定位
  12. 【iOS】swift 排序Sort函数用法(包含NSDictionary排序)
  13. mysql5.7安装和修改密码
  14. 使用Java泛型返回动态类型
  15. 【SPOJ GSS】数据结构题选做
  16. Informix 常用函数
  17. Python 各种测试框架简介(三):nose
  18. Java G1学习笔记
  19. 五、Mosquitto 高级应用之权限管理
  20. python写http post请求的四种请求体

热门文章

  1. C#高性能大容量SOCKET并发(七):协议字符集
  2. 利用Socket通信
  3. IntelliJ IDEA Maven工程保证JDK版本不变
  4. Qt设置窗体的透明度: setWindowOpacity
  5. Android项目开发之--------地铁时光机(一,搭建主框架)
  6. 《菜鸟也要学会C》-和大家聊一聊
  7. java写出PDF
  8. hdoj1009 FatMouse' Trade——贪心算法
  9. 在django中使用vue.js需要注意的地方
  10. (一)C#编程基础复习——开启编程之旅