this 关键字在 Javascript 中很是常见,可是不少开发者很难说清它到底指向什么。大部分人会从字面意思上去理解 this,认为 this 指向函数自身,实际上this 是在运行时进行绑定的,并非在编写时绑定,它的上下文取决于函数调 用时的各类条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。javascript
总结: 函数被调用时发生 this 绑定,this 指向什么彻底取决于函数在哪里被调用。java
this 一共有 4 中绑定规则,接下来一一介绍每种规则的解释和规则直接的优先级markdown
独立函数调用app
function foo() { console.log(this.a); } var a = 2; foo(); // 2 复制代码
严格模式下:函数
function foo() { "use strict"; console.log(this); //undefined console.log(this.a); //Uncaught TypeError: Cannot read property 'a' of undefined } var a = 2; foo(); 复制代码
注意下边两种状况this
var age = "18"; var obj = { name: "heyushuo", age: 25, fn: function() { function sayName() { console.log(this); //window console.log(this.age); //undefined } sayName(); } }; obj.fn(); 复制代码
函数 sayName 虽然是在 obj.fn 内部定义的,可是它仍然是一个独立函数调用,this 仍然指向 window。spa
var a = "global"; var obj = { a: 2, foo: function() { console.log(this.a); //global } }; var bar = obj.foo; // 函数别名! bar(); 复制代码
虽然 bar 是 obj.foo 的一个引用,可是实际上,它引用的是函数自己,所以此时的 bar() 实际上是一个不带任何修饰的独立函数调用,所以应用了默认绑定。prototype
当函数引用有上下文对象时(例如:obj.foo 这个时候使用 obj 上下文来引用函数 foo),隐式绑定规则会把函数中的 this 绑定到这个上下文对象。code
var obj = {
name: "heyushuo,
foo: function() {
console.log(this.name); //heyushuo
}
};
obj.foo();
复制代码
对象属性引用链中只有上一层或者说最后一层在调用中起做用。orm
var obj = { name: "heyushuo", obj1: { name: "kebi", foo: function() { console.log(this.name); // kebi } } }; obj.obj1.foo(); 复制代码
隐式丢失 被隐式绑定的函数会丢失绑定对象,而应用默认绑定,把 this 绑定到全局对象或者 undefined(严格模式) 上。 第一种
var a = "global"; var obj = { a: 2, foo: function() { console.log(this.a); //global } }; var bar = obj.foo; // 函数别名! bar(); 复制代码
虽然 bar 是 obj.foo 的一个引用,可是实际上,它引用的是函数自己,所以此时的 bar() 实际上是一个不带任何修饰的独立函数调用,所以应用了默认绑定。
第二种传入回调函数时:
var a = "global"; var obj = { a: 2, foo: function() { console.log(this.a); //global } }; var bar = obj.foo; // 函数别名! function doFoo(fn) { fn(); // <-- 调用位置! } doFoo(bar); //global //和下边这种同样 setTimeout(obj.foo, 300); 复制代码
经过 call() 或者 apply()方法。第一个参数是一个对象,在调用函数时将这个对象绑定到 this 上,称之为显示绑定。
function foo() { console.log(this.a); } var obj = { a: 2 }; foo.call(obj); // 2 复制代码
显示绑定引伸出来一个硬绑定,代码以下
function foo(something) { console.log( this.a, something ); return this.a + something; } // 简单的辅助绑定函数 function bind(fn, obj) { return function() { return fn.apply( obj, arguments ); //内部已经强制绑定了传入函数this的指向 }; } var obj = { a:2 }; var bar = bind( foo, obj ); var b = bar( 3 ); // 2 3 console.log( b ); // 5 复制代码
bar函数不管如何调用,它总会手动在 obj 上调用 fn,强制把 fn 的 this 绑定到了 obj。这样也解决前面提到的丢失绑定问题
因为硬绑定是一种很是经常使用的模式,因此在 ES5 中提供了内置的方法 Function.prototype.bind
function foo(something) { console.log( this.a, something ); return this.a + something; } var obj = { a:2 }; var bar = foo.bind( obj ); var b = bar( 3 ); // 2 3 console.log( b ); // 5 复制代码
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操做。
例如:
function foo() { this.name = "heyushuo"; this.age = 25 } foo.prototype.sayName = function(){ console.log(this.name+this.age); } var bar = new foo(); console.log(bar); //{name: "heyushuo", age: 25} //这个新对象会绑定到函数调用的 this。因此此时的this就是bar对象 console.log( bar.age ); // 25 复制代码
以下图是 new foo() 这个对象
判断this,能够按照下面的顺序来进行判断:
var bar = new foo() 复制代码
var bar = foo.call(obj2) 复制代码
var bar = obj1.foo() 复制代码
var bar = foo() 复制代码