大家好,我是蓝胖子的小叮当,今天分享的是JavaScript的第五章call、apply、bind,大家在阅读期间有任何的意见或建议都可以留言给我哈!
5.1前言
在日常做业务需求开发执行函数时,有时需要保证函数内部的this不被污染或者需要将函数内部的this指向到指定对象的时候,就可以使用call、apply、bind来实现。
5.2call
1)使用方法
fun.call(thisArg, param1, param2, ...)
// fun是要改变this的函数
// thisArg是this的指向
// param1,param2是fun的函数入参
第一个参数是this的指向(参数为null,undefined时,默认指向window),第二个参数是函数接收的参数列表(与apply有区别),立即执行,临时改变this一次
2)手写call
Function.prototype.myCall = function(fun,...args){
fun = fun || window
//如果target非真值(null或undefined)就指向window
const symbolKey = Symbol()
//添加了一个key,这里用symbolKey而不是随便定义的一个key名,是因为随意添加的名字,可能target对象上面正好有
fun[symbolKey] = this
const res = fun[symbolKey](...args)
// args本身是rest参数,搭配的变量是一个数组,数组解构后就可以一个个传入函数中
delete fun[symbolKey]
// 执行完借用的函数后,删除掉
return res
}
5.3apply
1)使用方法
fun.apply(thisArg, [param1,param2,...])
// fun是要改变this的函数
// thisArg是this的指向
// 数组内是fun的函数入参
第一个参数是this的指向(参数为null,undefined时,默认指向window),第二个参数是函数接收的参数(数组形式传入),改变this指向后立即执行,临时改变this一次
2)手写apply
Function.prototype.myApply = function(fun,args){
// 区别就是这里第二个参数直接就是个数组(与call的区别就在于此)
fun = fun || window
//如果target非真值(null或undefined)就指向window
const symbolKey = Symbol()
//添加了一个key,这里用symbolKey而不是随便定义的一个key名,是因为随意添加的名字,可能target对象上面正好有
fun[symbolKey] = this
const res = fun[symbolKey](...args)
// args本身是个数组,所以我们需要解构后一个个传入函数中
delete fun[symbolKey]
// 执行完借用的函数后,删除掉
return res
}
5.4bind
1)使用方法
fun.bind(thisArg, param1, param2, ...)
// fun是要改变this的函数
// thisArg是this的指向
// param1,param2是fun的函数入参
第一个参数也是this的指向,后面传入的也是一个参数列表(这个参数列表可以分多次传入,call则必须一次性传入所有参数)
2)手写bind
Function.prototype.myBind = function (fun,...outArgs) {
fun = fun || {}
// 处理边界条件
const symbolKey = Symbol()
fun[symbolKey] = this
return function (...innerArgs) {
// 返回一个函数
const res = fun[symbolKey](...outArgs, ...innerArgs)
// outArgs和innerArgs都是一个数组,解构后传入函数
// delete target[symbolKey] 这里千万不能销毁绑定的函数,否则第二次调用的时候,就会出现问题。
return res
}
}
5.5区别
1)call和apply改变了函数的this上下文后会立即执行该函数,而bind则是返回改变了上下文后的一个函数不会立即执行,需要调用这个函数才会执行。
2)call和apply的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展现,apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数
3)call和bind都是传入一个参数列表,但bind可以分多次传入,call则必须一次性传入
4)应用场景:
-
call 经常做继承。
-
apply 经常跟数组有关系 (因为apply接收的参数是个数组) ,比如借助于数学对象实现数组最大值最小值。
-
bind 不调用函数,但是还想改变this指向 (因为bind不会直接调用函数) ,比如改变定时器内部的this指向。
5.6应用场景手写代码块
1)求数组中最大和最小值
var arr = [34,5,3,6,54,6,-67,5,7,6,-8,687];
Math.max(...arr)
Math.max.apply(Math, arr)
Math.max.call(Math, ...arr)
2)将伪数组转化为数组
var arrayLike = {
0: 'qianlong',
1: 'ziqi',
2: 'qianduan',
length: 3
}
var arr = Array.prototype.slice.call(arrayLike);
3)数组追加
var arr1 = [1,2,3];
var arr2 = [4,5,6];
[].push.apply(arr1, arr2);
// arr1 [1, 2, 3, 4, 5, 6]
// arr2 [4,5,6]
4)判断变量类型
function typeOf(obj){
return Object.prototype.toString.call(obj).slice(8,-1).toLowrCase()
}
好啦,关于call、apply、bind的知识点就总结到这里,如果有什么疑问、意见或建议,都可畅所欲言,谢谢大家,我也将持续更新。
预告:在开发中是否会需要减少重复代码、封装共有属性和方法、提高方法的多态性?欢迎收看JavaScript基础的下一章:继承