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