先来整体预告一下:
- 全局对象的 this 指向 window 对象
- 全局函数执行 this 指向 window
- this 永远指向最后调用它的那个对象
- new 关键词改变了 this 指向
- apply call bind 可以改变 this 指向
- 箭头函数的 this 在定义时确定
- 匿名函数的 this 永远指向 window
one
全局对象的 this 指向 window 对象
var a = 1000;
var obj = {
a: 1,
b: this.a+1
}
console.log(obj.b);
普通对象 obj 没有 this,里面访问的 this指向 window 对象。上面的代码会输出 1001
two
全局函数执行 this 指向 window
function fun(){
console.log(this === window) //true
}
那么:
var name = "William";
function test(){
var name = "Lisi";
let a = {
name: this.name
}
console.log(a.name);
}
test();
这里要和构造函数区别开来。
test() 执行后,输出 William
three
this 永远指向最后调用它的那个对象 出自 juejin.cn/post/684490… 这句话适用于非箭头函数的调用。同前面2句是相通。
例子1
var a = 1000;
function fun(){
var obj = {
a: 1,
c: this.a + 2
}
return obj.c;
}
console.log(fun());
fun 函数执行的时候内部 this 指向 window,所以 obj.c = 1002; 输出 1002
例2
var name = "windowsName";
function fn() {
var name = 'Cherry';
innerFunction();
function innerFunction() {
console.log(this.name);
}
}
fn()
fn 函数执行的时候内部 this 指向 window。内部执行 innerFunction 函数,innerFunction 函数内部没有 this.name,随着作用域向上级查找,最终访问的是 window.name
例子3
let obj = {
name: "William",
say: function(){
console.log(this.name);
}
}
obj.say();
看到了吧,“this 永远指向最后调用它的那个对象”。say 函数在执行的时候,是被 obj 对象调用的,所以指向的是 obj 对象,this.name 访问的是 obj.name。
four
new 关键词改变了 this 指向
const a = 1000;
function f1(a, b){
this.a = a;
this.b = b;
console.log(this===window);
}
f1(); //true
const f = new f1(1,2); //false
console.log(f.a);
所以 new 操作符做了什么?
- 首先内部创建了一个空对象 obj
- 将新对象的__proto__指向构造函数的 prototype 对象
- 将构造函数的作用域赋值给新的对象(也就是this 指向新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回apply执行的结果或者返回对象
//模拟 new 代码
function create(fn, ...rest){
const obj = Object.create(fn.prototype);
const result = fn.apply(obj, rest);
if(typeof result === "object"){
return result;
}else{
return obj;
}
}
five
apply call bind 可以改变 this 指向 准确的说是改变非箭头函数的 this 指向。
let obj = {
name: "William",
say: function(){
console.log(this.name);
}
}
obj.say();
obj.say.apply({name: "Ben"})
six
箭头函数的 this 在定义时确定。 其实,箭头函数的特性还有很多,总结下:
- 箭头函数的 this 在定义时确定
- 箭头函数没有prototype属性,是更纯粹的函数。
- 箭头函数不能使用new 关键字。
- 箭头函数没有 arguments,可以使用 ...rest 解决参数不定长的问题
- apply call bind 不能修改箭头函数的 this
通过 Babel 将 ES6 的代码进行转义,可以看到箭头函数的 this 在定义的时候会进行一个转换,所以说箭头函数的 this 在定义的时候确定。
window.color = "red";
let color = "green";
// var color = "black";
let obj = {
color: "blue"
};
let sayColor = () => {
return this.color;
};
console.log(sayColor.apply(obj));
sayColor 是一个箭头函数,applay 是不起作用的,所以函数里面的this 没有被改变。这里箭头函数里面的 this 按照定义的时候 this 的指向的。定义 sayColor 的时候 this 指向全局 window。所以 this.color 指的是 window.color。同时还有一个知识点就是 let const 定义的变量并不会加载到 window 上面的。所以 let color并不会改变 window.color的值。
seven
匿名函数的 this 永远指向 window 没有变量指向匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window
什么是没有变量指向的匿名函数?比如:
setTimeout(function(){}, 100)
arr.forEach(function(){})
function(){ return function(){} }
里面的函数没有变量指向它们,这些函数执行的时候执行环境具有全局性,因此其 this 对象通常指向 window。
下面的代码中 obj.getNameFunc 指向了后面的匿名函数,所以obj.getNameFunc执行里面this 指向obj。箭头函数的出现解决了这一弊端。
var name = "The window";
var object = {
name: 'My Object',
getNameFunc: function () {
console.log(this);
return function () {
return this.name;
}
},
getName: function () {
return ()=>{
return this.name;
}
}
}
console.log(object.getNameFunc()());
console.log(object.getName()());
console.log(object.getNameFunc()()); 执行输出 "The window"
console.log(object.getName()()); 执行输出 "My Object"
易混淆的一些题目(持续更新)
题目1:
var name = "The Window";
let obj = {
name: "My Object",
say: function(){
console.log(this.name);
}
};
obj.say();
(obj.say)();
const fn = obj.say;
fn();
(obj.say=obj.say)();
obj.say(); 和 (obj.say)(); 效果是一样的。因为 obj.say(); 和 (obj.say)(); 的定义是相同的。
const fn = obj.say; 是一条赋值语句,赋值表达式的值是函数本身,所以 this 的值不能得到维护,结果就返回 "The Window"
同样, (obj.say=obj.say)(); 是先执行了赋值语句修改了 obj.say 的内容,赋值表达式的值是函数本身,所以 this 的值不能得到维护,结果就返回 "The Window"
题目2:
var a = function(){
this.b = 3;
}
var c = new a();
a.prototype.b = 9;
var b = 7;
a();
console.log(b);
console.log(c.b);
其中,a 函数是一个全局函数,不是某个对象的属性。所以执行 a();的时候, this 指向全局 window, 全局 b 变为 3。执行构造函数 new a(); 的时候,c.b = 3; 所以会输出两个 3
题目3:
var a = [1, 2, 3, 4];
for (var i = 0; i< a.length; i ++ ) {
setTimeout(() => {
console.log(a[i])
}, i * 1000);
}
当 for 循环执行完的时候,i 已经变成 4 了,那么 a[4] 为 undefined
最后
如果有错误或者不严谨的地方,烦请给予指正,十分感谢。如果喜欢或者有所启发,欢迎点赞,对作者也是一种鼓励。