js中call()、apply()、bind()的使用及实现

使用

call()、apply()、bind()都是用来改变this的指向的。
一、A.a.call(B, x, y)
A是一个对象,a是对象A里的方法函数,x和y是a函数的参数,B也是一个对象,但对象B想调用对象A的a方法,那么call()的作用就是改变函数a的this指向,让函数a的this指向B,对象B就能调用a方法

let A = {
  num: 1,
  a: function(x, y){
      console.log('我是A对象的a方法,参数:', x, y, 'num:', this.num)
  }
}
let x = 2
let y = 3
let B = {
  num: 4,
}

console.log(A.a(x, y)) // 我是A对象的a方法,参数: 2 3 num: 1
console.log(A.a.call(B, x, y)) // 我是A对象的a方法,参数: 2 3 num: 4

二、A.a.apply(B, [x, y])
可以看到call和apply的用法和作用基本一致,只是接受参数的方式不太一样,即call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里

let A = {
  num: 1,
  a: function(x, y){
      console.log('我是A对象的a方法,参数:', x, y, 'num:', this.num)
  }
}
let x = 2
let y = 3
let B = {
  num: 4,
}

console.log(A.a(x, y)) // 我是A对象的a方法,参数: 2 3 num: 1
console.log(A.a.apply(B, [x, y])) // 我是A对象的a方法,参数: 2 3 num: 4

三、A.a.bind(B, x, y)()
bind()除了返回是函数以外,它的参数和call()一样。bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值,例如:f.bind(obj),实际上可以理解为obj.f(),这时f函数体内的this自然指向的是obj

let A = {
  num: 1,
  a: function(x, y){
      console.log('我是A对象的a方法,参数:', x, y, 'num:', this.num)
  }
}
let x = 2
let y = 3
let B = {
  num: 4,
}

console.log(A.a(x, y)) // 我是A对象的a方法,参数: 2 3 num: 1
console.log(A.a.bind(B, x, y)()) // 我是A对象的a方法,参数: 2 3 num: 4

// 关于bind()的一些题目

function f(y,z){
   return this.x+y+z;
}
var m = f.bind({x:1},2);
console.log(m(3)); // 6
// 分析:这里的bind方法会把它的第一个实参绑定给f函数体内的this,所以里的this即指向{x:1}对象;
// 从第二个参数起,会依次传递给原始函数,这里的第二个参数2即是f函数的y参数;
// 最后调用m(3)的时候,这里的3便是最后一个参数z了,所以执行结果为1+2+3=6
// 分步处理参数的过程其实是一个典型的函数柯里化的过程(Curry)

var a = document.write;
a('hello'); // error
a.bind(document)('hello'); // hello
a.call(document,'hello'); // hello
// 分析:直接调用a的话,this指向的是global或window对象,所以会报错;
// 通过bind或者call方式绑定this至document对象即可正常调用

常见用法

// 数组之间追加
var arr1 = [1 , 2 , 3];
var arr2 = [4 ,5];
console.log(Array.prototype.push.apply(arr1, arr2))

// 获取数组中的最大值和最小值
let list = [1, 2, 3, -4]
console.log(Math.max.apply(Math, list))   // 3
console.log(Math.min.apply(Math, list))   // -4

// 判断数据类型
var  whichDataType = {
 isObj:function(o){
     if (Object.prototype.toString.call(o)==="[object Object]")
     return true;
     else 
     return false
 },
 isArray:function(o){
     if(Object.prototype.toString.call(o)==="[object Array]")
     return true;
     else 
     return false
 },
 isNULL:function(o){
     if (Object.prototype.toString.call(o)==="[object Null]")
     {return true;}
     else 
     return false
 },
 isNumber:function(o){
     if (Object.prototype.toString.call(o)==="[object Number]")
     {return true;}
     else 
     return false
 },
 isFunction:function(o){
     if (Object.prototype.toString.call(o)==="[object Function]")
     {return true;}
     else 
     return false
 },
 isUndefine:function(o){
     if (Object.prototype.toString.call(o)==="[object Undefine]")
     {return true;}
     else 
     return false
 },
 isString:function(o){
     if (Object.prototype.toString.call(o)==="[object String]")
     {return true;}
     else 
     return false
 },
 isBoolen:function(o){
     if (Object.prototype.toString.call(o)==="[object Boolen]")
     {return true;}
     else 
     return false
 },
 isSymbol:function(o){
     if (Object.prototype.toString.call(o)==="[object Symbol]")
     {return true;}
     else 
     return false
 },
}
console.log(whichDataType.isObj({a:1}))

// 伪数组转真数组
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"))
console.log(domNodes)

// 绑定函数
let A = {
   num: 1,
    a: function(){
        console.log(this.num)
    }
}
this.num = 2
let a = A.a
A.a() // 1
a() // 2
a.bind(A) // 1

// 闭包经典题目
for(var i = 1; i <= 5; i++) {
  setTimeout(console.log.bind(console, i), i * 1000);
}
// 顺便贴一下其他方法
for(var i = 1; i <= 5; i++) {
  !function(i) {
    setTimeout(function() {
      console.log(i);
    }, i * 1000);
  }(i);
}
for(let i = 1; i <= 5; i++) {
  setTimeout(function() {
    console.log(i);
  }, i * 1000);
}
// 没有对比没有伤害啊

实现

一、call()

// 思路:将要改变this指向的方法挂到目标this上执行并返回
Function.prototype.mycall = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('not funciton')
  }
  context = context || window
  context.fn = this
  let arg = [...arguments].slice(1)
  let result = context.fn(...arg)
  delete context.fn
  return result
} 

二、apply()

// 思路:将要改变this指向的方法挂到目标this上执行并返回
Function.prototype.myapply = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('not funciton')
  }
  context = context || window
  context.fn = this
  let result
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}

三、bind()

// 思路:类似call,但返回的是函数
Function.prototype.mybind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  let _this = this
  let arg = [...arguments].slice(1)
  return function F() {
    // 处理函数使用new的情况
    if (this instanceof F) {
      return new _this(...arg, ...arguments)
    } else {
      return _this.apply(context, arg.concat(...arguments))
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值