js 概念,閉包,call,apply,prototype等
1,型別
javascript 簡單型別null,undefined,boolean,string,number,複雜型別為object。js是區分大小寫的,不要Number, String, Object, Function等JavaScript內建函式混淆了。
undefined: 代表一切未知的事物,啥都沒有,無法想象,程式碼也就更無法去處理了。
注意:typeof(undefined) 返回也是 undefined。
可以將undefined賦值給任何變數或屬性,但並不意味了清除了該變數,反而會因此多了一個屬性。
null: 有那麼一個概念,但沒有東西。無中似有,有中還無。雖難以想象,但已經可以用程式碼來處理了。
注意:typeof(null)返回object,但null並非object,具有null值的變數也並非object。
boolean: 是就是,非就非,沒有疑義。對就對,錯就錯,絕對明確。既能被程式碼處理,也可以控制程式碼的流程。
number: 線性的事物,大小和次序分明,多而不亂。便於程式碼進行批量處理,也控制程式碼的迭代和迴圈等。
注意:typeof(NaN)和typeof(Infinity)都返回number 。
NaN參與任何數值計算的結構都是NaN,而且 NaN != NaN 。
Infinity / Infinity = NaN 。
string: 面向人類的理性事物,而不是機器訊號。人機資訊溝通,程式碼據此理解人的意圖等等,都靠它了。
簡單型別都不是物件,JavaScript沒有將物件化的能力賦予這些簡單型別。直接被賦予簡單型別常量值的識別符號、變數和引數都不是一個物件。
所謂“物件化”,就是可以將資料和程式碼組織成複雜結構的能力。JavaScript中只有object型別和function型別提供了物件化的能力。
(以上來自
悟透JavaScripthttp://www.cnblogs.com/leadzen/archive/2008/02/25/1073404.html
)
2,函式
{
alert("hello");
};
alert(typeof(myfunc));
執行之後可以看到typeof(myfunc)返回的是function。以上的函式寫法我們稱之為“定義式”的,如果我們將其改寫成下面的“變數式”的,就更容易理解了:
{
alert("hello");
};
alert(typeof(myfunc));
這裡明確定義了一個變數myfunc,它的初始值被賦予了一個function的實體。因此,typeof(myfunc)返回的也是function。其實,這兩種函式的寫法是等價的,除了一點細微差別,其內部實現完全相同。也就是說,我們寫的這些JavaScript函式只是一個命了名的變數而已,其變數型別即為function,變數的值就是我們編寫的函式程式碼體。
第二種變數方式:
var myfunc = function ()
{
alert("hello");
};
myfunc(); //第一次呼叫myfunc,輸出hello
myfunc = function ()
{
alert("yeah");
};
myfunc(); //第二次呼叫myfunc,將輸出yeah
第一種定義方式:
function myfunc ()
{
alert("hello");
};
myfunc(); //這裡呼叫myfunc,輸出yeah而不是hello
function myfunc ()
{
alert("yeah");
};
myfunc(); //這裡呼叫myfunc,當然輸出yeah
兩次呼叫都只是最後那個函式裡輸出的值!顯然第一個函式沒有起到任何作用。JavaScript執行引擎並非一行一行地分析和執行程式,而是一段一段地分析執行的。而且,在同一段程式的分析執行中,定義式的函式語句會被提取出來優先執行。函式定義執行完之後,才會按順序執行其他語句程式碼。也就是說,在第一次呼叫myfunc之前,第一個函式語句定義的程式碼邏輯,已被第二個函式定義語句覆蓋了。所以,兩次都呼叫都是執行最後一個函式邏輯了。
一段程式碼中的定義式函式語句會優先執行,這似乎有點象靜態語言的編譯概念。所以,這一特徵也被有些人稱為:JavaScript的“預編譯”。
大多數情況下,我們也沒有必要去糾纏這些細節問題。只要你記住一點:JavaScript裡的程式碼也是一種資料,同樣可以被任意賦值和修改的,而它的值就是程式碼的邏輯。只是,與一般資料不同的是,函式是可以被呼叫執行的。
3,物件
anObject.aProperty = "Property of object"; //物件的一個屬性
anObject.aMethod = function(){alert("Method of object")}; //物件的一個方法
//主要看下面:
alert(anObject["aProperty"]); //可以將物件當陣列以屬性名作為下標來訪問屬性
anObject["aMethod"](); //可以將物件當陣列以方法名作為下標來呼叫方法
for( var s in anObject) //遍歷物件的所有屬性和方法進行迭代化處理
alert(s + " is a " + typeof(anObject[s]));
同樣對於function型別的物件也是一樣:
aFunction.aProperty = "Property of function"; //函式的一個屬性
aFunction.aMethod = function(){alert("Method of function")}; //函式的一個方法
//主要看下面:
alert(aFunction["aProperty"]); //可以將函式當陣列以屬性名作為下標來訪問屬性
aFunction["aMethod"](); //可以將函式當陣列以方法名作為下標來呼叫方法
for( var s in aFunction) //遍歷函式的所有屬性和方法進行迭代化處理
alert(s + " is a " + typeof(aFunction[s]));
是的,物件和函式可以象陣列一樣,用屬性名或方法名作為下標來訪問並處理。
function型別的東西都是和object型別一樣的東西,這種東西被我們稱為“物件”。我們的確可以這樣去看待這些“物件”,因為它們既有“屬性”也有“方法”。
JavaScript中也有this,但這個this卻與C++、C#或Java等語言的this不同。一般程式語言的this就是物件自己,而 JavaScript的this卻並不一定!this可能是我,也可能是你,可能是他,反正是我中有你,你中有我,這就不能用原來的那個“自我”來理解 JavaScript這個this的含義了。為此,我們必須首先放下原來物件的那個“自我”。
在JavaScript函式中,你只能把this看成當前要服務的“這個”物件。this是一個特殊的內建引數,根據this引數,您可以訪問到“這個”物件的屬性和方法,但卻不能給this引數賦值。在一般物件語言中,方法體程式碼中的this可以省略的,成員預設都首先是“自己”的。但JavaScript卻不同,由於不存在“自我”,當訪問“這個”物件時,this不可省略!
JSON物件:
JSON的形式就是用大括“{}”號包括起來的專案列表,每一個專案間並用逗號“,”分隔,而專案就是用冒號“:”分隔的屬性名和屬性值。這是典型的字典表示形式,也再次表明了 JavaScript裡的物件就是字典結構。不管多麼複雜的物件,都可以被一句JSON程式碼來建立並賦值。
其實,JSON就是JavaScript物件最好的序列化形式,它比XML更簡潔也更省空間。物件可以作為一個JSON形式的字串,在網路間自由傳遞和交換資訊。而當需要將這個JSON字串變成一個JavaScript物件時,只需要使用eval函式這個強大的數碼轉換引擎,就立即能得到一個JavaScript記憶體物件。
new物件:
除JSON外,在JavaScript中我們可以使用new操作符結合一個函式的形式來建立物件。例如:
var anObj = new MyFunc(); //使用new操作符,藉助MyFun函式,就建立了一個物件
JavaScript的這種建立物件的方式可真有意思,如何去理解這種寫法呢?
其實,可以把上面的程式碼改寫成這種等價形式:
var anObj = {}; //建立一個物件
MyFunc.call(anObj); //將anObj物件作為this指標呼叫MyFunc函式
我們就可以這樣理解,JavaScript先用new操作符建立了一個物件,緊接著就將這個物件作為this引數呼叫了後面的函式。其實,JavaScript內部就是這麼做的,而且任何函式都可以被這樣呼叫!
4,作用域:函式內部宣告變數的時候,一定要使用var命令。如果不用的話,你實際上宣告瞭一個全域性變數!
function f1(){
n=1;
}
f1();
alert(n); // 1
function f1(){
var n=1;
}
f1();
alert(n); // error
5,閉包:閉包就是能夠讀取其他函式內部變數的函式。
由於在Javascript語言中,只有函式內部的子函式才能讀取區域性變數,因此可以把閉包簡單理解成“定義在一個函式內部的函式”。
所以,在本質上,閉包就是將函式內部和函式外部連線起來的一座橋樑。
閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函式內部的變數,另一個就是讓這些變數的值始終保持在記憶體中。
前兩年,微軟在設計AJAX類庫的初期,用了一種被稱為“閉包”(closure)的技術來模擬“類”。其大致模型如下:
{
//私有變數:
var _firstName = firstName;
var _lastName = lastName;
//公共變數:
this.age = age;
//方法:
this.getName = function()
{
return(firstName + " " + lastName);
};
this.SayHello = function()
{
alert("Hello, I'm " + firstName + " " + lastName);
};
};
var BillGates = new Person("Bill", "Gates", 53);
var SteveJobs = new Person("Steve", "Jobs", 53);
BillGates.SayHello();
SteveJobs.SayHello();
alert(BillGates.getName() + " " + BillGates.age);
alert(BillGates.firstName); //這裡不能訪問到私有變數
很顯然,這種模型的類描述特別象C#語言的描述形式,在一個建構函式裡依次定義了私有成員、公共屬性和可用的方法。特別是“閉包”機制可以模擬對私有成員的保護機制,做得非常漂亮。
所謂的“閉包”,就是在建構函式體內定義另外的函式作為目標物件的方法函式,而這個物件的方法函式反過來引用外層外層函式體中的臨時變數。這使得只要目標物件在生存期內始終能保持其方法,就能間接保持原建構函式體當時用到的臨時變數值。儘管最開始的建構函式呼叫已經結束,臨時變數的名稱也都消失了,但在目標物件的方法內卻始終能引用到該變數的值,而且該值只能通這種方法來訪問。即使再次呼叫相同的建構函式,但只會生成新物件和方法,新的臨時變數只是對應新的值,和上次那次呼叫的是各自獨立的。的確很巧妙!
但是前面我們說過,給每一個物件設定一份方法是一種很大的浪費。還有,“閉包”這種間接保持變數值的機制,往往會給JavaSript的垃圾回收器製造難題。特別是遇到物件間複雜的迴圈引用時,垃圾回收的判斷邏輯非常複雜。無獨有偶,IE瀏覽器早期版本確實存在JavaSript垃圾回收方面的記憶體洩漏問題。再加上“閉包”模型在效能測試方面的表現不佳,微軟最終放棄了“閉包”模型,而改用“原型”模型。
6,prototype
JavaScript的所有function型別的物件都有一個prototype屬性。這個prototype屬性本身又是一個object型別的物件,因此我們也可以給這個prototype物件新增任意的屬性和方法。既然prototype是物件的“原型”,那麼由該函式構造出來的物件應該都會具有這個“原型”的特性。事實上,在建構函式的prototype上定義的所有屬性和方法,都是可以通過其構造的物件直接訪問和呼叫的。也可以這麼說,prototype提供了一群同類物件共享屬性和方法的機制。
{
this.name = name; //設定物件屬性,每個物件各自一份屬性資料
};
Person.prototype.SayHello = function() //給Person函式的prototype新增SayHello方法。
{
alert("Hello, I'm " + this.name);
}
var BillGates = new Person("Bill Gates"); //建立BillGates物件
var SteveJobs = new Person("Steve Jobs"); //建立SteveJobs物件
BillGates.SayHello(); //通過BillGates物件直接呼叫到SayHello方法
SteveJobs.SayHello(); //通過SteveJobs物件直接呼叫到SayHello方法
alert(BillGates.SayHello == SteveJobs.SayHello); //因為兩個物件是共享prototype的SayHello,所以顯示:true
程式執行的結果表明,建構函式的prototype上定義的方法確實可以通過物件直接呼叫到,而且程式碼是共享的。顯然,把方法設定到prototype的寫法顯得優雅多了,儘管呼叫形式沒有變,但邏輯上卻體現了方法與類的關係。
在JavaScript中,prototype不但能讓物件共享自己財富,而且prototype還有尋根問祖的天性,從而使得先輩們的遺產可以代代相傳。當從一個物件那裡讀取屬性或呼叫方法時,如果該物件自身不存在這樣的屬性或方法,就會去自己關聯的prototype物件那裡尋找;如果prototype沒有,又會去prototype自己關聯的前輩prototype那裡尋找,直到找到或追溯過程結束為止。
在JavaScript內部,物件的屬性和方法追溯機制是通過所謂的prototype鏈來實現的。當用new操作符構造物件時,也會同時將建構函式的prototype物件指派給新建立的物件,成為該物件內建的原型物件。物件內建的原型物件應該是對外不可見的,儘管有些瀏覽器(如Firefox)可以讓我們訪問這個內建原型物件,但並不建議這樣做。內建的原型物件本身也是物件,也有自己關聯的原型物件,這樣就形成了所謂的原型鏈。
在原型鏈的最末端,就是Object建構函式prototype屬性指向的那一個原型物件。這個原型物件是所有物件的最老祖先,這個老祖宗實現了諸如toString等所有物件天生就該具有的方法。其他內建建構函式,如Function, Boolean, String, Date和RegExp等的prototype都是從這個老祖宗傳承下來的,但他們各自又定義了自身的屬性和方法,從而他們的子孫就表現出各自宗族的那些特徵。
這不就是“繼承”嗎?是的,這就是“繼承”,是JavaScript特有的“原型繼承”。
function Person(name) //基類建構函式
2 {
3 this.name = name;
4 };
5
6 Person.prototype.SayHello = function() //給基類建構函式的prototype新增方法
7 {
8 alert("Hello, I'm " + this.name);
9 };
10
11 function Employee(name, salary) //子類建構函式
12 {
13 Person.call(this, name); //呼叫基類建構函式
14 this.salary = salary;
15 };
16
17 Employee.prototype = new Person(); //建一個基類的物件作為子類原型的原型,這裡很有意思
18
19 Employee.prototype.ShowMeTheMoney = function() //給子類添建構函式的prototype新增方法
20 {
21 alert(this.name + " $" + this.salary);
22 };
23
24 var BillGates = new Person("Bill Gates"); //建立基類Person的BillGates物件
25 var SteveJobs = new Employee("Steve Jobs", 1234); //建立子類Employee的SteveJobs物件
26
27 BillGates.SayHello(); //通過物件直接呼叫到prototype的方法
28 SteveJobs.SayHello(); //通過子類物件直接呼叫基類prototype的方法,關注!
29 SteveJobs.ShowMeTheMoney(); //通過子類物件直接呼叫子類prototype的方法
30
31 alert(BillGates.SayHello == SteveJobs.SayHello); //顯示:true,表明prototype的方法是共享的
7,call,apply
call和apply,它們的作用都是將函式繫結到另外一個物件上去執行
兩者的格式和引數定義:
call( thisArg [,arg1,arg2,… ] ); // 引數列表,arg1,arg2,...
apply(thisArg [,argArray] ); // 引數陣列,argArray
上面兩個函式內部的this指標,都會被賦值為thisArg,這可實現將函式作為另外一個物件的方法執行的目的
區分apply,call就一句話,
foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments)==this.foo(arg1, arg2, arg3)
call, apply都屬於Function.prototype的一個方法,它是JavaScript引擎內在實現的,因為屬於Function.prototype,所以每個Function物件例項,也就是每個方法都有call, apply屬性.既然作為方法的屬性,那它們的使用就當然是針對方法的了.這兩個方法是容易混淆的,因為它們的作用一樣,只是使用方式不同.
相同點:兩個方法產生的作用是完全一樣的
不同點:方法傳遞的引數不同
我們就上面的foo.call(this, arg1, arg2, arg3)展開分析.
foo是一個方法,this是方法執行時上下文相關物件,arg1, arg2, arg3是傳給foo方法的引數.這裡所謂的方法執行時上下文相關物件, 如果有物件導向的程式設計基礎,那很好理解,就是在類例項化後物件中的this.
在JavaScript中,程式碼總是有一個上下文物件,程式碼處理該物件之內. 上下文物件是通過this變數來體現的, 這個this變數永遠指向當前程式碼所處的物件中.
call, apply作用就是借用別人的方法來呼叫,就像呼叫自己的一樣.
call, apply方法區別是,從第二個引數起, call方法引數將依次傳遞給借用的方法作引數, 而apply直接將這些引數放到一個陣列中再傳遞, 最後借用方法的引數列表是一樣的.
當引數明確時可用call, 當引數不明確時可用apply給合arguments
相關文章
- js的繼承方法小結(prototype、call、apply)JS繼承APP
- Function.prototype.call.apply作用詳解FunctionAPP
- JS閉包函式概念JS函式
- js中的call、applyJSAPP
- JS中的call、apply、bindJSAPP
- js call,apply,bind總結JSAPP
- JS開發者應懂的33個概念系列6--this,bind,call,applyJSAPP
- js call、apply、bind的實現JSAPP
- JS中的call、apply、bind方法JSAPP
- 深入理解JS的apply()、call()JSAPP
- JS中的call()和apply()方法JSAPP
- js中call、apply、bind函式JSAPP函式
- js中call、apply、bind的用法JSAPP
- JS中apply和call的用法JSAPP
- Function.prototype.callFunction
- js之閉包(概念、優缺點、應用)JS
- 理解JS中的call、apply、bind方法(********************************************************JSAPP
- 理解JS函式之call,apply,bindJS函式APP
- 回味JS基礎:call apply 與 bindJSAPP
- js中call、apply、bind的區別JSAPP
- 【轉】JS中的call()和apply()方法JSAPP
- js call apply bind簡單的理解JSAPP
- 淺談JS的__proto__和prototype,引伸callJS
- js總結(原型和原型鏈,閉包等)JS原型
- this、apply、call、bindAPP
- this、call和applyAPP
- javacscript apply and callJavaAPP
- Array.prototype.slice.call
- js深入之實現call、apply和bindJSAPP
- 手寫JS函式的call、apply、bindJS函式APP
- 隨筆——js中的this指向,apply()與 call()JSAPP
- 【閉包概念】關於閉包概念不同解讀——你可以自己理解。
- 閉包概念是掌握React.JS的關鍵 - NitsanReactJS
- javascript閉包概念介紹JavaScript
- 深入學習js之——call和apply#10JSAPP
- 重寫JS中的apply,call,bind,new方法JSAPP
- 簡單快速理解js中的this、call和applyJSAPP
- JS學習筆記之call、apply的用法JS筆記APP