1. call()
// 函数上下文调用 : 可以修改this指向
// 函数名.call(修改的this指向,参数1,参数2…………)
function fn(a, b){
console.log( a + b ) // 30
console.log( this ) // {name:'张三'}
}
// 函数名.call(this指向,参数1,参数2,………………)
fn.call({name:'张三'}, 10, 20)
call() 的应用场景---万能数据类型检测
/*
1. typeof 数据 : 有两种数据类型无法检测
null和数组,得到的都是object
2. 万能数据类型 : Object.prototype.toString.call(数据)
*/
// (1) 值类型
const str = 'abc'
const num = 10
const bol = true
const und = undefined
const nul = null
// (2) 引用类型
const arr = [10, 20, 30]
const fn = function() {}
const obj = {name: '张三'}
console.log( typeof str ) // 'string'
console.log( typeof num ) // 'number'
console.log( typeof bol ) // 'boolean'
console.log( typeof und ) // 'undefined'
console.log( typeof nul ) // 'object'
console.log( typeof arr ) // 'object'
console.log( typeof fn ) // 'function'
console.log( typeof obj ) // 'object'
/*
(1) Object.prototype有一个方法toString(), 会得到固定格式字符串 '[object 数据类型]'
(2) arr.toString() 不会调用Object原型中的toString, 因为Array的原型也有toString
(3) 结论: Object.prototype.toString.call() 修改里面的this,让这个方法知道你要检测谁的类型
*/
console.log( Object.prototype.toString.call( str ) ) // '[object String]'
console.log( Object.prototype.toString.call( num ) ) // '[object Number]'
console.log( Object.prototype.toString.call( bol ) ) // '[object Boolean]'
console.log( Object.prototype.toString.call( und ) ) // '[object Undefined]'
console.log( Object.prototype.toString.call( nul ) ) // '[object Null]'
console.log( Object.prototype.toString.call( arr ) ) // '[object Array]'
console.log( Object.prototype.toString.call( fn ) ) // '[object Function]'
console.log( Object.prototype.toString.call( obj ) ) // '[object Object]'
2. apply()
// 函数上下文调用 : 可以修改this指向
// 函数名.apply(修改的this, 数组/伪数组 )
function fn(a, b){
console.log( a + b ) // 30
console.log( this ) // {name: '李四')
}
// 函数名.apply( 修改的this, 数组/伪数组 )
fn.apply({name: '李四'}, [10, 20])
apply() 的应用场景
(1) 伪数组转数组
/*
1.伪数组 : 有数组三要素(下标、元素、长度), 没有数组的方法
* 伪数组本质是对象
*/
const obj = {
0: 20,
1: 55,
2: 60,
3: 80,
length: 4
}
// ES5方式
const arr = []
// arr.push(obj[0], obj[1], obj[2], obj[3])
// 这里只是借助 apply传参的特点,不需要修改this
// 因为这行代码 arr.push()原本的this就是arr, 所以apply第一个参数也传arr
arr.push.apply( arr, obj )
console.log(arr) // [20, 55, 60, 80]
// ES6方式
// 静态方法 Array.from( 伪数组 )
const newArr = Array.from( obj )
console.log( newArr ) // [20, 55, 60, 80]
(2) 求数组最大值
const arr = [20, 50, 66, 100, 88]
// (1) 声明变量存储最大值
let max = arr[0]
// (2) 遍历数组
for (let i = 1; i < arr.length; i++) {
// (3) 依次比较大小
if( arr[i] > max ){
max = arr[i]
}
}
console.log( max ) // 100
// 2. Math.max.apply(Math,数组)
const max1 = Math.max.apply(Math, arr)
console.log(max1) // 100
// 3. ES6
// ... 展开运算符,相当于自动遍历数组每一个元素。 在这里类似于apply功能
const max2 = Math.max(...arr)
console.log(max2) // 100
3. bind()
// 函数上下文调用 : 可以修改this指向
// 函数名.bind(修改的this)
// * bind()不会立即执行函数,而是返回一个修改this之后的新函数
function fn(a,b){
console.log( a + b ) // 110
console.log( this ) // {name: '王五'}
}
// 函数名.bind(修改的this)
// bind()不会立即执行函数,而是得到一个修改this之后的新函数
// (1)定时器 (2)事件处理函数
let newFn = fn.bind({name: '王五'})
newFn(50, 60)
bind() 的应用场景---修改定时器的this
/*
1. 定时器函数中的this 默认一定是window
2. 要想修改定时器函数中的this,需要用bind
*/
const fn = function () {
console.log(this)
}
const newFn = fn.bind({name: '王五'})
// newFn : 取值(函数地址) newFn() : 调用函数得到返回值
setTimeout(newFn , 2000) // {name: '王五'}
setTimeout(function() {
console.log(this)
}.bind({name: '张三'}), 3000) // {name: '张三'}
总结
相同点 :
- 作用一致, 都是用来改变函数的this指向
- 第一个参数都是this要指向的对象
- 都可以利用后续参数传参 不同点 :
- 传参方式不同 :
call是单个传参,apply是数组/伪数组传参- 执行机制不同 :
call和apply会立即执行,bind不会立即执行而是得到修改this的新函数