js 你不知道的apply和call(方法借用)

我们都知道apply,call,和bind方法都是改变this指向的,但是他们之间的区别以及核心原理你真的懂了吗

//代码1
var obj={
    name:"li",
    f: function(age,fm){
        console.log(this.name+" "+age+" "+fm);
    }
}
var robber={name:"wang"}
 obj.f(18,"北京")    //li 18 北京

obj.f.call(robber,88,"新乡")  //wang 88 新乡
obj.f.apply(robber,[66,"上海"])  //wang 66 上海

obj.f.bind(robber,77,"郑州")()   //wang 77 郑州

通过上面的代码1,我们可以清楚的了解到,三种方法都能改变this的指向,也就是原本指向obj的this,被替换成了robber对象,而他们的区别呢?

bind与apply和call的区别
调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,而在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。
而apply和call却是直接在原有函数的基础上直接调用。
apply与call的区别呢:
他们两个的区别只有一个,那就是传参数的时候apply是数组,而call是传多个参

核心原理: 方法借用

看完区别,我们应该想到,为什么js要在bind的基础上再去增加apply和call这两个函数,现在我们再回头看代码1,我们换个角度来理解它

obj.f.call(robber,88,"新乡")  //wang 88 新乡
obj.f.apply(robber,[66,"上海"])  //wang 66 上海

与其说是obj.f通过call和apply改变自身的this指向,不如说是robber通过apply和call借用obj.f函数来完成输出。
而通过这个思想我们可以借用各种函数来完成我们的目标。

判断数据类型
Object.prototype.toString用来判断类型再合适不过,借用它我们几乎可以判断所有类型的数据:

//代码2
function isType(data, type) {
    const typeObj = {
        '[object String]': 'string',
        '[object Number]': 'number',
        '[object Boolean]': 'boolean',
        '[object Null]': 'null',
        '[object Undefined]': 'undefined',
        '[object Object]': 'object',
        '[object Array]': 'array',
        '[object Function]': 'function',
        '[object Date]': 'date', // Object.prototype.toString.call(new Date())
        '[object RegExp]': 'regExp',
        '[object Map]': 'map',
        '[object Set]': 'set',
        '[object HTMLDivElement]': 'dom', // document.querySelector('#app')
        '[object WeakMap]': 'weakMap',
        '[object Window]': 'window',  // Object.prototype.toString.call(window)
        '[object Error]': 'error', // new Error('1')
        '[object Arguments]': 'arguments',
    }
    let name = Object.prototype.toString.call(data) 
    // 借用Object.prototype.toString()获取数据类型
    /*Object.prototype.toString()内部使用this获取对象的[[class]]属性,
    而[[class]]属性显示每个对象的数据类型。
    所以如果不使用call来改变this指向,直接使用Object.prototype.toString('我要判断的类型'),
    this就会默认指向为Object.prototype,也就返回Object类型;
    */
    
    let typeName = typeObj[name] || '未知类型' // 匹配数据类型
    return typeName === type // 判断该数据类型是否为传入的类型
}
console.log(
    isType({}, 'object'), // true
    isType([], 'array'), // true
    isType(new Date(), 'object'), // false
    isType(new Date(), 'date'), // true
)

类数组借用数组的方法

类数组因为不是真正的数组所有没有数组类型上自带的种种方法,所以我们需要去借用数组的方法。

比如借用数组的push方法:

//代码3
var arrayLike = {
  0: 'OB',
  1: 'Koro1',
  length: 2
}
Array.prototype.push.call(arrayLike, '添加元素1', '添加元素2');
console.log(arrayLike) // {"0":"OB","1":"Koro1","2":"添加元素1","3":"添加元素2","length":4}

上述代码中借用了push方法来给类数组增加值,那我们来看一下为什么可以这样借用,看代码4,我们先手写一个push方法

//代码4
Array.prototype.push=function(){
			for(i=0;i<arguments.length;i++){
	//this指向调用该函数的数组,每次赋一个值,该数组的长度就会加1
				this[this.length] = arguments[i]
			}
		}

可以看到,push方法是使用arguments和this来进行增加操作的,那我们现在来看代码3,我们借用push方法,来把this指向arrayLike对象,就可以轻易的完成类数组的增加操作。
apply获取数组最大值最小值
apply直接传递数组做要调用方法的参数,也省一步展开数组,比如使用Math.max、Math.min来获取数组的最大值/最小值:

//代码5
const arr = [15, 6, 12, 13, 16];
const max = Math.max.apply(Math, arr); // 16
const min = Math.min.apply(Math, arr); // 6

我们知道js的Math.max没有用到this,
那么我们上述代码的第一个参数就可以是任意值甚至是null和undefined,
而上述代码只是想使用apply的传参为数组的特性,去调用max和min方法。

当然这样的借用样例还有很多,大家可以自己去实践,关键在于它们借用方法的理念。

参考链接:

https://www.imooc.com/article/290456
https://blog.csdn.net/qq_37167086/article/details/103750979

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值