第二章 函數(shù)

我們已經(jīng)在第1章討論過,在javascript中,函數(shù)其實就是對象,使函數(shù)不同意其他對象的決定性特點是函數(shù)存在一個被稱為[[Call]]的內(nèi)部屬性。內(nèi)部屬性無法通過代碼訪問而是定義了代碼執(zhí)行時的行為。ECMAScript為javascript的對象定義了多種內(nèi)部屬性,這些內(nèi)部屬性都用雙重中括號來標注。

?[[Call]]屬性是函數(shù)獨有的,表明該對象可以被執(zhí)行。由於僅有函數(shù)擁有該屬性,ECMAScript定義typeof操作符對任何具有[[Call]]屬性的對象返回“funciton”。這在過去曾經(jīng)導(dǎo)致了一些問題,因為某些瀏覽器曾經(jīng)在正則表達式中包含了[[ Call]]屬性,導(dǎo)致後者被錯誤鑑定為函數(shù),現(xiàn)在,多有瀏覽器的行為都一致,typeof不會將正則表達式鑒別為函數(shù)了。

2.1聲明還是表達式

函數(shù)具有兩種字面形式,第一種事函數(shù)聲明,以function關(guān)鍵字開頭,後面跟著函數(shù)的名字。函數(shù)的內(nèi)容放在大括號內(nèi),例如下面就是函數(shù)聲明。


function add(num1,num2){return num1+num2}


第二種形式是函數(shù)表達式,funciton關(guān)鍵字後面不需要加上函數(shù)的名字。這種函數(shù)被稱為匿名函數(shù),因為函數(shù)對象本身沒有名字。取而代之的函數(shù)表達式通常會被一個變量或者引用,下面就是函數(shù)表達式。

var add =function(num1,num2){

return num1+num2;

};

這段代碼時機上將一個函數(shù)作為值賦給變量add,除了沒有函數(shù)名並在最後多了一個分號以外,函數(shù)表達式幾乎和函數(shù)聲明完全一樣。函數(shù)表達式賦值通常在最後一個分號,就如同其他對像的賦值一樣。

雖然這兩種代碼形式頗為相似,但是他們有一個非常重要的區(qū)別,函數(shù)聲明會被提升至上下文(要麼是該函數(shù)被聲明時所在的函數(shù)的範圍,要麼是全局範圍)的頂部,這意味著你可以先使用函數(shù)後聲明他們。例如:

var result =add(5,5);

function add(num,num2){

return num1+num2;

}

這段代碼看上去似乎會造成錯誤,但實際上可以工總,那是因為javascript引擎將函數(shù)聲明提升至頂部來執(zhí)行,就好像他被寫成如下形式:

//how the javascript engine interpers the code

function add(num1,num2){

return num1+num2;

}

var result=add(5,5);

javascript能對函數(shù)聲明進行提升,這是因為引擎提前知道了函數(shù)的名字。而函數(shù)表達式僅能通過變量引用,因此無法提升,所以下面這段代碼會導(dǎo)致錯誤

//error

var result =add(5,5);

var add=function(num1,num2){

return num1+num2;

};

只要你始終在使用函數(shù)之前定義他們,你就可以隨意使用函數(shù)聲明或表達式。

2.2 函數(shù)就是值

函數(shù)是javascript的一大重點,你可以像使用對象一樣使用函數(shù)。也可以將它們福祉給變量,在對象中添加它們,將它們當成參數(shù)傳遞給別的函數(shù),或從別的函數(shù)中返回。基本上只要是可以使用其他引用值的地方,你就可以使用函數(shù)。這使得javascript的函數(shù)威力無窮,考慮下面的例子:

function sayHi(){

console.log("Hi");

}

sayHi();//outputs "hi";

var sayHi2=sayHi;

sayHi2();//outputs "hi";

這段代碼首先要有一個函數(shù)聲明sayHi。然後有一個變量sayHi2被創(chuàng)建並被賦予sayHi的值,sayHi和sayHi2現(xiàn)在指向同一個函數(shù),兩者都可以被執(zhí)行,並具有相同的結(jié)果。為了更好的理解這點,讓我來看一下用function構(gòu)造函數(shù)重寫具有相同功能的代碼。

var sayHi=new Function("console.log(\"Hi!\");");

sayHi();

var sayHi2=sayHi;

sayHi2(); //outputs "Hi"

Function 構(gòu)造函數(shù)更加清楚地表明sayHi能夠像其他對象一樣被傳來傳去。只要你記住函數(shù)就是對象,很多行為就變得容易理解了。

例如。你可以將函數(shù)當成參數(shù)傳遞給其他的函數(shù)。javascript數(shù)組的sort()方法接受耶和華 i 個比較函數(shù)作為可選參數(shù),每當數(shù)組中兩個值需要進行比較時都會調(diào)用此函數(shù)。如果第一個值小於第二個。比較函數(shù)會返回一個負數(shù)。如果第一個值大於第二個,比較函數(shù)會返回一個正數(shù) ,如果兩個值相等,函數(shù)返回0;

在默認情況下,sort()將數(shù)組中的每個對象轉(zhuǎn)換成字符串然後進行比較。這意味著,你無法在不指定比較函數(shù)的情況下為數(shù)字的數(shù)組進行精確排序。

var number=[1,5,8,4,7,10,2,6];

numbers.sort(function(first,second)){

return first-second;

}

console.log(numbers);

numbers.sortI();

console.log(numbers);

在本例,被傳遞給sort()的比較函數(shù)其實是一個函數(shù)表達式。請注意它沒有名字,僅作為引用被傳遞給另一個函數(shù)(著使得它被稱為匿名函數(shù))。比較函數(shù)對兩個值進行比較相減法以返回正確的結(jié)果。

作為對比,第二次sort()不使用比較函數(shù)。結(jié)果和預(yù)期不太一樣,1後面跟著的事10.這是因為默認的比較函數(shù)將所有值都轉(zhuǎn)換成字符串比較。

2.3 參數(shù)

javascript函數(shù)的另一個獨特之處在於你可以給函數(shù)傳遞任意的參數(shù)卻不造成錯誤。那是因為函數(shù)參數(shù)實際上被保存在一個被稱為arguments的蕾絲數(shù)組的對象中。如果一個普通的javascirpt數(shù)組,arguments可以自由增長來包含人一個數(shù)的值,這些值可通過數(shù)字索引來引用。arguments的length屬性會告訴你目前與有多少個值。

arguments對象自動存在於函數(shù)中。也就是說,函數(shù)的命名參數(shù)不過是為了方便,並不真的限制了該函數(shù)可以接受參數(shù)的個數(shù)。


注意:arguments對象不是一個數(shù)組的實例,其擁有的方法與數(shù)組不同,array.isArray(arguments) 永遠返回false。


另一方面,javascript耶沒有忽視那些命名參數(shù)。函數(shù)期望的參數(shù)個數(shù)保存在函數(shù)的length屬性中。還記得嗎?函數(shù)就是對象,所有它可以有屬性。length屬性表明了該函數(shù)的期望參數(shù)個數(shù)。了解函數(shù)的期望參數(shù)個數(shù)在javascript中是非常重要的,因為給他傳遞過多或者過少的參數(shù)都不會拋出錯誤。

下面是一個簡單的使用arguments和函數(shù)的期望參數(shù)個數(shù)的例子。注意實際傳入的參數(shù)的數(shù)量不影響函數(shù)的期望參數(shù)的個數(shù)。


function reflect(value){

return value;

}

console.log(reflect("Hi!"));

console.log(reflect("Hi"),25);

?console.log(reflect.length);//1

reflect=function(){

return arguments[0];

};

console.log(reflect("Hi")); //hi

console.log(reflect("hi",25));

console.log(reflect.length);//0;


本例先定義了一個具有單一命名的參數(shù)reflect()函數(shù),但是當有兩個參數(shù)傳遞給它時沒有任何錯誤發(fā)生。由於只有一個命名參數(shù),length屬性為1.代碼隨後重新定義reflect()為無命名參數(shù)的函數(shù),它返回傳入的第一個參數(shù)arguments『0』。這個新版本的函數(shù)和前一個版本的輸出一模一樣,但是他的length為0;

因為使用了命名參數(shù),reflect()的第一個實現(xiàn)容易理解(和在別的語言裡一樣)。使用arguments對象的版本有點讓人莫名其妙,因為沒有命名參數(shù),你不得不瀏覽整個函數(shù)體來確定是否使用了參數(shù),這就是為什麼許多開發(fā)者盡可能避免arguments的原因。

不過,在某些情況下使用argumengs比命名參數(shù)更有效果。例如,假設(shè)你想創(chuàng)建一個函數(shù)接受任意數(shù)量的參數(shù)並返回它們的和,因為你不知道會有多少個參數(shù),所以你無法使用命名參數(shù)。在這種情況下,使用arguments是最好的選擇。


function sum(){

var result=0,

i=0;

len=arguments.length;

while(i<len){

result+=arguments[i];

i++;

}

return result;

}

console.log(sum(1,2));

console.log(sum(3,4,5,6));

console.log(sum());//0


sum()函數(shù)接受任意數(shù)量的參數(shù)並在while循環(huán)中遍歷他們的值並求和。這就和對一個數(shù)組中的數(shù)字球和一樣。由於result出事值為0.該函數(shù)就算沒有參數(shù)也能正常工作。

2.4 重載

大多數(shù)面向?qū)ο笳Z言支持函數(shù)重載,它能讓一個函數(shù)具有多個簽名。函數(shù)簽名由函數(shù)的名字,參數(shù)的個數(shù)及其類型組成。因此,一個函數(shù)可以有一個接受一個字符串參數(shù)的簽名和另一個接受兩個數(shù)字參數(shù)的簽名。javascript語言根據(jù)實際傳入的參數(shù)決定調(diào)用函數(shù)的哪個版本。

之前已經(jīng)提過,javascript函數(shù)可以接受任意數(shù)量的參數(shù)且參數(shù)類型完全沒有限制。這說明javascript函數(shù)其實根本沒有簽名,因此也不存在重載。看看當你試圖聲明兩個同名函數(shù)會發(fā)生什么。

javascript函數(shù)沒有簽名這個事實并不意味著你不能模仿函數(shù)重載。你可以用arguments對象獲取傳入的參數(shù)個數(shù)并決定怎么處理。例如:

function sayMessage(message){

if(arguments.length===0){

message="Default message";

}

console.log(message);

}

sayMessage("Hello");

本例中國年,sayMessage()函數(shù)的行為視傳入?yún)?shù)的個數(shù)而定。如果沒有傳入?yún)?shù),那么就使用默認的信息。否則使用第一個傳入的參數(shù)信息。和其他樹言中的重載相比。這里有更多的人為介入。但是結(jié)果是相同的。如果你還想檢查不同的數(shù)據(jù)類型,你可以使用typeof和instanceof。

注意:在實際使用中,檢查命名參數(shù)是否為未定義比依靠arguments .length 更常見。

?2.5 對象方法

第一章中介紹了可以在任何時候給對象添加或刪除屬性。如果屬性的值是函數(shù),則該屬性被稱為方法。你可以像添加屬性那樣給對象添加方法。例如,在下面的代碼中,變量person被賦予了一個對像的字面形式,包含屬性name和方法sayName。

var person={

name:"Nicholas",

sayName:function(){

console.log(person.name)

? ? ? ? };

};

person.sayName();

注意定義數(shù)據(jù)屬性和方法的愈發(fā)完全相同--標示符后面跟著冒號和值。只不過sayName的值正好是一個函數(shù)。定義好以后你立刻就能在對象上調(diào)用方法person.sayName();

2.5.1 this對象

你可能已經(jīng)注意到前面的例子中一些奇怪之處。sayName()方法直接引用了person.name。在方法和對象間建立了緊耦合。有太多理由證明這是有問題的。首先,如果你改變了變量名,你也必須要改變方法中引用的名字。其次,這種緊耦合使得痛一個方法很那被不同對象使用。幸好javascript對此有一個解決辦法。

javascript所有的函數(shù)作用域內(nèi)都有一個this對象代表調(diào)用函數(shù)的對象。在全局作用域中,this代表全局對象(瀏覽器里的window)。當一個函數(shù)作為對象的方法被調(diào)用時,默認this的值等于那個對象。所以你應(yīng)該在方法內(nèi)引用this而不是直接飲用一個對象。前例代碼改寫如下。

var person={

name:"Nicholas",

sayName:function(){

console.log(this.name);

};

};

person.sayName();

這段代碼和前面的版本輸出相同,但是這一次,sayName()引用this而不是person。這意味著你可以輕易改變變量名,甚至是將該函數(shù)用在不同對象上。

function sayNameForAll(){

console.log(this.name);

}

var person1={

name:"Nicholas",sayName:sayNameForAll};

var person2={

name:"Greg",

sayName:sayNameForAll

};

var name="Michael";

person1.sayName();

person2.sayName();

sayNameForAll();

本例先定義函數(shù)sayNameForAll,然后以字面形式創(chuàng)建兩個對象以sayNameForAll函數(shù)作為sayName方法。函數(shù)就是飲用值,所以你可以把它們作為屬性值賦給任意個對象。當person1調(diào)用sayName方法時,輸出“Naicholas”;person2則輸出“Greg”,那是因為this函數(shù)調(diào)用時才設(shè)置,所以this.name是正確的。

本例最后部分定義了全局變量name。全局變量被認為是全局對象的屬性,所以當調(diào)用sayNameForAll時輸出"Michael".

2.5.2 改變this

在javascript中,使用和操作函數(shù)中this的能力時良好地面向?qū)ο缶幊痰年P(guān)鍵。函數(shù)會在各種不同上下文中被使用,它們必須到哪里都能正常工作。一般this會被自動設(shè)置,但是你可以改變它們的值來完成不同的目標。有3種函數(shù)方法允許你改變this的值。(記住函數(shù)時對象,而對象可以有方法,所以函數(shù)也有)

1.call()方法

第一個用戶操作this的函數(shù)方法是call(),它以指定的this值和參數(shù)來執(zhí)行函數(shù)。call()的第一個參數(shù)制定了函數(shù)執(zhí)行時this的值,其后的所有參數(shù)都是需要被傳入函數(shù)的參數(shù)。假設(shè)你更新sayNameForAll讓它接受一個參數(shù),代碼如下:

function sayNameForAll(label){

console.log(label+":"+this.name);

}

var person1={

name:"Nicholas"

};

var person2={

name:"Greg"

};

var name ="Michael";

sayNameForAll.call(this,"global");//outputs "global:micahael"

sayNameForAll.call(person1,"person1");//outputs "person1:Nicholas"

sayNameForAll.call(person2,"person2");//outputs "person2:Greg"


在本例中,sayNameForAll()接受一個label參數(shù)用于輸出。然后該函數(shù)被調(diào)用3次。注意調(diào)用函數(shù)時在函數(shù)名后沒有小括號,因為它被作為對象訪問而不是被執(zhí)行的代碼。第一次調(diào)用使用全局this并傳入?yún)?shù)"global"來輸出“blobal:michael”。之后兩吃調(diào)用分別使用person1 和person2。由于使用了call()方法,你不需要講函數(shù)加入每個對象--你顯式指定了this的值而不是javascript引擎自指定。

2. apply()方法

apply()時你可以用來操作this的第二個函數(shù)方法。apply()的工作方式和call()完全一樣,但它只接受兩個參數(shù):this的值和一個數(shù)組或者類似數(shù)組的對象,內(nèi)含需要被傳入函數(shù)的參數(shù)(也就是說你可以把arguments對象作為apply()的第二個參數(shù))。你不需要像使用call()那樣指定一個參數(shù),而是可以輕松傳遞正個數(shù)組給apply()。除此之外,call()和apply()表現(xiàn)的完全一樣,下例演示了apply()的用法。

function sayNameForAll(label){

console.log(label+":"+this.name);

}

var person1={

name:"Nicholas"

};

var person2={

name:"Greg"

};

var name ="Michael";

sayNameForAll.call(this,[global]);//outputs "global:micahael"

sayNameForAll.call(person1,[person1]);//outputs "person1:Nicholas"

sayNameForAll.call(person2,[person2]);//outputs "person2:Greg"

這段代碼借用了前例并用apply()替換了call();結(jié)果完全相同。你通常會根據(jù)你手頭已有的數(shù)據(jù)決定使用哪個方法。如果哦哦你已經(jīng)有一個數(shù)組,你應(yīng)該用apple();如果你有的只是一個個單獨的變量,則用call()

3 bind()方法

改變this的第三個函數(shù)方法是bind()。ECMAScript5中添加的這個方法和之前的那兩個有些不同。 按照慣例。bind()的第一個參數(shù)是要傳遞給新函數(shù)的this的值。其他所有參數(shù)代表需要唄永久設(shè)置在新函數(shù)中的命名參數(shù)。你可以在之后繼續(xù)設(shè)置任何非永久參數(shù)。

下面代碼掩飾了兩個使用bind()的例子。創(chuàng)建sayNameForPerson1()函數(shù)并將person1綁定微其this對象的值。然后創(chuàng)建sayNameForPerosn2()并將person2并定為其this對象的值,“person2”綁定微其第一個參數(shù)。

function sayNameForAll(label){

console.log(label+":"+this.name);

}

var person ={

name:"Nicholas"

};

var person2={

name:"Greg"

};

//create a function just for person1

var sayNameForPerson1=sayNameForAll.bind(person1);

sayNameForPerson1("person1");//outputs "person1:"Nicholas

//create a function just for person2

var sayNameForPerson2=sayNameForAll.bind(person2,"person2");

sayNameForPerson2();

//attaching a method to an ?object doesn't change this

person2.sayName=sayNameForPerson1;

person2.sayName("person2");

sayNameForPerson1()沒有綁定參數(shù),所以你仍然需要傳入label參數(shù)用于輸出。sayNameForPerson2()不僅綁定this為person2,同時也綁定了第一個參數(shù)為“person2”。這意味著你可以調(diào)用sayNameForPerson2()而不傳入任何額外參數(shù)。

2.6 總結(jié)

javascript函數(shù)的獨特之處在于他們同時也是對象,也就是說他們可以被訪問,復(fù)制和覆蓋,就像其他對象一樣。javascript中的函數(shù)和其他對象最大的區(qū)別在于他們有一個特殊的內(nèi)部屬性[[Call]],包含了該函數(shù)的執(zhí)行指令。typeof操作符會在對象內(nèi)查找這個內(nèi)部屬性,如果找到,它返回“function”;

函數(shù)的字面形式有兩種種;聲明和表達式。函數(shù)的聲明視function關(guān)鍵字右邊跟著函數(shù)名稱。函數(shù)聲明會被提升至上下文頂部。函數(shù)表達式可被用于任何使用值的地方,例如賦值語句,函數(shù)參數(shù)活另一個函數(shù)的返回值。

函數(shù)是對象,所以存在一個function構(gòu)造函數(shù)。你可以用function構(gòu)造函數(shù)創(chuàng)建新的函數(shù),不過沒有人會建議你這么做,因為它會使你的代碼難以理解和調(diào)試。但是有時你可能不得不使用這個方法。例如在函數(shù)的真實形式直到運行時才能確定的時候。

為了理解javascript的面相對象編程。你需要好好理解它的函數(shù)。因為javascript沒有類的概念,能夠幫助你實現(xiàn)聚合和繼承的只有函數(shù)和其他對象了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內(nèi)容

  • 也許你還沒有理解構(gòu)造函數(shù)和原型對象的時候已經(jīng)在javascript的路上走了很久,但直到你很好的掌握它們之前你不會...
    WanLum閱讀 415評論 0 1
  • 盡管javascript里有大量內(nèi)建引用對象,很可能你還說會頻繁創(chuàng)建自己的對象。當你在這么做的時候,記得javas...
    WanLum閱讀 535評論 1 3
  • 學(xué)習(xí)如何創(chuàng)建對象時理解面向?qū)ο蟮牡谝徊健5诙繒r理解繼承。在傳統(tǒng)面向?qū)ο蟮恼Z言中,類從其他類繼承屬性。然而在jav...
    WanLum閱讀 267評論 0 0
  • javascript有很多創(chuàng)建對象的模式,完成工作的方式也不只一種。你可以隨時定義自己的類型或自己的泛用對象。可以...
    WanLum閱讀 266評論 0 0
  • 為何叫做 shell ? shell prompt(PS1) 與 Carriage Return(CR) 的關(guān)系?...
    Zero___閱讀 3,178評論 3 49