最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 分析this绑定规则及优先级

    正文概述 掘金(清风乍起)   2021-03-02   658

    1.为什么要用this

    this提供了一种更优雅的方式来隐式“传递”一个对象引用,更加简洁并且易于复用。

    var me = {
        name:'Kyle'
    }
    function identtify(){
        return this.name;
    }
    identtify.call(me);// Kyle
    
    // 如果不使用this,需要给identtify()显示传入一个上下文对象
    var me = {
        name:'Kyle'
    }
    function identtify(context){
        return context.name;
    }
    identtify(me);// Kyle
    

    2.this两种误解

    2.1 指向自身

    console.log()打印了4次,说明foo()被调用了4次,但是为啥foo.count === 0? 执行foo.count = 0时候,向函数对象foo添加了一个属性count。但是函数内部代码this.count中的this并不是指向函数对象,所以虽然属性名相同,根对象并不相同。其实this.count无意中创建了一个全局变量count,它的值为NaN。

    function foo(num) {
        console.log('foo:',num);
        // 记录foo被调用的次数
        this.count++;
    }
    foo.count = 0;
    for (var index = 0; index < 10; index++) {
        if (index > 5) {
            foo(index)
        }
    }
    // foo: 6
    // foo: 7
    // foo: 8
    // foo: 9
    // foo被调用多少次
    console.log(foo.count);// 0
    

    如果要从函数对象内部引用它自身,有下面两种方法:

    // 第一种,通过一个指向函数对象的词法标识符(变量)来引用它
    function foo(num) {
        console.log('foo:',num);
        // 记录foo被调用的次数
        foo.count++;
    }
    foo.count = 0;
    for (var index = 0; index < 10; index++) {
        if (index > 5) {
            foo(index)
        }
    }
    // foo: 6
    // foo: 7
    // foo: 8
    // foo: 9
    // foo被调用多少次
    console.log(foo.count);// 4
     
    // 第二种,强制this指向foo函数对象
    function foo(num) {
        console.log('foo:',num);
        // 记录foo被调用的次数
        this.count++;
    }
    foo.count = 0;
    for (var index = 0; index < 10; index++) {
        if (index > 5) {
            foo.call(foo,index)
        }
    }
    // foo: 6
    // foo: 7
    // foo: 8
    // foo: 9
    // foo被调用多少次
    console.log(foo.count);// 4
    

    2.2 它的作用域

    在某种情况下,它是正确的,但是在其他情况下他却是错误的。但是需要明确的是,this在任何情况下都不指向函数的词法作用域。 这段代码试图通过this.bar()来引用bar函数。这是不可能成功;此外还试图使用this联通foo()和bar()的词法作用域,从而让bar()可以访问foo()作用域里的变量a,这也是不可能实现的,不能使用this来引用一个词法作用域内部的东西。

    function foo() {
        var a = 2;
        this.bar();
    }
    function bar() {
        console.log(this.a);
    }
    foo();// this.bar is not a function
    

    3.this到底是什么?到底是一种什么样的机制?

    this是在运行的时候进行绑定的,并不是编写时候绑定的。this的绑定和函数声明的位置没有任何关系,this的指向完全取决于函数的“调用位置”。

    4.调用位置

    “调用位置”就是函数在代码中被调用的位置,而不是声明位置。如何找到“函数被调用的位置”?需要分析“调用栈”(就是为了到达当前执行位置所调用的所有函数),“调用位置”就是在当前正在执行的函数的前一个调用中。

    function baz() {
        // 当前调用栈:baz
        // 当前调用位置:全局作用域
        console.log('baz');
        bar();// bar的调用位置
    }
    function bar() {
        // 当前调用栈:baz -> bar
        // 当前调用位置:在baz中
        console.log('bar');
        foo();// foo的调用位置
    }
    function foo() {
        // 当前调用栈:baz -> bar -> foo
        // 当前调用位置:在bar中
        console.log('foo');
    }
    baz();// baz的调用位置
    

    5.绑定规则

    函数在执行过程中调用位置如何决定this的绑定对象?首先需要找到调用位置,然后判断符合下面四条规则中的那一条(如果符合多条规则,那么是有优先级的,后面会谈到这点)。

    5.1 默认绑定

    在本例中,函数调用时应用了this的默认绑定,因此this指向全局对象。通过分析调用位置来看,foo()是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定。

    function foo() {
       console.log(this.a);
    }
    var a = 2;
    foo();// 2
    
    
    //  注意:如果使用严格模式,那么全局对象无法使用默认绑定,因为this会绑定到undefined
    function foo() {
    	// "use strict"
       console.log(this.a);
    }
    var a = 2;
    foo();// this is undefined
    

    5.2 隐式绑定

    隐式绑定需要考虑调用位置是否有上下文对象。

    当foo()被调用时,函数引用有上下文对象obj,根据隐式绑定规则,会把函数调用中的this绑定到这个上下文对象,因此this.a 等价于 obj.a。

    function foo() {
       console.log(this.a);
    }
    var obj = {
        a:2,
        foo:foo
    }
    obj.foo();// 2
     
    // 注意:对象属性引用链中只有最后一层会影响调用位置
    function foo() {
       console.log(this.a);
    }
    
    var obj2 = {
        a:42,
        foo:foo
    }
    var obj1 = {
        a:2,
        obj2:obj2
    }
    obj1.obj2.foo();// 42
    

    隐式丢失:被隐式绑定的函数会丢失绑定对象,从而应用默认绑定,因此this绑定到了全局对象或undefined(严格模式)

    在本例中,虽然bar()是obj.foo的一个引用,但是它引用的是foo函数本身,因此此时bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

    function foo() {
       console.log(this.a);
    }
    
    var obj = {
        a:2,
        foo:foo
    }
    var bar = obj.foo;// 函数别名
    var a = 3;// a是全局对象的属性
    bar()// 3
    
    
    // 发生在回调函数时的隐式丢失(参数传递其实就是一种隐式赋值,故结果和上面一样)
    function foo() {
        console.log(this.a);
    }
    function doFoo(fun) {
        // fun其实引用的是foo
        fun();
    }
    var obj = {
        a:2,
        foo:foo
    }
    var a = 3;
    doFoo(obj.foo); // 3
    

    5.3显示绑定

    call(...)和apply(...)方法,它们的第一个参数是一个对象,它们会把这个对象绑定到this,接着在调用函数时指定这个this。(它们的区别自行百度)

    // 通过foo.call(...),我们可以在调用foo时强制把它的this绑定到obj
    function foo() {
        console.log(this.a);
    }
    var obj = {
        a:2
    }
    foo.call(obj);// 2
    
    

    但是,显示绑定仍然无法解决前面说的丢失绑定问题,因此有下面这种方式:

    5.3.1硬绑定

    应用场景:创建一个包裹函数,传入所有参数并返回接收到的所有值

    // 通过foo.apply(obj,arguments);强制把foo的this绑定到了obj。无论之后如何调用函数bar,它总会手动在obj上调用foo。
    function foo(b) {
        console.log(this.a,b);
    }
    var obj = {
        a:2
    }
    var bar = function () {
        return foo.apply(obj,arguments);
    }
    bar(3); // 2 3
     
    // 因为硬绑定是一种常用模式,在ES5中提供内置方法 Function.prototype.bind,bind(...)会返回一个硬绑定的新函数,用法如下:
    function foo(b) {
        console.log(this.a,b);
    }
    var obj = {
        a:2
    }
    var bar = foo.bind(obj);
    bar(3); // 2 3
    

    5.4new 绑定

    前言:在Js中,构造函数只是一些使用new操作符时被调用的函数。他们并不属于某个类,也不会实例化一个类,它们只是被new操作符调用的普通函数而已。(实际上并不存在所谓"构造函数",只有对于函数的"构造调用")。

    使用new来调用函数,或说发生构造函数调用时候,会执行下面操作:

    . 创建一个全新的对象

    . 新对象会被执行[[原型]]连接

    . 这个新对象会被绑定到函数调用的this

    . 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象

    // 使用new 来调用foo(...)时,生成一个新对象,并把新对象绑定到foo(...)中调用的this,这种绑定称之为new绑定
    function foo(a) {
        this.a = a;
    }
    var bar = new foo(2);
    console.log(bar.a);//2
    

    6 优先级

    6.1 默认绑定的优先级最低

    6.2 显示绑定 > 隐式绑定

    function foo(a) {
        console.log(this.a);
    }
    var obj1 = {
        a:2,
        foo:foo
    }
    var obj2 = {
        a:3,
        foo:foo
    }
    // 显而易见,显示绑定 > 隐式绑定
    obj1.foo.call(obj2);// 3
    obj2.foo.call(obj1);// 2
    

    6.3 new绑定 > 隐式绑定

    function foo(a) {
        this.a = a;
    }
    var obj = {
        foo:foo
    }
    
    obj.foo(2);
    console.log(obj.a);// 2
    
    var bar = new obj.foo(4);
    console.log(obj.a);//2
    // 显而易见,new绑定 > 隐式绑定
    console.log(bar.a);//4
    

    6.4 new绑定 > 显示绑定

    new 和 call/apply无法一起使用,不能通过new foo.call(obj)来直接测试,这里使用硬绑定来测试他们的优先级。

    function foo(a) {
        this.a = a;
    }
    var obj = {}
    var bar = foo.bind(obj);
    bar(2);
    console.log(obj.a);//2
    
    var baz = new bar(3);
    console.log(obj.a);//2
    // 出乎意料,前面讲过foo(...)通过bind硬绑定后,foo(...)中this会被绑定到指定的对象,并返回一个新函数bar(...)。无论之后如何调用函数bar,它总会手动在指定的对象上调用foo。
    // 但是,为何这里通过new 来调用bar的时候,this被重新绑定了呢?
    // 原因:在ES5中内置的Function.prototype.bind(...)中是会判断硬绑定函数是否被new调用,如果是的话就是使用新创建的this替换硬绑定的this
    console.log(baz.a);//3
    

    总结:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

    7.规则总有例外

    7.1被忽略的this

    如果把null或undefined作为this的绑定对象传入call,apply,bind,这些值在调用时会被忽略,实际应用默认绑定规则:

    function foo(a) {
        console.log(this.a);
    }
    var a = 2;
    foo.call(null);//2
    

    7.2间接引用

    创建一个函数的“间接引用”,在这种情况下会应用默认绑定规则

    function foo(a) {
        console.log(this.a);
    }
    var a = 2;
    var obj1 = {
        a:3,
        foo:foo
    }
    var obj2 ={
        a:4
    }
    obj1.foo();// 3
    (obj2.foo = obj1.foo)();//2
    

    8.ES6中的箭头函数

    箭头函数不使用this的四种规则标准,而是根据外层(函数或全局)作用域来决定this

    function foo() {
        return (a)=>{
            //this继承foo()
            console.log(this.a);
        }
    }
    var obj1 = {
        a:2
    }
    var obj2 ={
        a:3
    }
    var bar = foo.call(obj1);
    // foo()内部创建的箭头函数会捕获调用foo()的this。由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,并且箭头函数的绑定无法被修改。(new 也不行!)
    bar.call(obj2);// 2 ,不是 3
    

    起源地下载网 » 分析this绑定规则及优先级

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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