前几天在牛客网上作题,遇到一道关于this指向的问题,之前就对this只知其一;不知其二的我瞬间懵逼,下面咱们来看看这道题,顺便讲解一下js中this的指向问题。javascript
题目:填写内容让下面代码支持a.name = “name1”; b.name = “name2”;java
function obj(name){ _______ } obj._____ = "name2"; var a = obj("name1"); var b = new obj;
在作这道题以前,咱们先来学习一下this的知识。首先咱们必需要明白的是,在javascript中this的指向是在函数被调用时才能肯定的,在定义时是不可以肯定的this指向的,准确一点说this最终指向的是调用它的对象。下面咱们举例说明:segmentfault
1 var name = "小黑"; // 全局变量name 2 function Func() { 3 var name = "大白"; // 局部变量name 4 console.log(this); 5 console.log(this.name); 6 } 8 Func(); 9 console.log(window.Func() === Func()) //true
上面的代码中,当咱们调用Func()函数时,实际上Func()是做为window对象的方法被调用的(第9行代码能够能够验证),所以this指向的就是全局对象window,第4行代码打印出来的也就是window,第5行代码打印出来的天然也就是全局变量name(全部的全局变量都做为window的属性)。app
仍是先看下面的示例代码:函数
1 var Obj = { 2 name: "大白", 3 getName: function() { 4 console.log(this.name) 5 } 6 } 7 Obj.getName(); // 大白 8 window.Obj.getName(); // 大白
这段代码中getName()做为对象Obj的方法被Obj调用,所以这个时候this指向的即是Obj对象,天然this.name获得的就是"大白"。那么在第8行代码中window调用了getName()为何不是指向window呢?由于window其实是经过调用Obj间接调用getName()的,因此this仍是指向直接调用它的Obj。学习
function Func() { this.name = "大白"; } var fn = new Func(); console.log(fn.name) // 大白
当咱们经过new关键字构造一个实例对象的过程当中,构造函数中的this通常状况下指向咱们构造出来的实例化对象(特殊状况后面有单独讲解),所以在构造过程当中this.name = "大白"这句代码就至关于给实例对象fn建立了一个name属性并赋值"大白"。this
1 function getName() { 2 console.log(this.name); 3 } 4 var Obj = { 5 name: "大白" 6 } 7 getName.apply(Obj); // 大白 8 console.log(Obj); //{ name: "大白" }
这里用到了apply()方法改变this的指向(不知道apply()用法的自行百度),第7行代码中咱们能够理解成将getName()函数临时绑定在Obj对象上做为Obj对象的方法,同时调用这个方法。这个时候getName()中的this就指向临时调用它的Obj对象了,天然this.name获得的就是"大白"。注意这里并无改变Obj对象,如第8行代码所示。prototype
最后要解决前面留下的题,咱们还须要讲个知识点:当构造函数中的this遇到return时的状况。code
//示例1 function Fn1() { this.name = "大白"; return { name: "小黑" }; // 返回一个空对象 } var fn1 = new Fn1(); console.log(fn1.name); // 小黑 //示例2 function Fn2() { this.name = "大白"; return true; // 返回true } var fn2 = new Fn2(); console.log(fn2.name); // 大白
你可能会奇怪,如出一辙的代码为何获得的值不同呢? 注意!这里两段代码是区别的,示例1中函数返回值是一个对象,示例2中的返回值是true,当构造函数中返回值是一个对象时,this指向的就是返回的那个对象,若是返回值不是对象时,返回值指向的就是构造函数的示例对象,所以实例1中的this.name获得的是小黑而不是大白。对象
如今咱们就能够来作上面的题了:填写内容让下面代码支持a.name = “name1”; b.name = “name2”;
//建立全局函数 1 function obj(name){ 2 if(name) { // 区分普通调用和实例化调用 this.name = name; } return this; // 返回this引用,调用时this指向window 3 } 4 obj.prototype.name = "name2"; // 设置原型对象 5 var a = obj("name1"); //直接调用函数,a等于window,name为window的属性。 6 var b = new obj; //调用函数实例化对象,this指向obj的实例化对象。
上面第4行代码涉及到对象原型的知识,若是对这方面还有疑问的能够看看我总结的另外一篇文章:javaScript原型及原型链详解(一)
以上内容都是我我的的理解,不免会有理解不到位的地方,但愿各位不吝赐教!你们一块儿进步。