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); //undefinedconsole.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 ); // 3bar.call( obj2 ); // 3
function foo() {console.log( this.a );}var a = 2;foo.call( null ); // 2foo.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(); //2foo.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绑定过程各种规则带来的复杂性。
