《Javascript》高級程序設(shè)計 第五章 詳細(xì)解釋引用類型(下)

我的文章會在我的Blog同步更新,Blog剛搭了幾天,用于記錄我的學(xué)習(xí)過程:Million

<h2>5.5Function類型</h2>
作者說ECMAScript中最有意思的是函數(shù),有意思的根源在于,函數(shù)實際上是對象。每個函數(shù)都是Function類型的實例
與其他引用類型一樣,函數(shù)類型也有屬性和方法。由于函數(shù)是對象,因此函數(shù)名實際上只是一個指向函數(shù)對象的指針,不會與某個函數(shù)綁定

函數(shù)通常是使用函數(shù)聲明語法定義的,如下面的例子所示:

function sum(num1,num2){
     return num1+num2;
}

這與下面使用函數(shù)表達式定義函數(shù)的方法幾乎相差無幾:

var sum = function(num1,num2){
     return num1+num2;
};   // ← 注意最后的分號,聲明完變量必加的分號

第二段代碼定義了變量sum并將其初始化為一個函數(shù)(注意這句話的順序,把一個定義好的變量初始化為一個函數(shù))。我們可以發(fā)現(xiàn)用函數(shù)表達式創(chuàng)建的函數(shù),function關(guān)鍵字后面沒有函數(shù)名。因為沒有必要。通過變量sum即可以引用到函數(shù)(注意,你不能說變量sum是函數(shù)名)。另外,注意函數(shù)末尾有一個分號,因為說到底sum還是個變量,只是變量的值是一個函數(shù)!


最后一種定義函數(shù)的方式,因為函數(shù)是對象,所以可以使用Function構(gòu)造函數(shù)。Function可以接收任意個數(shù)量的參數(shù),最后一個參數(shù)始終被當(dāng)作函數(shù)體,前面的都是函數(shù)的參數(shù)。例子:

var sum = new Function("num1","num2","num3","return num1+num2");   //不推薦

從技術(shù)的角度講,這是一個函數(shù)表達式,但是不推薦使用。因為這種語法會導(dǎo)致解析兩次代碼(第一次是解析常規(guī)ECMAScript,第二次是解析傳入構(gòu)造函數(shù)中的字符串),從而影響性能。不過這種語法有利于理解“函數(shù)是對象,函數(shù)名是指針”這個概念。


由于函數(shù)名僅僅是指針(好唏噓,函數(shù)名以為用它的名字創(chuàng)建的函數(shù)就是它的,結(jié)果,它只是一個指針。)一個對象可以有很多指針,所以一個函數(shù)也可以有多個名字。例子:

function sum(num1,num2){
     return num1+num2;
}

var anotherSum = sum;
alert(anotherSum(10,10));    //20

sum = null;
alert(anotherSum(10,10));     //20

即使把sum給設(shè)置為null,但因為這個指針已經(jīng)復(fù)制了一份給anotherSum,所以這個函數(shù)對象還是可以被引用到,不會因為sum不見了,函數(shù)就跟著沒了。(親測PHP似乎就不能用這種方法“轉(zhuǎn)換函數(shù)名”)注意,使用不帶圓括號的函數(shù)名是訪問函數(shù)指針,使用帶括號的函數(shù)名就表示要調(diào)用這個函數(shù)


<h3>5.5.1沒有重載</h3>
把函數(shù)名想象為指針,就能理解為什么ECMAScript沒有重載。例子:

function sum(num1,num2){
    return num1+100;
}
function sum(num1){
    return num1+200;
}

console.log(sum(100,100));   // 300

顯然,例子中聲明了兩個同名函數(shù),而結(jié)果是后面的函數(shù)覆蓋了前面的函數(shù)。因為在創(chuàng)建第二個函數(shù)時,實際上覆蓋了引用第一個函數(shù)的變量addSomeNumber。


<h3>5.5.2函數(shù)聲明與函數(shù)表達式</h3>
實際上,解析器在向執(zhí)行環(huán)境中加載數(shù)據(jù)時,對函數(shù)聲明和函數(shù)表達式并非一視同仁解析器會率先讀取函數(shù)聲明并使其在執(zhí)行任何代碼之前可用(可以訪問);至于函數(shù)表達式,就必須等到解析器執(zhí)行到它所在的代碼行,才會真正被解釋執(zhí)行。(所以函數(shù)聲明有函數(shù)聲明提升(function declaration hoisting),函數(shù)表達式?jīng)]有)函數(shù)表達式不能寫在要調(diào)用這個函數(shù)的代碼之后。
函數(shù)聲明提升:Javascript引擎在第一遍會聲明函數(shù)并將它們放到源代碼樹的頂部。所以即使聲明函數(shù)的代碼在調(diào)用它的代碼后面。JavaScript也能把函數(shù)聲明提升到頂部。
除了什么時候可以通過變量訪問函數(shù)這一點區(qū)別之外,函數(shù)聲明與函數(shù)表達式的語法其實是等價的。再無其他差別。

? 有人覺得不妨在使用函數(shù)表達式的同時給函數(shù)命名,like this :

var sum = function sum(){ }
但這種語法在 Safari 中會出錯。

<h3>5.5.3作為值的函數(shù)</h3>
因為ECMAScript中的函數(shù)名本身就是變量,所以函數(shù)也可以作為一個值來使用。也就是說,不僅可以像傳遞參數(shù)一樣把一個函數(shù)當(dāng)作參數(shù)傳遞給一個函數(shù),而且可以將一個函數(shù)作為另一個函數(shù)的結(jié)果返回。例子:

function callSomeFunction(someFunc,someArgument){
     return someFunc(someArgument)
}

這個函數(shù)接收兩個參數(shù),一個是函數(shù),一個是普通參數(shù),這個普通參數(shù)給傳進來的函數(shù)使用。然后,就可以像下面這樣傳遞函數(shù)了:

function add10(num){
    return num+10;
}
var result1 = callSomeFunction(add10,10);   //注意函數(shù)作為參數(shù)傳遞是不帶括號的,帶括號的函數(shù)會執(zhí)行,參數(shù)就變成這個函數(shù)執(zhí)行完成返回的那個值了
alert(result1);                             //20               

function getGreeting(name){
    return "Hello " + name;
}

var result2 = callSomeFunction(getGreeting,"Nicholas");
alert(result2);                              // Hello Nicholas

注意,要訪問函數(shù)的指針而不執(zhí)行函數(shù)的話,必須去掉函數(shù)名后面的括號。因此上面例子給callSomeFunction()傳遞的是add10 和 getGreeting。而不是執(zhí)行它們之后的結(jié)果。


可以從一個函數(shù)中返回一個函數(shù),是一項極為有用的技術(shù)。例如,假設(shè)有一個對象數(shù)組(由對象組成的數(shù)組),我們想要根據(jù)對象中的某一個屬性對數(shù)組進行排序,而傳遞給sort()方法的比較函數(shù)要接收兩個參數(shù),即要比較的值。可是,我們需要一種方式來告訴sort()按照哪個對象屬性排序。要解決這個問題,可以定義一個函數(shù),它接收一個屬性名,然后根據(jù)屬性名來創(chuàng)建一個匿名比較函數(shù)。下面就是這個函數(shù)的定義:

function creatComparisonFunction(propertyName){
    return function(obj1,obj2){
        var val1 = obj1[propertyName];   //細(xì)節(jié),用方括號訪問保存在變量propertyName中的屬性,終于知道用處了
        var val2 = obj2[propertyName];

        if(val1<val2){
            return -1;
        }else if(val1>val2){
            return 1;
        }else{
            return 0;
        }
    };
}

看起來很復(fù)雜,其實就是嵌套了一個函數(shù),而且內(nèi)部函數(shù)前面加了一個return操作符。在內(nèi)部函數(shù)接收到propertyName參數(shù)后,它會用方括號表示法來取得給定屬性的值。取得這個對象屬性后,就用這個屬性進行比較。完成對象數(shù)組的排序。用法:

var data = [{name:"Zachary",age:28},{name:"Nicholas",age:29}];
data.sort(creatComparisonFunction("name"));
console.log(data[0].name);                    //Nicholas

data.sort(creatComparisonFunction("age"));
console.log(data[0].name);                    //Zachary



<h3>5.5.4函數(shù)內(nèi)部的屬性</h3>
在函數(shù)內(nèi)部,有兩個特殊的對象(函數(shù)對象中的對象):argumentsthis。arguments在第三章介紹過,是一個類數(shù)組對象,包含這個傳入函數(shù)的所有參數(shù)。雖然arguments的主要用途是保存所有函數(shù)參數(shù),但arguments對象還有一個名叫callee的屬性,該屬性是一個指針。arguments.callee( )可以指代這個函數(shù)本身。用處是可以消除函數(shù)的執(zhí)行與函數(shù)名之間的耦合。還是那個階乘的例子:

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num*factorial(num-1);
    }
}

如果我把factorial這個函數(shù)名改為另一個函數(shù)的函數(shù)名:

var trueFactorial = factorial;
function factorial(){
     return 0;
}
console.log(trueFactorial(5))               // 0

這個時候trueFactorial()函數(shù)就出錯了,要降低這種耦合,使用arguments.callee( )是最好方法。

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num*arguments.callee(num-1);  //即使factorial被改寫,arguments.callee()仍代表當(dāng)前函數(shù)。
    }
}

函數(shù)內(nèi)部的另一個特殊對象是this,其行為與Java和C#中的this大致類似。換句話說,this引用的是函數(shù)據(jù)以執(zhí)行的環(huán)境對象。當(dāng)在網(wǎng)頁的全局作用域中調(diào)用函數(shù)時,this對象引用的就是window。例子:

indow.color = "red";
var o = {color:"blue"};

function sayColor(){
     console.log(this.color);
}
sayColor();               //red
o.sayColor=sayColor;
o.sayColor();             //blue

函數(shù)sayColor( )首先是在全局作用域中定義的,它引用了this對象。但在調(diào)用它之前,并不確定是在哪個作用域中調(diào)用。當(dāng)在全局作用域中調(diào)用sayColor( ),this引用的是全局對象,所以this.color = window.color。結(jié)果返回red。當(dāng)把這個函數(shù)賦給對象o,并調(diào)用o.sayColor( )時,this引用的是對象o,因此this.color = o.color。結(jié)果返回blue。(親測把sayColor()函數(shù)放在另一個函數(shù)中去調(diào)用,最后得到的結(jié)果還是window,再嵌多一層函數(shù)也還是window,說明this引用的不是作用域,而是函數(shù)據(jù)以執(zhí)行的環(huán)境對象,還有一個原因是當(dāng)你把函數(shù)嵌套進另一個函數(shù)的時候,返回是window的原因是你此時調(diào)用的實際上已經(jīng)不是一個方法,而是一個函數(shù)。

ECMAScript5還規(guī)范化了另一個函數(shù)對象的屬性caller。除了Opera早期版本不支持,其他瀏覽器IE,F(xiàn)irefox,Chrome,和Safari的所有版本及Opear9.6都支持caller屬性。這個屬性保存著調(diào)用當(dāng)前函數(shù)的函數(shù)的引用,如果是在全局作用域中調(diào)用當(dāng)前函數(shù),它的值為null。例如:

function outer(){
    inner();
}
function inner(){
    console.log(inner.caller);     //會打印出outer()的源代碼
}
outer();

以上代碼會打印出outer( )的源代碼,因為outer()調(diào)用了inner(),所以inner.caller就指向outer()。當(dāng)然不能試圖用inner.caller( )來調(diào)用outer。否則會陷入無限回調(diào)使代碼出錯。為了更松散的耦合,也可以通過arguments.callee.caller()來訪問相同的信息。

function outer(){
    inner();
}
function inner(){
    console.log(arguments.callee.caller);     //會打印出outer()的源代碼
}
outer();

arguments在嚴(yán)格模式下無法使用,arguments對象也有一個caller屬性,但這個值永遠是undefined。定義這個屬性是為了分清arguments.caller和函數(shù)的caller屬性,這個變化是出于增強這門語言的安全性。
嚴(yán)格模式還有一個限制:不能為函數(shù)的caller屬性賦值,否則會導(dǎo)致錯誤。


<h3>5.5.5函數(shù)屬性和方法</h3>
前面提過ECMAScript中函數(shù)是對象,所以函數(shù)就有屬性和方法,上面介紹了一個caller。還有兩個是lengthprototype
length屬性表示函數(shù)希望接收的命名參數(shù)的個數(shù)。命名參數(shù)有1個,length的值就是1,參數(shù)有兩個,length的值就是2。不解釋。

作者說,在ECMAScript核心所定義的全部屬性中,最耐人尋味的就要數(shù)prototype屬性了(不止函數(shù)的prototype,其他對象的prototype屬性也耐人尋味!)對與ECMAScript中的引用類型而言,prototype是保存他們所有實例方法的真正所在。換句話說,諸如valueOf(),toString()等方法實際上都是保存在了prototype屬性名下。只不過是通過各自對象的實例訪問到了。在創(chuàng)建自定義引用類型以及實現(xiàn)繼承時,prototype屬性的作用是極為重要的(第6章會詳細(xì)介紹)。
在ECMAScript中,prototype屬性是不可枚舉的,因此無法用for-in無法發(fā)現(xiàn)prototype這個屬性。(對property屬性的講解到此為止)

每個函數(shù)都包含兩個非繼承而來的方法:apply()call()。這兩個方法的用途都是在特定的作用域中調(diào)用函數(shù)。實際上等于設(shè)置函數(shù)體內(nèi)this對象的值。兩個方法的區(qū)別只是在傳入?yún)?shù)的方法上有區(qū)別而已。
首先,apply()方法接收兩個參數(shù):一個是在其中運行函數(shù)的作用域,另一個是參數(shù)數(shù)組。其中,第二個參數(shù)可以是Array的實例(就是普通數(shù)組),也可以是arguments對象。例子:

function sum(num1,num2){
    return num1+num2;
}

function callSum1(num1,num2){
    return sum.apply(this,arguments);
}

function callSum2(num1,num2){
    return sum.apply(this,[num1,num2]);
}
console.log(callSum1(10,10));          //20
console.log(callSum2(10,10));          //20

在上面這個例子中,callSum1( )在執(zhí)行sum()函數(shù)時傳入了 this 作為 this 值(???)(因為是在全局作用域中調(diào)用的,所以傳入的就是window對象)和arguments對象,而callSum2傳入的第二個參數(shù)則是數(shù)組型。這兩個函數(shù)都會正常執(zhí)行并返回正確結(jié)果。(這個例子只是為了說明兩種傳入?yún)?shù)的方式都可以,還沒體現(xiàn)出apply()和call()的作用)

?在嚴(yán)格模式下,未指定環(huán)境對象而用函數(shù),則this值不會轉(zhuǎn)型為window(嚴(yán)格模式下的this不會默認(rèn)轉(zhuǎn)型為window)除非明確把函數(shù)添加到某個對象或者調(diào)用apply()或call(),否則this值將是undefined

call()方法與apply()方法的作用相同,區(qū)別僅在于接收參數(shù)的方式不同。對于call()方法,第一個參數(shù)仍是this不變,變化的是其余參數(shù)都是直接傳遞給函數(shù)。換句話說,在使用call()方法時,參數(shù)都是逐個列舉出來的。例子:

function sum(num1,num2){
    return num1+num2;
}

function callSum1(num1,num2){
    return sum.call(this,num1,num2);
}
console.log(callSum1(10,10));          // 20

在使用call()方法的情況下,callSum( )必須明確地傳入每一個參數(shù)。結(jié)果與apply()沒有什么不同。使用哪一種完全是看你覺得哪個更方便(MD以前還覺得這兩個方法多深奧ZZ)
apply()和call()的真正強大之處,是能夠擴充函數(shù)賴以運行的作用域。例子:

window.color = "red";
var o = {color:"blue"};

function sayColor(){
    console.log(this.color)
}

sayColor();                   //red
sayColor.call(window);        //red
sayColor.call(o);             //blue

第一次調(diào)用sayColor()時在全局作用域中調(diào)用它,會顯示“red”——因為對this.color的求值會轉(zhuǎn)換成對window.color的求值。當(dāng)運行sayColor。call(o)時,函數(shù)的執(zhí)行環(huán)境對象就不一樣了,因此此時函數(shù)體內(nèi)的this對象指向了O,于是結(jié)果顯示“blue”。
使用call()和apply()的最大好處,就是對象不需要與方法有任何耦合關(guān)系(不用為了在這個對象內(nèi)調(diào)用方法而把函數(shù)寫進對象中)。在書中前面的第一個例子中(筆記沒有),為了在對象o中調(diào)用sayColor( ),要把這個函數(shù)寫進對象o中,再通過o調(diào)用,而在這里的例子中,就不需要那么多步驟了。
ECMAScript5又定義了一個方法:bind()(所以函數(shù)對象的方法有三個)。這個方法會創(chuàng)建一個函數(shù)的實例(會創(chuàng)建一個函數(shù)),其this值會被綁定到傳給bind()函數(shù)的值。例子:

window.color = "red";
var o = {color:"blue"};
function sayColor(){
    console.log(this.color)
}

var objSayColor = sayColor.bind(o);
objSayColor();

在這里,sayColor()調(diào)用bind()并傳入對象o,創(chuàng)建了objSayColor()函數(shù)。objSayColor()的this值等于o。因此無論在哪個環(huán)境對象中調(diào)用這個函數(shù),都會看到“blue”。這種技巧的優(yōu)點參考第22章。
因為是ECMAScrip5才有的方法,所以可以使用bind() 的瀏覽器有: IE9+,F(xiàn)irefox4+,Safari 5.1+,Opera 12+和Chrome。(兼容性挺低的)

最后說每個引用類型值都有繼承的,都會被重寫的toLocaleString()和toString(),valueOf(),這三個方法都始終返回函數(shù)的代碼。返回代碼的格式根據(jù)瀏覽器的不同會有差異。因為有返回值差異,所以返回的結(jié)果無法被用來實現(xiàn)任何功能,不過這些信息在調(diào)試代碼時很有用。

<h2>5.6基本包裝類型</h2>
讀到這里又刷新了三觀。這里說Boolean、Number、String是3個ECMAScript提供的特殊引用類型。這些類型與本章介紹的其他引用類型相似,但同時也具有與各自的基本類型相應(yīng)的特殊行為。實際上,每創(chuàng)建一個基本類型的時候,后臺就會創(chuàng)建一個對應(yīng)的基本包裝類型對象(WTF???),從而讓我們能夠調(diào)用一些方法來操作這些數(shù)據(jù)(這解釋了為什么三種基本類型值也有自帶的屬性和方法)。例子,我們可以這樣使用基本類型值的方法:

var s1 = "some text";
var s2 = s1.subString(2);

可以看到我們可以調(diào)用String類型自帶的方法,但如下面的例子,我們不能在運行時為基本類型值添加屬性和方法

var s1 = "some text";
s1.color = "red";
console.log(s1.color);     //undefined

我們知道,基本類型不是對象,因而從邏輯上它不應(yīng)該有方法(盡管如我們所愿,它有方法)。其實,為了讓我們能用方法操作基本類型值,后臺已經(jīng)自動完成了一系列的處理(注意后面是原理):當(dāng)?shù)谝欢未a的第二行(var s2 = s1.subString(2); )訪問s1時,訪問過程處于一種讀取模式,也就是要從內(nèi)存中讀取這個字符串的值。而在讀取模式中訪問字符串時,后臺都會自動完成下列處理:
(1)創(chuàng)建String類型的一個實例
(2)在實例上調(diào)用指定的方法;
(3)銷毀這個實例。
以上三個步驟用代碼表示就是:

var s1 = new String("some txt");
var s2 = s1.subString(2);    //把結(jié)果放回給另一個變量
s1 = null;                   //銷毀這個對象

上面的步驟也適用于Boolean,Number類型。這也就解釋了為什么不能給基本類型值添加屬性和方法,因為基本類型值對象在調(diào)用完方法后就會被后臺自動銷毀。

引用類型和基本包裝類型的主要區(qū)別就是對象的生存期。使用new操作符創(chuàng)建的引用類型的實例,在執(zhí)行流離開當(dāng)前作用域之前都一直保存在內(nèi)存中(離開了作用域后可能就會被標(biāo)記,然后被垃圾收集機制回收)。而自動創(chuàng)建的基本包裝類型的對象,則只存在于一行代碼的執(zhí)行瞬間,然后立即被銷毀(親測手動用new操作符創(chuàng)建的基本類型對象,可以添加屬性和方法。)所以我們無法為基本類型值添加屬性和方法。
當(dāng)然如果沒有必要的話,就不要顯示地調(diào)用Boolean和Number、String來創(chuàng)建基本包裝類型對象(不要var s1 = new String("some txt")這樣寫)。因為這樣容易讓人分不清自己是在處理基本類型還是引用類型的值。

Object構(gòu)造函數(shù)也會像工廠方法一樣,根據(jù)傳入值的類型返回相應(yīng)基本包裝類型的實例。例如:

var obj = new Object("some text");
console.log(obj instanceof String);   //true
console.log(obj instanceof Object);   //true

就是把字符串傳給Object構(gòu)造函數(shù),就會創(chuàng)建String的實例,傳入數(shù)值參數(shù)就會得到Number實例,傳入布爾值參數(shù)就會得到Boolean實例,但是你檢測它是以上基本類型值會返回true,檢測是不是Object也會返回true。
下面會講每個基本包裝類型提供的操作相應(yīng)值的便捷方法。

<h3>5.6.1Boolean類型</h3>
這節(jié)的重點是理解基本類型的布爾值與Boolean對象之間的區(qū)別。當(dāng)然,作者的建議是永遠不要使用Boolean對象,這節(jié)就當(dāng)冷知識吧。

創(chuàng)建Boolean對象可以像下面這樣調(diào)用Boolean構(gòu)造函數(shù)并傳入true或false值。

var booleanObject = new Boolean(false)

Boolean類型的實例重寫了的valueOf( )方法會返回基本類型的true和false,重寫了的toString( )會返回字符串型的“true”和“false”。
書中前面提到過,但是當(dāng)時不懂有什么用就沒記的一段話:布爾表達式中的所有對象都會被轉(zhuǎn)換為true。例子:

var falseObject = new Boolean(false);
var result = falseObject && true;
console.log(result);               //true

在這個例子中我們用false值創(chuàng)建了Boolean對象的實例falseObject,并對這個對象與true進行&&運算,卻發(fā)現(xiàn)返回到結(jié)果是true。因為,示例中的代碼是對falseObject而不是對它的值(false)求值。而布爾表達式中的所有對象都會被轉(zhuǎn)換為true,結(jié)果true&&true當(dāng)然就等于true了。

<h3>5.6.2Number類型</h3>
Number類型也重寫了valueOf()、toLocaleString()、toString()。valueOf()返回這個對象表示的基本類型的數(shù)值(親測就是一個數(shù))。toLocaleString()、toString()返回字符串類型的數(shù)值。
Number的toString()方法可以傳遞一個表示基數(shù)的參數(shù),參數(shù)是幾,就按幾進制的數(shù)字的字符串形式返回。

var num = 10;
console.log(num.toString(2))     //"1010"

注意返回的是字符串形式的。


上面的幾個是繼承的方法,Number類型提供了一些可以將數(shù)值格式化的方法。

  • toFixed():按照指定的小數(shù)位返回數(shù)字的字符串表示,且支持四舍五入。比如25.005用toFixed(2),會變成“25.01”。
  • toExponential(():返回以指數(shù)表示法表示的數(shù)值的字符串形式。接收的參數(shù)也是指定輸出結(jié)果中的小數(shù)位數(shù)。
var num = 10.005;
console.log(num.toFixed(2))     //"10.01"

var num = 10;
console.log(num.toExponential(1))//"1.0e+1"

<h3>5.6.3String類型</h3>
String對象繼承的valueOf()、toLocaleString()、toString()返回的都是這個對象表示的基本字符串值。
String類型的每個實例都有一個length屬性,表示字符串中有多少個字符(親測中文也是有多少個字長度就是幾)。書中說了,即使字符串中包含雙字節(jié)字符(不是占一個字節(jié)的ASCII字符,每個字符也仍然算一個字符)。

String類型提供的方法很多,看到都怕。
<h4>1.字符方法</h4>
兩個用于訪問字符串中特定位置字符的方法:charAt( )charCodeAt( )。兩個方法都接收一個參數(shù),即基于0的字符位置。兩個方法的區(qū)別是charAt( )返回的是給定位置的那個字符。charCodeAt( )返回的是那個字符的字符編碼。例子:

var stringValue = "hello World";
console.log(stringValue.charAt(1));     // "e"
console.log(stringValue.charCodeAt(1)); // "101"  是小寫字母e的字符編碼

ECMAScript5定義了另一個訪問個別字符的方法,可以用方括號加數(shù)字索引來訪問字符串中的特定字符。例子:

var stringValue = "hello World";
console.log(stringValue[1]);     //"e"

類似于把字符串當(dāng)成一個“數(shù)組”,按位置取出所在位置的字符。親測可以直接在字符串后面加方括號,例子:

console.log("sdsdsdsd"[2]);      //"s"  66666

支持這個語法的瀏覽器有IE8,F(xiàn)irefox,Safari,Opera 和Chrome所有版本的支持。冷知識:在IE7及更早版本使用這種語法會返回undefined值,盡管不是特殊的undefined值(不是很懂最后一句的意思,我以為返回的那個undefined是字符串型的,但是不是)

<h4>2.字符串操作方法</h4>
操作字符串的方法有幾個,第一個是concat( ),可以將一或多個字符串拼接起來,參數(shù)就是要添加進去的字符串,參數(shù)數(shù)量不限。返回得到的新字符串。數(shù)組類型也有這個方法,不解釋。不過在實踐中使用最多的是加號操作符(+)(MDZZ)。
ECMAScript還提供了三個基于子字符串創(chuàng)建新字符串的方法:slice()、substr()、substring()。三個方法都返回被操作字符串的一個子字符串。三個方法的第一個參數(shù)都是指定子字符串的開始位置。slice()和substring()的第二個參數(shù)指定的是子字符最后一個字符后面的位置(忽略結(jié)尾那個位置)。而substr() 的第二個參數(shù)指定的則是返回字符的個數(shù)。如果沒有第二個參數(shù),則默認(rèn)將字符串的長度作為結(jié)束的位置。(太亂實際用再百度)。與concat()方法一樣,這三個方法也不會修改本身的那個字符串。對原始字符串無任何影響。例子:

var stringValue = "Hello World";
console.log(stringValue.slice(3));               //"lo world"
console.log(stringValue.substring(3));           //"lo World"
console.log(stringValue.substr(3));              //"lo World"
console.log(stringValue.slice(3,7));             //"lo W"
console.log(stringValue.substring(3,7));         //"lo W"
console.log(stringValue.substr(3,7));            //"lo Worl"   substr()第二個參數(shù)表示要截取的字符個數(shù)

看起來三個方法作用很容易理解,但是傳遞給這些方法的參數(shù)是負(fù)數(shù)的情況下,他們的行為就不盡相同了。其中,slice()會將傳入的負(fù)值與字符串的長度相加,substr()將負(fù)的第一個參數(shù)加上字符串的長度,而將第二個負(fù)的參數(shù)轉(zhuǎn)換為0.最后,substring()方法會將所有負(fù)值參數(shù)都轉(zhuǎn)換為0.很恐怖,不想舉例子了,實踐中沒事不要用負(fù)數(shù)就行。需要再看書。
<h4>3.字符串位置方法</h4>
有兩個可以從字符串中查找子字符串的方法:indexOf( )和lastIndexOf( )。這兩個方法都是從一個字符串中搜索給定的子字符串,然后返回子字符串的位置。查找不到就返回-1.兩個方法的區(qū)別是一個從頭向后查詢,一個從后向前查詢。例子:

var stringValue = "hello world";
console.log(stringValue.indexOf("o"));    //4
console.log(stringValue.lastIndexOf("o")) //7

這兩個方法都接收第二個參數(shù),表示從字符串哪個位置開始搜索。換句話說,indexOf()從該參數(shù)指定的位置向后搜索,忽略之前的所有字符;而lastIndexOf()則會從指定的位置向前搜索,忽略該位置之后的所有字符(為什么在講第二次的時候才講這么清楚)。例子:

var stringValue = "hello world";
console.log(stringValue.indexOf("o",6));   //7
console.log(stringValue.lastIndexOf("o",6))//4

indexOf( )從位置6(字母“w”)開始向后搜索,在位置7找到“o”。lastIndexOf( )從位置6開始向前搜索,找到的是“hello”中的“o”,所以返回4。

我們可以在使用第二個參數(shù)的情況下,通過循環(huán)把所有匹配的子字符串找出來,例子:

var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
var positions = new Array();
var pos = stringValue.indexOf("e");

while(pos>-1){
    positions.push(pos);
    pos = stringValue.indexOf("e",pos+1);
}
console.log(positions);                    //[3, 24, 32, 35, 52]

<h4>4.trim()方法</h4>
ECMAScript5定義了trim()方法,該方法會創(chuàng)建一個字符串副本刪除前置和后置的所有空格,然后返回結(jié)果。
支持這個方法的瀏覽器有:IE9+,F(xiàn)irefox 3.5+,Safari 5+,Opera 10.5+和Chrome。此外,F(xiàn)irefox 3.5+,Safari 5+和Chrome 8+還支持非標(biāo)準(zhǔn)的trimLeft( )和trimRight( ) ,分別用于刪除字符串開頭和末尾的空格。

<h4>5.字符串大小寫轉(zhuǎn)換方法</h4>
toLowerCase()、toLocaleLowerCase()、toUpperCase()和toLocaleUpperCase()。為什么會有toLocaleLowerCase()、toLocaleUpperCase()這兩個方法是因為有些地區(qū)比如土耳其語回味iUnicode大小寫轉(zhuǎn)換應(yīng)用特殊的規(guī)則也是醉了,所以用這兩個針對地區(qū)的方法來保證正確的轉(zhuǎn)換。
<h4>6.字符串的模式匹配方法</h4>
正則看得頭大
<h4>7.localCompare方法</h4>
tolocalCompare()方法就是用字符串跟方法的字符串參數(shù)比較,如果字符串在字母表中應(yīng)該排在字符串參數(shù)之前,則返回一個負(fù)數(shù)(大多數(shù)情況下是-1),如果字符串等于字符串參數(shù),則返回0;如果字符串在字母表中應(yīng)該排在字符串參數(shù)之后,則返回一個正數(shù)(大多數(shù)情況下是1)。
而且localCompare()方法有個與眾不同的地方,大小寫是否區(qū)分要視使用方法的地區(qū)不同而定。比如美國以英語作為ECMAScript實現(xiàn)的標(biāo)準(zhǔn)語言,因此localCompare()就是區(qū)分大小寫的,則大寫字母在字母表中會排在小寫字母前面。所以是否區(qū)分大小寫要根據(jù)地區(qū)而定。(親測中國地區(qū)區(qū)分大小寫且大寫字母在小寫字母之前

<h4>8.fromCharCode()方法</h4>
String構(gòu)造函數(shù)(是構(gòu)造函數(shù)的方法不是普通字符串的方法)本身還有一個靜態(tài)方法:fromCharCode( )。這個方法可以接收一或多個字符編碼,將他們轉(zhuǎn)換為字符串。例子:

<h4>9.HTML方法</h4>
作者建議盡量不用這些方法,因為他們創(chuàng)建的標(biāo)記通常無法表達語義(不懂)。方法有12個,需要再查書吧。

<h3>5.7單體內(nèi)置對象</h3>
ECMA-262對內(nèi)置對象的定義是:“由ECMAScript實現(xiàn)提供的,不依賴宿主環(huán)境的對象,這些對象在ECMAScript程序執(zhí)行之前就已經(jīng)存在了,所以開發(fā)人員不用顯示地實例化內(nèi)置對象,因為他們已經(jīng)實例化了。”前面介紹的Object、Array和String等都是內(nèi)置對象。ECMA-262還定義了兩個單體內(nèi)置對象:Global和Math

<h4>5.7.1Global對象</h4>
Global(全局)對象是ECMAScript中最特別的一個對象,因為你不管從什么角度上看,這個對象都是不存在的(???)。ECMAScript中的Global對象在某種意義上是作為一個“兜底兒對象”。換句話說,不屬于任何對象的屬性和方法,都是global對象的屬性和方法。事實上,沒有全局變量和全局函數(shù),因為所有在全局作用域中定義的屬性和函數(shù),都是Global對象的屬性(只是屬性,函數(shù)也是Global對象的屬性)。本書前面介紹過的那些函數(shù),比如isNan(),isFinite()、parseInt()以及parseFloat(),都是Global對象的方法(array,object等類型對象的方法當(dāng)然不是Global對象的,是array、object等這些對象的,不要混淆)。除此之外,Global還有其他一些方法。下面介紹這幾個方法:
<h4>1.URI編碼方法</h4>
Global對象的encodeURI()和encodeURIComponent()方法可以對URI(Uniform Resource Identifiers,通用資源標(biāo)識符)進行編碼,以便發(fā)送給瀏覽器。
有效的URI不能包含某些字符,例如空格,而這兩個URI編碼方法就會對URI進行編碼,用特殊的UTF-8字符替換所有無效的字符,讓瀏覽器能夠接受和理解。encodeURI()和encodeURIComponent()的第一個區(qū)別是,encodeURI()只會把URI中無效的字符替換掉,encodeURIComponent()會把正確的特殊符號例如冒號、正斜杠、問號和井號也給替換掉。
第二個區(qū)別是,encodeURI()用于整個URI,而encodeURIComponent()用于對URI中的某一段進行編碼。例子:

var uri = "http://www.wrow.com/illegal  value.html#start";

console.log(encodeURI(uri));              //http://www.wrow.com/illegal%20%20value.html#start

console.log(encodeURIComponent(uri));       //http%3A%2F%2Fwww.wrow.com%2Fillegal%20%20value.html%23start

第二個方法中連http://都被轉(zhuǎn)換了字符導(dǎo)致無法訪問,說明了一、encodeURIComponent()只適合編碼部分URI;二、encodeURIComponent()會把一切正確,不正確的特殊符號都進行UTF-8字符編碼

有編碼的函數(shù),就有解碼的函數(shù),與這兩個函數(shù)對應(yīng)的函數(shù)是decodeURI()和decodeURIComponent()。其中,decodeURI()只能對非法字符進行解碼,decodeURIComponent()可以對所有被編譯的符號進行解碼。例如,decodeURI()可以將%20替換成空格,但不能對%23做任何處理,因為%23表示井字號(#),而井字號是合法符號。但是decodeURIComponent()就%20和%23都可以解碼。

<h4>2.eval()方法</h4>
最后一個Global對象的方法,大概是整個ECMAScript語言中最強大的一個方法:eval()(見仁見智,有些人覺得這個方法弊端多多)。eval()就像一個完整的ECMAScript解析器,它只接收一個參數(shù),即要執(zhí)行的ECMAScript(或JavaScript)字符串。例子:

eval("alert('hi')");

以上代碼等價于下面這行(so what??):

alert('hi');

當(dāng)解析器發(fā)現(xiàn)代碼中調(diào)用eval()方法時,它會將傳入的參數(shù)當(dāng)作實際的ECMAScript語句來解析,然后把執(zhí)行結(jié)果插入到原來位置。通過eval()方法執(zhí)行的代碼被認(rèn)為是包含該次調(diào)用的執(zhí)行環(huán)境的一部分,因此被執(zhí)行的代碼具有與該執(zhí)行環(huán)境相同的作用域鏈。這意味著通過eval()執(zhí)行的代碼可以引用在包含環(huán)境中定義的變量。例子:

var msg = "hello world";
eval("alert(msg)");

可見,變量msg是在eval()調(diào)用的環(huán)境之外定義的,但其中調(diào)用的alert()仍然能夠顯示“hello world”。這是因為上面第二行代碼最終被替換成了一行真正的代碼(so?那干嘛要加個eval(),直接寫進執(zhí)行環(huán)境不就好了嗎)。同樣地,我們也可以在eval()調(diào)用中定義一個函數(shù),然后在外部代碼中引用這個函數(shù):

eval("function sayHi(){ alert('Hi'); }");
sayHi();

顯然,函數(shù)sayHi()是在eval()內(nèi)部定義的。但由于對eval()的調(diào)用最終會被替換成定義函數(shù)的實際代碼,因此可以在下一行的外部代碼中調(diào)用sayHi( )。

在eval()中創(chuàng)建的仍和變量或函數(shù)都不會被提升,原理:因為在解析代碼的時候,他們被包含在一個字符串中,它們只在eval()執(zhí)行的時候創(chuàng)建。

嚴(yán)格模式下,在外部訪問不到eval()中創(chuàng)建的任何變量或函數(shù),因此前面的那個例子就會導(dǎo)致錯誤。在嚴(yán)格模式下,為eval賦值也會導(dǎo)致錯誤:

"use strict";
eval = "hi";   //causes error

<h4>3.Global對象的屬性</h4>
前面說過了,沒有明確對象的屬性,最后都是Global的屬性,例如,特殊的值undefined、NaN以及Infinity等。此外,所有原生引用類型的構(gòu)造函數(shù),像Object,F(xiàn)unction也都是Global對象的屬性。書中列出了所有Global對象的所有屬性。在P133.

ECMAScript 5明確禁止給undefined、NaN和Infinity賦值,這樣做即使在非嚴(yán)格模式下也會導(dǎo)致錯誤。
<h4>4.window對象</h4>
ECMAScript沒有明確告訴我們?nèi)绾沃苯釉L問Global對象,但Web瀏覽器都是將這個全局對象作為window對象的一部分加以實現(xiàn)的(在瀏覽器中,Global對象是window對象的一部分,注意范圍:瀏覽器中,window對象的一部分。)例子:

var color = "red";

function sayColor(){
     alert(window.color);
}
window.sayColor();     //"red"

在這里我們定義了一個名為color的全局變量和一個sayColor()全局函數(shù)。在sayColor()內(nèi)部,我們用window.color來訪問變量,前面我們說過,全局環(huán)境中定義的變量和函數(shù),實際上都是Global的屬性,所以window.color等價于Global.color。以此說明,全局變量是window對象的屬性。然后,又使用window.color來直接通過window.sayColor( )直接通過window對象調(diào)用這個函數(shù)。

?JavaScript中的window對象除了扮演ECMAScript規(guī)定的Global對象的角色外,還承擔(dān)了很多任務(wù),所以說Global對象這是window對象的一部分。第8章在討論瀏覽器對象模型時將詳細(xì)介紹window對象。

另一種取得Global對象的方法是使用以下代碼:

var global = function(){
     return this;          // 親測return出來的是 window
}();

這是個立即調(diào)用的函數(shù)表達式,返回this的值。如前所述,在沒有給函數(shù)明確指定this值的情況下,this值等于Global對象。而像這樣通過簡單地返回this來取得Global對象,在任何執(zhí)行環(huán)境下都是可行的。(因為是立即執(zhí)行函數(shù)的原因?)

<h4>5.7.2Math對象</h4>
不想寫了這個,就是Math下的各種方法。可以直接在書中查閱。

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

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

  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,263評論 0 4
  • 第5章 引用類型 引用類型的值(對象)是引用類型的一個示例。在ECMAScript 中,引用類型是一種數(shù)據(jù)結(jié)構(gòu),用...
    力氣強閱讀 724評論 0 0
  • 今天身邊一個同事跟他老公鬧離婚,我勸他,聽她說離婚的理由連同她的憤恨。我忽然很難受,我想到了自己,我不知道我有多惡...
    辛亞玲閱讀 116評論 0 0
  • 時南踟躕舉起的手又放了下來,楠姐只讓他先在這候著,他沒有資格先行打擾受訪者但又想起自己在小孩面前承認(rèn)與主人家是朋友...
    遇鬼閱讀 149評論 0 1
  • 0x001 Canvas是啥? 說白了Canvas就是一塊畫布,可以使用js當(dāng)畫筆在上面繪畫的畫布,可以顯示在網(wǎng)頁...
    賣梳子的鯉魚閱讀 1,871評論 1 21