11

快速搞懂call,apply和bind

 3 years ago
source link: https://zhuanlan.zhihu.com/p/109123248
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

快速搞懂call,apply和bind

腾讯 高级前端工程师

call与apply

call和apply相信很多人用过,或者看源码看到过,在这里简单说说他们之间的关系。首先call和apply都是改变this指向的api。他的区别仅仅只是call和apply的第二位参数起的差别。

function callTest() { console.log(this); }
function applyTest() { console.log(this); }

const obj = {};

callTest.call(obj, arg1, arg2, arg3...);
applyTest.apply(obj, [arg1, arg2, arg3]);

那么如果有一个问题,call和apply之间,那个性能更高,你会怎么觉得呢?

按照函数的调用方式来说,一般都是采用多个参数传入的方式(arg1,arg2,arg3)。那么实际上call和apply之间,其实是call的性能更好。为什么呢?

其实在底层运行上,apply在调用apply的时候,还需要对传入的第二个参数进行解构赋值。

applyTest.apply(obj, [arg1, arg2, arg3]);

function apply() {
    this.call(obj, [...arguments]);
}

所以从运行的效率的角度上来说,call少了一次解构赋值,运行效率会比apply会更高。

bind原理

我使用bind大概也是因为我用react的缘故。在对组件的onClick绑定事件的时候,往往this的执行是存在问题的,因为我们onClick的事件很多时候是需要调用this.setState的。

class App extends Component {

    constructor() {
        this.state = {
            text: '1'
        }
    }
  
    clickHandle() {
        this.setState({
            text: '2'
        })
    }

    render () {
        return (
            <div onClick={this.clickHandle}>{this.state.text}</div>
        )
    }
}

如果我们这样写的话,那么在clickHandle函数中,this的执行就并非App这个class的实例了,而且event对象,这个时候setState就会报错。

如果我们如下修改

render () {
    return (
        <div onClick={() => {this.clickHandle()}}>{this.state.text}</div>
    )
}

那么这个时候就能正常运行,但是这里会有一个性能问题,当我们足够多的组件的时候,通过这样绑定事件回调是会存在性能问题,因为在每一次render的时候,我们使用箭头函数是一个匿名函数,所以每一次都会重新声明一次函数,导致性能下降,所以这个时候就可以使用bind来解决这个问题了。

class App extends Component {

    constructor() {
        this.state = {
            text: '1'
        }
        this.clickHandle = this.clickHandle.bind(this);
    }

    clickHandle() {
        this.setState({
            text: '2'
        })
    }

    render () {
        return (
            <div>OMG-CLI</div>
        )
    }
}

bind的作用实际就是将当前的函数的this进行绑定,而且放在constructor中进行绑定也是有原因的,因为constructor只会在class初始化的时候执行,所以函数也只需要进行一次绑定就可以了。那么有不少面试题里面都会有叫你使用js来实现bind。其实也很简单。

Function.prototype.bind = function() {
    // 保存当前调用bind的函数指针
    const self = this;
    // 获取第一个传入参数,你要绑定的this指针
    const context = Array.prototype.shift.call(arguments);
    // 将传入的参数转为数组
    const args = Array.prototype.slice.call(arguments);
    // 返回一个函数,通过闭包来保存调用函数的指向,this的指针以及传入的参数
    return function() {
        self.apply(context, [].concat.call(args, [].slice.call(arguments)));
    }
}

所以bind的实现也是相当简单的,只要清楚call和apply以及bind的原理,即可手写一个bind出来。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK