淺談Javascript中的函數(shù)、this以及原型
關(guān)于函數(shù)
在Javascript中函數(shù)實(shí)際上就是一個(gè)對(duì)象,具有引用類型的特征,所以你可以將函數(shù)直接傳遞給變量,這個(gè)變量將表示指向函數(shù)“對(duì)象"的指針,例如:
function test(message){
alert(message);
}
var f = test;
f('hello world');
你也可以直接將函數(shù)申明賦值給變量:
var f = function(message){
alert(message);
};
f('hello world');
在這種情況下,函數(shù)申明中可以省略函數(shù)名稱,因?yàn)榇藭r(shí)名稱已經(jīng)沒有任何意義,我們可直接通過變量f來調(diào)用函數(shù)。
通過Function類型,我們可以更好地理解函數(shù)即對(duì)象:
var f = new Function("message","alert(message);");
f('hello world');
關(guān)于this
this可以看成調(diào)用函數(shù)的實(shí)際作用域上下文。比較以下函數(shù)的執(zhí)行結(jié)果:
function test(){
this.property = 'hello world';
}
test();
alert(window.property); //由于在全局范圍內(nèi)調(diào)用,test函數(shù)中的this實(shí)際指向全局對(duì)象(window)
var obj = {};
test.call(obj); //通過call第一個(gè)參數(shù)指定執(zhí)行上下文范圍,所以test函數(shù)中this指向obj實(shí)例。
alert(obj.property);
var obj2 = {};
obj2.test2 = test; //將obj2實(shí)例方法test指向 全局test方法
obj2.test2(); //由于是在obj2上調(diào)用test方法,所以test函數(shù)中的this也指向了obj2實(shí)例
alert(obj2.property);
定義類型
在Javascript中可以定義構(gòu)造函數(shù),構(gòu)造函數(shù)與一般函數(shù)沒有任何區(qū)別,在創(chuàng)建實(shí)例時(shí),如果我們使用了new關(guān)鍵字,那么這個(gè)函數(shù)就具有構(gòu)造函數(shù)的特性,否則就是一般函數(shù),如下所示,我們定義了一個(gè)Person類型:
function Person(){
this.name = 'xfrog';
this.Say = function(){
alert(this.name);
};
}
當(dāng)使用new關(guān)鍵字時(shí),可以創(chuàng)建一個(gè)新的Person對(duì)象實(shí)例:
var p1 = new Person(); p1.Say();
如果不使用new關(guān)鍵字,將直接執(zhí)行Person函數(shù),由于執(zhí)行上下文為全局范圍,故name屬性和Say方法將被添加到window對(duì)象:
Person(); Say(); window.Say();
原型
注意上述Person的定義方式,當(dāng)使用new來創(chuàng)建Person實(shí)例時(shí),將會(huì)執(zhí)行Person構(gòu)造函數(shù),也就是會(huì)聲明name屬性和Say方法,這樣可能產(chǎn)生效率問題,注意以下代碼:
var p1 = new Person(); var p2 = new Person(); var test = p1.Say == p2.Say;
比較p1和p2兩個(gè)Say函數(shù)指針,返回false,表示每個(gè)Person實(shí)例中的Say方法都是獨(dú)立的,而事實(shí)上Say函數(shù)的功能是完全一樣的,我們完全沒有必要為每個(gè)對(duì)象重新分配Say函數(shù)”對(duì)象“,如果Person實(shí)例很多,將會(huì)造成大量的內(nèi)存耗用。
如果將Say函數(shù)提取出來放入全局執(zhí)行范圍,似乎可解決次問題:
function Person(){
this.name = 'xfrog';
this.Say = say;
}
function say(){
alert(this.name);
}
var p1 = new Person();
var p2 = new Person();
alert(p1.Say == p2.Say);
p1.name = 'wang';
p1.Say();
由于this始終和執(zhí)行上下文相關(guān),p1和p2實(shí)例中的Say方法中會(huì)正確地返回對(duì)應(yīng)實(shí)例的name屬性。但是,使用此方式有違面向?qū)ο蟮乃枷耄彩チ祟愋兔芊獾脑瓌t。還會(huì)造成大量的全局函數(shù)。
為了解決這些缺點(diǎn),Javascript引出了原型的概念,簡(jiǎn)單理解,原型可以看成是類型的共享區(qū),原型本身是一個(gè)對(duì)象,而對(duì)象中的屬性對(duì)于類型來說是共享的。Javascript中每個(gè)類型通過prototype屬性來表示原型,通過這個(gè)屬性可指定共享方法:
function Person(){
}
Person.prototype.name = 'xfrog';
Person.prototype.Say = function(){
alert(this.name);
};
var p1 = new Person();
var p2 = new Person();
alert(p1.Say == p2.Say); //返回true
為什么這里可以通過p1.Say來訪問Say方法呢?這是因?yàn)镋CMAScript標(biāo)準(zhǔn)規(guī)定了類型屬性的查找順序:先在類型的實(shí)例上查找,如果沒有則繼續(xù)在類型原型上查找,這一查找路徑采用短路算法,即找到首個(gè)后即返回,考慮如下代碼:
function Person(){
this.name = 'wang';
}
Person.prototype.name = 'xfrog';
Person.prototype.Say = function(){
alert(this.name);
}
var p1 = new Person();
p1.Say(); //將返回wang
上面提到prototype實(shí)際上是一個(gè)對(duì)象,那么我們是否可以直接訪問呢? 在一些瀏覽器實(shí)現(xiàn)(如Chrome、Fixfox等)的確可通過實(shí)例的__proto__屬性來訪問內(nèi)部的prototype對(duì)象,這種特征表明Javascript引擎在每個(gè)對(duì)象的內(nèi)部都是通過一個(gè)變量來保存對(duì)prototype的引用,這保證了prototype對(duì)應(yīng)整個(gè)類型的實(shí)例來說是共享的,例如,你可在Chrome瀏覽器內(nèi)使用如下方式來訪問Say方法:
p1.__proto__["Say"]();
由于原型是一個(gè)對(duì)象,我們可以直接將一個(gè)對(duì)象賦值給prototype:
function Person(){
}
Person.prototype = {name:'xfrog', Say:function(){
alert(this.name);
}};
注意這個(gè)方式下,實(shí)際上是完全替換了Person的prototype,這與上面Person.prototype.name方式還是有細(xì)微差異的,這是因?yàn)槿魏晤愋停琂avascript引擎都會(huì)添加默認(rèn)的prototype,在這個(gè)prototype中包含一個(gè)對(duì)構(gòu)造函數(shù)的引用,即原型對(duì)象屬性constructor,所以通常使用替代prototype方式時(shí),我們需要手動(dòng)加上constructor屬性:
Person.prototype = {
constructor: Person,
name :'xfrog',
Say:function(){
alert(this.name);
}
}
注意,由于prototype對(duì)于整個(gè)類型是共享的,那么在prototype中的引用類型可能會(huì)存在問題,前面的Say函數(shù)作為一個(gè)對(duì)象,也是引用類型,所以每個(gè)實(shí)例中的Say都指向原型對(duì)象中的同一個(gè)函數(shù),這本身沒有問題,也是我們使用原型的初衷,但對(duì)于其他引用對(duì)象,可能結(jié)果并不是我們想要的:
function Person(){
}
Person.prototype = {
name: 'xfrog',
obj : { age: 18 },
Say : function(){
alert(this.obj.age);
}
};
var p1 = new Person();
var p2 = new Person();
p1.obj.age = 20;
p1.Say();
p2.Say();
p2.Say返回的是20,這是因?yàn)閛bj屬性作為原型屬性是共享的,在內(nèi)存中只存在一個(gè)實(shí)例,所以通過p1修改后,p2只能得到修改后的狀態(tài)。如果要避免此情況,可將obj屬性放到實(shí)例中:
function Person(){
this.obj = { age: 18 };
}
以上就是小編為大家?guī)淼臏\談Javascript中的函數(shù)、this以及原型全部?jī)?nèi)容了,希望大家多多支持腳本之家~
- 改變javascript函數(shù)內(nèi)部this指針指向的三種方法
- JS函數(shù)this的用法實(shí)例分析
- 詳解JS構(gòu)造函數(shù)中this和return
- JavaScript函數(shù)中的this四種綁定形式
- JavaScript 嵌套函數(shù)指向this對(duì)象錯(cuò)誤的解決方法
- 深入理解Javascript箭頭函數(shù)中的this
- js對(duì)象內(nèi)部訪問this修飾的成員函數(shù)示例
- 深入理解js函數(shù)的作用域與this指向
- JS匿名函數(shù)內(nèi)部this指向問題詳析
- JavaScript箭頭函數(shù)中的this詳解
- js函數(shù)和this用法實(shí)例分析
相關(guān)文章
基于slideout.js實(shí)現(xiàn)移動(dòng)端側(cè)邊欄滑動(dòng)特效
這篇文章主要為大家詳細(xì)介紹了基于slideout.js實(shí)現(xiàn)移動(dòng)端側(cè)邊欄滑動(dòng)特效,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
javascript appendChild,innerHTML,join性能比較代碼
在實(shí)際應(yīng)用中,應(yīng)該避免直接用innerHTML,對(duì)于大量的字符連接運(yùn)算,應(yīng)該考慮先運(yùn)算再輸出。2009-08-08
javascript實(shí)現(xiàn)禁止鼠標(biāo)滾輪事件
這篇文章主要介紹了javascript實(shí)現(xiàn)禁止鼠標(biāo)滾輪事件的相關(guān)資料,需要的朋友可以參考下2015-07-07
Javascript中查找不以XX字符結(jié)尾的單詞示例代碼
我在寫這篇文章之前花了2個(gè)多小時(shí)在弄正則表達(dá)式,下為大家介紹下具體的實(shí)現(xiàn)思路,感興趣的朋友可以參考下2013-10-10
CSS+Js遮罩效果的TAB及焦點(diǎn)圖片切換(推薦)
CSS+Js圖片切換技術(shù),類似的已有不少了,這一個(gè)使用了遮罩過渡的效果,同樣應(yīng)用到了TAB選項(xiàng)卡上,本頁(yè)面僅是為了演示,大家用時(shí)候把它拆分開來,這個(gè)效果也對(duì)學(xué)習(xí)圖片效果制作很有幫助。2009-11-11
JavaScript實(shí)現(xiàn)復(fù)制文本到剪切板功能的方法小結(jié)
這篇文章給大家介紹了三種JavaScript實(shí)現(xiàn)復(fù)制文本到剪切板的方法,Clipboard API,document.execCommand以及useClipboard這三個(gè)接口,文章通過代碼示例給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11
原生JavaScript實(shí)現(xiàn)輪播圖效果
這篇文章主要為大家詳細(xì)介紹了原生JavaScript實(shí)現(xiàn)輪播圖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09

