全面理解call-apply-bind

81 阅读3分钟

一、什么是this指针

简单来说,js中的this指针指向的是调用它的对象(下面各种例子读者可自行揣摩)

        // 1、理解this指针
        var name = "windowName";
        function a() {
            var name = "li";
            console.log(this.name); // windowName
            // ES5中 指向的是调用它的对象,
            console.log(this); // Window {window: Window, self: Window, document: document, name: 'windowName', location: Location, …}
        }
        a(); // 没有调用者,所以this指向的默认对象就是window
        
        
        var name = "windowName";
        var a = {
            name: "cherry",
            func: function() {
                console.log(this.name); // cherry this指向它的调用者a,所以是cherry
            }
        }
        a.func();


        var name = "windowName";
        var a = {
            name: "cherry",
            func: function() {
                console.log(this.name); // cherry this还是指向它的调用者a,不管前面有多少个调用,所以是cherry
            }
        }
        window.a.func();


        var name = "windowName";
        var a = {
            // name: "cherry",
            func: function() {
                console.log(this.name); // undefined this指向调用者a,只会在当前对象里找,但是没有name,所以undefined
            }
        }
        window.a.func();


        var name = "windowName";
        var a = {
            name: "cherry",
            func: function() {
                console.log(this.name); // windowName 这里的this永远指向它的调用者
            }
        }
        var f = a.func; // 把这个函数付给了一个变量,通过这个变量执行(地址引用)
        f(); // 这样执行是没有调用者的,默认指向window,所以就是windowName


        var name = "windowName";
        function func() {
            var name = "cherry";
            console.log(this); // Window {window: Window, self: Window, document: document, name: 'windowName', location: Location, …}
            innerFunc(); // 这个函数也没有调用者

            function innerFunc() {
                console.log(this.name);
            }
        }
        func(); // windowName 没有调用者 所以this就是window

二、如何改变指针的指向?

    // 如何改变指针
    var name = "windowName";
    var a = {
        name: "cherry",
        func1: function() {
            console.log('this.name :>> ', this.name);
        },
        func2: function() {
            var _this = this;

            // setTimeout 这是普通函数写法,没有调用者,所以this指向window
            setTimeout(function() {
                this.func1(); // this
            }, 100)

            // 改变指针指向  
            // 1、箭头函数,下面这个时候this指向的是a对象
            setTimeout(() => {
                this.func1(); // this
            }, 100)

            // 2、闭包
            setTimeout(function() {
                _this.func1(); // this
            }, 100)

            // 3、call、apply
            // call--执行一个函数  函数名.call(作用域对象)---就是将函数放到特定的作用域对象中执行
            setTimeout(function() {
                    _this.func1(); // this
                }.call(a), 100) // 就是将函数放到特定的作用域对象中执行,这个时候this指向就是a

            // call--执行一个函数  函数名.apply(作用域对象)---就是将函数放到特定的作用域对象中执行
            setTimeout(function() {
                    _this.func1(); // this
                }.call(a), 100) // 就是将函数放到特定的作用域对象中执行,这个时候this指向就是a
        }
    };
    a.func2();

三、call、apply的区别

        // call、apply的区别
        
        var a = {
            name: "cherry",
            func: function(a, b) {
                console.log('a+b :>> ', a + b);
            }
        }
        var b = a.func;
        
        // apply----第一个参数a:作用域对象,第二个参数--必须数组形式
        b.apply(a, [1, 2]); // 3
        
        // call----第一个参数a:作用域对象,第二个参数可以是多个
        b.call(a, 1, 2); // 3
        
        // bind可以绑定作用域,但不立即执行----此时c函数的作用域就是a
        var c = b.bind(a, 1, 2); // 3
        c();

四、call、apply实现原理

        // call、apply是怎样实现的
        
        Function.prototype.mycall = function(ctx) {   // ctx----作用域对象
            ctx = ctx || window; // 传参,有作用域就用,没有就window
            ctx.func = this; // 添加一个属性 如: a.func = this;  this==b  ------ 在a这个函数里面添加了一个函数func,这个函数就是b函数
            // console.log(arguments); // arguments是js内置对象----函数参数集合(可以理解为一个数组,这个数组里面装的就是[a,1,2])
            // 取参数1,2
            // [...arguments]--(将参数展开)--[a,1,2]----slice(1)--从下标为1的开始分割
            let arg = [...arguments].slice(1);
            // 执行b函数
            let result = ctx.func(...arg);
            return result;
        }
        b.mycall(a, 1, 2); // mycall的调用者是b,所以上面的this就是b