原文连接javascript
与其余语言相比,函数的this
关键字在JavaScript中的行为略有不一样。而且它在严格模式和非严格模式之间也有一些区别。html
在绝大多数状况下,函数的调用方式决定了this
的值。this
不能在执行期间被赋值,在每次函数被调用时this
的值也可能会不一样。ES5引入了bind
方法来设置函数的this
值,而不用考虑函数如何被调用的。java
var name = "caibaojian.com"; var person = { name: "kang", pro: { name: "Michael", getName: function() { return this.name; } } }; console.log(person.pro.getName()); // Michael var pepole = person.pro.getName; console.log(pepole()); // caibaojian.com
var name = "caibaojian.com"; var person = { name: "kang", pro: { name: "Michael", getName: function() { console.log(this); return this.name; } } }; console.log(person.pro.getName()); // Object { name: "...", getName: () }, Michael var pepole = person.pro.getName; console.log(pepole()); // Window, caibaojian.com
'use strict'; var name = "caibaojian.com"; var person = { name: "kang", pro: { name: "Michael", getName: function() { console.log(this); return this.name; } } }; console.log(person.pro.getName()); // Object { name: "...", getName: () }, Michael var pepole = person.pro.getName; console.log(pepole()); // undefined
var name = "caibaojian.com", person = { name : "kang", getName : function(){ return function(){ return this.name; }; } }; console.log(person.getName()()); // caibaojian.com
this
的用法:在全局运行上下文中(在任何函数体外部),this
指代全局对象,不管是否在严格模式下。在浏览器中,全局对象为window
对象。面试
console.log(this.document === document); // true console.log(this === window); // true this.a = 37; console.log(window.a); // 37
在函数内部,this
的值取决于函数是如何调用的。浏览器
在非严格模式下,this
的值不会在函数执行时被设置,此时的this
的值会默认设置为全局对象。app
function foo(){ return this; } foo() === window; // true
在严格模式下,this
将保持他进入执行环境时的值,因此下面的this
将会默认为undefined
函数
function foo(){ "use strict"; // 严格模式 return this; } foo() === undefined; // true
在严格模式下,若是this
未被执行的上下文环境定义,那么它将会默认为undefined
。this
this
当以对象里的方法的方式调用函数时,它们的this
是调用该函数的对象。prototype
下面的例子中,当obj.f()
被调用时,函数内的this
将绑定到obj
对象。code
var obj = { prop: 37, foo: function() { return this.prop; } }; console.log(obj.foo()); // 37
注意,在何处或者如何定义调用函数彻底不会影响到this
的行为。
在上一个例子中,咱们在定义obj
的时候为其成员foo
定义了一个匿名函数。可是,咱们也能够首先定义函数而后再将其附属到obj.foo
。这样作this
的取值也和上面一致:
var obj = { prop: 37 }; function independent() { return this.prop; } obj.foo = independent; console.log(obj.foo()); // 37
这说明this
的值只与函数foo
做为obj
的成员被调用有关系。
相似的,this
的绑定只受最靠近的成员引用的影响。
在下面的这个例子中,咱们把一个方法g
看成对象obj.b
的函数调用。在此次执行期间,函数中的this
将指向obj.b
。事实上,这与对象自己的成员没有多大关系,最靠近的引用才是最重要的。
var obj = { prop: 37 }; function independent() { return this.prop; } obj.b = { g: independent, prop: 42 }; console.log(obj.b.g()); // 42
this
相同的概念在定义在原型链中的方法也是一致的。若是该方法存在于一个对象的原型链上,那么this
指向的是调用这个方法的对象,表现得好像是这个方法就存在于这个对象上同样。
var obj = { f : function(){ return this.a + this.b; } }; var p = Object.create(obj); p.a = 1; p.b = 4; console.log(p.f()); // 5
在这个例子中,对象p
没有属于它本身的f
属性,它的f
属性继承自它的原型。可是这对于最终在obj
中找到f
属性的查找过程来讲没有关系;查找过程首先从p.f
的引用开始,因此函数中的this
指向p
。也就是说,由于f
是做为p
的方法调用的,因此它的this
指向了p
。这是JavaScript的原型继承中的一个有趣的特性。
getter
与setter
中的this
相同的概念也适用时的函数做为一个getter
或者setter
调用。做为getter
或setter
函数都会绑定this
到从设置属性或获得属性的那个对象。
function modulus(){ return Math.sqrt(this.re * this.re + this.im * this.im); } var obj = { re: 1, im: -1, get phase(){ return Math.atan2(this.im, this.re); } }; Object.defineProperty(obj, 'modulus', { get: modulus, enumerable: true, configurable: true }); console.log(obj.phase, obj.modulus); // -0.785 1.414
this
当一个函数被做为一个构造函数来使用(使用new
关键字),它的this
与即将被建立的新对象绑定。
注意:当构造器返回的默认值是一个this
引用的对象时,能够手动设置返回其余的对象,若是返回值不是一个对象,返回this
。
function Fn(){ this.a = 37; } var obj = new Fn(); console.log(obj.a); // 37 function Foo(){ this.a = 37; return { a: 38 }; } obj = new Foo(); console.log(obj.a); // 38
call
和apply
当一个函数的函数体中使用了this
关键字时,经过全部函数都从Function
对象的原型中继承的call()
方法和apply()
方法调用时,它的值能够绑定到一个指定的对象上。
function add(c, d){ return console.log(this.a + this.b + c + d); } var obj = { a: 1, b: 3 }; add.call(obj, 5, 7); // 1 + 3 + 5 + 7 = 16 add.apply(obj, [10, 20]); // 1 + 3 + 10 + 20 = 34
使用call
和apply
函数的时候要注意,若是传递的this
值不是一个对象,JavaScript将会尝试使用内部 ToObject
操做将其转换为对象。所以,若是传递的值是一个原始值好比7
或foo
,那么就会使用相关构造函数将它转换为对象,因此原始值7
经过new Number(7)
被转换为对象,而字符串foo
使用new String('foo')
转化为对象,例如:
function bar() { console.log(Object.prototype.toString.call(this)); } bar.call(7); // [object Number]
bind()
方法ECMAScript 5引入了Function.prototype.bind
。调用fn.bind(someObject)
会建立一个与fn
具备相同函数体和做用域的函数,可是在这个新函数中,this
将永久地被绑定到了bind
的第一个参数,不管这个函数是如何被调用的。
function fn(){ return this.a; } var g = fn.bind({ a: "azerty" }); console.log(g()); // "azerty" var obj = { a: 37, foo: fn, go: g }; console.log(obj.foo(), obj.go()); // 37, "azerty"
this
当函数被用做事件处理函数时,它的this
指向触发事件的元素(一些浏览器在动态添加监听器时不遵照这个约定,除非使用addEventListener
)。
// 获取文档中的全部元素的列表 var elements = document.getElementsByTagName('*'); for(var i = 0; i < elements.length; i++){ // 当元素被点击时,就会变成蓝色 elements[i].addEventListener('click', function (e) { e.stopPropagation(); console.log(this); console.log(e.currentTarget); console.log(e.target); // 上面3个值是同样的 this.style.backgroundColor = '#A5D9F3'; }); }
this
当代码被内联处理函数调用时,它的this
指向监听器所在的DOM元素:
<button onclick="alert(this.tagName.toLowerCase());"> Show this </button>
上面的alert
会显示button
。注意只有外层代码中的this
是这样设置的:
<button onclick="alert((function(){return this})());"> Show inner this </button>
在这种状况下,没有设置内部函数的this
,因此它指向global/window
对象(即非严格模式下调用的函数未设置this
时指向的默认对象)。