全
市
第
一
javascript是一種腳本語言只要有javascript引擎即可(Mozilla,Google,微軟,其它宿主環(huán)境)
? javascript是基于對象的語言1)可以創(chuàng)建對象
2)可以使用內(nèi)置對象3)沒有類的概念,沒有方法的重載
? 與java無任何關(guān)系,除了名字和部分語法
? javascript不能打開、讀、寫、保存計(jì)算機(jī)上的文件
? javascript不能編寫破壞計(jì)算機(jī)上任何資源的病毒
? javascript在不同瀏覽器的差異性
? JS是弱類型的,內(nèi)置類型簡單且清晰
? number : 數(shù)字
? boolean : 布爾值
? string : 字符串
? undefined : 未定義
? null :空值
? function : 函數(shù)
? object : 對象
? 代表一切未知的事物,js引擎不能理解的東西,如 :
1) 對象沒有的屬性
2) 未傳入的參數(shù)
3) 函數(shù)未返回值
例:var a=b+1;
例:var a=1;.? Number.MAX_VALUE : 返回 JScript 能表達(dá)的最大的數(shù)
。約等于 1.79E+308。? Number.MIN_VALUE : 返回 JScript 最接近0的數(shù)。約等
于 5E-324。全局對象(Gobal)中還有兩個屬性與number類型的運(yùn)
算有關(guān):? NaN : 算術(shù)表達(dá)式的運(yùn)算結(jié)果不是數(shù)字,則返回NaN
值。例: var v1=10 * 'a';
isNaN(v1);
? Infinity : 比MAX_VALUE更大的數(shù)。
? 例:var a=true,b=false;
? javascript中的if語句中的條件
1) boolean
2) string 空字符串返回的結(jié)果是false,其他是true
3)object類型,null是false,其他是true
4)function類型,都輸true
5)number,0,0.00為false,其他為true
6)undefined, 都是false
? 例:var a="hello world!";
? concat
? indexof
? charAt
? lastIndexOf
? 在javascript中,很多功能、靈活性以及表達(dá)能力都來自函數(shù),包括實(shí)現(xiàn)面向?qū)ο笳Z法。
? function定義有2種
函數(shù)function
? 第一種(聲明式)
function myfunc()
{
alert("hello");
};
print(typeof(myfunc));
? 第二種(變量式)
var myfunc=function ()
{
alert("hello");
};
print(typeof(myfunc));
JavaScript 函數(shù)只是一個命了名的變量而已,其變量類型即為function,變量的值就是我
們編寫的函數(shù)代碼體。
問題:既然函數(shù)只是變量,那么變量就可以被隨意賦值并用到任意地方?
var myfunc=function ()
{
alert("hello");
};
myfunc(); //第一次調(diào)用myfunc,輸出hello
myfunc=function ()
{
alert("yeah");
};
myfunc(); //第二次調(diào)用myfunc,將輸出yeah
答案是肯定的!在第一次調(diào)用函數(shù)之后,函數(shù)變量又被賦予了新的函數(shù)
代碼體,使得第二次調(diào)用該函數(shù)時,出現(xiàn)了不同的輸出。
function myfunc ()
{
print("hello");
};
myfunc(); //這里調(diào)用myfunc,輸出yeah 而不是hello
function myfunc ()
{
print("yeah");
};
myfunc(); //這里調(diào)用myfunc,當(dāng)然輸出yeahJavaScript 執(zhí)行引擎并非一行一行地分析和執(zhí)行程序,而是一段一段地分析執(zhí)行的。而且,在
同一段程序的分析執(zhí)行中,定義式的函數(shù)語句會被提取出來優(yōu)先執(zhí)行。函數(shù)定義執(zhí)行完之后,才會按順序
執(zhí)行其他語句代碼。也就是說,在第一次調(diào)用myfunc 之前,第一個函數(shù)語句定義的代碼邏輯,已被第二
個函數(shù)定義語句覆蓋了。所以,兩次都調(diào)用都是執(zhí)行最后一個函數(shù)邏輯了。
函數(shù)function
? 函數(shù)也可以有自己的成員,函數(shù)。如:function myfun(){};
myfun.name='123';
myfun.yourfun=function(){}
? 任何一個函數(shù)都可以為其動態(tài)地添加或去除屬性,這些屬性可以是簡單類型,可以是對象,也可以是其他函數(shù)。也就是說,函數(shù)具有對象的全部特征,你完全可以把函數(shù)當(dāng)對象來用。其實(shí),函數(shù)就是對象,只不過比一般的對象多了一個括號"()"操作符,這個操作符用來執(zhí)行函數(shù)的邏輯。即,函數(shù)本身還可以被調(diào)用,一般對象卻不可以被調(diào)用,除此之外完全相同
? javascript中的方法沒有重載,同名函數(shù)將被覆蓋。
每個函數(shù)都有一個prototype屬性,該屬性儲存的就是原型對象
function foo(a,b){ return a*b;}
typeof(foo.prototype) // object
foo.prototype={
x : 111,
y:222,
fn : function(){this.x+this.y}
}
? JavaScript 中不管多么復(fù)雜的數(shù)據(jù)和代碼,都可以組織成object 形式的
? 定義方式:
方式1:var obj=new Object();
方式2:var obj={};
從方式2可以看出,javascript的對象并不需要類就能產(chǎn)生,其實(shí)javascript沒有類的概念
? Javascript是一種基于對象(object-based)的語言,你遇到的所有東西幾乎都是對象。但是,它又不是一種真正的面向?qū)ο缶幊蹋∣OP)語言,因?yàn)樗恼Z法中沒有class(類)。
? 那么,如果我們要把"屬性"(property)和"方法"(method),封裝成一個對象,甚至要從原型對象生成一個實(shí)例對象,我們應(yīng)該怎么做呢?
? 假定我們把貓看成一個對象,它有"名字"和"顏色"兩個屬性。
var Cat={
name : '',
color : ''
}
? 現(xiàn)在,我們需要根據(jù)這個原型對象的規(guī)格(schema),生成兩個實(shí)例對象。
var cat1={}; // 創(chuàng)建一個空對象cat1.name="大毛"; // 按照原型對象的屬性賦值cat1.color="黃色";
var cat2={};
cat2.name="二毛";cat2.color="黑色";
? 好了,這就是最簡單的封裝了,把兩個屬性封裝在一個對象里面。但是,這樣的寫法有兩個缺點(diǎn),一是如果多生成幾個實(shí)例,寫起來就非常麻煩;二是實(shí)例與原型之間,沒有任何辦法,可以看出有什么聯(lián)系。
原始模式的改進(jìn)? 我們可以寫一個函數(shù),解決代碼重復(fù)的問題。
function Cat(name,color){
return {
name:name,
color:color
}
}
? 然后生成實(shí)例對象,就等于是在調(diào)用函數(shù):
? var cat1=Cat("大毛","黃色");
? var cat2=Cat("二毛","黑色");
? 這種方法的問題依然是,cat1和cat2之間沒有內(nèi)
在的聯(lián)系,不能反映出它們是同一個原型對象的實(shí)例。
? 為了解決從原型對象生成實(shí)例的問題,Javascript提供了一個構(gòu)造函數(shù)(Constructor)模式。
? 所謂"構(gòu)造函數(shù)",其實(shí)就是一個普通函數(shù),但是內(nèi)部使用了this變量。對構(gòu)造函數(shù)使用new運(yùn)算符,就能生成實(shí)例,并且this變量會綁定在實(shí)例對象上。
? 比如,貓的原型對象現(xiàn)在可以這樣寫,function Cat(name,color){
this.name=name;
this.color=color;
}
? 我們現(xiàn)在就可以生成實(shí)例對象了。var cat1=new Cat("大毛","黃色");var cat2=new Cat("二毛","黑色");alert(cat1.name); // 大毛alert(cat1.color); // 黃色
? 這時cat1和cat2會自動含有一個constructor屬性,指向它們的構(gòu)造函數(shù)。
? alert(cat1.constructor==Cat); //true
? alert(cat2.constructor==Cat); //true
? Javascript還提供了一個instanceof運(yùn)算符,驗(yàn)證原型對象與實(shí)例對象之間的關(guān)系。
? alert(cat1 instanceof Cat); //true
? alert(cat2 instanceof Cat); //true
? 對象的成員名稱可以為任意字符串
? 訪問方式
1)obj.成員名
2)obj['成員名']
? 遍歷一個對象所有成員的方式 for(var item in obj){obj[item]}
構(gòu)造函數(shù)方法很好用,但是存在一個浪費(fèi)內(nèi)存的問題。
? 請看,我們現(xiàn)在為Cat對象添加一個不變的屬性"type"(種類),再添加一個方法eat(吃老鼠)。那么,原型對象Cat就變成了下面這樣:
function Cat(name,color){
this.name=name;
this.color=color;
this.type="貓科動物";
this.eat=function(){alert("吃老鼠");};
}
? 還是采用同樣的方法,生成實(shí)例:
? var cat1=new Cat("大毛","黃色");
? var cat2=new Cat ("二毛","黑色");
? alert(cat1.type); // 貓科動物
? cat1.eat(); // 吃老鼠
? 表面上好像沒什么問題,但是實(shí)際上這樣做,有一個很大的弊端。那就是對于每一個實(shí)例對象,type屬性和eat()方法都是一模一樣的內(nèi)容,每一次生成一個實(shí)例,都必須為重復(fù)的內(nèi)容,多占用一些內(nèi)存。這樣既不環(huán)保,也缺乏效率。
? alert(cat1.eat==cat2.eat); //false
? 能不能讓type屬性和eat()方法在內(nèi)存中只生成一次,然后所有實(shí)例都指向那個內(nèi)存地址呢?回答是可以的。
? Javascript規(guī)定,每一個構(gòu)造函數(shù)都有一個prototype屬性,指向另一個對象。這個對象的所有屬性和方法,都會被構(gòu)造函數(shù)的實(shí)例繼承。
? 這意味著,我們可以把那些不變的屬性和方法,直接定義在prototype對象上。
function Cat(name,color){
this.name=name;
this.color=color;
}
Cat.prototype.type="貓科動物";
Cat.prototype.eat=function(){alert("吃老鼠")};
? 然后,生成實(shí)例。
? var cat1=new Cat("大毛","黃色");
? var cat2=new Cat("二毛","黑色");
? alert(cat1.type); // 貓科動物
? cat1.eat(); // 吃老鼠
? 這時所有實(shí)例的type屬性和eat()方法,其實(shí)都是同一個內(nèi)存地址,指向prototype對象,因此就提高了運(yùn)行效率。
? alert(cat1.eat==cat2.eat); //true
? 搞了this是誰,記住一句話就行:方法是誰的this就是誰,方法誰也不是,this就是window,請看例子:
function WhoAmI() //定義一個函數(shù)WhoAmI{
alert("I'm " + this.name + " of " + typeof(this));
};
function 中的this關(guān)鍵字
var BillGates={name: "Bill Gates"};
BillGates.WhoAmI=WhoAmI;
BillGates.WhoAmI();//I'm Bill Gates of object
var SteveJobs={name: "Steve Jobs"};
SteveJobs.WhoAmI=WhoAmI;
SteveJobs.WhoAmI();//I'm Steve Jobs of object
? 要理解閉包,首先必須理解Javascript特殊的變量作用域
? 變量的作用域無非就是兩種:全局變量和局部變量。
? Javascript語言的特殊之處,就在于函數(shù)內(nèi)部可以直接讀取全局變量。
? 例:
var n=999;
function f1(){
alert(n);
}
f1(); // 999
? 另一方面,在函數(shù)外部自然無法讀取函數(shù)內(nèi)的局部變量。
? 例:function f1(){
var n=999;
}
alert(n); // error
? 這里有一個地方需要注意,函數(shù)內(nèi)部聲明變量的時候,一定要使用var命令。如果不用的話,你實(shí)際上聲明了一個全局變量!
例:function f1(){
n=999;
}
f1();
alert(n); // 999
二、如何從外部讀取局部變量?
? 出于種種原因,我們有時候需要得到函數(shù)內(nèi)的局部變量。但是,前面已經(jīng)說過了,正常情況下,這是辦不到的,只有通過變通方法才能實(shí)現(xiàn)。
? 例:function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
在函數(shù)的內(nèi)部,再定義一個函數(shù)。f2可以讀取f1中的局部變量,那么只要把f2作為返回值,我們不就可以在f1外部讀取它的內(nèi)部變量了
? 上一節(jié)代碼中的f2函數(shù),就是閉包。
? 各種專業(yè)文獻(xiàn)上的"閉包"(closure)定義非常抽象,很難看懂。我的理解是,閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
? 由于在Javascript語言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,因此可以把閉包簡單理解成"定義在一個函數(shù)內(nèi)部的函數(shù)"。
? 所以,在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。
? 閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數(shù)內(nèi)部的變量,另一個就是讓這些變量的值始終保持在內(nèi)存中。
例:function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
? 1)由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。
? 2)閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當(dāng)作對象(object)使用,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。
繼承? 構(gòu)造函數(shù)的繼承
一、 構(gòu)造函數(shù)綁定
二、 prototype模式
三、 直接繼承prototype
四、 利用空對象作為中介
五、 拷貝繼承
? 非構(gòu)造函數(shù)的繼承
一、object()方法
二、淺拷貝
三、深拷貝
構(gòu)造函數(shù)的繼承? 比如,現(xiàn)在有一個"動物"對象的構(gòu)造函數(shù)。
function Animal(){
this.species="動物";}
? 還有一個"貓"對象的構(gòu)造函數(shù)。function Cat(name,color){
this.name=name;
this.color=color;
}
怎樣才能使"貓"繼承"動物"呢?
構(gòu)造函數(shù)綁定? 第一種方法也是最簡單的方法,使用call或apply方法,將
父對象的構(gòu)造函數(shù)綁定在子對象上,即在子對象構(gòu)造函數(shù)中加一行:
function Cat(name,color){
Animal.apply(this, arguments);
this.name=name;
this.color=color;
}
var cat1=new Cat("大毛","黃色");alert(cat1.species); // 動物
prototype模式? 第二種方法更常見,使用prototype屬性。
? 如果"貓"的prototype對象,指向一個Animal的實(shí)例,那么所有"貓"的實(shí)例,就能繼承Animal了。
Cat.prototype=new Animal();
Cat.prototype.constructor=Cat; // 重置構(gòu)造函數(shù)
var cat1=new Cat("大毛","黃色");
alert(cat1.species); // 動物
直接繼承prototype? 第三種方法是對第二種方法的改進(jìn)。由于Animal對象中,不變的屬性
都可以直接寫入Animal.prototype。所以,我們也可以讓Cat()跳過Animal(),直接繼承Animal.prototype。
? Animal對象改寫function Animal(){ }
Animal.prototype.species="動物";
? 然后,將Cat的prototype對象,然后指向Animal的prototype對象,這樣就完成了繼承。
Cat.prototype=Animal.prototype;
Cat.prototype.constructor=Cat; // 重置構(gòu)造函數(shù)var cat1=new Cat("大毛","黃色");alert(cat1.species); // 動物
? 與前一種方法相比,這樣做的優(yōu)點(diǎn)是效率比較高(不用執(zhí)行和建立Animal的實(shí)例了),比較省內(nèi)存。缺點(diǎn)是 Cat.prototype和Animal.prototype現(xiàn)在指向了同一個對象,那么任何對Cat.prototype的修改,都會反映到Animal.prototype。
? 所以,上面這一段代碼其實(shí)是有問題的。請看第二行
? Cat.prototype.constructor=Cat;
? 這一句實(shí)際上把Animal.prototype對象的constructor屬性也改掉了!
? alert(Animal.prototype.constructor); // Cat
利用空對象作為中介? 由于"直接繼承prototype"存在上述的缺點(diǎn),所以就有第四種方法,利用一個空對象作為中介。
var F=function(){};
F.prototype=Animal.prototype;
Cat.prototype=new F();
Cat.prototype.constructor=Cat;
? 我們將上面的方法,封裝成一個函數(shù),便于使用。
function extend(Child, Parent) {
var F=function(){};
F.prototype=Parent.prototype;
Child.prototype=new F();
Child.prototype.constructor=Child;
Child.uber=Parent.prototype; // 子對象訪問父對象通道}
extend(Cat,Animal);
var cat1=new Cat("大毛","黃色");alert(cat1.species); // 動物
這個extend函數(shù),就是YUI庫如何實(shí)現(xiàn)繼承的方法。
拷貝繼承
? 上面是采用prototype對象,實(shí)現(xiàn)繼承。我們也可以換一種思路,純粹采用"拷貝"方法實(shí)現(xiàn)繼承。簡單說,如果把父對象的所有屬性和方法,拷貝進(jìn)子對象,不也能夠?qū)崿F(xiàn)繼承嗎?這樣我們就有了第五種方法。
? 首先,還是把Animal的所有不變屬性,都放到它的prototype對象上。function Animal(){}
Animal.prototype.species="動物";
? 然后,再寫一個函數(shù),實(shí)現(xiàn)屬性拷貝的目的。
function extend2(Child, Parent) {
var p=Parent.prototype;
var c=Child.prototype;
for (var i in p) {
c[i]=p[i];
}
c.uber=p;
}
? 這個函數(shù)的作用,就是將父對象的prototype對象中的屬性,一一拷貝給Child對象的prototype對象。
? 使用的時候,這樣寫:
extend2(Cat, Animal);
var cat1=new Cat("大毛","黃色");
alert(cat1.species); // 動物
非構(gòu)造函數(shù)的繼承? 比如,現(xiàn)在有一個對象,叫做"中國人"。
var Chinese={
nation:'中國'};
? 還有一個對象,叫做"醫(yī)生"。var Doctor={
career:'醫(yī)生'}
請問怎樣才能讓"醫(yī)生"去繼承"中國人",也就是說,我怎樣才能生成一個"中國醫(yī)生"的對象?
這里要注意,這兩個對象都是普通對象,不是構(gòu)造函數(shù),無法使用構(gòu)造函數(shù)方法實(shí)現(xiàn)"繼承"。
object()方法? json格式的發(fā)明人Douglas Crockford,提出了一個
object()函數(shù),可以做到這一點(diǎn)。
function object(o) {
function F() {}
F.prototype=o;
return new F();
}
? 這個object()函數(shù),其實(shí)只做一件事,就是把子對象的prototype屬性,指向父對象,從而使得子對象與父對象連在一起。
? 使用的時候,第一步先在父對象的基礎(chǔ)上,生成子對象:
? var Doctor=object(Chinese);
? 然后,再加上子對象本身的屬性:
? Doctor.career='醫(yī)生';
? 這時,子對象已經(jīng)繼承了父對象的屬性了。
? alert(Doctor.nation); //中國
淺拷貝? 除了使用"prototype鏈"以外,還有另一種思路:把父對象的屬性,全部拷貝給子對象,
也能實(shí)現(xiàn)繼承。? 下面這個函數(shù),就是在做拷貝
function extendCopy(p) {
var c={};
for (var i in p) {
c[i]=p[i];
}
c.uber=p;
return c;
}
? 使用的時候,這樣寫:
var Doctor=extendCopy(Chinese);
Doctor.career='醫(yī)生';
alert(Doctor.nation); // 中國
? 但是,這樣的拷貝有一個問題。那就是,如果父對象的屬性等于數(shù)組或另一個對象,那么實(shí)際上,子對象獲得的只是一個內(nèi)存地址,而不是真正拷貝,因此存在父對象被篡改的可能。
? 請看,現(xiàn)在給Chinese添加一個"出生地"屬性,它的值是一個數(shù)組。? Chinese.birthPlaces=['北京','上海','香港'];? 通過extendCopy()函數(shù),Doctor繼承了Chinese。
? var Doctor=extendCopy(Chinese);
? 然后,我們?yōu)镈octor的"出生地"添加一個城市:
? Doctor.birthPlaces.push('廈門');
? 發(fā)生了什么事?Chinese的"出生地"也被改掉了!
? alert(Doctor.birthPlaces); //北京, 上海, 香港, 廈門
? alert(Chinese.birthPlaces); //北京, 上海, 香港, 廈門
? 所以,extendCopy()只是拷貝基本類型的數(shù)據(jù),我們把這種拷貝叫做"淺拷貝"。這是早期jQuery實(shí)現(xiàn)繼承的方式。
深拷貝? 所謂"深拷貝",就是能夠?qū)崿F(xiàn)真正意義上的數(shù)組和對象的拷貝。它的實(shí)現(xiàn)并不難,只要
遞歸調(diào)用"淺拷貝"就行了。
function deepCopy(p, c) {
var c=c || {};
for (var i in p) {
if (typeof p[i]==='object') {
c[i]=(p[i].constructor===Array) ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i]=p[i];
}
}
return c;
}
? 使用的時候這樣寫:
? var Doctor=deepCopy(Chinese);
? 現(xiàn)在,給父對象加一個屬性,值為數(shù)組。然后,在子對象上修改這個屬性:
? Chinese.birthPlaces=['北京','上海','香港'];
? Doctor.birthPlaces.push('廈門');
? 這時,父對象就不會受到影響了。
? alert(Doctor.birthPlaces); //北京, 上海, 香港, 廈門
? alert(Chinese.birthPlaces); //北京, 上海, 香港
? 目前,jQuery庫使用的就是這種繼承方法。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。