一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
apply call bind使用
call、apply
和bind
是挂在Function
对象上的三个方法,调用这三个方法的必须是一个函数。这三个方法都是改变func中的this指向,调用后this指向传入的第一个参数。
func.call(thisArg, param1, param2, ...)
func.apply(thisArg, [param1,param2,...])
func.bind(thisArg, param1, param2, ...)
- call:①使用call会立即调用函数;②函数中this指向call的第一个参数;③call接收两个以上实参,可以将第二个及以后的参数传给函数
- apply:①使用apply会立即调用函数;②函数中this指向apply的第一个参数;③apply接收两个实参,第二个参数是个数组,apply将第二个参数的数组按顺序拆散成多个实参传给函数。
- bind:①创建一个跟原函数一样的新函数,但不执行;②永久的替换新函数中的this指向;③bind可以接收其他实参做永久替换部分形参变量为固定的实参值
关于绑定规则:
call、apply、bind
中的this被强绑定在指定的那个对象上;
let a = {
value: 1
}
function getValue(name, age) {
console.log(name)
console.log(age)
console.log(this.value)
}
getValue.call(a, 'poe', '24')
getValue.apply(a, ['poe', '24'])
- 如果把
null
或undefined
作为this
的绑定对象传入call
、apply
、bind
,这些值在调用时会被忽略,实际应用的是默认绑定规则。
var a = 'hello'
function foo() {
console.log(this.a)
}
foo.call(null) // 'hello'
// 此时this指向window
- 被bind()永久绑定的this,即便用call,也无法修改this的指向了,即多次bind绑定也只有第一次的生效
var obj1 = {a: 'obj1'}
var obj2 = {a: 'obj2'}
function foo() {
console.log(this.a)
}
var bindFun1 = foo.bind(obj1)
bindFun1.call(obj2) // 输出:obj1(bind后再使用call或apply不生效)
var bindFun2 = foo.bind(obj1).bind(obj2)
bindFun2() // 输出:obj1(多次bind只有第一个生效)
方法的应用场景示例
- 判断数据类型
用
Object.prototype.toString
来判断类型是最合适的,借用它我们几乎可以判断所有类型的数据
function getType(obj){
let type = typeof obj;
if (type !== "object") {
return type;
}
return Object.prototype.toString.call(obj).replace(/^$/, '$1');
}
- 类数组借用方法
类数组因为不是真正的数组,所有没有数组类型上自带的种种方法,所以我们就可以利用一些方法去借用数组的方法,比如借用数组的 push
方法,看下面的一段代码。
var arrayLike = {
0: 'a',
1: 'b',
length: 2
}
Array.prototype.push.call(arrayLike, 'c', 'd');
console.log(typeof arrayLike); // 'object'
console.log(arrayLike);
// {0: "a", 1: "b", 2: "c", 3: "d", length: 4}
用 call
的方法来借用 Array 原型链上的 push
方法,可以实现一个类数组的 push
方法,给 arrayLike
添加新的元素
- 获取数组的最大 / 最小值
我们可以用 apply 来实现数组中判断最大 / 最小值,apply
直接传递数组作为调用方法的参数,也可以减少一步展开数组,可以直接使用 Math.max、Math.min
来获取数组的最大值 / 最小值,示例:
let arr = [13, 6, 10, 11, 16];
const max = Math.max.apply(Math, arr);
const min = Math.min.apply(Math, arr);
console.log(max); // 16
console.log(min); // 6
手写实现
对于这几个函数改变this指向的实现思路:
- 不传入第一个参数,那么默认为
window
- 改变了
this
指向 - 新的对象可以执行该函数
那么我们可以给新的对象添加一个函数,然后在执行完以后删除
实现一个 bind 函数
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var _this = this
var args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
实现一个 call 函数
Function.prototype.myCall = function (context) {
var context = context || window
// 给 context 添加一个属性
// getValue.call(a, 'pp', '24') => a.fn = getValue
context.fn = this
// 将 context 后面的参数取出来
var args = [...arguments].slice(1)
// getValue.call(a, 'pp', '24') => a.fn('pp', '24')
var result = context.fn(...args)
// 删除 fn
delete context.fn
return result
}
实现一个 apply 函数
Function.prototype.myApply = function(context = window, ...args) {
// this-->func context--> obj args--> 传递过来的参数
// 在context上加一个唯一值不影响context上的属性
let key = Symbol('key')
context[key] = this; // context为调用的上下文,this此处为函数,将这个函数作为context的方法
// let args = [...arguments].slice(1) //第一个参数为obj所以删除,伪数组转为数组
let result = context[key](args); // 这里和call传参不一样
delete context[key]; // 不删除会导致context属性越来越多
return result;
}
// 使用
function f(a,b){
console.log(a,b)
console.log(this.name)
}
let obj={
name:'张三'
}
f.myApply(obj,[1,2]) //arguments[1]