this的指向问题

一、非箭头函数

关于 this 的指向,记住最核心的一句话: 哪个对象调用函数,函数里面的this指向哪个对象。

(一)全局环境

全局环境中(在任何函数体外部)this都指向全局对象

//浏览器环境 Window对象即是全局对象
console.log(this === window); // true
//node环境 由于模块化node中this指向module.exports node的全局对象为global
console.log(this === module.exports); // true

(二)普通函数

在函数内部,this的值取决于函数被调用的方式。此外,在严格模式和非严格模式之间也会有一些差别

非严格模式

——非严格模式下,this指向window

var name = "xiaoyu"
function fn() {
    var name = "小鱼"
    console.log(this.name)
}
fn() //xiaoyu

严格模式

  1. 严格模式下,this将保持进入执行环境时的值
  2. 没有被执行环境(execution context)定义,this保持为 undefined
 "use strict"
var name = "xiaoyu"
function fn() {
    var name = "小鱼"
    console.log(this)
    console.log(this.name)    
}
fn() //Cannot read property 'name' of undefined

(三)对象方法

——对象方法 this指向该方法所属对象

var name = "xiaoyu"
var object = {
    name: "小鱼",
    sayHi() {
        console.log(this.name)
    }
}
object.sayHi()	//小鱼

(四)构造函数

——构造函数的this 指向创建出来的实例对象

——new实例对象的过程改变this指向

var name = "xiaoyu"
function Person() {
    this.name = "小鱼"
    console.log(this); //Person {name: "小鱼"}
}
var person = new Person() 

(五)绑定事件函数

——事件绑定的处理函数 this指向函数的调用者 (绑定该函数的元素)

<button>点击</button>
<script>
    var btn = document.querySelector('button')
    btn.onclick = function() {
        console.log('事件处理函数的this:' + this)
        //事件处理函数的this:[object HTMLButtonElement]
        console.log(this) //<button>点击</button>
    }
</script>

(六)定时器函数

——定时器函数 this指向window

window.setTimeout(function(){
    console.log('定时器函数中this:' + this)
    //定时器函数中this:[object Window]
    console.log(this) //window
}, 1000)

(七)立即指向函数

——立即执行函数 this指向window

(function() {
    console.log('立即执行函数中this' + this)
    //立即执行函数中this[object Window]
    console.log(this) //window
})()

(八)匿名函数

——匿名函数的执行环境是全局性的

var name = 'xiaoyu'
var person = {
    name :'小鱼',
    sayName:function () {
        return function () {
            console.log(this.name) 
        }
    }
}
person.sayName()()  //xiaoyu

小结

函数调用方式 函数内部this的指向
普通函数调用 window
对象方法调用 该方法所属的对象
构造函数调用 创建的实例对象
事件绑定方法 绑定事件的的对象
定时器函数 window
立即执行函数 window
匿名函数 window

二、箭头函数

箭头函数出现的意义:让函数表达更简洁,增强可读性;解决匿名函数、setTimeout和setInterval的回调函数 this 指向的问题

箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”

var name = "xiaoyu"
var object = {
    name: "小鱼",
    sayHi: () => { console.log(this.name) }
}
object.sayHi()	//xiaoyu this继承自父执行上下文

三、 改变this的指向

var name = "xiaoyu"
var obj = {
    name: "小鱼",
    fn: function() {
        setTimeout( function() {
            console.log(this.name)
        }, 0)
    }
}
obj.fn() //xiaoyu

(一)使用that保存this变量

var name = "xiaoyu"
var obj = {
    name: "小鱼",
    fn: function() {
        var that = this;
        setTimeout( function() {
            console.log(that.name)
        }, 0)
    }
}
obj.fn() //小鱼

(二)使用 call apply bind

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用

返回值:改变this指向的原函数拷贝

var name = "xiaoyu"
var obj = {
    name: "小鱼",
    fn: function() {
        setTimeout( function() {
            console.log(this.name)
        }.bind(obj), 0)
    }
}
obj.fn() //小鱼

(三)new 实例

使用new创建新对象的过程

  1. 创建一个空对象
  2. 将构造函数的作用域赋值给该对象 (将this绑定在刚创建的对象上)
  3. 执行构造函数的代码,为该对象添加属性
  4. 返回一个新的的对象
var Person = function(name, age) {
    this.name = name;
    this.age = age;
};
Person.prototype.show = function() {
    console.log(this.name, this.age);
};
var p = new Person("xiaoyu", 18);
console.log(p);

(四)箭头函数

var name = "xiaoyu"
var obj = {
    name: "小鱼",
    fn: function() {
        setTimeout( () => console.log(this.name), 0)
    }
}
obj.fn() //小鱼

四、面试题

var baz = 0;
let foo = {
    bar:function() {
        console.log(this,this.baz); 
        return this.baz;
    },
    baz:1
};
let foo2 = {
    baz:2
};

let a = foo.bar();  
//作为对象的方法调用,this指向调用函数的对象,即foo
let b = foo.bar.call(foo2); 
//使用call方法将this绑定到第一个参数对象向,此时,this指向foo2
let fn = foo.bar;
let c = fn(); 
//let fn创建的对象,此时,fn = function(){...},此时函数执行时,默认指向全局window对象
let d;
(function test(){
  d = arguments[0]()
})(foo.bar);  
// arguments.callee包括了一个函数的引用去创建一个arguments对象,它能让一个匿名函数很方便的指向本身,即此时this指向arguments类数组对象

console.log(a,b,c,d)  //1 2 0 undefined

参考文章

https://juejin.im/post/59bfe84351882531b730bac2#heading-2

posted @ 2020-05-17 14:40  鱼一十三  阅读(188)  评论(0编辑  收藏  举报