前幾天在牛客網上作題,遇到一道關於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原型及原型鏈詳解(一)
以上內容都是我我的的理解,不免會有理解不到位的地方,但願各位不吝賜教!你們一塊兒進步。