Number類型應該是ECMAScript中最令人關注的數據類型了,這種類型使用IEEE754格式來表示整數和浮點數值(浮點數值在某些語言中也被稱為雙精度數值)。為支持各種數值類型,ECMA-262定義了不同的數值字面量。
最基本的數值字面量格式是十進制整數,十進制整數可以像下面這樣直接在代碼中輸入:
var item =55; //整數
除了以十進制表示外,整數還可以通過八進制(以8為基數)或十六進制(以16為基數)的字面值來表示。其中,八進制字面值的第一位必須是零(0),然后是八進制數字序列(0-7)。如果字面值中的數值超出了范圍,那么前導零將被忽略,后面的數值將被當做十進制數值解析。請看下面的例子:
var color1=070; //八進制的56
var color2=079; //無效的八進制數值——解析為79
var color3=08; //無效的八進制數值——解析為8
八進制字面量在嚴格模式下是無效的,會導致支持該模式的JavaScript引擎拋出錯誤。
十六進制字面值的前兩位必須是0x,后跟任何十六進制數字(0-9及A-F)。其中,字面A-F可以大寫,也可以小寫。如下面的例子所示:
var num1=0xA; //十六進制的10
var num2=0x1f; //十六進制的31
在進行算術計算時,所有以八進制和十六進制表示的數值最終都將被轉換成十進制數值。
1.浮點數值
所謂浮點數值,就是該數值中必須包含一個小數點,並且小數點后面必須至少有一位數字。雖然小數點前面可以沒有整數,但我們不推薦這種寫法。一下是浮點數值的幾個例子:
var floatNum1=1.1;
var floatNum2=0.1;
var floatNum3=.1; //有效,但不推薦
由於保存浮點數值需要的內存空間是保存數值的兩倍,因此ECMAScript會不失時機地將浮點數值轉換為整數值。顯然,如果小數點后面沒有跟任何數字,那么這個數值就可以作為整數值來保存。同樣地,如果浮點數值本身表示的就是一個整數(如1.0),那么該值也會被轉換為整數,如下面的例子所示:
var floatNum1=1.; //小數點后面沒有數字——解析為1
var floatNum2=10.0; //整數——解析為10
對於那些極大或極小的數值,可以用e表示法(即科學計數法)表示的浮點數值表示。用e表示法表示的數值等於e前面的數值乘以10的指數次冪。ECMAScript中e表示法的格式也是如此,即前面是一個數值(可以是整數也可以是浮點數),中間是一個大寫或小寫的字面E,后面是10的冪中的指數,該冪值將用來與前面的數相乘。下面是一個使用e表示法表示數值的例子:
var floatNum=3.125e7 ; //等於31250000
這個例子中,使用e表示法表示的變量floatNum的形式雖然簡潔,但它的實際值則是31250000.在此,e表示法的實際含義就是“3.125乘以10的七次方”。
也可以使用e表示法表示極小的數值,如0.00000000000000003,這個數值可以使用更簡潔的3e-17表示。在默認情況下,ECMAScript會將那些小數點后面帶有6個零以上的浮點數值轉換為以e表示法表示的數值(例如,0.0000003會被轉換成3e-7)。
浮點數值的最高精度是17位小數,但在進行算術計算時其精確度遠遠不如整數。例如,0.1+0.2的結果不是0.3,而是0.300000000000000004。這個小小的舍入誤差會導致無法測定特定的浮點數值。例如:
if(a+b == 0.3){ //不要做這樣的測試
alert("You got 0.3.");
}
在這個例子中,我們測試的是兩個數的和是不是等於0.3。如果這兩個數是0.05和0.25,或者是0.15和0.15都不會有問題。而如前所述,如果這兩個數是0.1和0.2,那么測試將無法通過。因此,永遠不要測試某個特定的浮點數值。
2.數值范圍
由於內存的限制,ECMAScript並不能保存世界上所有的數值。ECMAScript能夠表示的最小數值保存在Number.MIN_VALUE中——在大多數瀏覽器中,這個值是5e-324;能夠表示的最大數值保存在Number.MAX_VLAUE中——在大多數瀏覽器中,這個值是1.7976931348623157e+328。如果某次計算結果得到了一個超出JavaScript數值范圍的值,那么這個數值將被自動轉換成特殊的Infinity值。具體來說,如果這個數值是負數,則會被轉換成-Infinity(負無窮),如果這個數值是正數,則會被轉換成Infinity(正無窮)。
如上所述,如果某次計算返回了正或負的Infinity值,那么該值將無法繼續參與下一次的計算,因為Infinity不是能夠參與計算的數值。要想確定一個數值是不是有窮的(換句話說,是不是位於最小和最大的數值之間),可以使用isFinite()函數。這個函數在參數位於最小與最大數值之間時會返回true,如下面的例子所示:
var result=Number.MAX_VALUE + Number.MAX_VALUE;
alert(isFinite(result)); //false
盡管在計算中很少出現某些值超出表示范圍的情況,但在執行極小或極大數值的計算時,檢測監控這些值是可能的,也是必須的。
3.NaN
NaN,即非數值(Not a Number)是一個特殊的數值,這個數值用於表示一個本來返回數值的操作數未返回數值的情況(這樣就不會拋出錯誤了)。例如,在其他編程語言中,任何數值除以0都會導致錯誤,從而停止代碼執行。但在ECMAScript中,任何數值除以0會返回NaN,因此不會影響其他代碼的執行。
NaN本身有兩個非同尋常的特點。首先,任何涉及NaN的操作(例如NaN/10)都會返回NaN,這個特點在多部計算中有可能導致問題。其次,NaN與任何值都不相等,包括NaN本身。例如,下面的代碼會返回false:
alert(NaN == NaN);//false
針對NaN的這兩個特點,ECMAScript定義了isNaN()函數。這個函數接受一個參數,該參數可以是任何類型,而函數會幫我們確定這個參數是否“不是數值”。isNaN()在接收到一個值之后,會嘗試將這個值轉換為數值。某些不是數值的值會直接轉換為數值,例如字符串“10”或“Boolean”值。而任何不能轉換為數值的值都會導致這個函數返回true。請看下面的例子:
alert(isNaN(NaN)) ; //true
alert(isNaN(10)); //false(10是一個數值)
alert(isNaN(“10”)); //false(可以被轉換成數值10)
alert(isNaN(“blue”)); //true(不能轉換成數值)
alert(isNaN(true)); //false (可以被轉換成數值1)
這個例子測試了5個不同的值。測試的第一個值是NaN本身,結果當然會返回true。然后分別測試了數值10和字符串“10”,結果這個兩個測試都返回了false,因為前者本身就是數值,而后者可以被轉換成數值。但是字符串“blue”不能被轉換成數值,因此函數返回了true。由於Boolean值true可以轉換成數值1,因此函數返回false。
4.數值轉換
有3個函數可以把非數值轉換為數值:Number()、parseInt()和parseFloat()。第一個函數,即轉型函數Number()可以用於任何數據類型,而另兩個函數則專門用於把字符串轉換成數值。這3哥函數對於同樣的輸入會有返回不同的結果。
Number()函數的轉換規則如下。
-
如果是Boolean值,true和false將分別被轉換成1和0.
-
如果是數字值,只是簡單的傳入和返回。
-
如果是null值,返回0。
-
如果是undefined,返回NaN。
-
如果是字符串,遵循下列規則:
a.如果字符串中只包含數字(包括前面帶正號或負號的情況),則將其轉換為十進制數值,即“1”會變成1,“123”會變成123,而“011”會變成11(注意:前導的零被忽略了)
b.如果字符串中包含有效的浮點格式,如“1.1”,則將其轉換為對應的浮點數值(同樣,也會忽略前導零);
c.如果字符串中包含有效的十六進制格式,例如:“0xf”,則將其轉換為相同大小的十進制整數數值;
d.如果字符串是空的(不包含任何字符),則將其轉換為0;
e.如果字符串中包含除上述格式之外的字符,則將其轉換為NaN。
6.如果是對象,則調用對象的valueOf()方法,然后依照前面的規則轉換返回的值。如果轉換的結果是NaN,則調用對象的toString()方法,然后再次依照前面的規則轉換返回的字符串值。
根據這么多的規則使用Number()把各種數據類型轉換為數值確實有點復雜。下面還是給出幾個具體的例子吧。
var num1=Number("Hello world!"); //NaN
var num2=Number(""); //0
var num3=Number("000011"); //11
var num4=Number(true); //1
首先,字符串"Hello World!"會被轉換為NaN,因為其中不包含任何有意義的數字值。空字符串會被轉換為0.字符串“000011”會被轉換為11,因為忽略了其前導的零。最后,true值被轉換為1。
由於Number()函數在轉換字符串時比較復雜而且不夠合理,因此在處理整數的時候更常用的是parseInt()函數。parseInt()函數在轉換字符串時,更多的是看其是否符合數值模式。它會忽略字符串前面的空格,直至找到第一個非空格字符。如果第一個字符不是數字字符或者負號,parseInt()就會返回NaN;也就是說,用parseInt()轉換空字符串會返回NaN(Number()對空字符串返回0)。如果第一個字符是數字字符,parseInt()會繼續解析第二個字符,直到解析完所有后續字符或者遇到了一個非數字字符。例如,"123blue"會被轉換為1234,因為"blue"會被完全忽略。類似地,"22.5"會被轉換為22,因為小數點並不是有效的數字字符。
如果字符串中的第一個字符是數字字符,parseInt()也能夠識別出各種整數格式(即前面討論的十進制、八進制和十六進制)。也就是說,如果字符串以"0x"開頭且后跟數字字符,就會將其當做一個十六進制整數;如果字符串以"0"開頭且后跟數字字符,則會將其當做一個八進制數來解析。
為了更好地理解parseInt()函數的轉換規則,下面給出一些例子:
var num1=parseInt("1234blue"); //1234
var num1=parseInt(""); //NaN
var num1=parseInt("0xA"); //10(十六進制數)
var num1=parseInt("22.5"); //22
var num1=parseInt("070"); //56(八進制數)
var num1=parseInt("70"); //70(十進制數)
var num1=parseInt("0xf"); //15(十六進制數)
在使用parseInt()解析像八進制字面量的字符串時,ECMAScript3和5存在分歧。例如:
//ECMAScript 3 認為是56(八進制),ECMAScript 5認為是70(十進制)
var num=parseInt("070");
在ECMAScript 3 JavaScript引擎中,"070"被當成八進制字面量,因此轉換后的值是十進制的56.而在ECMAScript 5 JavaScript引擎中,parseInt()已經不具有解析八進制值的能力,因此前導的零會被認為無效,從而將這個值當成"70",結果就得到十進制的70。在ECMAScript 5 中,即使是在非嚴格模式下也會如此。
為了消除在使用parseInt()函數時可能導致的上述困惑,可以為這個函數第二個參數:轉換時使用的技術(即多少進制)。如果知道要解析的值是十六進制格式的字符串,那么指定基數16作為第二個參數,可以保證得到正確的結果,例如:
var num=parseInt("0xAF",16); //175
實際上,如果指定了16作為第二個參數,字符串可以不帶前面的"0x"如下所示:
var num1=parseInt("AF",16); //175
var num2=parseInt("AF"); //NaN
這個例子中的第一個轉換成功了,而第二個則失敗了。差別在於第一個轉換傳入了基數,明確告訴parseInt()要解析一個十六進制格式的字符串;要第二個轉換發現第一個字符不是數字字符,因此就自動終止了。
var num1=parseInt("10",2); //2 (按二進制解析)
var num2=parseInt("10",8); //8 (按八進制解析)
var num3=parseInt("10",10); //10 (按十進制解析)
var num4=parseInt("10",16); //16 (按十六進制解析)
不指定基數意味着讓parseInt()決定如何解析輸入的字符串,因此為了避免錯誤的解析,我們建議無論在什么情況下都明確指定基數。
與parseInt()函數類似,parseFloat()也是從第一個字符(位置0)開始解析每個字符。而且也是一直解析到字符串末尾,或者解析到遇見一個無效的浮點數字字符為止。也就是說,字符串中的每一個小數點是有效的,而第二個小數點就是無效的了,因此它后面的字符串將被忽略。舉例來說,"22.34.5"將會被轉換為22.34。
除了第一個小數點有效之外,parseFloat()與parseInt()的第二個區別在於它始終都會忽略前導的零。parseInt()可以識別前面討論過的所有浮點數值格式,也包括十進制整數格式。但十六進制格式的字符串則始終被轉換成0。由於parseFloat()只解析十進制值,因此它沒有用第二個參數指定基數的用法。最后還要注意一點:如果字符串包含的是一個可解析為整數的數(沒有小數點,或者小數點后都是零),parseFloat()會返回整數。以下是使用parseFloat()轉換數值的幾個典型示例。
var num1 = parseFloat("1234blue"); //1234(整數)
var num2 = parseFloat("0xA"); //0
var num3 = parseFloat("22.5"); //22.5
var num4 = parseFloat("22.34.5"); //22.34
var num5 = parseFloat("0908.5); //908.5
var num6 = parseFloat("3.125e7"); //3125000
String類型用於表示由零或多個16位Unicode字符組成的字符序列,即字符串。字符串可以由雙引號(“)或單引號(‘)表示,因此下面兩種字符串的寫法都是有效的:
var firstName="Marry";
var firstName='Jane';
與PHP中的雙引號和單引號會影響對字符串的解釋方式不同,ECMAScript中的這兩種語法形式沒有什么區別。用雙引號表示的字符串和用單引號表示的字符串完全相同。不過,以引號開頭的字符串也必須以雙引號結尾,而以單引號開頭的字符串必須以單引號結尾。例如,下面這種字符串表示法會導致語法錯誤:
//語法錯誤(左右引號必須匹配)
var firstName="Nicholas';
1、字符字面量
String 數據類型包含一些特殊的字符字面量,也叫轉義序列,用於表示非打印字符,或者具有其他用途的字符。這些字符字面量如下表所示:
這些字符字面量可以出現在字符串中的任意位置,而且也將被作為一個字符來解析,如下面的例子所示:
var text="sigma: \u03a3.";
這個例子中的變量text有28個字符,其中6個字符長的轉移序列表示1個字符。任何字符串的長度都可以通過訪問其length屬性取得,例如:
alert(text.length);//輸出28
這個屬性返回的字符數包括16位字符的數目。如果字符串包含雙字節字符,那么length屬性可能不會精確的返回字符串中字符數目。
2.字符串的特點
ECMAScript中的字符串是不可改變的,也就是說,字符串一旦創建,它們的值就不能改變。要改變某個變量保存的字符串,首先要銷毀原來的字符串,然后再用另一個包含新值的字符串填充該變量,例如:
var lang="Java";
lang=lang+"Script";
以上示例中的變量lang開始時包含字符串“Java”。而第二行代碼把lang的值重新定義為“Java”與“Script”的組合,即“JavaScript”。實現這個操作的過程如下:首先創建一個能容納10個字符的新字符串,然后再這個字符串中填充"Java"和“Script”,最后一步是銷毀原來的字符串“Java”和字符串“Script”,因為這兩個字符串已經沒用了。這個過程是在后台發生的,而這也是在某些舊版本的瀏覽器(如版本低於1.0的Firefox,IE6等)中拼接字符串時速度很慢的原因所在。但這些瀏覽器后來的版本已經解決了這個低效率的問題。
3.轉換為字符串
要把一個值轉換為一個字符串有兩種方式。第一種是使用幾乎每個值都有的toString()方法。這個方法唯一要做的就是返回相應值的字符串表現。來看下面的例子:
var age=11;
//字符串“11”
var ageAsString=age.toString();
var fount=true;
//字符串"true"
var foundAsString=found.toString()
數值,布爾值,對象和字符串值(沒錯,每個字符串也都有一個toString()方法,該方法返回值返回串的一個副本)都有toString()方法。但null和undefined值沒有這個方法。
多數情況下,調用toString方法不必傳遞參數。但是,在調用數值的toString()方法時,可以傳遞一個參數:輸出數值的基數。默認情況下,toString()方法以十進制格式返回數值的字符串表示。而通過傳遞基數,toString()輸出以二進制、八進制、十六進制,乃至其他任意有效進制格式表示的字符串值。下面給出幾個例子:
var num=10;
alert(num.toString()); //"10"
alert(num.toString(2)); //"1010"
alert(num.toString(8)); //"12"
alert(num.toString(10)); //"10"
alert(num.toString(16)); //"a"
通過這個例子可以看出,通過指定基數,toString()方法會改變輸出的值。而數值10根據基數的不同,可以在輸出時被轉換為不同的數值格式。注意,默認的(沒有參數的)輸出值與指定基數10時的輸出值相同。
在不知道要轉換的值是不是null或undefined的情況下,還可以使用轉型函數String(),這個函數能夠將任何類型的值轉換為字符串。String()函數遵循下列轉換規則:
如果值有toString()方法,則調用該方法(沒有參數)並返回相應的結果;
如果值是null,則返回"null";
如果值是undefined,則返回“undefined”
下面再看幾個例子:
var value1=10;
var value2=true;
var value3=null;
alert(String(value1)); //"10"
alert(String(value2)); //"true"
alert(String(value3)); //"null"
alert(String(value4)); //"undefined"
這里先后轉換了4個值:數值、布爾值、null和undefined。數值和布爾值的轉換結果與調用toString()方法得到的結果相同。因為null和undefined沒有toString()方法,所以String()函數就返回了這兩個值的字面量。
ECMAScript中的對象是可變的鍵控集合(即一組數據和功能的集合)。它將很多值聚合在一起,可通過名字訪問這些值。對象也可看做屬性的容器,每個屬性都是一個名/值對。屬性的名字可以是包括空字符串在內的任意字符串。屬性值可以是除undefined值之外的任何值。對象最常見的用法是創建(create)、設置(set)、查找(query)、刪除(delete)、檢測(test)和枚舉(enumerate)他的屬性。
一、屬性類型
ECMA-262第5版在定義只有內部采用的特性時,描述了屬性的各種特征。為了表示特性時內部值,該規范把它們放在了兩對方括號中,例如:[[Enumerable]]。ECMAScript對象中有兩種屬性:數據屬性和訪問器屬性。
1.數據屬性
數據屬性包含一個數據值的位置。在這個位置可以讀取和寫入值。數據屬性有4個描述其行為的特性。
a.[[Configurable]]:表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。這個特性默認值為true。
b.[[Enumerable]]:表示能否通過for-in循環返回屬性。這個特性默認值為true。
c.[[Writable]]:表示能否修改屬性的值。這個特性默認值為true。
d.[[Value]]:包含這個屬性的數據值。讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。這個特性的默認值為undefined。
2.訪問器屬性:
a.[[Configurable]]:表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為數據屬性。對於直接在對象上定義的屬性,這個特性的默認值為true.
b.[[Enumerable]]:表示能否通過for-in循環返回屬性。對於直接在對象上定義的屬性,這個特性的默認值為true。
c.[[Get]]:在讀取屬性時調用的函數。默認值為undefined。
d.[[Set]]:在寫入屬性時調用的函數。默認值為undefined。
二、創建對象
1.通過new創建對象
new 運算符創建並初始化一個新對象。關鍵字new后跟隨一個函數調用。這里的函數稱做構造函數,構造函數用以初始化一個新創建的對象。JavaScript語言核心中的原始類型都包含內置構造函數。例如:
除了這些內置構造函數,用自定義構造函數來初始化新對象也是非常常見的。
2.對象字面量
創建對象最簡單的方式就是在JavaScript代碼中使用對象字面量。對象字面量是由若干名/值對組成的映射表,名/值對中間用冒號分隔,名/值對之間用 逗號分隔,整個映射表用花括號括起來,結構為:{屬性名1:屬性值1,屬性名2:屬性值2,……}。屬性名可以是JavaScript標識符也可以是字符 串字面量(包括空字符串)。屬性的值可以是任意類型的JavaScript表達式,表達式的值(可以是原始值也可以是對象值)就是這個屬性的值。請看示 例:
3.原型
我們創建的每個對象都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬 性和方法。如果按照字面意思來理解,那么prototype就是通過調用構造函數而創建的那個對象實例的原型對象。使用原型對象的好處是可以讓所有對象實 例共享它所包含的屬性和方法。換句話說,不必在構造函數中定義對象實例的信息,而是可以將這些信息直接添加到原型對象中,請看示例:
在此,我們將sayName()方法和所有屬性直接添加到了Person的prototype屬性中,構造函數變成了空函數。即使如此,也仍然可以通過調用構造函數來創建新對象,而且新對象還會具有相同的屬性和方法。但與構造函數不同的是,新對象的這些屬性和方法是由所有實例共享的。 換句話說,person1和person2訪問的都是同一組屬性和同一個sayName()函數。由於對象的原型牽扯到的知識點非常多且復雜,在此就不向大家闡述,我將會在以后的文章中為大家詳細講述原型(prototype)這一概念。除此之外,JavaScript還有一種 Object.create()方法來創建對象
三、基於對象屬性的各種操作
1.對象屬性的獲取
訪問對象屬性可以使用點表示法和方括號表示法。對於點(.)來說,右側必須是一個以屬性名稱命名的簡單標識符。對於方括號來說([]),方括號內必須是一個計算結果為字符串的表達式,這個字符串就是屬性的名字。請看示例:
當使用方括號時,我們說方括號內的表達式必須返回字符串。其實更嚴謹的說,表達式必須返回字符串或返回一個可以轉換為字符串的值(這個特點可以用於處理很多情況)。由此我們可以聯想到數組,這和數組獲取元素的方法極為相似,其實數組也是對象,只是其屬性名為數組下標而已。
2.屬性訪問錯誤
屬性訪問並不總是返回或設置一個值。查詢一個不存在的屬性並不會報錯,如果在對象o自身的屬性或繼承的屬性中均未找到屬性x,屬性訪問表達式o.x返回undefined。回想一下我們person對象有屬性"name",而沒有屬性"subname":
但是如果對象不存在,那么試圖查詢這個不存在的對象的屬性就會報錯。null和undefined值都沒有屬性,因此查詢這些值的屬性會報錯,請看示例:
除非確定person和person.subname都是(或在行為上)對象,否則不能寫這樣的表達式person.subname.length,因為這樣會報錯,下面提供了兩種避免出錯的方法:
第二種示例使用了 "&&"的一種復合語言習慣的用法,因此只有在person為真值(不能是null或者undefined)的情況下才會計算之后的值,&&的這一行為稱作“短路”。
當然,給null和undefined設置屬性也會報類型錯誤。給其他值設置屬性也不總是成功,有一些屬性時只讀的,不能重新復制,有一些對象不允許新增屬性,但讓人頗感意外的是,這些設置屬性的失敗操作不會報錯:
這是一個歷史遺留問題,這個bug在ECMAScript5的嚴格模式中已經修復。在嚴格模式中,任何失敗的屬性設置操作都會拋出一個類型錯誤異常。
3.刪除屬性
delete運算符可以刪除對象的屬性。它的操作數應當是一個屬性訪問表達式。讓人感到意外的是,delete只是斷開屬性和宿主對象的聯系,而不會去操作屬性中的屬性。
delete運算符只能刪除自有屬性,不能刪除繼承屬性(要刪除繼承屬性必須從定義這個屬性的原型對象上刪除它,而且這會影響到所有繼承自這個原型的對象)
當delete表達式刪除成功或沒有任何副作用(比如刪除不存在的屬性)時,它返回true。如果delete后不是一個屬性訪問表達式,delete同樣返回true:
delete 不能刪除那些可配置性為false的屬性(盡管可以刪除不可擴展的可配置屬性)。某些內置對象的屬性是不可配置的,比如通過變量聲明和函數聲明創建的全局 對象的屬性。在嚴格模式中,刪除一個不可配置屬性會報一個類型錯誤。在非嚴格模式中,這些情況下的delete操作會返回false:
在非嚴格模式中刪除全局對象的可配置屬性時,可以省略對全局對象的引用,直接在delete操作符后跟隨要刪除的屬性名即可:
然后再嚴格模式中,delete后跟隨一個非法的操作數(比如x),則會報一個語法錯誤,因此必須顯式指定對象及其屬性:
4.枚舉屬性
for-in語句可用來遍歷一個對象中的所有屬性名。該枚舉過程將會列出所有的屬性——包括函數和你可能不關心的原型中的屬性——所以有必要過濾掉那些你不想要的值。最為常用的過濾器是hasOwnProperty方法,以及使用typeof來排除函數:
屬性名出現得順序是不確定的,因此要對任何可能出現的順序有所准備。如果你想要確保屬性以特定的順序出現,最好的辦法就是完全避免使用for-in語句,而是創建一個數組,在其中以正確的順序包含屬性名:
通過使用for而不是for-in ,可以得到我們想要的屬性,而不用擔心可能發掘出原型鏈中的屬性,並且我們按正確的順序獲得了它們的值。
轉載自:https://mp.weixin.qq.com/s/-nEBCHqgQVf1arFhHNpRLw(僅作為記錄參考,侵刪)
time:2020 04 19