call,apply,bind,jsonp的实现

call的实现

先看下call的实例

let person = {
    name: 'SpwaN'
}
function getName() {
    console.log(this.name)
}
getName.call(person)   // SpwaN

call的特点:
1. 改变this指向
2. 直接调用函数
3. 传null的时候,指向window
4. 可以传参,参数分开传

ES6 写法
Function.prototype.call1 = function (context, ...args) {
    var context = context || window    // 解决第三步
    context.fn = this                  // 解决第一步
    context.fn(...args)                // 解决第二步,第四步
    delete context.fn                  // 处理小尾巴
}

ES5 写法
Function.prototype.call2 = function (context) {
    var context = context || window
    context.fn = this
    let args = []
    for (let i = 0; i < arguments.length; i++) {
        args.push('arguments[' + i + ']')
    }
    eval('context.fn(' + args + ')')
    delete context.fn
}

apply 的实现

apply 的特点:
1. 改变this指向
2. 直接调用函数
3. 传null的时候,指向window
4. 可以传参,参数是个数组

ES6 写法
Function.prototype.apply1 = function (context, args) {
  var context = context || window
  context.fn = this
  args ? context.fn(...args) : context.fn()
  delete context.fn
}

ES5 写法
Function.prototype.apply1 = function (context, arr) {
  var context = context || window
  context.fn = this
  if (!arr) {
      context.fn()
  } else {
      let args = []
      for (let i = 1; i < arr.length; i++) {
          args.push('arguments['+ i +']')
      }
      eval('context.fn('+ args +')')
  }
  delete context.fn
}

bind的实现

bind的特点
1. 改变this指向
2. 返回一个函数
3. 传null的时候,指向window
4. 可以传参,参数分开传,也可以先传一部分,再调用函数的时候再传一部分
5. 返回的函数如果当做构造函数的话,那bind中的this将无效,但参数依然生效

// 第一版  实现this绑定和传参 (未实现当把return的函数当做构造函数时候的this绑定失效)
Function.prototype.bind1 = function (context) {
    var _this = this
    // 拿到除绑定对象之后的参数
    var args = Array.prototype.slice.call(arguments, 1)

    return function () {
        // 拿到返回函数的所有参数
        var newArgs = Array.prototype.slice.call(argument)
        return _this.apply(this, args.concat(newArgs))
    }
}

// 第二版
Function.prototype.bind2 = function (context) {
    var _this = this
    var args = Array.prototype.slice.call(arguments, 1)
    var middlewares = {}

    var f = function () {
        var newArgs = Array.prototype.slice.call(arguments)
        // 如果this等于实例,那么就把this绑定到实例上,如果不是就把bind上绑定的this添加过来
        _this.apply(this instanceof f ? this : context, args.concat(newArgs))
    }

    middlewares .prototype = this.prototype
    // new一个中间函数  防止修改f的原型而改变了this的原型
    f.prototype = new middlewares()
    return f
}

// 第三版 ES6写法
Function.prototype = function (context, ...args) {
    let _this = this
    let obj = {}
    let f = function (...newArgs) {
        return _this.apply(this instanceof f ? this : context, [...args, ...newArgs])
    }
    obj.prototype = this.prototype
    f.prototype = new obj()
    return f
}

jsonp的实现

jsonp的特点:
创建script 通过src来获取数据 后台用FunctionName包裹数据 调用前台设置的FunctionName方法

/**
 * opts:
 *     - param { String } (callback)
 *     - prefix { String } (__jp)
 *     - name { String } (prefix + incr)
 *     - timeout { Number } (60000)
 * @param  {String}   url     fetch url
 * @param  {Object}   opts 配置项
 * @param  {Function} fn      callback
 */
function originJsonp (url, opts, fn) {
    let prefix = opts.prefix || '__jp'
    let id = opts.name || (prefix + '0')
    let param = opts.param || 'callback'
    let target = document.getElementsByTagName('script')[0] || document.head
    let script

    // 清除script 及 定时器
    function clear () {
        if (script.parentNode) {
            script.parentNode.removeChild(script)
        }
        window[id] = {}                
    }

    window[id] = function (data) {
        clear()
        fn && fn(data)
    }

    // 不用判断 > -1   因为~-1 === 0
    url += (~url.indexOf('?') ? '&' : '?') + param + '=' + encodeURIComponent(id);
    url = url.replace('?&', '?');

    script = document.createElement('script')
    script.src = url
    target.parentNode.insertBefore(script, target)
}

function jsonp (url, opts) {
    return new Promise((resolve, reject) => {
        originJsonp(url, opts, (data) => {
            resolve(data)
        })
    })
}

jsonp('https://sug.so.360.cn/suggest?encodein=utf-8&encodeout=utf-8&format=json&fields=word&word=%E6%88%91', {
    'prefix': 'suggest_so'
}).then(res => {
    console.log(res)
})

猜你喜欢

转载自blog.csdn.net/qq_35534823/article/details/79555807