call、apply、bind的区别、应用、底层实现

700 阅读2分钟

区别

相同点都可以改变函数内部this的指向

不同点:call、apply传递的参数不同,bind返回一个修改this指向的新函数

call

call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。

function fn(m,n){
    console.log(this,m,n)
}
fn.call({a:1},1,2)  //{a:1} 1 2

apply

apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。

function fn(m,n){
    console.log(this,m,n)
}
fn.apply({a:1},[1,2])  //{a:1} 1 2

事实上apply 和 call 的用法几乎相同, 唯一的差别在于:当函数需要传递多个变量时, apply 可以接受一个数组作为参数输入, call 则是接受一系列的单独变量。

bind

bind返回一个修改this指向的新函数,并且只能bind一次

function fn(m,n,x,y){
    console.log(this,m,n,x,y)
}
let fn1 = fn.bind({a:1},1,2);
fn1(3,4)  //{a:1} 1 2 3 4

应用

1、最大最小值

Math.max(1,2,3)  // 3
Math.min(1,2,3)  // 1
let arr = [1,2,3]
Math.max.apply(null,arr)  // 3
// 当然也可以es6中的扩展运算符
Math.max(...arr)  // 3

2、类数组转数组

let divs = document.querySelector("div")
Array.prototype.slice.call(divs)
Array.from(divs)
[...divs]

3、数组追加元素

let arr = [1,2,3]
let arr1 = [4,5,6]
Array.prototype.push.apply(arr,arr1)

4、精准判断数据类型

let a1 = 1, a2 = "1", a3 = true, a4 = undefined, a5 = null, a6 = [], a7 = {}, a8 = function(){}
Object.prototype.toString.call(a1)  // "[object Number]"
Object.prototype.toString.call(a2)  // "[object String]"
Object.prototype.toString.call(a3)  // "[object Boolean]"
Object.prototype.toString.call(a4)  // "[object Undefined]"
Object.prototype.toString.call(a5)  // "[object Null]"
Object.prototype.toString.call(a6)  // "[object Array]"
Object.prototype.toString.call(a7)  // "[object Object]"
Object.prototype.toString.call(a8)  // "[object Function]"

5、原型继承

function Person(name, age){
    this.name = name
    this.age = age
}
function People(name, age){
    Person.call(this, name, age)
}
let people = new People("zhangSan", 25)

底层实现

Function.prototype.bind = function(context,...arg) {
    if (typeof this !== 'function') {
        throw new TypeError('Error')
    }
    let _this = this;
    context = context == undefined ? window : context;
    let type = typeof context;
    if (!/^(object|function)$/.test(type)) {
        if (/^(symbol|bigint)$/.test(type)) {
            context = Object(context);
        } else {
            context = new context.constructor(context);
        }
    }
    return function anonymous(...arg1){
        _this.call(context,...arg,...arg1)
    }
};

Function.prototype.call = function(context,...arg) {
    // console.log(arg)
    if (typeof this !== 'function') {
        throw new TypeError('Error')
    }
    context = context == undefined ? window : context;
    let type = typeof context;
    if(!/^(object|fucntion)$/.test(type)){
        if(/^(symbol|bigint)$/.test(type)){
            context = Object(context)
        }else{
            context = new context.constructor(context)
        }
    }
    let key = Symbol('key'),
        result;
    context[key] = this;
    result = context[key](...arg)
    delete context[key]
    return result
}

Function.prototype.apply = function(context,arg) {
    // console.log(arg)
    if (typeof this !== 'function') {
        throw new TypeError('Error')
    }
    context = context == undefined ? window : context;
    let type = typeof context;
    if(!/^(object|fucntion)$/.test(type)){
        if(/^(symbol|bigint)$/.test(type)){
            context = Object(context)
        }else{
            context = new context.constructor(context)
        }
    }
    let key = Symbol('key'),
        result;
    context[key] = this;
    // 处理参数和 call 有区别
    if (arg) {
        result = context[key](...arg)
    } else {
        result = context[key]()
    }
    delete context[key]
    return result
}

以上就是对这三个函数的一些总结了,好了,今天就到这里了,剩下的,同学你来吧!