zoukankan      html  css  js  c++  java
  • this 到底指向谁

    this 的指向,是在调用函数时根据执行上下文所动态确定的。

    • 在函数体中,简单调用该函数时(非显式/隐式绑定下),严格模式下 this 绑定到 undefined,否则绑定到全局对象 windowglobal;
    • 一般构造函数 new 调用,绑定到新创建的对象上;
    • 一般由 call/apply/bind 方法显式调用,绑定到指定参数的对象上;
    • 一般由上下文对象调用,绑定在该对象上;
    • 箭头函数中,根据外层上下文绑定的 this 决定 this 指向。

    例题组合 1:全局环境下的 this

    函数在浏览器全局环境中被简单调用,非严格模式下指向 window,严格模式下指向 undefined

    function fn1() {
        console.log(this) 
    }
    function fn2() {
        'use strict'
        console.log(this)
    }
    fn1() // window
    fn2() // undefined
    

    在执行函数时,如果函数中的 this 是被上一级的对象所调用,那么 this 指向的就是上一级的对象; 否则指向全局环境。

    const foo = {
        bar: 10,
        fn: function() {
            console.log(this)
            console.log(this.bar)
        }
    }
    var fn1 = foo.fn
    fn1() // 直接调用,this 指向 window,window.bar => undefined
    foo.fn() // 通过 foo 调用,this 指向 foo,foo.bar => 10
    
    const student = {
        name: 'Lucas',
        fn: function() {
            return this
        }
    }
    console.log(student.fn() === student) // true
    var fn1 = student.fn;
    console.log(fn1() === student) // false
    

    例题组合 2:上下文对象调用中的 this

    const person = {
        name: 'Lucas',
        brother: {
            name: 'Mike',
            fn: function() {
                return this.name
            }
        }
    }
    console.log(person.brother.fn()) // this 指向最后调用它的对象,输出 => Mike
    
    const o1 = {
        text: 'o1',
        fn: function() {
            return this.text
        }
    }
    const o2 = {
        text: 'o2',
        fn: function() {
            return o1.fn()
        }
    }
    const o3 = {
        text: 'o3',
        fn: function() {
            var fn = o1.fn
            return fn()
        }
    }
    console.log(o1.fn()) // this 指向 o1,输出 o1
    console.log(o2.fn()) // 最终调用 fn 的是 o1,所以 this 指向 o1,输出 o1
    console.log(o3.fn()) // fn() 是被直接调用的,所以 this 指向 window,window.text => undefind
    

    如果想让

    console.log(o2.fn())
    

    输出 o2,除了使用 bind/call/apply 以外

    console.log(o1.fn.call(o2))
    console.log(o1.fn.apply(o2))
    console.log(o1.fn.bind(o2)())
    

    还可以用这种方式

    const o1 = {
        text: 'o1',
        fn: function() {
            return this.text
        }
    }
    const o2 = {
        text: 'o2',
        fn: o1.fn
    }
    console.log(o2.fn()) // o1.fn 挂到了 o2 对象上,所以 this 指向 o2,输出 o2
    

    this 指向最后调用它的对象,在 fn 执行时,挂到 o2 对象上即可,提前进行了赋值操作

    例题组合 3:bind/call/apply 改变 this 指向

    bind/call/apply 都是用来改变相关函数 this 指向的,但是 call/apply 是直接进行相关函数调用; bind 不会执行相关函数,而是返回一个新的函数,这个新的函数已经自动绑定了新的 this 指向,开发者只需要手动调用即可。

    三者代码上的区别如下:

    const target = {}
    fn.call(target, 'arg1', 'arg2')
    
    const target = {}
    fn.apply(target, ['arg1', 'arg2'])
    
    const target = {}
    fn.bind(target, 'arg1', 'arg2')()
    
    const foo = {
        name: 'lucas',
        logName: function () {
            console.log(this.name)
        }
    }
    const bar = {
        name: 'mike'
    }
    foo.logName() // lucas; logName 由 foo 调用,this 指向 foo
    foo.logName.call(bar) // mike; logName 被 call() 改变 this 的指向为 bar
    

    例题组合 4:构造函数和 this

    function Foo() {
        this.bar = 'Lucas'
    }
    const instance = new Foo()
    console.log(instance.bar) // Lucas
    

    new 操作符调用构造函数具体做了什么?
    如下:

    • 创建一个新的对象
    • 将构造函数的 this 指向这个新对象
    • 为这个对象添加属性、方法等
    • 最终返回新对象

    以上过程,也可以用代码表述:

    var obj = {}
    obj.__proto__ = Foo.prototype
    Foo.call(obj)
    

    如果在构造函数中出现了显式 return 的情况,那么需要注意分为两种场景:

    function Foo() {
        this.user = 'Lucas'
        const o = {}
        return o
    }
    const instance = new Foo()
    console.log(instance.user) // undefined,此时 instance 返回的是空对象 o
    
    function Foo() {
        this.user = 'Lucas'
        return 1
    }
    const instance = new Foo()
    console.log(instance.user) // Lucas,此时 instance 返回的是目标对象实例 this
    

    结论:如果构造函数中显式返回一个值,且返回的是一个对象,那么 this 就指向这个返回的对象; 如果返回的不是一个对象,那么 this 仍然指向实例。

    例题组合 5:箭头函数中的 this 指向

    结论:箭头函数使用 this 不适用以上标准规则,而是根据外层(函数或者全局)上下文决定。

    this 出现在 setTimeout() 中的匿名函数,因此 this 指向 window 对象。

    const foo = {
        fn: function () {
            setTimeout(function () {
                console.log(this === window) // true
                console.log(this === foo) // false
            });
        }
    }
    foo.fn()
    

    如果需要 this 指向 foo 这个 object 对象,可以使用箭头函数解决。

    const foo = {
        fn: function () {
            setTimeout(() => {
                console.log(this === window) // false
                console.log(this === foo) // true
            });
        }
    }
    foo.fn()
    

    例题组合 6:this 优先级相关

    我们常常把通过 callapplybindnewthis 绑定的情况称为显式绑定; 根据调用关系确定的 this 指向称为隐式绑定。

    function foo(a) {
        console.log(this.a)
    }
    const obj1 = {
        a: 1,
        foo: foo
    }
    const obj2 = {
        a: 2,
        foo: foo
    }
    obj1.foo.call(obj2) // 2
    obj2.foo.call(obj1) // 1
    
    

    输出分别为 2、1,也就是说 callapply 的显式绑定一般来说优先级更高

    function foo(a) {
        this.a = a
    }
    const obj1 = {}
    var bar = foo.bind(obj1)
    bar(2)
    console.log(obj1.a) // 2
    

    上面代码将 bar 函数中的 this 指向为 obj1 对象。执行 bar(2) 后,obj1.a 的值为 2。即经过 bar(2) 执行后,obj1 对象为:{a: 2}

    当再使用 bar 作为构造函数时:

    var baz = new bar(3)
    console.log(baz.a) // 3
    console.log(obj1.a) // 2
    

    bar 函数本身是通过 bind 方法构造的函数,其内部已经将 this 绑定为 obj1,它再作为构造函数通过 new 调用时,返回的实例就会与 obj1 解绑,也就是说:

    new 绑定修改了 bind 绑定中的 this,因此 new 绑定的优先级比 bind 绑定更高。

    function foo() {
        return () => {
            console.log(this.a)
        }
    }
    const obj1 = {
        a: 2
    }
    const obj2 = {
        a: 3
    }
    const bar = foo.call(obj1) // 将 foo() 的 this 绑定为 obj1
    bar() // 2; 此时 bar 为 foo() 中返回的箭头函数,箭头函数的 this 指向上层的 this,所以 bar 的 this 也会绑定到 obj1
    bar.call(obj2) // 2; 箭头函数的绑定无法被修改,也就是说如果 bar 为普通函数,this 就可以被修改绑定
    
    
    var a = 123
    const foo = () => () => {
        console.log(this.a)
    }
    
    const obj1 = {
        a: 2
    }
    const obj2 = {
        a: 3
    }
    const bar = foo.call(obj1) // 此时 foo() 为箭头函数,所以 this 指向 window
    bar.call(obj2) // 123; bar 作为箭头函数指向上层,所以 this 也指向 window
    

    如果将 var a = 123 改为 const a = 123,将会输出 undefined。因为使用 const 声明的变量不会挂载到 window 全局对象当中。因此 this 指向 window 时,自然也找不到 a 变量了。

    参考文章:《前端开发核心知识进阶》LucasHC(侯策):一网打尽 this,对执行上下文说 Yes

  • 相关阅读:
    三剑客之Grep家族
    Linux默认权限命令:umask
    Linux特殊权限管理SUID|SGID|STICKY
    zabbix的web界面出现乱码解决方案
    启动zabbix-server/agent报错:cannot open "/etc/zabbix/logs/zabbix_server.log": [13] Permission denied
    Zabbix-Agent配置文件详解
    使用yum安装报错:[Errno 256] No more mirrors to try
    grep/sed/awk命令查看指定时间段的日志
    数据库SQL练习(一):数据查询
    连接数据库报错:ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
  • 原文地址:https://www.cnblogs.com/linyisme/p/11678115.html
Copyright © 2011-2022 走看看