JavaScript--call,apply,bind的区别以及如何手动实现他们?

call,apply,bind的区别

我们知道call, apply, bind的最大用处就是用来改变函数中this的指向问题。那么他们有什么区别呢?

  • call和apply的区别
    call和apply的第一个参数都是绑定的对象,只是第二个参数不一样,call的第二个参数是一个序列还的列表,apply的第二个参数是一个数组。并且call和apply会立即执行。
  • call, apply和bind的区别
    bind方法是事先把fn的this改变为我们要想要的结果,并且把对应的参数值准备好,以后要用到了,直接的执行即可,也就是说bind同样可以改变this的指向,但和apply、call不同就是不会马上的执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var name = '李雷'
var age = 3
var obj = {
name: '韩梅梅',
age: 8
fun: function (from, to) {
console.log(this.name + this.age + '岁来自' + from + '去往' + to)
}
}
var db = {
name: 'lily',
age: 7
}

obj.fun.call(db, '北京', '上海') // lily7岁来自北京去往上海
obj.fun.apply(db, ['北京', '上海']) // lily7岁来自北京去往上海
obj.fun.bind(db, '北京','上海')() // lily7岁来自北京去往上海
obj.fun.bind(db, ['北京','上海'])() // lily7岁来自北京,上海去往undefined

call, apply的应用

求最大值和最小值

1
2
3
4
5
var arr = [1,4,2,5,76,3,20]
Math.max.call(null, 1,4,2,5,76,3,20) // 76
Math.max.apply(null, arr) // 76
Math.min.call(null, 1,4,2,5,76,3,20) // 1
Math.min.apply(null, arr) // 1

判断数据的类型

1
2
3
Object.prototype.toString.call(null) // "[Object Null]"
Object.prototype.toString.call(1) // "[Object Number]"
Object.prototype.toString.call({}) // "[Object Object]"

数组的拼接

1
2
3
var arr1 = [1, 2, 3]
var arr2 = [4, 5, 6]
[].push.apply(arr1, arr2)

将伪数组转化为数组

函数内的arguments

1
2
3
4
function fun() {
return Array.prototype.slice.call(arguments);
}
console.log(fun(1,2,3,4,5)); // [1,2,3,4,5]

含有length属性的对象

1
2
3
4
5
6
7
let obj4 = {
0: 1,
1: 'thomas',
2: 13,
length: 3 // 一定要有length属性
};
console.log(Array.prototype.slice.call(obj4)); // [1, "thomas", 13]

实现继承

1
2
3
4
5
6
7
8
9
10
11
function Animal (name) {
this.name = name
this.showName = function () {
console.log(this.name)
}
}
function Dog () {
Animal.call(this)
}
let dog = new Dog('小明')
dog.showName() // 小明

手动实现call,apply, bind

call的实现

思路:

  • 将函数设置为对象的属性
  • 执行该方法
  • 删除该方法

注意:

  • this的参数可能为null或者是undefined,此时,this指向window
  • this的类型可能为基本类型,原生的call会将其转为对象
  • 函数是有返回值的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Function.prototype._call = function (context) {
    context = context ? Object(context) : window
    context.fn = this
    let arg = [], result
    for(let i = 0;i < arguments.length;i++) {
    arg.push('arguments[' + i + ']')
    }
    result = eval('context.fn(' + arg +')')
    delete context.fn;
    return result
    }

    Function.prototype._call = function (context) {
    context = context ? Object(context) : window
    context.fn = this
    let arg = [...arguments].slice(1)
    let result = context.fn(...arg)
    delete context.fn
    return
    }

    apply的实现

    写过了call,再来实现apply应该不是什么问题,因为apply和call的不同之处只是在于第二个参数,代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    Function.prototype._apply = function (context, arr) {
    context = context ? Object(context) : window
    context.fn = this
    var result
    if (!arr) {
    result = context.fn()
    } else {
    let arg = []
    for (let i = 1;i < argument.length;i++) {
    arg.push('arguments[' + i + ']')
    }
    result = eval('context.fn(' + arg + ')')
    }
    delete context.fn
    return result
    }

    Function.prototype._apply = function (context, arr) {
    context = context ? Object(context) : window
    context.fn = this
    let arg = [...agruments].slice(1)
    let result
    if (Array.isArray(arr) && arr.length) {
    result = context.fn(...arg)
    } else {
    result = context.fn()
    }
    delete context.fn
    return result
    }

    bind的实现

    bind方法的实现主要有以下四点:
  • bind方法接受参数
  • bind方法返回一个新函数
  • bind方法可以指定this
  • 颗粒化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Function.prototype._bind = function (context) {
    if (typeof this !== 'function') {
    throw new Error('请正确使用bind')
    }
    const _this = this
    var arg = [...arguments].slice(1)
    var obind = function () {}
    var bind = function () {
    var bindArg = [...arguments].slice(1)
    return _this.apply(this instanceof obind ? this : context, arg.concat(bindArg))
    }
    obind.prototype = this.prototype
    bind.prototype = new obind()
    return bind
    }
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2015-2022 Lee
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信