polyfill

... 2020-9-25 About 3 min

# polyfill

使用es6的一些方法的时候都是会用的,但是有时候去做浏览器兼容的时候总是直接使用polyfill.js了

这个地方就通过自己的学习来了解一些操作数组的方法

# 说明forEach的工作原理

if (!Array.prototype.forEach) {
  if (Object.prototype.toString.call(this) !== '[object Array]') {
    return 'forEach为数组方法'
  }
  Array.prototype._forEach = function (a, b) {
    for ( let i = 0; i < this.length; i++ ) {
      a.call(b, this[i], i, this) // 将a的上下文指向b,forEach中的三个参数分别是当前值,当前序号,数组本身
    }
  }
}
1
2
3
4
5
6
7
8
9
10

# 手动实现call

if (!Function.prototype.call) {
  Function.prototype.call = function (oThis) {
    if (typeof this !== 'function') {
      throw new TypeError('当前调用call方法的不是函数!')
    }
    var args = [].slice(arguments, 1)
    // 获取需要指定的this,如果是null、undefined的时候我们去window当为this
    var self = oThis || window
    // 借助一个不可变量存入到self中
    const fn = Symbol('fn')
    self[fn] = this
    // 调取函数
    var result = self[fn](...args)
    // 执行完成之后删除过渡的属性,避免污染作用域
    delete self[fn]
    // 返回call之后的结果
    return result
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 手动实现apply

当我们知道call的首先原理的时候,显然再去理解apply的原理就会简单很多了

if (!Function.prototype.apply) {
  Function.prototype.apply = function (oThis) {
    if (typeof this !== 'function') {
      throw new TypeError('当前调用call方法的不是函数!')
    }
    var args = arguments[1] // 因为对于apply来讲只会取第二个参数数组作为实参
    // 获取需要指定的this,如果是null、undefined的时候我们去window当为this
    var self = oThis || window
    // 借助一个不可变量存入到self中
    const fn = Symbol('fn')
    self[fn] = this
    // 调取函数
    var result = self[fn](...args)
    // 执行完成之后删除过渡的属性,避免污染作用域
    delete self[fn]
    // 返回call之后的结果
    return result
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 手动实现bind

当我们实现了call和apply之后,就可以尝试着去实现一下bind,这个相对来讲我觉得是比较难的

if (!Function.prototypt.bind) {
  Function.prototype.bind = function (oThis) { // 1
    if (typeof this !== 'function') {
      return 'bind需要function调用'
    }
    var self = this
    var args = [].slice.call(arguments, 1) // 这个地方为1处的参数,取oThis之后的参数去做数据初始化
    return function () { // 2
      // 这个地方注意一下bind的使用,func.bind(this),这个时候并没有调用func;需要func.bind(this)()
      return self.call(oThis, args.concat([].slice.call(arguments)))
      // 这个地方arguments为2的参数,两个地方的arguments得弄清楚就明白了bind的实现原理了
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在mdn上的解释是只有以上的这些的,但是如果使用new关键字的时候使用以上的方法是有一些问题,所以我们还需要对以上的方法进行进一步的优化

if (!Function.prototypt.bind) {
  if (typeof this !== 'function') {
    return 'bind需要function调用'
  }
  var self = this
  var args = [].slice.call(arguments, 1)
  var fBound = function () {
    if (this instanceof fBound) {
      // 如果在bind之后的函数在调用的时候this指向fBound,说明使用new关键字在生成,我们需要把调用者的this指回去
      return self.apply(this, args.concat([].slice.call(arguments)))
    } else {
      // 否则还是走原来bind(oThis)的oThis
      return self.apply(oThis, args.concat([].slice.call(arguments)))
    }
  }
  // 同时我们在使用new的时候需要去把原型链维护好
  var cont = function () {}
  // 如果绑定的函数有prototype属性则需要指定到fBound,没有的话fBound为空对象
  if (this.prototype) {
    cont.prototype = this.prototype
  }
  fBound.prototype = new cont()
  return fBound
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

这样就可以兼容到所有的情况了,这里面一些比较绕的东西,我觉得我在不记得时候应该好好看一下

# 手动实现一个new关键字

function _new () {
  var args = [].slice.call(arguments, 1)
  var constructor = args.shift() // 先把构造函数推出去,同时也方便了之后apply的参数赋值
  var context = Object.create(constructor.prototype) // 这个地方要使用prototype,因为function的prototype是一个对象,不能够是constructor,因为他是一个函数
  var result = context.apply(constructor, args)
  return (typeof result === 'object' && result !== null) ? result : context
}
1
2
3
4
5
6
7
Last update: December 23, 2022 13:14
Contributors: liaoxuan , salvatoreliaoxuan