概念
call,apply,bind 都可以改变 this 的指向 我们先来看一个例子
function fn(a, b) {
console.log(this.msg, a, b)
}
fn(1, 2) // undefined 1 2
const obj1 = {
msg: 'hello'
}
// 此时,fn的this的指向就是obj1
fn.call(obj1, 1, 2) // hello 1 2
fn.apply(obj1, [1, 2])// hello 1 2
const fn1 = fn.bind(obj1, 1, 2) // 返回的是一个函数,不会立即调用
fn1() // hello 1 2
- call、apply 的第一个参数还是调用函数this的指向,
- call的第二参数是列表(arg1, arg2...), apply第二个参数是数组[arg1, arg2...]
- bind返回的是一个函数,不会立即调用
应用
- 使用 Math 的 max/min 求最大最小值
let arr = [3, 8, 2, 6]
let max = Math.max.apply(null, arr) // 8
let min = Math.min.apply(null, arr) // 2
当然也用ES6的的展开运算符
Math.max(...arr)Math.min(...arr)
- Object.prototype.toString.call() 判断数据类型
因为
Object.prototype.toString() 方法会返回对象的类型字符串,输出"[object Object]"其中第二个Object是传入参数的构造函数。所以使用call就可以指定任意的值和结合toString将组成的构造函数类型返回来判断类型。同样道理换成apply/bind同样也可以判断
const toString = Object.prototype.toString
console.log(toString.call(123)) // [object Number]
console.log(toString.call('abc')) // [object String]
console.log(toString.call(true)) // [object Boolean]
console.log(toString.call(undefined)) // [object Undefined]
console.log(toString.call(null)) // [object Null]
console.log(toString.call([])) // [object Array]
console.log(toString.call({})) // [object Object]
console.log(toString.call(function() {})) // [object Function]
- 使用 call() 实现将类数组转化成数组
let array = [12, 23, 45, 65, 32]
function fn(array){
var args = [].slice.call(arguments)
return args[0]
}
fn(array) // [12, 23, 45, 65, 32]
let array = [12, 23, 45, 65, 32]
function fn(array){
var args = [].slice.call(arguments)
return args[0]
}
fn(array) // 12
手写实现
call
Function.prototype.myCall = function(context) {
// 如果没有传或传的值为空对象 context指向window
context = context || window
let fn = Symbol()
// 将this指向
context[fn] = this
// 处理参数 去除第一个参数, 获取其它传入fn函数
let args = [...arguments].slice(1)
// 执行fn
context[fn](...args)
// 删除方法
delete context[fn]
}
// 测试结果:
function fn(a, b) {
console.log(this.msg, a, b)
}
const obj1 = {
msg: 'hello'
}
fn.call(obj1, 1, 2) // hello 1 2
fn.myCall(obj1, 1, 2) // hello 1 2
apply
apply跟call的实现比较类似
Function.prototype.myApply = function(context) {
context = context || window
const fn = Symbol()
context[fn] = this
const args = [...arguments][1] || []
context[fn](...args)
delete context[fn]
}
// 测试结果:
function fn(a, b) {
console.log(this.msg, a, b)
}
const obj1 = {
msg: 'hello'
}
fn.apply(obj1, [1, 2]) // hello 1 2
fn.myApply(obj1, [1, 2]) // hello 1 2
bind
Function.prototype.myBind = function(context) {
context = context || window
const self = this
const args = [...arguments].slice(1)
function fnBind() {
const newArgs = [...arguments]
// 判断是否为new操作
// 如果当前函数的this指向的是构造函数中的this 则判定为new 操作
const _this = this instanceof self ? this : context
return self.apply(_this, args.concat(newArgs))
}
// 维持原型链关系
const fnNop = function() {}
if (this.prototype) {
fnNop.prototype = this.prototype
}
fnBind.prototype = new fnNop()
return fnBind
}
// 测试结果
const obj1 = {
}
function Foo(name) {
this.name = name
console.log(this.name)
}
const f1 = Foo.myBind(obj1)
f1('abc') // abc
const f2 = new f1('abc')
console.log(f2.name) // abc