zoukankan      html  css  js  c++  java
  • this 全面解析(笔记)

    this 既不指向函数自身,也不指向函数的词法作用域。它实际上是在函数被调用时发生的绑定,指向什么取决于函数的调用位置或调用方法。

    调用位置不是声明位置。有些编程模式可能会隐藏真正的调用位置。

    使用浏览器的调试工具,可以查看调用栈,调用栈的第二个元素,就是真正的调用位置。

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

    以上就是函数执行过程中的调用位置。

    this有四条绑定规则。

    规则1. 默认绑定。

    最常用的函数调用类型:独立函数调用。

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

    调用foo()时候,this.a 被解析成了全局变量 a。函数调用时应用了this的默认绑定,因此this指向全局对象。

    如果使用严格模式,那么全局对象无法使用默认绑定,this就会绑定到 undefined。

        function foo(){
            "use strict";
            console.log(this.a);
        }
        var a = 2;
        foo(); // TypeError: Cannot read property 'a' of undefined

    this 的绑定规则完全取决于调用位置,但是只有在非严格模式下,默认绑定才能绑定到全局对象。

    规则2. 隐式绑定

    函数调用位置是否有上下文对象。

    function foo() {
        console.log(this.a);
    }
    var obj={
        a:2,
        foo:foo
    };
    obj.foo(); //2

    foo()被当作引用属性添加到obj中。调用位置使用obj上下文来引用函数,隐式绑定规则将函数调用中的this绑定到这个上下文对象:obj。因此this.a和obj.a是一样的。

    对象属性引用链中只有最顶层或者说最后一层会影响调用位置。比如:

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

    隐式丢失:

    一个最常见的this绑定问题就是隐式绑定的函数会丢失绑定对象。它会应用默认绑定,从而把this绑定到全局对象或者undefined上。取决于是否使用严格模式。

    function foo() {
        console.log(this.a);
    }
    var obj={
        a:2,
        foo:foo
    };
    var bar = obj.foo;
    var a = "全局对象";
    bar(); //"全局对象"

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

    另外一种情况发生在传入回调函数时:

    function foo() {
        console.log(this.a);
    }
    function doFoo(fn) {
        fn();
    }
    var obj={
        a:2,
        foo:foo
    };
    var a = "全局对象";
    doFoo(obj.foo); //"全局对象"

    参数传递其实就是一种隐式赋值,因此传入函数时也会被隐式赋值。

    如果把函数传入语言内置的函数而不是自己声明的函数,结果也是一样的:

    function foo() {
        console.log(this.a);
    }
    function doFoo(fn) {
        fn();
    }
    var obj={
        a:2,
        foo:foo
    };
    var a = "全局对象";
    setTimeout(obj.foo, 100); //"全局对象"

    回调函数丢失this绑定是非常常见的。除此之外,调用回调函数的函数可能会修改this。一些流行的javascript库中,事件处理器常会把回调函数的this强制绑定到触发事件的DOM元素上。

    由于回调函数的执行方式无法控制,影响绑定的调用位置也就没有办法控制。那么如何固定this来修复呢。

    规则3. 显示绑定

    使用函数的call()和apply()方法,可以在某个对象上强制调用函数。这两个方法的第一个参数是一个对象,它们会把这个对象绑定到this,接着做调用函数时指定这个this。

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

    显示绑定也无法解决前面的丢失绑定问题。但是显示绑定的一个变种可以解决这个问题:

    3.1 硬绑定

    function foo(something) {
        console.log(this.a, something);
        return this.a+something;
    }
    var obj={
        a:2
    };
    var bar = function () {
        return foo.apply(obj, arguments);
    };
    var b=bar(3); //2 3
    console.log(b); //5

    创建函数bar(),在内部手动调用foo.apply(),强制把foo的this绑定到了obj。之后无论如何调用函数bar,它总会手动在obj上调用foo。这就是显示的强制绑定。

    另外一个例子,创建一个 i 可以重复使用的辅助函数:

    function foo(something) {
        console.log(this.a, something);
        return this.a+something;
    }
    //简单的辅助绑定函数
    function bind(fn, obj) {
        return function () {
            return fn.apply(obj, arguments);
        };
    }
    var obj={
        a:2
    };
    var bar = bind(foo, obj);
    var b=bar(3); //2 3
    console.log(b); //5

    ES5提供内置方法:Function.prototype.bind,因此可以这样:

    function foo(something) {
        console.log(this.a, something);
        return this.a+something;
    }
    var obj={
        a:2
    };
    var bar = foo.bind(obj);
    var b=bar(3); //2 3
    console.log(b); //5

    bind()会返回一个硬编码的新函数,会把参数设置为this的上下文并调用原始函数。

    2.2 API调用的“上下文”

    javascript的内置函数,基本都提供了一个可选的参数,通常被称为“上下文”(context),其作用和bind()一样。确保你的回调函数使用指定的this。

    function foo(ele) {
        console.log(ele, this.id);
    }
    var obj={
        id: "awesome"
    };
    
    ['a','b','c'].forEach(foo,obj);
    //a "awesome"   b "awesome"  c "awesome"

    调用foo()时把this绑定到obj。

    规则4. new 绑定

    function Foo(a) {
        this.a = a;
    }
    var bar = new Foo(2);
    console.log(bar.a); //2

    使用new来调用Foo()时,我们会构造一个新对象并把它绑定到foo()调用中的this上。如果函数没有返回其他对象,new表达式中的函数调用会自动返回这个新对象。

  • 相关阅读:
    MongoDB慢查询性能分析
    redis的LRU算法(二)
    Skynet服务热点火焰图分析
    内存爆灯
    时区问题
    与机器共生
    bug狩猎
    Lesson Learned
    下划线引起的血案
    Intel的CPU漏洞:Spectre
  • 原文地址:https://www.cnblogs.com/dodocie/p/6909335.html
Copyright © 2011-2022 走看看