javaScript中this的指向?

javaScript中this对象是在运行时基于函数的执行环境绑定的,在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。

但在实际中,代码环境复杂,this的指向并非那么直接判断出来。下面来做一下总结。

1,全局执行环境下的普通函数

function f1 () {
    console.log(this)
}
function f2 () {
    'use strict'
    console.log(this)
}
f1() // window
f2() // undefined

分为严格模式和非严格模式。非严格模式下等于window,严格模式下为undefined。

2,事件对象中的this

this指的是事件对象本身,是指event.target

<!DOCTYPE html>
<html >
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div >我是一个div</div>
    <script>
      window.id = 'window'
      document.getElementById('div1').onclick = function (){
          alert(this.id) // 输出div1
          
      }
    </script>
</body>
</html>

3,对象中的方法引用

 const foo = {
        bar: 10,
        fn: function() {
           console.log(this)
           console.log(this.bar)
        }
    }
    foo.fn() // this指的是foo
    var fn1 = foo.fn
    fn1() // this指的是window

4,构造函数中的this

// this指instance
function Foo() {
    this.bar = "ceshi"
}
const instance = new Foo()
console.log(instance.bar)

// this指的是 {}
function Foo(){
    this.user = "ceshi"
const o = {} return o } const instance = new Foo() console.log(instance.user)

这里的this之所以不同是由构造返回值是否是对象导致的

5,丢失的this

<!DOCTYPE html>
<html >
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div >我是一个div</div>
    <script>
       let getId = document.getElementById
       console.log(getId('div1')) // 报错,报错这是因为document.getElementById方法内的this指向window导致的
       // 修复
       document.getElementById = (function(func){
         return function(){
             return func.apply(document,arguments)
         }
       })(document.getElementById)
       let getId =  document.getElementById 
       console.log(getId('div1'))
    </script>
</body>
</html>

6,借助bind、Object.prototype.call和Object.prototype.apply改变this的指向

Object.prototype.call和Object.prototype.apply的是后者参数为数组,前者为多个参数。

用代码来总结:

const target = {}
fn.call(target, 'arg1', 'arg2')
相当于:

const target = {}
fn.apply(target, ['arg1', 'arg2'])
相当于:

const target = {}
fn.bind(target, 'arg1', 'arg2')()

借助call改变

const foo = {
    name: 'ceshi',
    logName: function() {
        console.log(this.name)
    }
}
const bar = {
    name: 'mike'
}
console.log(foo.logName.call(bar)) // mike

7,其他一些复杂的场景

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

let,const不会挂在window上面,而var可以

  const a = 999
        let aa = 1000
        var b =1001
        function too(){
            console.log(this.a)
            console.log(this.aa)
            console.log(this.b)
        }
        too()

对象嵌套中的this

const person = {
    name: 'ceshi',
    brother: {
        name: 'zhen',
        fn: function() {
            return this.name
        }
    }
}
console.log(person.brother.fn())  // zhen

更复杂的情景

     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()) // o1
        console.log(o2.fn()) // o1
        console.log(o3.fn()) // undefined

箭头函数的this提前绑定好

const foo = {  
    fn: function () {  
        setTimeout(() => {  
            console.log(this)
        })
    }  
} 
console.log(foo.fn()) // {fn: ƒ}

8,通过 call、apply、bind 绑定的情况称为显式绑定;根据调用关系确定的 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

显式绑定的优先级高于隐式绑定。