普通函数的this
1. this总是代表它的直接调用者, 例如 obj.func ,那么func中的this就是obj
2. 调用的时候,没有任何前缀,则指向window,new的时候,指向new出来的对象。
3.在默认情况(非严格模式下,未使用 'use strict'),没找到直接调用者,则this指的是 window
4.在严格模式下,没有直接调用者的函数中的this是 undefined
5.使用call,apply,bind(ES5新增)绑定的,this指的是 绑定的对象
全局环境下
在全局环境下,this 始终指向全局对象(window), 无论是否严格模式
console.log(this.document===document) //true
console.log(this===window); //true
this.a=12;
console.log(window.a); //12
函数上下文调用
函数直接调用
普通函数内部的this分两种情况,严格模式和非严格模式。
非严格模式下,this 默认指向全局对象window
function f1(){
return this;
}
console.log( f1() === window ); // true
而严格模式下, this为undefined
function f2(){
"use strict"; // 这里是严格模式
return this;
}
console.log( f2() === undefined ); // true
对象中的this
对象内部方法的this指向调用这些方法的对象,
- 函数的定义位置不影响其this指向,this指向只和调用函数的对象有关。
- 多层嵌套的对象,内部方法的this指向离被调用函数最近的对象(window也是对象,其内部对象调用方法的this指向内部对象, 而非window)。
//1.
var prop=10
var o = {
prop: 20,
f: function() {
return this.prop;
}
};
console.log(o.f()); //20
//2.
var a = o.f; //此时“return this.prop;”中的this指向了window
console.log(a()); //10 如果window中没有prop变量,则返回undefined
//3.
var o = {prop: 30};
function independent() {
return this.prop;
}
console.log(o.f); //undefined,后面定义的对象o覆盖了之前的对象,此时f函数不存在
// console.log(o.f()); //o.f is not a function
o.f = independent; //此时,independent()函数中的this指向的是后面定义的o对象
console.log(o.f); //ƒ independent() {return this.prop;}
console.log(o.f()); // 30
//4.
function independent1() {
return this;
}
o.b = {
f:independent,
f1:independent1,
prop: 42
};
console.log(o.b.f()); // 42
console.log(o.b.f1()); // {f: ƒ, f1: ƒ, prop: 42},此时的this指向的是o.b对象,就近原则
原型链中this
原型链中的方法的this仍然指向调用它的对象,与以上讨论一致;看个例子,
var o = {
f : function(){
return this.a + this.b;
},
f1:function () {
return this;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
console.log(p.f1()); //{a: 1, b: 4}
console.log(o.f()); //NaN
console.log(o.f1()); //{f: ƒ, f1: ƒ}
可以看出, 在p中没有属性f,当执行p.f()时,会查找p的原型链,找到 f 函数并执行,但这与函数内部this指向对象 p 没有任何关系,只需记住谁调用指向谁。
以上对于函数作为getter & setter 调用时同样适用。
构造函数中this
构造函数中的this与被创建的新对象绑定。
注意:当构造器返回的默认值是一个this引用的对象时,可以手动设置返回其他的对象,如果返回值不是一个对象,返回this。
//1.
function C1(){
this.a = 37;
}
var o1 = new C1();
console.log(o1.a); // 37
//2.
function C2(){
return this;
}
var o2 = new C2();
console.log(o2); // C2{}
//3.
function C3(){
this.a = 37;
return {a:38};
}
var b = new C3();
console.log(b.a); // 38
以上两个例子内部的this都指向对象o, 看到这里的你不妨在控制台执行下以上代码,看看对象 o 和 b ,这些是属于构造函数的内容了,此处不多做介绍。(C3函数中的this.a = 37 对整个过程完全没有影响的, 可以被忽略的)。
DOM 事件处理函数中的 this & 内联事件中的 this
DOM事件处理函数
- 当函数被当做监听事件处理函数时, 其 this 指向触发该事件的元素 (针对于addEventListener事件)
function bluify(e) {
// 在控制台打印出所点击的元素
console.log(this);
// 阻止事件冒泡
e.stopPropagation();
// 阻止默认事件
e.preventDefault();
// 当前元素的背景颜色改变
this.style.backgroundColor="red"
}
var el=document.getElementsByTagName("div");
for(var i=0;i<el.length;i++){
el[i].addEventListener('click',bluify,false) //默认false,在冒泡阶段执行
}
内联事件
内联事件中的this指向分两种情况:
- 当代码被内联处理函数调用时,它的this指向监听器所在的DOM元素
- 当代码被包括在函数内部执行时,其this指向等同于 ****函数直接调用****的情况,即在非严格模式指向全局对象window, 在严格模式指向undefined
<button onclick="console.log(this);"></button>
<!--<button onclick="console.log(this);"></button>-->
<button onclick="(function() {console.log(this);})()"></button>
<!--window-->
<button onclick="(function () {'use strict';console.log(this);})()"></button>
<!--undefined-->
setTimeout & setInterval
对于延时函数内部的回调函数的this指向全局对象window(当然我们可以通过bind方法改变其内部函数的this指向)
//默认情况下代码
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this);
}, 1000);
}
Person() //window
var p = new Person(); //1秒后返回 window 对象
// 通过bind绑定
function Person1() {
this.age = 0;
setTimeout((function() {
console.log(this);
}).bind(this), 1000);
}
var p = new Person1(); //1秒后返回构造函数新生成的对象 Person{...}
Person1(); //window
例子:
eg01:
解析:谁调用的函数,函数体中的this就指向谁
eg02:
解析:这次this指向了最外层的window对象,为什么呢,还是那个道理,这次this出现在全局函数setTImeout()中的匿名函数里,并没有某个对象进行显示调用,所以this指向window对象,因此调用了window中num的值
eg03:
var obj = {
func: function() {
console.log(this); //Object
},
say: function () {
var that = this; //此时的this就是obj对象
setTimeout(function () {
console.log(this) //Window
that.func()
});
}
}
obj.say();
解析:匿名函数,定时器中的函数,由于没有默认的宿主对象,所以默认this指向window, 如果想要在setTimeout中使用这个对象的引用,需要先将this存起来,我们通常使用that = this, 或者 _this = this来保存我们需要的this指针!
eg04:
window.val = 1;
var obj = {
val: 2,
dbl: function () {
console.log(val); //1
console.log(this.val); //2
this.val *= 2;
val *= 2;
console.log(val); //2
console.log(this.val); //4
}
};
obj.dbl();
解析:
val变量在没有指定对象前缀,默认从所属的函数中找(即从db1函数中找,该函数作用域内没有val变量),找不到则从window中找全局变量
即 val *=2 就是 window.val *= 2
this.val默认指的是 obj.val ;因为 dbl()第一次被obj直接调用
eg05:
window.val = 1;
var obj = {
val: 2,
dbl: function () {
console.log(this); //Window
console.log(val); //1
console.log(this.val); //1
this.val *= 2;
val *= 2;
console.log(val); //4
console.log(this.val); //4
}
};
var func=obj.dbl;
func();
归纳:
func() 没有任何前缀,类似于全局函数,即 window.func调用,所以此时调用的时候, this指的是window, val指的是window.val,this.val也指的是window.val
**********************************************************************************************************************************************************
箭头函数的this
默认指向在定义它时,它所处的对象,而不是执行时的对象, 定义它的时候,可能环境是window(即继承父级的this);
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或 new.target。这些函数表达式更适用于那些本来需要匿名函数的地方,并且它们不能用作构造函数。
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this
。因此,在下面的代码中,传递给setInterval
的函数内的this
与封闭函数中的this
值相同:
function Person(){
this.age = 0;
setTimeout(() => {
this.age++; // this 指向 Person
console.log(this); //指向Person ,且age=1
}, 1000);
}
var p = new Person();
console.log(p); //指向Person ,且 age=0
eg01:
var obj = {
say: function () {
setTimeout(() => {
console.log(this)
});
}
}
obj.say(); // obj
此时的this是定义它的对象,即继承了父级的this,父级中的this指的是obj,而非window
eg02:
var obj = {
say: function () {
var f1 = () => {
console.log(this); // obj
setTimeout(() => {
console.log(this); // obj
})
}
f1();
}
}
obj.say()
f1继承父级this指代的obj,不管f1有多层箭头函数嵌套,都是obj.
eg03:
var obj = {
say: function () {
var f1 = function () {
console.log(this); // window, f1调用时,没有宿主对象,默认是window
setTimeout(() => {
console.log(this); // window
})
};
f1();
}
}
obj.say()
第一个this:f1调用时没有宿主对象,默认是window
第二个this:继承父级的this,父级的this指代的是window.
eg04:
//1
var x=1;
var obj={
x:2,
say:()=>{
console.log(this); // window
console.log(this.x); // 1
}
}
obj.say();
//2
var a=3;
function test(){
this.a=4;
var fn=()=>{
console.log(this); //test
console.log(this.a) //4
}
fn();
}
var x=new test();
//3
var b=5;
function test2(){
this.b=6;
let func=()=>{
console.log(this); //window
console.log(this.b) //6
}
func();
}
test2();
第一个:ES6中定义的时候绑定this的继承的是父执行上下文里面的this,普通对象(非函数是,没有执行上下文的),因此第一个的this指向了window
第二个:this指向new出来的对象,即test
第三个:test2() 没有任何前缀,类似于全局函数,即 window.test2调用,所以此时调用的时候, this指的是window,
执行上下文(Execution Context)。
执行上下文可以理解为函数执行的环境,每一个函数执行时,都会给对应的函数创建这样一个执行环境。
每次当控制器转到可执行代码的时候,就会进入一个执行上下文。执行上下文可以理解为当前代码的执行环境,它会形成一个作用域。JavaScript中的运行环境大概包括三种情况。
- 全局环境:JavaScript代码运行起来会首先进入该环境
- 函数环境:当函数被调用执行时,会进入当前函数中执行代码
- eval(不建议使用,可忽略)
因此在一个JavaScript程序中,必定会产生多个执行上下文,JavaScript引擎会以栈的方式来处理它们,这个栈,我们称其为函数调用栈(call stack)。栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。当代码在执行过程中,遇到以上三种情况,都会生成一个执行上下文,放入栈中,而处于栈顶的上下文执行完毕之后,就会自动出栈。
作者:这波能反杀
链接:https://www.jianshu.com/p/a6d37c77e8db
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
参考:
https://www.cnblogs.com/lemonmonster/p/8482232.html