Javascript this 关键字:
面向对象语言中 this 表示当前对象的一个引用。但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象。
函数中使用 this(默认)
在函数中,函数的所属者默认绑定到 this 上。
在浏览器中,window 就是该全局对象为 [object Window]:
function myFunction() {
return this;
}
函数中使用 this(严格模式)
严格模式下函数是没有绑定到 this 上,这时候 this 是 undefined。
"use strict";
function myFunction() {
return this;
}
var o = {
user:"追梦子",
fn:function(){
console.log(this.user); //追梦子
}
}
window.o.fn(); // 相同于:o.fn();
var o = {
a:10,
b:{
fn:function(){
console.log(this.a); //undefined
}
}
}
o.b.fn();
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行,所以最终指向的是window。
function Fn(){
this.user = "追梦子";
}
var a = new Fn();
console.log(a.user); //追梦子
这里之所以对象a可以点出函数Fn里面的user是因为new关键字可以改变this的指向,将这个this指向对象a,为什么我说a是对象,因为用了new关键字就是创建一个对象实例。
this碰到return时:
new 后面的构造函数可以不加括号。
// 例子1:
function fn()
{
this.user = '追梦子';
return {};
}
var a = new fn();
console.log(a.user); //undefined
// 例子2:
function fn()
{
this.user = '追梦子';
return function(){};
}
var a = new fn();
console.log(a.user); //undefined
// 例子3:
function fn()
{
this.user = '追梦子';
return 1;
}
var a = new fn();
console.log(a.user); //追梦子
// 例子4:
function fn()
{
this.user = '追梦子';
return undefined;
}
var a = new fn();
console.log(a.user); //追梦子
如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。
**
还有一点就是虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。
function fn()
{
this.user = '追梦子';
return null;
}
var a = new fn;
console.log(a.user); //追梦子
知识点补充:
1.在严格版中的默认的this不再是window,而是undefined。
2.new操作符会改变函数this的指向问题,虽然我们上面讲解过了,但是并没有深入的讨论这个问题,网上也很少说,所以在这里有必要说一下。
function fn(){
this.num = 1;
}
var a = new fn();
console.log(a.num); //1
注意:在严格模式下(strict mode),全局对象将无法使用默认绑定,即执行会报undefined的错误.
function foo() {
"use strict";
console.log( this.a );
}
var a = 2;
foo(); // Uncaught TypeError: Cannot read property 'a' of undefined
function foo() {
console.log( this.a );
}
var a = 2;
var obj = {
a: 3,
foo: foo
};
setTimeout( obj.foo, 100 ); // 2
同样的道理,虽然参传是obj.foo
,因为是引用关系,所以传参实际上传的就是foo对象本身的引用。对于setTimeout
的调用,还是 setTimeout -> 获取参数中foo的引用参数 -> 执行 foo 函数,中间没有obj的参与。这里依旧进行的是默认绑定。
function foo() {
console.log( this.a );
}
var a = 2;
var obj1 = {
a: 3,
};
var obj2 = {
a: 4,
};
var bar = function(){
foo.call( obj1 );
}
setTimeout( bar, 100 ); // 3
bar.call( obj2 ); // 3
function foo() {
console.log( this.a );
}
var a = 2;
foo.call( null ); // 2
foo.call( undefined ); // 2
这种情况主要是用在不关心this的具体绑定对象(用来忽略this),而传入null实际上会进行默认绑定,导致函数中可能会使用到全局变量,与预期不符。
所以对于要忽略this的情况,可以传入一个空对象ø,该对象通过Object.create(null)创建。这里不用{}的原因是,ø是真正意义上的空对象,它不创建Object.prototype委托,{}和普通对象一样,有原型链委托关系。
扩展:箭头函数
最后,介绍一下ES6中的箭头函数。通过“=>”而不是function创建的函数,叫做箭头函数。它的this绑定取决于外层(函数或全局)作用域。
function foo(){
console.log( this.a );
}
var a = 2;
var obj = {
a: 3,
foo: foo
};
obj.foo(); //3
var foo = () => {
console.log( this.a );
}
var a = 2;
var obj = {
a: 3,
foo: foo
};
obj.foo(); //2
foo.call(obj); //2 ,箭头函数中显示绑定不会生效
function foo(){
return function(){
console.log( this.a );
}
}
var a = 2;
var obj = {
a: 3,
foo: foo
};
var bar = obj.foo();
bar(); //2
function foo(){
return () => {
console.log( this.a );
}
}
var a = 2;
var obj = {
a: 3,
foo: foo
};
var bar = obj.foo();
bar(); //3
通过上面两个列子,我们看到箭头函数的this绑定只取决于外层(函数或全局)的作用域,对于前面的4种绑定规则是不会生效的。它也是作为this机制的一种替换,解决之前this绑定过程各种规则带来的复杂性。