js深入之call、apply和bind

2019/7/21 11:52:07 人评论 次浏览 分类:学习教程

一. 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

相关资讯

    暂无相关的资讯...

共有访客发表了评论 网友评论

验证码: 看不清楚?
    -->