This的含义
首先,this在构造函数中可以表示实例对象。
function Person(name,gender){
this.name = name;
this.gender= gender;
}
而在其他地方,this则表示属性和方法当前所在的对象。this
的值就是在点之前的这个对象,即调用该方法的对象。
不管在什么场合,This总是返回一个对象!
例子:方法中的this
let user = {
name: "John",
age: 30,
sayHi() {
// "this" 指的是“当前的对象”
alert(this.name);
}
};
user.sayHi(); // John
这里调用sayHi()
的点之前的对象是user,因此this指向user。
结论:以“方法”的语法调用函数时:object.method(),调用过程中的 this 值是 object。
因为对象的属性可以赋值给另一个对象,所以this的指向也是可以改变的。
var A = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe;
B.describe() // "姓名:李四"
B.describe
的属性是从A
得到的。此时B.describe
中this的指向就指向了自己。B也就变成了这样:
var B = {
name: '李四'
describe: function () {
return '姓名:'+ this.name;
}
};
上面的代码也可以这样写:
function f() {
return '姓名:'+ this.name;
}
var A = {
name: '张三',
describe: f
};
var B = {
name: '李四',
describe: f
};
A.describe() // "姓名:张三"
B.describe() // "姓名:李四"
// f中this的指向不同,结果不同。
但是要注意的是,“复杂”调用中的this会失去目标。
var obj ={
foo: function () {
console.log(this);
}
};
obj.foo() // obj
(obj.foo = obj.foo)() // window
(false || obj.foo)() // window
(1, obj.foo)() // window
后三种调用的运行环境都是全局环境,因此this指向了window
。
可以这样理解,JavaScript 引擎内部,obj和obj.foo储存在两个内存地址,称为地址一和地址二。obj.foo()这样调用时,是从地址一调用地址二,因此地址二的运行环境是地址一,this指向obj。但是,上面后三种情况,都是直接取出地址二进行调用,这样的话,运行环境就是全局环境,因此this指向全局环境。后三种情况等同于下面的代码。
// 情况一
(obj.foo = function () {
console.log(this);
})()
// 等同于
(function () {
console.log(this);
})()
// 情况二
(false || function () {
console.log(this);
})()
// 情况三
(1, function () {
console.log(this);
})()
如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。
var a = {
p: 'Hello',
b: {
m: function() {
console.log(this.p);
}
}
};
a.b.m() // undefined
因为a.b.m
方法在a
对象的第二层,这时方法中的this指向了a.b
。所以它找不到a
中的p属性。也就是说,上面的a.b.m() == (a.b).m()
。
使用this时的其它注意事项
- 避免多层this
在函数中多层使用this会导致深层this指向不明。
var o = {
f1: function () {
console.log(this);
var f2 = function () {
console.log(this);
}();
}
}
o.f1()
// Object
// Window
上面的代码实际上可以视为这样
var temp = function () {
console.log(this);
};
var o = {
f1: function () {
console.log(this);
var f2 = temp();
}
}
解决方法有二。一是用一个临时变量来记住this,二是使用箭头函数。
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = function() {
console.log(that);
}();
}
}
o.f1()
// Object
// Object
箭头函数的this指向自己的上一层的this。下例中f2的this就指向了f1的this。
var o = {
f1: function() {
console.log(this);
var f2 = ( () => console.log(this) )();
}
}
o.f1()
// Object
// Object
顺便一提,如果使用了JS的严格模式。若函数内部的this指向了顶层对象,就会直接报错。
- 在处理数组时使用this。
数组的map
和foreach
方法,允许提供一个函数作为参数。这个函数内部不应该使用this。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
});
}
}
o.f()
// undefined a1
// undefined a2
同样的,里面的this指向了window对象。
解决方法之一同上,用一个中间变量记住this。还有一个方法是把this作为foreach的第二个参数,固定它的运行环境。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
}, this);
}
}
//也可以用箭头函数改写:
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
this.p.forEach((item) => {
console.log(this.v + ' ' + item);
});
}
}
o.f()
// hello a1
// hello a2
3.不在回调函数里用this
回调函数中的this往往会改变指向,最好避免使用。
var o = new Object();
o.f = function () {
console.log(this === o);
}
// jQuery 的写法
$('#button').on('click', o.f);
上面代码中,点击按钮以后,控制台会显示false。原因是此时this不再指向o对象,而是指向按钮的 DOM 对象,因为f方法是在按钮对象的环境中被调用的。这种细微的差别,很容易在编程中忽视,导致难以察觉的错误。
为了解决这个问题,可以采用一些方法对this进行绑定,也就是使得this固定指向某个对象,减少不确定性。`