快速搞懂call,apply和bind

2022-09-26 10:43:36 浏览数 (3)

call与apply

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

代码语言:javascript复制
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的时候,还需要对传入的第二个参数进行解构赋值。

代码语言:javascript复制
applyTest.apply(obj, [arg1, arg2, arg3]);

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

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

bind原理

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

代码语言:javascript复制
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就会报错。

如果我们如下修改

代码语言:javascript复制
render () {
    return (
        <div onClick={() => {this.clickHandle()}}>{this.state.text}</div>
    )
}

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

代码语言:javascript复制
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。其实也很简单。

代码语言:javascript复制
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出来。

0 人点赞