zoukankan      html  css  js  c++  java
  • 关于 js 中 this 指向的问题

    关于 js 中 js 指向的问题

    本文章通过代码的运行来描述 this 的具体指向:

    默认绑定

    场景一:

    console.log('普通情况', this) // window
    

    在普通状态下, this 指向的是全局变量 window

    场景二:

    function foo() {
        console.log('普通函数体', this)
    }
    
    foo() // window
    

    在函数体内, this 也是指向全局变量 window

    结论一:

    在正常,无任何调用的情况下, this 是指向全局变量 window 的

    隐式绑定:

    场景三:

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

    在 obj.get 调用下, get 中 this 指向的就是 obj 这个对象

    场景四:

    var obj2 = {
        obj,
        a: 1
    }
    
    obj2.obj.get() // 2
    

    可以看到,尽管有二层调用, this 指向的还是最近一层中的 this

    结论二

    从而得出, this 他所在的函数被调用了,那么他就会指向该函数, 并且会指向他最近的一个调用对象

    this 丢失的情况:

    此种情况是针对 结论二 的不足所作出的补充

    场景五:

    function get() {
        console.log(this.a);
    }
    
    var obj = {
        a: 2,
        get
    }
    
    // 更换调用方式:
    var getIns = obj.get
    getIns() // undefined
    

    如果调用对象被赋值, 在指向之后他其实是并没有调用对象的, 他的所采用对象会指向 window, 但是因为 window 中并没有 a 变量,所以输出了 undefined

    场景六:

    var o1 = {
        text: 'o1',
        fn: function () {
            return this.text
        }
    }
    
    var o3 = {
        text: 'o3',
        fn: function () {
            console.log('o3', this)
            var fn = o1.fn
            return fn()
        }
    }
    
    console.log('o3 丢失 this 的情况', o3.fn())  // undefined
    

    可以看到 即使在 o3 中的 fn 里被调用, this 依旧是处于空的指向
    此场景是为了加强场景五所作出的验证

    结论三:

    如果调用对象被赋值后调用,如果没有绑定 this, 那么他的指向将会丢失

    为什么会丢失

    原因的话,等你看完这篇文件,大抵可以明白了: http://www.ruanyifeng.com/blog/2018/06/javascript-this.html

    显示绑定

    场景七:

    var getIns = obj.get.bind(obj)
    getIns() // 2
    

    套用情景 5 this 丢失的场景, 可以看到, 现在打印的是我们想要的值了

    场景八:

    var o3 = {
        text: 'o3',
        fn: function () {
            var fn = o1.fn
            return fn.call(this)
        }
    }
    
    console.log('情景 8  o3 丢失 this 的情况修改', o3.fn()) // undefined
    

    该场景使用 call/apply
    这两个函数的作用是一样的,唯一不一样在于传参:
    call(this,1,2,3)
    apply(this, [1,2,3])

    apply 这些函数特殊的地方:

    var array = ['a', 'b'];
    var elements = [0, 1, 2];
    array.push.apply(array, elements);
    console.log(array); // ["a", "b", 0, 1, 2]
    

    通过 apply 直接 push 了多个参数
    在 es6 之后 也快使用这种方式: array.push(...elements)

    场景九:

    class Foo {
        name = "mike"
    
        say() {
            console.log(this.a)
        }
    
    }
    
    var FooIns = new Foo()
    FooIns.say() // undefined
    

    class 中的函数, 这也属于一种 this 丢失的场景
    熟悉 react 的人应该就知道这个场景

    new 绑定

    这里说的new 绑定和情景 9 是不一样, 说的是构造函数的绑定:

    场景十

    // es5代码
    function Foo2() {
        this.bar = "LiLz"
    }
    
    const Foo2Ins = new Foo2()
    console.log(Foo2Ins.bar) // LiLz
    

    在上面情景一中 this 指向的是 window, 在场景 10 中, 通过 new 成功将 this 绑定在了 Foo2Ins 上

    场景十一

    class Foo3 {
        constructor() {
            this.bar = 'lisa'
        }
    }
    
    var Foo3Ins = new Foo3()
    console.log(Foo3Ins.bar) // lisa
    

    从这里可以看到 new 的作用, 虽然改变指向只是他的一部分功能

    结论四

    隐式/显示调用可以改变 this 指向,而且比隐式调用的优先级要大

    特殊情况-严格模式

    场景十二

    function foo2() {
        'use strict'
        console.log('严格模式下的 this', this)
    }
    
    foo2() // undefined
    
    

    可以看到在严格模式下 普通的 this 指向就是会丢失

    特殊情况-箭头函数

    我们来看看箭头函数对应的 this 有什么区别

    场景十三

    const bar = () => {
        'use strict'
        console.log('箭头函数', this)
    }
    bar() // windows
    
    

    即使在严格模式下 箭头函数中的 this 也不会是 undefined

    场景十四

    const get2 = () => {
        console.log(this, this.a);
    }
    
    var obj3 = {
        a: 2,
        get2
    }
    
    obj3.get2() // window, undefined
    

    隐式调用无法改变其指向

    场景十五

    obj3.get2.bind(obj3)() // 同场景 14
    obj3.get2.call(obj3) // 场景 14
    

    结论五

    这里可以得出结论, 隐式/显示调用都不能改变箭头函数的指向

    场景十六

    
    const  Foo4 = () => {
        this.bar = "LiLz"
    }
    
    const Foo4Ins = new Foo4()
    console.log(Foo4Ins.bar) // TypeError: Foo4 is not a constructor
    

    遇到 new 时, 可以看到箭头函数,他是无法被作为构造函数的

    场景十七

    class Foo5 {
        name = "mike"
    
        say = () => {
            console.log(this.name)
        }
    
    }
    
    var Foo5Ins = new Foo5()
    Foo5Ins.say() // mike
    

    结论六

    至此可以得出 this 改变的权重: 箭头函数 = new > bind/apply/call > 函数调用

    总结:

    • 在正常,无任何调用的情况下, this 是指向全局变量 window 的
    • this 他所在的函数被调用了,那么他就会指向该函数, 并且会指向他最近的一个调用对象
    • 如果调用对象被赋值后调用,如果没有绑定 this, 那么他的指向将会丢失
    • 隐式/显示调用可以改变 this 指向,而且比隐式调用的优先级要大
    • 隐式/显示调用都不能改变箭头函数的指向, 箭头函数与 new 的构造函数相矛盾
    • this 改变的优先级: 箭头函数 = new > bind/apply/call > 函数调用

    本文中的所有例子都在 GitHub 中: https://github.com/Grewer/JsDemo/blob/master/jsthis/index.html

  • 相关阅读:
    可方便扩展的JIRA Rest Web API的封装调用
    小诗一首
    jxse2.6在jdk8下,JxtaMulticastSocket存在的问题
    http://blogs.msdn.com/b/pranavwagh/archive/2007/03/03/word-2007-file-seems-to-be-deleted-when-you-open-and-save-it-using-dsoframer.aspx
    how to javafx hide background header of a tableview?
    Styling FX Buttons with CSS
    2d网络游戏的延迟补偿(Lag compensation with networked 2D games)
    Fast-paced Multiplayer
    jspace2d——A free 2d multiplayer space shooter
    JXSE and Equinox Tutorial, Part 2
  • 原文地址:https://www.cnblogs.com/Grewer/p/14095066.html
Copyright © 2011-2022 走看看