Javascript this關鍵字流量分析

來源:互聯網
上載者:User

關於js中的this關鍵字的文章已經不少了,我看過幾篇,我寫這篇文章的目的是從執行個體中分析出this的工作原理,希望對大家有所協助。

一、基本的:

複製代碼 代碼如下:function doSomething(){
alert(this.id);
}
alert(window.doSomething);//證明了doSomething是屬於window的
doSomething();//undefined
window.onload = function(){
document.getElementById("div2").onclick = doSomething;//div2
document.getElementById("div3").onclick = function(){doSomething();}//undefined
}

1、對於doSomething這個函數: 複製代碼 代碼如下:function doSomething(){
alert(this.id);
}

這個函數是全域函數,這種全域函數實際上是屬於window的(可以通過window.doSomething來訪問),如果直接調用,那麼根據“this always refers to the “owner” of the function we're executing”,那麼函數中的this就是window,但是window沒有id屬性,所以顯示“undefined”;

2、在html元素中這樣調用 複製代碼 代碼如下:<div id="div1" onclick="doSomething();">div1</div>

這時也會顯示“undefined”,這就相當於如下代碼: 複製代碼 代碼如下:document.getElementById("div1").onclick = function(){doSomething();}

當點擊div1時,調用屬於window的doSomething函數,所以也是顯示“undefined”;

3、通過js來綁定事件,在div2載入過後: 複製代碼 代碼如下:document.getElementById("div2").onclick = doSomething;

當點擊div2時,顯示“div2”,因為在給div2的onclick賦值,是將doSomething拷貝了一次,這時拷貝的這個函數是屬於div2的了,跟屬於window的doSomething沒有任何關係了。點擊div2時,就會觸發屬於div2的doSomething,這裡的this就是指div2。

二、attachEvent和addEventListener

attachEvent是在ie中綁定事件的方法,會將相應函數拷貝到全域(即響應函數的owner為window),但是在DOM標準中,addEventListener綁定的事件時拷貝的響應函數的owner為事件所綁定的對象 複製代碼 代碼如下:function doSomething(){
alert(this.id);
alert(this == window);
}
window.onload = function(){
var div1 = document.getElementById("div1");
if(div1.attachEvent){
div1.attachEvent("onclick",doSomething);
document.body.appendChild(document.createTextNode("attachEvent"));
}else if(div1.addEventListener){
div1.addEventListener("click",doSomething,false);
document.body.appendChild(document.createTextNode("addEventListener"));
}else{
div.onclick = doSomething;
}
}

對於函數doSomething 複製代碼 代碼如下:function doSomething(){
alert(this.id);
alert(this == window);
}

1、使用attachEvent綁定到div1的click事件上,doSometing會被複製到window,這時doSomething裡面的this指的是window,點擊div1時會顯示“undefined”和“true”

2、使用addEventListener綁定div1的click事件,這時將doSomething拷貝,這個拷貝過後的函數是屬於div1的,所以點擊div1時會顯示“div1”和“false”

註:http://www.quirksmode.org/js/this.html裡認為attachEvent只是使用了函數的引用,看如下代碼: 複製代碼 代碼如下:var obj = new Object();
obj.color = "black";
obj.showColor = function(){
alert(this.color);
alert(this == window);
}
obj.showColor();

var div1 = document.getElementById("div1");
div1.attachEvent("onclick",obj.showColor);

此時點擊div1的時候,會顯示“undefined”和“true”,如果attachEvent僅僅是引用obj.showColor的話,那麼this還是應該指的是obj,但是實際上這裡this指的是window,所以我認為這裡不是引用,而是拷貝到全域的。

三、關於對象冒充的繼承方式

1、new與不new的區別

對於如下function 複製代碼 代碼如下:function ClassA(sColor){
this.color = sColor;
this.sayColor = function(){
alert(this.color);
}
}

這是一個類還是一個函數?隨你而定!

如果你認為這是一個函數,那麼我們可以這樣來調用它:

ClassA("red");
“red”是傳遞的一個參數,ClassA中的this指的是當然就是指的window了,所以現在window有了color屬性和sayColor方法,並且color有“red”這個值。

這是調用sayColor或者window.sayColor都可以顯示“red”;

window.sayColor();

如果你認為這是一個類,那麼我們應該這樣使用它:

var obj = new ClassA("red");
new這個關鍵詞的出現讓上面這一句代碼增加了不少內容:首先,建立一個Object執行個體,然後,將ClassA中的this指向建立的這個Object中,最後返回這個Object,所以返回的這個Object就賦值給了obj。所以我們可以說this指向的是obj,obj擁有了color屬性和sayColor方法,並且color屬性值為“red”。

2、函數的owener 複製代碼 代碼如下:function showId(){
alert(this.id);
}
window.onload = function(){
var div1 = document.getElementById("div1");
div1.onclick = showId;
div1.show = showId;
div1.show();

var obj = new Object();
obj.id = "obj";
obj.show = showId;
obj.show();
}

我們可以將showId這個函數賦值給click事件,也可以賦值給任何一個對象的任何一個屬性,這是也會拷貝showId這個方法的,所以我們在調用div1.show方法時,this是指向div1的,在調用obj.show時,this指向的是obj的。

3、對象冒充的原理

下面的代碼是通過對象冒充方法實現的繼承 複製代碼 代碼如下:function ClassA(sColor){
this.color = sColor;
this.sayColor = function(){
alert(this.color);
}
}

function ClassB(sColor,sName){
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;

this.name = sName;
this.sayName = function(){
alert(this.name);
}
}

var objB = new ClassB("color of objB","name of objB");
objB.sayColor();

objB是ClassB的一個執行個體,objB是如何擁有color屬性和sayColor方法的呢?

首先從執行個體化的代碼看起:

var objB = new ClassB("color of objB","name of objB");

這裡ClassB是個類,ClassB中的this當然就是指的objB這個對象;

在ClassB中,前三行代碼會用到ClassA,這時就把ClassA看作一個函數,而不是類。

我們如果直接調用ClassA這個函數,那麼很顯然,ClassA中的this指的就是window對象了,所以我們先將ClassA拷貝到objB的newMethod這個屬性中(this.newMethod = ClassA),

然後再調用this.newMethod,這是這個方法的owener明顯的已經成了this,而ClassB中的this在當前指的是objB,所以此時ClassA中(嚴格的說是newMethod中,因為這是拷貝過後的,跟ClassA已經是兩個方法了)的this就是指的objB,這樣在通過newMethod的調用,就給objB賦值了color屬性和sayColor方法。用call和apply方法來實現繼承實際上也是一個原理,call和apply可以看作是改變方法的owner的方法,而這裡ClassB中的前三句代碼也就是起這個作用的。

四、prototype1.6中的Class.create 複製代碼 代碼如下:prototype1.6中的Class.create方法大致如下:

var Class = {
create: function() {
//

function klass() {
this.initialize.apply(this, arguments);
}

//

for (var i = 0; i < properties.length; i++)
klass.addMethods(properties[i]);

//

return klass;
}
};

在使用的時候是這樣的:

複製代碼 代碼如下:var Person = Class.create({
initialize:function(name){
this.name = name;
},
say:function(message){
alert(this.name + ":" + message);
}
});

var aPerson = new Person("name1");
aPerson.say("hello1");

Person實際上是通過Class.create這個方法所返回的klass(klass是Class.create中的局部變數,是一個function),Class.create所傳遞的參數(initialize方法和say方法)傳遞到create方法中的properties數組中並且通過addMethods方法讓klass的prototype擁有這些方法。那麼最關鍵的地方也是最難以理解的地方是:klass中的this究竟是指的是什麼。仔細想一想就不難得到答案,Person實際上就是klass,而我們在執行個體化Person對象的時候,是用了new關鍵詞的:

var aPerson = new Person("name1");

這就等價於

var aPerson = new klass("name1");

雖然klass在外面不能被訪問到,但是這樣能很輕易的說明問題,klass是一個類而不是簡單的一個函數(我們看作如此,因為用了new關鍵字),那麼klass中的this就指的是聲明的執行個體,在這裡就是aPerson,aPerson通過klass的prototype能夠擁有initialize方法和say方法,在new的過程中,也會執行klass中的代碼,所以initialize在執行個體化的時候會執行,即建構函式。(在klass裡兩個this都是指的aPerson,為什麼還要通過apply調用一次呢?這主要是為了傳遞建構函式的參數,用apply方法可以將數目不定的多個參數通過數組方便的傳到initialize方法中去。)

五、再分析幾個例子

從別的文章裡看到的例子,我在這裡分析一下:

1、運行如下代碼 複製代碼 代碼如下:function OuterFoo(){
this.Name = 'Outer Name';

function InnerFoo(){
var Name = 'Inner Name';
alert(Name + ', ' + this.Name);
}
return InnerFoo;
}
OuterFoo()();

所顯示的結果是“Inner Name, Outer Name”

OuterFoo是一個函數(而不是類),那麼第一句

this.Name = 'Outer Name';

中的this指的是window對象,所以OuterFoo()過後window.Name = ‘Outer Name';

並且將InnerFoo返回,此時InnerFoo同樣是一個函數(不是類),執行InnerFoo的時候,this同樣指window,所以InnerFoo中的this.Name的值為”Outer Name”(window.Name充當了一個中轉站的角色,讓OuterFoo能夠向InnerFoo傳遞“Outer Name”這個值),而Name的值即為局部變數”Inner Name”

2、運行如下代碼

複製代碼 代碼如下:function JSClass(){

this.m_Text = 'division element';
this.m_Element = document.createElement('DIV');
this.m_Element.innerHTML = this.m_Text;

if(this.m_Element.attachEvent)
this.m_Element.attachEvent('onclick', this.ToString);
else if(this.m_Element.addEventListener)
this.m_Element.addEventListener('click', this.ToString,false);
else
this.m_Element.onclick = this.ToString;
}

JSClass.prototype.Render = function(){
document.body.appendChild(this.m_Element);
}

JSClass.prototype.ToString = function(){
alert(this.m_Text);
alert(this == window);
}

window.onload = function(){
var jc = new JSClass();
jc.Render();
jc.ToString();
}

點擊“division element”會顯示“undefined”,在ie下還要顯示“true”,其他瀏覽器中還要顯示“false”。

執行個體聲明和調用執行個體方法都沒什麼可說的,元素的click事件的綁定到了一個執行個體的方法,那麼通過addEventListener綁定到的方法是拷貝過後的,所以this指的是html元素,這個元素沒有m_Text屬性(m_Text屬性是屬於JSClass的執行個體的,即屬於jc的),所以點擊元素顯示undefined,attachEvent綁定的事件會將函數複製到全域,此時this指的是window對象,點擊元素也會顯示“undefined”。只有在調用jc.ToString()方法是,this指的是jc這個對象,因為jc擁有m_Text,所以能夠顯示“division element”。

六、總結

怎樣在一個代碼環境中快速的找到this所指的對象呢?我想要注意以下三個方面:
1、 要清楚的知道對於函數的每一步操作是拷貝還是引用(調用)
2、 要清楚的知道函數的擁有者(owner)是什麼
3、 對於一個function,我們要搞清楚我們是把它當作函數使用還是在當作類使用

補充:
1.在執行個體和類上都可以直接定義函數
2.不能在執行個體上使用prototype定義函數,只能在類上使用prototype定義函數
3.類上直接定義的函數不能使用this訪問對象的屬性
4.在類的prototype上建立的函數可以用this,在類內部定義的函數可以使用this,在對象執行個體上建立的函數額可以this 複製代碼 代碼如下:window.alert=function (msg)
{
document.write(msg+"<br>");
}
function say()
{
this.f="props";
this.func3=function(){alert("f3,"+this.f);}
}
say.func1=function(){alert("func1,"+this.f);}; //Error,類上直接定義的函數,不能使用this
say.prototype.func2=function(){alert("func2,"+this.f);}
say.func1();
(new say()).func2();
say.func2(); //Error, 在用prototype定義的函數,必須執行個體化對象才能調用
say.func3(); //Error,在類上定義的函數,必須執行個體化才能調用
(new say()).func3();
var obj={
fld1:10,
func1:function(msg){alert(msg);},
func4:function(){alert(this.fld1);}
}
obj.prototype.func=function(){alert("func");}; //Error 執行個體對象上不能使用prototype定義對象
obj.func2=function(){alert("func2,"+this.fld1);}; //ok,執行個體上直接定義的函數可以使用this,訪問對象的屬性
alert(obj.fld1);
obj.func1("func1");
obj.func2();
obj.func4();

javascript this用法小結
http://www.jb51.net/article/16863.htm

JavaScript this 深入理解
http://www.jb51.net/article/19425.htm

JAVASCRIPT THIS詳解 物件導向
http://www.jb51.net/article/17584.htm

Javascript this指標
http://www.jb51.net/article/19434.htm

JavaScript中this關鍵字使用方法詳解
http://www.jb51.net/article/7954.htm

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.