Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

51 信用卡: apply,call,bind 的区别(一面) #30

Open
brickspert opened this issue May 1, 2019 · 2 comments
Open

51 信用卡: apply,call,bind 的区别(一面) #30

brickspert opened this issue May 1, 2019 · 2 comments

Comments

@brickspert
Copy link

brickspert commented May 1, 2019

面试公司:

51信用卡

面试环节:

一面

问题:

请说明 apply,call,bind 的区别及用法

@brickspert brickspert changed the title To brickspert: apply,call,bind 的区别及源码实现(51 信用卡管家) To brickspert: apply,call,bind 的区别(51 信用卡管家) May 1, 2019
@brickspert
Copy link
Author

brickspert commented May 1, 2019

所有准备面试的同学,乍一看这个题目,嘿嘿一笑,so easy!我觉得十个同学,有九个都能回答上来。

如果这道题满分是 10 分,我们尝试来回答一下,看看能拿几分?

初级回答(4 分)

这三个函数都是改变了当前函数的 this 指向。

  • apply 接收的是数组,并会立即执行
  • call 接收的是用逗号隔开的参数,并会立即执行
  • bind 接收的是用逗号隔开的参数,但是不会立即执行,而是返回一个新的函数

此时我的内心 OS:“妥妥的!心里美滋滋,正好是我准备的题目,下一题把”!
but,此时面试官再次发问:“你能讲讲它们三个的实现原理吗?能自己实现一下这三个函数吗?”
我:.....

进阶回答(8分)

我们重点讲一下 call 的用法和源码实现,其余的 apply 和 bind 道理一样。

call

call 的基本用法

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

var o = {a: 1, b: 2};

add.call(o, 3, 4);

如果我们不用 call,怎么实现这样的效果?

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

var o = {a: 1, b: 2};

// add.call(o, 3, 4);

o.add = add;

o.add(3, 4);

delete o.add;

我们能看到就三步:

  1. 把函数变成 object 的一个属性。
  2. 执行这个 object 下面的函数。
  3. 删除这个 object 下的这个函数。

其实就是利用了,object 的属性的 this 指向该 object 的特性来实现的哦。

我们尝试用源码实现一下

Function.prototype.call(context, ...args)
{
    context = context || window;  // context 如果是 null,则指向 window
    context.fn = this;
    var result = context.fn(...args);
    delete context.fn;
    return result;
}

apply

apply 和 call 是一个道理

Function.prototype.apply(context, args)
{
    context = context || window;
    context.fn = this;
    var result = context.fn(...args);
    delete context.fn;
    return result;
}

bind

bind 有一点特殊,但是道理都是一个道理。

我们先写个例子看看 bind 怎么用

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

var o = {a: 1, b: 2};

var bindAdd = add.bind(o, 3);
bindAdd(4);

来看看源码怎么实现

Function.prototype.bind = function (context, ...rest) {
    var self = this;
    return function F(...args) {
        return self.apply(context, rest.concat(args)); // 两次的参数 rest,args 合并到一起,作为函数的参数
    }
}

此时我的内心 OS:“哈哈哈哈哈,我好机智,准备的太全了!”!
面试官内心 OS:“还行吧,不过不要高兴的太早了。”
面试官问:你能告诉下面程序的输出是什么吗?

function add(c, d) {  
  console.log(this.a + this.b + c + d);
}
var o = {a: 1, b: 2};

var bindAdd = add.bind(o, 3);
new bindAdd(4);

再次进阶(9 分)

我们可以看到上面的程序仅仅是增加了一个 new,但是它的输出变成了 NaN,为什么?这是因为:" bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效"

我们再来丰富下 bind 的实现

Function.prototype.bind = function (context, ...rest) {
    var self = this;

    return function F(...args) {
        /*如果是 new 的,则不要之前的 context 啦*/
        if (this instanceof F) {
            return self(...rest, ...args);
        }
        return self.apply(context, rest.concat(args));
    }
}

到这里应该已经结束了,但你以为你能拿满分了?too native!

面试官继续提问了:"你能讲讲 new 的原理吗?"

我:"….."

@brickspert
Copy link
Author

其实这道题最想教的不是这道题怎么来回答,而是想说明几点:

  1. 同样一道题目,每个层级的同学答的深度都不一样。
  2. 面试官会从一个很简单的问题,层层深入,挖到你不会为止,看看你的深浅。
  3. 任何面试题,不要仅仅停留在表面,要知其然,知其所以然。

打个广告:去年写的一个面试题汇总《前端小白半年准备,成功进入BAT

@brickspert brickspert changed the title To brickspert: apply,call,bind 的区别(51 信用卡管家) To brickspert: apply,call,bind 的区别(51 信用卡) May 1, 2019
@acodercc acodercc added this to the 已回答 milestone May 2, 2019
@acodercc acodercc changed the title To brickspert: apply,call,bind 的区别(51 信用卡) 51 信用卡: apply,call,bind 的区别(一面) May 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants