# 一、call的实现

# 1.1call实现的主要思路

  • 将传入的context添加该函数 ( context.fn = this )
  • 执行该函数 ( context.fn() )
  • 删除该函数 ( delete context.fn )

# 1.2注意细节

  • 当call的参数为null时,会绑定到window上 ( var context = context || window )
  • 传参数问题
    • 通过for循环取出从1到最后一个的arguments,因为第0个是要绑定的参数
    • 取出的参数存入数组 arg
    • 通过eval解析并执行

# 1.3代码实现(es5版)

Function.prototype.mycall = function (context) {
  var context = context || window;
  context.fn = this
  var args = []
  for (var i = 1; i < arguments.length; i++) {
    args.push('arguments[' + i + ']')
  }
  var result = eval('context.fn(' + args + ')')
  return result
}

var obj = {
  name: 'qmj',
  age: 18
}

var name = 'hahh'
var age = 100

function foo(name, age) {
  console.log(this.name, this.age, name, age)
}

foo.mycall()

# 1.4ES6版

Function.prototype.mycall = function(context){
  context = context || window
  context.fn = this;
  let args = [...arguments].slice(1)
  let result = context.fn(...args)
  delete context.fn
  return result
}

# 二、实现apply

# 2.1实现思路

apply实现的思路与call实现的思路差不多,区别是传参数的方法有点不同

  • 1.添加函数到context的属性上
  • 2.判断有无传入数组,没传数组:直接执行;传入数组:取出数组item存入args,用eval解析执行

# 2.2代码实现(es5版)

Function.prototype.myapply = function(context, arr) {
  var conetxt = context || window;
  context.fn = this
  if(!arr) {
    context.fn()
  }else {
    let args = []
    for(let i = 0; i<arr.length; i++) {
      args.push('arr['+ i + ']') // 存进去的是arr[0],arr[1]...
    }
    eval('conetxt.fn('+ args + ')')
  }
}


var obj = {
  name: 'qmj',
  age: 18
}
function foo(name,age) {
  console.log(this.name,this.age,name,age)
}

foo.myapply(obj,['xjp',55])

# 2.3ES6版

Function.prototype.apply = function(content = window) {
    content.fn = this;
    let result;
    // 判断是否有第二个参数
    if(arguments[1]) {
        result = content.fn(...arguments[1]);
    } else {
        result = content.fn();
    }
    delete content.fn;
    return result;
}

# 三、实现bind

: : : tip

bind方法会场见一核心函数。当这个形函数被调用时,bind()的第一个参数将作为他运行时的this,之后的一序列参数将会在传递的实参前传入作为他的参数

: : :

Function.prototype.bind = function(content) {
   if(typeof this != 'function') {
      throw Error('not a function');
   }
   let _this = this;
   let args = [...arguments].slice(1);
   return function F() {
      // 判断是否被当做构造函数使用
      if(this instanceof F) {
         return _this.apply(this, args.concat([...arguments]))
      }
      return _this.apply(content, args.concat([...arguments]))
   }
}

# 三、实现instanceof

# 3.1instanceof原理:

  • 右边的prototype是否在左边的原型链上

# 3.2 instanceof使用的例子:

// 例子一
var arr [1,2,3]
arr instanceof Array  // true
arr instanceof Object // true
arr instanceof Function // false 因为不在该原型链上

// 例子二
let foo = function() {
    console.log('hello javascript')
}
foo instanceof Function // true
foo instanceof Object  // true

# 3.3 实现instanceof

function instance_of(L,R) {
  let O = R.prototype
  while(L.__proto__) {
    if(L.__proto__ === O) {
      return true
    }else{
      L = L.__proto__
    }
  }
  return false
}

// 测试1
let arr = [1,2,3]
console.log(instance_of(arr,Array)) // true
console.log(instance_of(arr,Object)) // true
console.log(instance_of(arr,Function)) // false

// 测试2
let foo = function() {
  console.log('nihao')
}
console.log(instance_of(foo,Array)) // false
console.log(instance_of(foo,Object)) // true
console.log(instance_of(foo,Function)) // true

# 四、实现typeof

function getType(temp) {
  var str = temp.constructor.toString();
  console.log(str)
  return str.slice(9, str.indexOf('(')).toLowerCase();
}

# 五、手写防抖/节流函数

# 1.防抖

# 七、手写深/浅拷贝

function deepCopy(obj){
    //判断是否是简单数据类型,
    if(typeof obj == "object"){
        //复杂数据类型
        var result = obj.constructor == Array ? [] : {};
        for(let i in obj){
            result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
        }
    }else {
        //简单数据类型 直接 == 赋值
        var result = obj;
    }
    return result;
}

# 八、reduce/map/filter

# reduce

Array.prototype.myReduce = function(cb, initValue) {
  if (!Array.isArray(this)) {
    throw new TypeError("not a array")
  }
  // 数组为空,并且有初始值,报错
  if (this.length === 0 && arguments.length < 2) {
    throw new TypeError('Reduce of empty array with no initial value')
  }
  let arr = this
  let res = null
  // 判断有没有初始值
  if (arguments.length > 1) {
    res = initValue
  } else {
    res = arr.splice(0,1)[0] //没有就取第一个值
  }
  arr.forEach((item, index) => {
    res = cb(res, item, index, arr) // cb 每次执行完都会返回一个新的 res值,覆盖之前的 res
  })
  return res
};

// 测试结果
let arr = [1,2,3,4]
let result = arr.myReduce((res, cur) => {
  return res + cur
})
console.log(result) // 10

# 九、手写一个new

# 9.1new之后做了什么呢?

  • 创建了一个新对象
  • 将新对象与构造函数原型连接起来(继承属性)
  • 将构造函数的this指向这个对象、
  • 返回这个新对象

# 9.2实现思路

很简单,只要完成上面四个步骤,就可以实现new了

# 9.3实现代码

/**
 * 创建一个new操作符
 * @param {*} Con 构造函数
 * @param  {...any} args 忘构造函数中传的参数
 */
  function createNew(Con, ...args) {
    let obj = {} // 创建一个对象,因为new操作符会返回一个对象
    Object.setPrototypeOf(obj, Con.prototype) // 将对象与构造函数原型链接起来
    // obj.__proto__ = Con.prototype // 等价于上面的写法
    let result = Con.apply(obj, args) // 将构造函数中的this指向这个对象,并传递参数
    return result instanceof Object ? result : obj
}
最后更新于: 12/25/2020, 9:20:27 AM