结论
- 普通函数中this指向函数的对象
- 箭头函数中的this指向该函数所在作用域指向的对象
关于作用域是什么,可以自行Google一下,后面会用例子说明
1.普通函数
代码:
function test(){
var a = 1;
console.log(this.a);
}
test(); //undefined
由于test函数是直接在外层函数运行的,所以test是被window对象调用的,所以这里的 this.a 对应的应该是全局变量而不是test函数里的局部变量,故输出undefined。
this在此处的一个应用
在一般函数方法中使用 this 指代全局对象,从而添加全局变量
function a() {
this.x = "test";
}
a(); //全局对象调用a,使得里面的this指向window
function b() {
console.log(this.x); //test
}
b();
console.log(x); //test
此处在函数a中用this添加了全局对象a,使得在函数b以及外层环境中都能访问到a
2.构造函数
构造函数中的this,指向其new出来的对象
new做了什么
-
创建了一个对象
-
把这个实例对象连接到了原型链上
-
改变了构造函数的this指向
-
把实例对象的地址赋值给左边变量
function Foo(){
this.x = 10;
console.log(this); //Foo {x:10}
}
var foo = new Foo();
console.log(foo.x); //10
console.log(foo); //Foo {x:10}
可以看到Foo里面打印的this与对象foo一样
构造函数 prototype 属性
在 Foo.prototype.getX 函数中,this 指向的 foo 对象。不仅仅如此,即便是在整个原型链中,this 代表的也是当前对象的值。
function Foo(){
//B
console.log(this); //Foo {}
this.x = 10;
console.log(this); //Foo { x: 10 }
}
Foo.prototype.getX = function () {
//E
console.log(this); //Foo {x: 10}
console.log(this.x); //10
}
var foo = new Foo(); //A
console.log(foo); //C Foo { x: 10 }
foo.getX(); //D
执行顺序A-B-C-D-E,在执行A后,进入Foo代码块B,此时this已经指向了对象foo(与C出结果一样),不过此时this.x还没挂载上去,所以看不到this.x,后跳出B代码块后进入C,打印obj对象,后进入D,调用obj原型上的getX,最后可以看到进入E后,obj原型上的this也和obj相同,打印的this.x为10。
拓展
在其原型的getX方法中修改this.x
function Foo(){
this.x = 10;
console.log(this); //Foo {x:10}
}
var foo = new Foo();
console.log(foo.x); //10
console.log(foo); //Foo {x:10}
function Foo(){
console.log(this); //Foo {}
this.x = 10;
console.log(this); //Foo { x: 10 }
}
Foo.prototype.getX = function () {
console.log(this); //Foo {x: 10}
this.x = 20;
console.log(this); // Foo { x: 20 }
console.log(this.x); //20
}
var foo = new Foo();
console.log(foo); //Foo { x: 10 }
foo.getX();
console.log(foo); //Foo { x: 20 }
console.log(foo.x); //在原型对象上修改之后,打印得20
在obj原型的方法上调用this,修改x,后打印的结果也是修改后的结果,表明在整个原型链中,this也是指向当前对象
对象中
如果函数作为对象的方法时,方法中的 this 指向该对象。
var obj = {
x: 10,
foo: function () {
console.log(this); //{x: 10, foo: ƒ}
console.log(this.x); //10
}
};
obj.foo();
拓展1:方法内再定义函数
若在对象方法中再定义函数,则该函数指向全局window。因为此时fn()为全局调用。
//当vscode node运行js文件时,浏览器控制台运行打印this.x仍为undefind(var x改为x即正常),但是this指向正确
var x = 25;
//在VS code输出undefined
var obj = {
x: 10,
foo: function () {
function f(){
// console.log(this); //Window
console.log(this.x + "kkk"); //25
}
f();
}
}
obj.foo();
若想要改正上述问题,把this,绑定给self即可
var x = 25;
var obj = {
x: 10,
foo: function () {
var self = this;
function f(){
console.log(self); //{ x: 10, foo: [Function: foo] }
console.log(self.x); //10
}
f();
}
}
obj.foo();
拓展2:匿名函数在对象中的使用
var nameH = 'China';
var obj = {
name : 'America',
show : function() {
return function(){
console.log(this.nameH);
}
}
}
var a = obj.show();
a(); // China
返回匿名函数时,相当于
var a = function(){
console.log(this.name);
}
调用对象为全局window
在DOM元素中
在一个 HTML DOM 事件处理程序里,this 始终指向这个处理程序所绑定的 HTML DOM 节点,就相当于是给函数传参,使 其运行时上下文改变了。
此处较少见,代码略,见情况六:DOM event this
箭头函数中
箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。
var nameH = 'window';
var A = {
name: 'A',
sayHello: () => {
console.log(this.nameH)
}
}
A.sayHello(); //window
因为sayHello所在的作用域其实是最外层的js环境,因为没有其他函数包裹;然后最外层的js环境指向的对象是winodw对象,所以这里的this指向的是window对象。
var obj = {
x: 10,
foo: function() {
var fn = () => {
return () => {
return () => {
console.log(this); //Object {x: 10}
console.log(this.x); //10
}
}
}
fn()()(); //A
}
}
obj.foo();
因为最里层返回的箭头函数,不管嵌套多少层,他都是在A处运行,即在fn()里面,所以this指向obj,所以this.x等于10
call、apply或者 bind 改变this
var obj = {
x: 10
}
function foo(){
console.log(this); //{x: 10}
console.log(this.x); //10
}
foo.call(obj);
foo.apply(obj);
foo.bind(obj)();
可以看到apply等可以改变普通函数的this指向,但是无法改变箭头函数中的指向,如下所示。
var A = {
name: 'A',
sayHello: function(){
var s = () => console.log(this.name)
return s//返回箭头函数s
}
}
var sayHello = A.sayHello();
sayHello();// 输出A
var B = {
name: 'B'
}
sayHello.call(B); //还是A
sayHello.call(); //还是A