This介绍

关于Javascript中的this,相信接触过的人都不会感到陌生。可以说javascript之所以如此的’强大’,this可以说是功不可没。它有以下几个特点:

  • 随着javascript函数的不同调用方式,this的取值可能会不同(this在global下指向的是window
  • 在函数的标识符解析的过程中,this的解析仅仅只限于当前函数作用域,而不会通过作用域链向上查找。
  • thisFunction.prototype.call/applyFunction.prototype.bind的相结合,使得有趣的事情发生。(this的指向变成我们所指定的)

关于this的常见用法相信你已经看了不少了,我google了一些还算不错的:

接下来的内容希望你能先看完上面的资料,至少有一个大概的印象,这样才能愉快的查看下面的栗子~~

举个栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
window.name = 'global';
jQuery.name = 'jQuery';
function jQuery(selector, context) {
return new jQuery.prototype.init();
};
jQuery.prototype = {
init: function() {
this.name = 'new obj'
return this;
},
getName: function() {
return this.name;
},
name: 'jQuery.prototype'
};
jQuery.prototype.init.prototype = jQuery.prototype;
var objA = jQuery();
var getName = objA.__proto__.getName;
var objB = {name:'simmer'};
objB.somemethod = getName;
var objC = new getName();
console.dir(objA.getName()); // 1
console.dir(getName()); // 2
console.dir(objA.__proto__.getName()); // 3
console.dir(objB.somemethod()); // 4
console.dir(objC); // 5

不要问我为什么是jQuery,最近刚好在看jQuery源码,就酱==
接下来根据你的判断分别写下1,2,3,4,5处console出的值。







防作弊线---------------







让我们看下结果(为了方便查看我吧代码全部截取下来并加上一些注解):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
window.name = 'global';
jQuery.name = 'jQuery';
function jQuery(selector, context) {
return new jQuery.prototype.init();
};
jQuery.prototype = {
init: function() {
this.name = 'new obj'
return this;
},
getName: function() {
return this.name;
},
name: 'jQuery.prototype'
};
jQuery.prototype.init.prototype = jQuery.prototype;
// 类似将jQuery的原型覆盖jQuery原型上init方法的原型
var objA = jQuery(); // 实例化一个对象A
var getName = objA.__proto__.getName;
// 引用实例对象A的__proto__属性(其实这是一个指针,指向的是一个构造器的原型对象)
var objB = {name:'simmer'}; // 新建一个对象B
objB.somemethod = getName; // 给B添加一个方法 这个方法是jQuery原型上的方法
var objC = new getName();
console.dir(objA.getName()); // 'new obj'
console.dir(getName()); // 'global'
console.dir(objA.__proto__.getName()); // 'jQuery.prototype'
console.dir(objB.somemethod()); // 'simmer'
console.dir(objC); // chrome返回'jQuery.getName' Firefox返回'[object Object]'

是不是和你分析的一样呢?我们一个个来分析下吧。

console 1

1
console.dir(objA.getName()); // 'new obj'

这里的objA是jQuery的一个实例,getName是jQuery原型上的方法,所以此处的getName方法是在实例上面通过__pro__属性查找并调用在jQuery.prototype上的getName方法。
所以我们可以知道通过原型链的查找并调用执行的function中的this指向的就是这个调用方法的实例。this === objA

console 2

1
console.dir(getName());  // 'global'

这里的getName是我们通过chrome和firefox私有属性__proto__(它指向的是实例的构造器的原型)获取到的jQuery.prototype上的方法。但是在这里getName中保存的是指向jQuery.prototype.getName的指针,他是在全局环境下运行的所以这里毫无疑问this === window也就是说this.name === window.name

console 3

1
console.dir(objA.__proto__ .getName()); // 'jQuery.prototype'

这里很明显也是通过访问实例的私有属性__proto__进而执行jQuery.prototype.getName,因为这里的执行环境是相当于调用jQuery.prototype对象上面的方法,故而this === jQuery.prototype

console 4

1
console.dir(objB.somemethod()); // 'simmer'

我们知道getName变量保存着指向jQuery.prototype.getName方法的引用,又由于在javascript中对象的赋值时按照引用来赋值的,所以这里呢objB.somemethod = getName;中objB的somemethod方法也保存指向jQuery.prototype.getName方法的引用,所以当通过objB.somemethod()这种形式调用jQuery.prototype.getName方法时,这个方法是作为对象的方法调用的,故而有this === objB

console 5

1
console.dir(objC); // chrome返回'jQuery.getName' Firefox返回'[object Object]'

最后一种情况也比较简单,我们知道函数的调用分为2种情况,一种是作为普通函数进行调用,另外一种就是通过使用new操作符的形式进行调用。熟悉javascript的童鞋应该知道javascript的类正是通过这种new的形式创建的。简单来说就是通过new来调用函数,在函数内部会自动新建一个对象,而在这个函数内部使用的this则会自动指向这个新建的对像,最后在自动返回这个新建对象(在没有return的情况下)。类似于:

1
2
var obj = {}; // 新建对象
constructor.apply(obj); // 更正this 指向新的对象

但是这里提前返回了this.name ,他是一个字符串,并不是对象,我通过typeof objC得到object,所以javascript解析器会默认返回一个空的对象。,在chrome中返回的jQuery.getName可能是默认返回构造器的引用组成的名字,相比指向firefox就靠谱多了,直接返回'[object Object]' 表示这是一个对象

小总结

  • JavaScript中的this可以有不同的取值,这是根据某个函数的具体调用情况来看的。
  • 我们知道当javascript在某个函数内部没有查找到需要的变量时候,会沿着作用域链向外层作用域环境中(全局环境是window)中查找变量。而神奇的this则不再这个变量查找机制范围内,每个函数内的this取值是要到函数运行时候才能确定的,这也是为什么javascript有着如此迷人的魅力原因所在。
  • 要想知道this的取值,关注函数具体的调用方式,全局调用?对象方法调用?使用new操作符构造实例调用?实例公有方法调用(__proto__)?还是通过Function.prototype.call/apply自定义this调用?亦或者是通过Function.prototype.bind(ES5)调用?(注意:每个函数只能bind一次,且以第一次bind为准)

(完)

由于水平有限,如有错误,欢迎在下方进行评论指出。