javascript-七个栗子深入理解this指向
作者:互联网
在了解this之前有必要了解一下-函数的生命周期
在调用一个函数的时候是先经历函数的创建然后再到执行的过程。而this的指向是再创建阶段就已经完成了。关于创建及执行的阶段做的事情看下面图图:
我曾写过这样一篇知识文章-点击传送
例如person.getName();当执行到这行代码时就会经历函数的创建执行。
执行上下文生命周期 | 描述 |
---|---|
创建 | 上下文分别创建变量对象,确认作用域链,以及确定this指向 |
执行 | 创建阶段之后,则执行代码,这个时候会完成变量赋值,函数引用,及其他代码 |
在创建函数的时候就确定this及作用域这些操作【对应上js是一门词法分析这个知识点是相互呼应的】,意思就是this在创建阶段就已经确认了指向,而不受函数内部代码所影响。
但也不是说函数在定义阶段就能够确定的喔。准备的说一个函数是经过定义 - 调用后再经历创建-执行的阶段。
---------------------------------人工分割线-------------------------------------------
5个例子完美解析this解析
栗子一:
function a() {
console.log('1')
}
a();
先说一个知识点:谁调用this指向谁。
那么a()是谁调用勒? 其实在非严格模式下这种调用方式会默认的加上window.a();而在严格模式下这种调用方式会报错。那么你该明白a内部如果使用this,其实就是相当于指向window对象
栗子二:
var a = 1;
function fn() {
var a = 2;
console.log(a); // 2
console.log(this.a); // 1
}
fn(); // 非严格模式下相当于 window.fn();
再强调一遍:谁调用this就指向谁
栗子三:
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
因为j()实际上是window.j();所以j()函数再执行的时候this指向window。不要被表面所迷惑,记住金句:谁调用this就指向谁
栗子四:new关键字下的this
function Person(){
this.name = "tony";
}
var tony = new Person();
console.log(tony.name); //tony
为什么tony为什么会有个name属性?
明白this.name = 'tony'这句代码为什么作用到了tony对象身上?
new关键字的特殊性,因为new关键字实际上做了以下的操作:
- 先创建空的、新的实例对象(New(func)返回的对象)
- 将实例对象的原型指向构造函数的原型
- 将构造函数的this改为指向实例对象 此时这个实例对象就是tony,并且this改成指向tony了
- 最后返回该实例对象(特殊情况:如果构造函数本身有返回,并且返回的是对象或者函数,则直接根据构造函数的返回)
至于怎么实际上是怎么修改了this指向这个问题稍后再谈!
栗子五:
箭头函数对this指向的特殊性
引用阮一峰老师在ES6的书籍中对箭头函数做出的解释:
函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
这句话老实说,看完之后总是似懂非懂的感觉。但能够确定的是原来的:函数由谁调用this就指向谁。这个在这里不适用。
并且需要弄清楚的一点:怎么找到《函数定义所在对象》?
// ES6
const obj = {
getArrow() {
return () => {
console.log(this);
};
}
}
当执行时发生了什么?
let arrowFn = obj.getArrow(); // 也就是arrowFn此时就会绑定obj对象。
let arrowFn2 = obj.getArrow.call(window); // arrowFn此时就会绑定window对象
一开始我出现过错误的看法:
const obj = {
getArrow() {
return () => {
console.log(this);
};
}
}
我以为仅从这段代码种便能确认this的执行,但实际结果很显然证明我是错误的。
结合到:== “ 函数getArrow在定义时所在的对象,而不是使用时所在的对象 ” ==
这句话得问题在于:
- 函数在何时发生定义?
- 函数在发生定义时的上下文this是什么?
// 此时的this是window
console.log(this); // this => window
const obj = {
getArrow() {
return () => {
console.log(this);
};
}
}
-
混淆点1:箭头函数的定义难道不是在this=window的下面进行的?
答案:不是,此时箭头函数实际上还未定义。定义实际上发生在getArrow()方法在被调用时才开始定义也就是执行代码obj.getArrow()时,当执行到return ()=>{console.log(this)}此时才会去寻找this并绑定。所以说如果在执行:return ()=>{console.log(this)}时的this是谁,箭头函数指向的this就是谁。 -
混淆点2:箭头函数的定义为什么不一定是obj对象?
答案:混淆点1的答案就能足够解释。这个例子理解的关键是箭头函数:return ()=>{console.log(this)}在什么时候发生定义?答案就是执行到这一句代码的时候,而不是写在obj里面最后绑定的就一定是obj。网上有很多有可能看着类似这样不太正确的解释。 -
混淆点3:函数定义一定在执行的时候就发生吗?
答案:这...下面再举例来解答这个问题。
栗子六:
let fnObj = {
getArrow : () => {
console.log(this)
}
}
fnObj.getArrow(); // 打印: window
那么,getArrow方法在什么时候发生定义?
在执行到:
let fnObj = {
getArrow : () => {
console.log(this)
}
}
主代码执行这句代码的时候getArrow发生定义。此时的this是window,那么getArrow箭头函数则绑定window。(箭头函数本身没有this,那么他在定义是会寻找他在定义时的this来作为自己的this)
栗子七:
var id = 'window';
let foo = {
id : 'foo',
arrow: function() {
setTimeout(() => {
console.log(' arrow id:', this.id);
}, 100);
},
fn: function() {
setTimeout(function() {
console.log(' fn id:', this.id);
}, 100);
},
}
foo.arrow(); // 思考打印 ??
foo.fn(); // 思考打印 ??
-
先说foo.fn() 答案: 打印window
原因:setTimeout函数是由window调用执行,所以打印window很好理解。 -
先说foo.arrow() 答案: 打印foo
原因:理解这个例子的关键在于理解:setTimeout中的匿名函数参数在什么时候定义?我相信换个写法就能够理解这个问题。
fn: function() {
setTimeout(() => {
console.log(' arrow id:', this.id);
}, 100);
},
// ---》等价于下面写法
fn: function() {
// arrowFn箭头函数的指向不懂的再回去看案例4
let arrowFn = () => {
console.log(' arrow id:', this.id);
}
setTimeout(arrowFn, 100);
},
不要被表面迷惑了。将问题转心在:箭头函数在发生定义时所在的上下文this。
---------------------------------Ending-------------------------------------------
- 有错误的非常感谢能够慷慨指出
- 写博客挺累人,但对于自己梳理知识点确非常有用
- 在写的过程中也找到了平常没留意的边边角角
标签:七个,console,log,指向,getArrow,javascript,window,栗子,函数 来源: https://www.cnblogs.com/bigname/p/13789868.html