js中call、apply、bind到底有什么区别?bind返回的方法还能修改this指向吗?


风晓
风晓 2024-01-03 00:30:10 62100 赞同 0 反对 0
分类: 资源
js中call、apply、bind到底有什么区别?bind返回的方法还能修改this指向吗?

 壹 ❀ 引

同事最近在看angularjs源码,被源码中各种bind,apply弄的晕头转向;于是他问我,你知道apply,call与bind的区别吗?我说apply与call是函数应用,指定this的同时也将方法执行,bind不同,它只是负责绑定this并返回一个新方法,不会执行。

他又问,那如果一个方法bind对象a后,再bind对象b,最后再bind对象c,此时执行函数this指向谁呢?(他经常问这种奇葩问题...);我不加思索的回答,是c吧;真的吗?他反问到。

反问的一瞬间,我知道我应该是说错了,写了个小demo验证下,果不其然,所以我觉得有必要详细去了解下三个方法的区别了。

复制代码
let o1 = {
    a: 1
};
let o2 = {
    a: 2
};
let o3 = {
    a: 3
};

function fn(b, c) {
    console.log(this.a);
};

let fn1 = fn.bind(o1);
let fn2 = fn1.bind(o2);
let fn3 = fn2.bind(o3);
fn3() //?
复制代码

 贰 ❀ 函数调用与函数应用

我们知道执行一个函数有两种方式,一种是常见的函数调用,第二种就是函数应用了。

从主动被动的关系去解释,函数调用显得更为被动,而函数应用就显得十分主动了,我们来看个例子:

复制代码
var name = "听风是风",
    obj = {
        name: 'echo'
    };

function fn() {
    console.log(this.name);
};

//函数调用
fn() //听风是风

//函数应用
fn.call(obj); //echo
复制代码

 fn() 等同于window.fn(),本质上方法fn是被window调用,所以this指向了window,这就像古代的娃娃亲,对于fn来说this被安排的明明白白,婚姻及其不自由。

而fn.call()虽然也等同于window.fn.call(),但call为fn提供了改变this的机会,此时的this也就是obj对象。可以看到随着思想的解放,fn有了选择婚姻的权利,更为自由和幸福。

那么说到这你应该明白了,call()与apply()就是提供函数应用的方法,它们让函数执行时的this指向更为灵活。

 叁 ❀ call与apply

站在函数应用的角度我们知道了call与apply的用途,那这两个方法又有什么区别呢,其实区别就一点,参数传递方式不同。

call方法中接受的是一个参数列表,第一个参数指向this,其余的参数在函数执行时都会作为函数形参传入函数。

fn.call(this, arg1, arg2, ...);

而apply不同的地方是,除了第一个参数作为this指向外,其它参数都被包裹在一个数组中,在函数执行时同样会作为形参传入。

fn.apply(this, [arg1, arg2, ...]);

除此之外,两个方法的效果完全相同:

复制代码
let o = {
    a: 1
};

function fn(b, c) {
    console.log(this.a + b + c);
};
fn.call(o, 2, 3); // 6
fn.apply(o, [2, 3]); //6
复制代码

 肆 ❀ 关于bind

bind之所以要拿出来单独说,是因为它与call,apply又存在一些不同。call与apply在改变this的同时,就立刻执行,而bind绑定this后并不会立马执行,而是返回一个新的绑定函数。

复制代码
let o = {
    a: 1
};

function fn(b, c) {
    console.log(this.a + b + c);
};

let fn1 = fn.bind(o, 2, 3);

fn1();//6
复制代码

还记得文章开头我同事提的问题吗,我之所以觉得bind多次后执行,this会指向最后一次bind的对象,是因为没能正确理解bind返回函数的含义。

尝试打印返回的新函数fn1,可以看到它并不是一个普通的function,而是一个bound function,简称绑定函数:

它的TargetFunction指向了bind前的的函数,BoundThis就是绑定的this指向,BoundArgs便是传入的其它参数了。

当我们执行fn1时,就有点类似于TargetFunction.apply(BoundThis,BoundArgs)。

我们可以得出一个结论,当执行绑定函数时,this指向与形参在bind方法执行时已经确定了,无法改变。

复制代码
let o1 = {
    a: 1
};
let o2 = {
    a: 2
};

function fn(b, c) {
    console.log(this.a + b + c);
};

let fn1 = fn.bind(o1, 2, 3);
//尝试再次传入形参
fn1(4, 4); //6

//尝试改变this
fn1.call(o2); //6
复制代码

其实很好理解,当执行fn1时,本质上等于window.fn1(),如果this还能被改变,那this岂不是得指向window,那bind方法就没太大意义了。

 伍 ❀ 应用

说到这,我们大概知道了这三个方法的作用与区别,那这三个方法有啥用?其实很常用。

我们都知道Math.max()方法能取到一组数字中的最大值,比如:

Math.max(1, 10); //10
Math.min(1, 10); //1

那我们如何利用此方法求数组中最大最小呢,这里就可以利用apply的特性了:

Math.max.apply(null, [1, 2, 10]); //10
Math.min.apply(null, [1, 2, 10]); //1

在非严格模式下,当我们让this指向null,或者undefined时,this本质上会指向window,不过这里的null就是一个摆设,我们真正想处理的其实是后面的数组。

还记得在考虑兼容情况下怎么判断一个对象是不是数组吗?再或者是不是一个函数?利用Object.prototype.toString()结合call方法就能解决这个问题:

let a = [];
let b = function () {};
Object.prototype.toString.call(a) === "[object Array]";//true
Object.prototype.toString.call(b) === "[object Function]";//true

 陆 ❀ 总

 那么到这里,我们一起理解了我同事提出的奇葩问题,为何bind多次后执行,函数this还是指向第一次bind的对象。

其次,除了函数调用,我们理解了函数应用的概念,也知道如何使用call和apply去实现一个函数应用,顺便通过bind还知道了绑定函数(bound function)的概念。

所以到这里,你知道call、apply与bind的区别了吗?

如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!

评价 0 条
风晓L1
粉丝 1 资源 2038 + 关注 私信
最近热门资源
分享如何统信UOS系统在屏蔽mysql显性的用户名称以及密码  614
分享免费开源高速下载器  577
分享如何在银河麒麟高级服务器操作系统V10SP3中需要启用内核审计功能。  572
通过shell脚本在统信UOS/麒麟系统中安装nginx  504
分享如何查看网卡中断的数量  422
分享查询网卡所在PCI插槽链路能力及当前链路状态  420
麒麟系统进行内存清理  413
统信UOS常见问题小总结  411
麒麟系统资源下载合集(适配各类cpu)  410
winrar绿色无广告版分享  393
最近下载排行榜
分享如何统信UOS系统在屏蔽mysql显性的用户名称以及密码 0
分享免费开源高速下载器 0
分享如何在银河麒麟高级服务器操作系统V10SP3中需要启用内核审计功能。 0
通过shell脚本在统信UOS/麒麟系统中安装nginx 0
分享如何查看网卡中断的数量 0
分享查询网卡所在PCI插槽链路能力及当前链路状态 0
麒麟系统进行内存清理 0
统信UOS常见问题小总结 0
麒麟系统资源下载合集(适配各类cpu) 0
winrar绿色无广告版分享 0
作者收入月榜
1

prtyaa 收益395.97元

2

zlj141319 收益228.77元

3

IT-feng 收益214.92元

4

1843880570 收益214.2元

5

风晓 收益208.24元

6

777 收益173.02元

7

哆啦漫漫喵 收益131.6元

8

Fhawking 收益106.6元

9

信创来了 收益105.97元

10

克里斯蒂亚诺诺 收益91.08元

请使用微信扫码

加入交流群

请使用微信扫一扫!