我们都知道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