手写源码系列(一):call、apply和bind

最近工做不是十分紧张,闲来无事,决定好好巩固一下基础知识。最近的风气是,各个公司都喜欢拿源码说事儿,那咱们就借此机会,关注一下你们平常开发中常常用到的方法、属性的源码。javascript

概念

在开始以前,咱们很是有必要去了解、巩固一下call、apply和bind在开发中的具体做用。前端

// call
    var a = {
        name : "Cherry",

        func1: function () {
            console.log(this.name)
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.call(a),100);
        }

    };

    a.func2() // "Cherry"
    
// apply 
    var a = {
        name : "Cherry",

        func1: function () {
            console.log(this.name)
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.apply(a),100);
        }

    };

    a.func2()   // Cherry
    
// bind
  var a = {
        name : "Cherry",

        func1: function () {
            console.log(this.name)
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.bind(a)(),100);
        }

    };

    a.func2()  // Cherry

复制代码

相同点:均可以改变this指向。
不一样点:call方法接受的是一个参数列表,apply的第二个参数为数组;bind方法返回的不是具体数值,而是函数。
若是想获取bind方法的返回值,可主动执行java

function sayHelloTo (to) {
    console.log(`${this.name} say hello to ${to}`)
}

var Jerry = {
  name: 'Jerry'
}
sayHelloTo.call(Jerry, 'Tom')
//Jerry say hello to Tom.

var Foo = {
  name: 'Foo'
}
sayHelloTo.apply(Foo, ['Bar'])
//Foo say hello to Bar.

var XYZ = {
  name: 'XYZ'
}
var say = sayHelloTo.bind(XYZ)
say('ABC')
//XYZ say hello to ABC.
复制代码

手写call方法

Function.prototype.myCall = function(context, ...args) {
  // 判断是不是undefined和null
  if (typeof context === 'undefined' || context === null) { //1
    context = window
  }
  let fnSymbol = Symbol()      //2
  context[fnSymbol] = this    // 3
  let fn = context[fnSymbol] (...args) //4
  delete context[fnSymbol] //5
  return fn  //6
}
复制代码

咱们利用上面的myCall方法,替换call来执行上面的例子,能够获得相同的结果es6

咱们分别来解读一下上面备注的数字:
一、判断context的值,若是没有或者为undefined,则this指向window。(与call方法中,若是第一个参数行为保持一致
二、为传入的context扩展一个属性。
三、将原函数指向这个属性。(this在调用时,指向call前面的函数
四、执行原函数。
五、删除传入的对象的无用属性。
六、返回执行后的值。数组

这里须要注意的地方有两点:
一、要熟练使用es6相关知识(...args,Symbol)
二、之因此能获取到this.name,是由于此时的this指向的是contextapp

手写apply方法

Function.prototype.myApply = function(context, args) { // 1
  // 判断是不是undefined和null
  if (typeof context === 'undefined' || context === null) {
    context = window
  }
  let fnSymbol = Symbol()
  context[fnSymbol] = this
  let fn = context[fnSymbol] (...args)
  return fn
}
复制代码

注意传参不一样。
告诉你个小秘密,如何记住传的是列表类仍是数组呢?Array与apply都是A开头,那就传数组喽!call不是A开头,那就是列表!函数

手写bind方法

Function.prototype.myBind = function(context) {
// 判断是不是undefined和null
    if (typeof context === "undefined" || context === null) {
    	context = window;
    }
    self = this;
    return function(...args) {
    	return self.apply(context, args);
    }
}
复制代码

能够借用apply或者call,间接实现bind,而且,注意开头提到的区别!
bind返回的是函数哦~post

若是文章对的前端学习有帮助,别忘了点赞关注哦~学习


摘录自
较真的前端
this、apply、call、bind
MDNui

相关文章
相关标签/搜索