最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • this 五种绑定机制详解

    正文概述 掘金(牧游)   2021-03-08   389

    前言

    this绑定是Javascript开发中老生常谈的问题了,但要彻彻底底弄的很明白的人应该不多,有时候我也会被this绑定题弄的晕头转向的,本人今天就从this绑定的五种场景 默认绑定隐式绑定显式绑定new绑定箭头函数绑定 详解

    一) this默认绑定

    看下面这个例子:

    //非严格模式
    var a = 2;
    function fn(){
        console.log(this.a);    // 2
    }
    fn();
    
    //严格模式
    function foo(){
        "use strict";
        console.log(this.a);    // TypeError: Cannot read property 'a' of undefined
    }
    foo(); 
    
    • 非严格模式下,全局作用域中的函数调用时,函数词法作用域内的 this 指向全局对象 window
    • 严格模式下,函数调用时词法作用域内的 this 指向 undefined , 报 TypeError 错误

    以上仅仅是举例,在实际开发中,不应混用严格与非严格模式

    二) 隐式绑定

    看下面这个例子:

    function foo() {
        console.log(this.name);
    };
    let obj = {
        name: '牧游',
        func: foo
    };
    obj.func() //牧游
    

    提出疑问: 如果函数前面存在多个调用它的对象,那么this会隐式绑定到哪个对象上呢?

    function foo() {
        console.log(this.name);
    };
    let obj = {
        name: '牧游',
        func: foo
    };
    
    let obj1 = {
        name: '张三',
        attr: obj
    }
    obj1.attr.func() //牧游
    

    隐式丢失

    在特定的场景下,隐式绑定也会存在丢失的问题,最常见的就是作为 参数传递 以及 变量赋值 ,我们先看下参数传递这个例子:

    var name = '张三';
    let obj = {
        name: '牧游',
        fn: function () {
            console.log(this.name);
        }
    };
    
    function fn1(param) {
        param();
    };
    fn1(obj.fn); // 张三
    

    导致以上问题原因: obj.fn 被当做函数入参传递到fn1函数里去执行,obj.fn函数内部this并没有跟其所在对象绑定,所以this找不到就会指向window

    第二个丢失问题:变量赋值

    var name = '张三';
    let obj = {
        name: '牧游',
        fn: function () {
            console.log(this.name);
        }
    };
    let fn1 = obj.fn;
    fn1(); //张三
    

    原因: 本质上与传参相同

    三) 显示绑定

    我们来看一下具体的例子:

    let obj1 = {
        name: '张三'
    };
    let obj2 = {
        name: '李四'
    };
    let obj3 = {
        name: '王五'
    }
    
    let name = '牧游';
    
    function fn() {
        console.log(this.name);
    };
    fn(); // 牧游
    fn.call(obj1); // 张三
    fn.apply(obj2); // 李四
    fn.bind(obj3)(); // 王五
    

    在上述代码中,分别通过call、apply、bind改变函数fn的this指向。其实这种做法,我们习惯称之为 函数调用

    注意事项: 如果在使用call之类的方法改变this指向时,指向参数提供的是null 或者 undefined, 那么this将指向全局对象。比如下面这个例子:

    let obj1 = {
        name: '张三'
    };
    let obj2 = {
        name: '李四'
    };
    let obj3 = {
        name: '王五'
    }
    
    let name = '牧游';
    
    function fn() {
        console.log(this.name);
    };
    fn(); // 牧游
    fn.call(undefined); // 牧游
    fn.apply(null); // 牧游
    fn.bind(undefined)(); // 牧游
    

    另外, JS中部分API方法也内置了显示绑定,以forEach为例:

    let obj = {
        name: '牧游'
    };
    
    [1, 2, 3].forEach(function () {
        console.log(this.name); //牧游 [3次]
    }, obj);
    

    四) new绑定

    function Fn() {
        this.name = '牧游';
    }
    
    let obj = new Fn();
    console.log(obj.name);
    

    五) this的绑定优先级

    我们前面介绍this的四种绑定规则,这边提出一个问题,如果一个函数调用存在多种绑定方法,this最终指向谁呢?

    这里我们直接先上答案,this绑定的优先级关系为:

    显式绑定 > 隐式绑定 > 默认绑定

    new绑定 > 隐式绑定 > 默认绑定

    那么我们结合几个例子来验证下上面的规律,首先是 显示绑定 > 隐式绑定 :

    //显式 > 隐式
    let obj = {
        name: '张三',
        fn: function () {
            console.log(this.name);
        }
    };
    let obj1 = {
        name: '牧游'
    };
    obj.fn.call(obj1);// 牧游
    

    其次是 new绑定 > 隐式绑定 :

    //new > 隐式
    let obj = {
        name: '张三',
        fn: function() {
            this.name = '牧游';
        }
    }
    
    let echo = new obj.fn();
    
    console.log(echo.name); // 牧游
    

    提出问题: 为什么显示绑定不和new绑定比较呢?

    答: 因为不存在这种绑定同时生效的情景,如果同时写这两种代码会直接抛错,比如:

    // error
    let obj = {
        name: '张三',
        fn: function() {
        }
    }
    
    let obj1 = {
        name: '牧游'
    }
    let echo = new obj.fn.call(obj1);  // Uncaught TypeError: obj.fn.call is not a constructor
    

    六) 箭头函数this

    ES6的箭头函数是另类的存在,为什么要单独说呢,这是因为箭头函数中的this不适用上面介绍的四种绑定规则。

    准备来说, 箭头函数中没有this, 箭头函数的this指向取决于外层作用域中的this, 外层作用域或函数的this指向谁, 箭头函数中的this便指向谁。比如下面这个例子:

    function fn() {
        return () => {
            console.log(this.name);
        };
    }
    let obj1 = {
        name: '牧游'
    };
    let obj2 = {
        name: '张三'
    };
    let bar = fn.call(obj1); // fn this指向obj1
    bar.call(obj2); // 牧游
    

    提出问题: 为什么我们第一次绑定this并返回箭头函数后,再次改变this指向没生效呢?

    答: 1) 箭头函数的this取决于外层作用域的this, fn函数执行是this指向了obj1, 所以函数的this也指向obj1。 2) 箭头函数this还有一个特性,就是 一旦箭头函数的this绑定成功,无法再次被修改

    再次提出问题: 如果箭头函数有多个外层作用域,那最终该指向谁呢?

    答: 箭头函数的this仅取决其直接外层作用域的this, 通俗一点说就是直接上级

    function fn() {
        return function () {
            return () => {
                console.log(this.name);
            };
        }
    }
    let obj1 = {
        name: '牧游'
    };
    let obj2 = {
        name: '张三'
    };
    let obj3 = {
        name: '李四'
    };
    let bar = fn.call(obj1);
    let foo = bar.call(obj2);
    let echo = foo.call(obj3); // 张三
    

    我们上面说了, "箭头函数的this一旦绑定成功,无法再次被修改", 其实我们通过可以修改外层函数this 指向达到间接修改箭头函数this的目的。比如下面这个例子:

    function fn() {
        return () => {
            console.log(this.name);
        };
    };
    
    let obj1 = {
        name: '牧游'
    };
    let obj2 = {
        name: '张三'
    };
    
    fn.call(obj1)(); // fn this指向obj1,箭头函数this也指向obj1
    fn.call(obj2)(); //fn this 指向obj2,箭头函数this也指向obj2
    

    总结

    通过上面的实例以及结合知识点讲解,总结了以下知识点:

    • 【默认绑定】严格模式与非严格模式this指向有所不同
    • 【隐式绑定】介绍了隐式丢失几种情况
    • 详细说明了this几种绑定优先级关系

    参考

    掘金

    木易杨前端进阶


    起源地下载网 » this 五种绑定机制详解

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元