淺談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ū)ο蟮乃枷?,也失去了類型密封的原則。還會(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)槿魏晤愋?,Javascript引擎都會(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)文章
layer.open 按鈕的點(diǎn)擊事件關(guān)閉方法
今天小編就為大家分享一篇layer.open 按鈕的點(diǎn)擊事件關(guān)閉方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08
JS判斷鼠標(biāo)進(jìn)入容器的方向與window.open新窗口被攔截的問題
這篇文章主要給大家介紹了利用Javascript判斷鼠標(biāo)進(jìn)入容器方向的方法,以及window.open新窗口被攔截的問題分析,文中給出了詳細(xì)圖文介紹和示例代碼,相信對(duì)大家的理解和學(xué)習(xí)具有一定的參考借鑒價(jià)值,下面來一起看看吧。2016-12-12
el-select加上搜索查詢時(shí)限制開頭空格輸入的解決方案
這篇文章主要介紹了el-select加上搜索查詢時(shí),限制開頭空格輸入的解決方案,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-02-02
JavaScript中閉包的作用和應(yīng)用場(chǎng)景
這篇文章將給大家詳細(xì)介紹JavaScript?中閉包是什么,有哪些應(yīng)用場(chǎng)景,文章通過代碼示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的參考價(jià)值,需要的朋友可以參考下2023-09-09
Javascript 構(gòu)造函數(shù) 實(shí)例分析
一般構(gòu)造函數(shù)沒有返回值,他們通過關(guān)鍵字this初始化對(duì)象,沒有返回值。當(dāng)然一個(gè)構(gòu)造器允許返回一個(gè)對(duì) 象,如果這樣的話 返回的對(duì)象將變成new 表達(dá)式的值,在這種情況下值為this的對(duì)象將拋棄2008-11-11
javascript監(jiān)聽鼠標(biāo)滾輪事件淺析
這篇文章主要介紹了javascript監(jiān)聽鼠標(biāo)滾輪事件淺析,使用具體例子說明,同時(shí)考慮了不同的瀏覽器,需要的朋友可以參考下2014-06-06
bootstrap fileinput組件整合Springmvc上傳圖片到本地磁盤
這篇文章主要介紹了bootstrap fileinput組件整合Springmvc上傳圖片到本地磁盤的方法,需要的朋友可以參考下2017-05-05
javascript中獲取元素標(biāo)簽中間的內(nèi)容的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄猨avascript中獲取元素標(biāo)簽中間的內(nèi)容的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10

