摘要:本部分主要是針對于中常用的數據結構類型進行分析說明。原始類型數值字符串布爾值分別返回。函數將字符串轉為數值,要比函數嚴格很多。表示沒有對象,即該處不應該有值。作為對象原型鏈的終點。調用函數時,應該提供的參數沒有提供,該參數等于。
[TOC]
本部分主要是針對于JavaScript中常用的數據結構類型進行分析說明。
變量與常量在JavaScript中,基本的變量聲明可以用var方式。JavaScript允許省略var,直接對未聲明的變量賦值。也就是說,var a = 1 與 a = 1,這兩條語句的效果相同。但是由于這樣的做法很容易不知不覺地創建全局變量(尤其是在函數內部),所以建議總是使用var命令聲明變量。
在ES6中,對于變量聲明的方式進行了擴展。引入了let方式,let即是定義塊級別的變量,不會以閉包方式擴展到塊外面。另一種引入了可以用于定義常量的const聲明。
function f() { { let x; { // okay, block scoped name const x = "sneaky"; // error, const x = "foo"; } // error, already declared in block let x = "inner"; } }變量作用域 變量提升
JavaScript是解釋執行的語言, JavaScript引擎的工作方式是,先解析代碼,獲取所有被聲明的變量,然后再一行一行地運行。這造成的結果,就是所有的變量的聲明語句,都會被提升到代碼的頭部,這就叫做變量提升(hoisting)。
console.log(a); var a = 1;
上面代碼首先使用console.log方法,在控制臺(console)顯示變量a的值。這時變量a還沒有聲明和賦值,所以這是一種錯誤的做法,但是實際上不會報錯。因為存在變量提升,真正運行的是下面的代碼:
var a; console.log(a); a = 1;
最后的結果是顯示undefined,表示變量a已聲明,但還未賦值。請注意,變量提升只對var命令聲明的變量有效,如果一個變量不是用var命令聲明的,就不會發生變量提升。
console.log(b); b = 1;
上面的語句將會報錯,提示“ReferenceError: b is not defined”,即變量b未聲明,這是因為b不是用var命令聲明的,JavaScript引擎不會將其提升,而只是視為對頂層對象的b屬性的賦值。
避免全局變量在計算機編程中,全局變量指的是在所有作用域中都能訪問的變量。全局變量是一種不好的實踐,因為它會導致一些問題,比如一個已經存在的方法和全局變量的覆蓋,當我們不知道變量在哪里被定義的時候,代碼就變得很難理解和維護了。在ES6中可以利用let關鍵字來聲明本地變量,好的 JavaScript 代碼就是沒有定義全局變量的。有一些技術可以幫助你讓所有的事情都保持在本地:
函數包裹為了避免全局變量,第一件事情就是要確保所有的代碼都被包在函數中。最簡單的辦法就是把所有的代碼都直接放到一個函數中去:
(function(win) { "use strict"; // 進一步避免創建全局變量 var doc = window.document; // 在這里聲明你的變量 // 一些其他的代碼 }(window));聲明命名空間
var MyApp = { namespace: function(ns) { var parts = ns.split("."), object = this, i, len; for(i = 0, len = parts.lenght; i < len; i ++) { if(!object[parts[i]]) { object[parts[i]] = {}; } object = object[parts[i]]; } return object; } }; // 定義命名空間 MyApp.namespace("Helpers.Parsing"); // 你現在可以使用該命名空間了 MyApp.Helpers.Parsing.DateParser = function() { //做一些事情 };模塊化
另一項開發者用來避免全局變量的技術就是封裝到模塊 Module 中。一個模塊就是不需要創建新的全局變量或者命名空間的通用的功能。不要將所有的代碼都放一個負責執行任務或者發布接口的函數中。最常見的 JavaScript 模塊類型就是異步模塊定義 Asynchronous Module Definition (AMD)。
//定義 define( "parsing", //模塊名字 [ "dependency1", "dependency2" ], // 模塊依賴 function( dependency1, dependency2) { //工廠方法 // Instead of creating a namespace AMD modules // are expected to return their public interface var Parsing = {}; Parsing.DateParser = function() { //do something }; return Parsing; } ); // 通過 Require.js 加載模塊 require(["parsing"], function(Parsing) { Parsing.DateParser(); // 使用模塊 });解構賦值
解構賦值允許你使用類似數組或對象字面量的語法將數組和對象的屬性賦給各種變量。這種賦值語法極度簡潔,同時還比傳統的屬性訪問方法更為清晰。
傳統的訪問數組前三個元素的方式為:
var first = someArray[0]; var second = someArray[1]; var third = someArray[2];
而通過解構賦值的特性,可以變為:
var [first, second, third] = someArray;數組與迭代器
以上是數組解構賦值的一個簡單示例,其語法的一般形式為:
[ variable1, variable2, ..., variableN ] = array;
這將為variable1到variableN的變量賦予數組中相應元素項的值。如果你想在賦值的同時聲明變量,可在賦值語句前加入var、let或const關鍵字,例如:
var [ variable1, variable2, ..., variableN ] = array; let [ variable1, variable2, ..., variableN ] = array; const [ variable1, variable2, ..., variableN ] = array;
事實上,用變量來描述并不恰當,因為你可以對任意深度的嵌套數組進行解構:
var [foo, [[bar], baz]] = [1, [[2], 3]]; console.log(foo); // 1 console.log(bar); // 2 console.log(baz); // 3
此外,你可以在對應位留空來跳過被解構數組中的某些元素:
var [,,third] = ["foo", "bar", "baz"]; console.log(third); // "baz"
而且你還可以通過“不定參數”模式捕獲數組中的所有尾隨元素:
var [head, ...tail] = [1, 2, 3, 4]; console.log(tail); // [2, 3, 4]
當訪問空數組或越界訪問數組時,對其解構與對其索引的行為一致,最終得到的結果都是:undefined。
console.log([][0]); // undefined var [missing] = []; console.log(missing); // undefined
請注意,數組解構賦值的模式同樣適用于任意迭代器:
function* fibs() { var a = 0; var b = 1; while (true) { yield a; [a, b] = [b, a + b]; } } var [first, second, third, fourth, fifth, sixth] = fibs(); console.log(sixth); // 5對象
通過解構對象,你可以把它的每個屬性與不同的變量綁定,首先指定被綁定的屬性,然后緊跟一個要解構的變量。
var robotA = { name: "Bender" }; var robotB = { name: "Flexo" }; var { name: nameA } = robotA; var { name: nameB } = robotB; console.log(nameA); // "Bender" console.log(nameB); // "Flexo"
當屬性名與變量名一致時,可以通過一種實用的句法簡寫:
var { foo, bar } = { foo: "lorem", bar: "ipsum" }; console.log(foo); // "lorem" console.log(bar); // "ipsum"
與數組解構一樣,你可以隨意嵌套并進一步組合對象解構:
var complicatedObj = { arrayProp: [ "Zapp", { second: "Brannigan" } ] }; var { arrayProp: [first, { second }] } = complicatedObj; console.log(first); // "Zapp" console.log(second); // "Brannigan"
當你解構一個未定義的屬性時,得到的值為undefined:
var { missing } = {}; console.log(missing); // undefined
請注意,當你解構對象并賦值給變量時,如果你已經聲明或不打算聲明這些變量(亦即賦值語句前沒有let、const或var關鍵字),你應該注意這樣一個潛在的語法錯誤:
{ blowUp } = { blowUp: 10 }; // Syntax error 語法錯誤
為什么會出錯?這是因為JavaScript語法通知解析引擎將任何以{開始的語句解析為一個塊語句(例如,{console}是一個合法塊語句)。解決方案是將整個表達式用一對小括號包裹:
({ safe } = {}); // No errors 沒有語法錯誤默認值
當你要解構的屬性未定義時你可以提供一個默認值:
var [missing = true] = []; console.log(missing); // true var { message: msg = "Something went wrong" } = {}; console.log(msg); // "Something went wrong" var { x = 3 } = {}; console.log(x); // 3
由于解構中允許對對象進行解構,并且還支持默認值,那么完全可以將解構應用在函數參數以及參數的默認值中。
function removeBreakpoint({ url, line, column }) { // ... }
當我們構造一個提供配置的對象,并且需要這個對象的屬性攜帶默認值時,解構特性就派上用場了。舉個例子,jQuery的ajax函數使用一個配置對象作為它的第二參數,我們可以這樣重寫函數定義:
jQuery.ajax = function (url, { async = true, beforeSend = noop, cache = true, complete = noop, crossDomain = false, global = true, // ... 更多配置 }) { // ... do stuff };
同樣,解構也可以應用在函數的多重返回值中,可以類似于其他語言中的元組的特性:
function returnMultipleValues() { return [1, 2]; } var [foo, bar] = returnMultipleValues();Spread Operator(… 擴展操作符)
function myFunction(x, y, z) { } var args = [0, 1, 2]; myFunction(...args);
還有另外一個好處就是可以用來替換Object.assign來方便地從舊有的對象中創建新的對象,并且能夠修改部分值。譬如
var obj = {a:1,b:2} var obj_new_1 = Object.assign({},obj,{a:3}); var obj_new_2 = { ...obj, a:3 }類型/格式判斷與轉換 typeof
typeof運算符可以返回一個值的數據類型,可能有以下結果。
(1)原始類型
數值、字符串、布爾值分別返回number、string、boolean。
typeof 123 // "number" typeof "123" // "string" typeof false // "boolean"
(2)函數
函數返回function。
// 定義一個空函數 function f(){} typeof f // "function"
(3)undefined
undefined返回undefined。
typeof undefined // "undefined"
利用這一點,typeof可以用來檢查一個沒有聲明的變量,而不報錯。
v // ReferenceError: v is not defined typeof v // "undefined"
實際編程中,這個特點通常用在判斷語句。
// 錯誤的寫法 if (v){ // ... } // ReferenceError: v is not defined // 正確的寫法 if (typeof v === "undefined"){ // ... }
(4)其他
除此以外,都返回object。
typeof window // "object" typeof {} // "object" typeof [] // "object" typeof null // "object"
從上面代碼可以看到,空數組([])的類型也是object,這表示在JavaScript內部,數組本質上只是一種特殊的對象。另外,null的類型也是object,這是由于歷史原因造成的,為了兼容以前的代碼,后來就沒法修改了,并不是說null就屬于對象,本質上null是一個類似于undefined的特殊值。
instanceoftypeof對數組(array)和對象(object)的顯示結果都是object,那么怎么區分它們呢?instanceof運算符可以做到。
var o = {}; var a = []; o instanceof Array // false a instanceof Array // true類型的自動轉換
當遇到以下幾種情況,JavaScript會自動轉換數據類型:
不同類型的數據進行互相運算;
對非布爾值類型的數據求布爾值;
對非數值類型的數據使用一元運算符(即“+”和“-”)。
基本類型(Basic) 數值類型 科學計算 隨機數random() 方法可返回介于 0 ~ 1 之間的一個隨機數。
類型轉換使用Number函數,可以將任意類型的值轉化成數字。
數值:轉換后還是原來的值。
字符串:如果可以被解析為數值,則轉換為相應的數值,否則得到NaN。空字符串轉為0。
布爾值:true轉成1,false轉成0。
undefined:轉成NaN。
null:轉成0。
Number函數將字符串轉為數值,要比parseInt函數嚴格很多。基本上,只要有一個字符無法轉成數值,整個字符串就會被轉為NaN。
parseInt("011") // 9 parseInt("42 cats") // 42 parseInt("0xcafebabe") // 3405691582 Number("011") // 11 Number("42 cats") // NaN Number("0xcafebabe") // 3405691582
如果Number傳入的參數是一個對象,那么轉換規則會相對復雜一點,具體而言描述如下:
先調用對象自身的valueOf方法,如果該方法返回原始類型的值(數值、字符串和布爾值),則直接對該值使用Number方法,不再進行后續步驟。
如果valueOf方法返回復合類型的值,再調用對象自身的toString方法,如果toString方法返回原始類型的值,則對該值使用Number方法,不再進行后續步驟。
如果toString方法返回的是復合類型的值,則報錯。
格式化顯示這里用的是numeraljs,格式化顯示的效果如下所示:
Number
Number | Format | String |
---|---|---|
10000 | "0,0.0000" | 10,000.0000 |
10000.23 | "0,0" | 10,000 |
10000.23 | "+0,0" | +10,000 |
Currency
Number | Format | String |
---|---|---|
1000.234 | "$0,0.00" | $1,000.23 |
1000.2 | "0,0[.]00 $" | 1,000.20 $ |
Bytes
Number | Format | String |
---|---|---|
100 | "0b" | 100B |
2048 | "0 b" | 2 KB |
Percentages
Number | Format | String |
---|---|---|
1 | "0%" | 100% |
0.974878234 | "0.000%" | 97.488% |
Time
Number | Format | String |
---|---|---|
25 | "00:00:00" | 0:00:25 |
238 | "00:00:00" | 0:03:58 |
布爾值代表“真”和“假”兩個狀態。“真”用關鍵字true表示,“假”用關鍵字false表示。布爾值只有這兩個值。如果JavaScript預期某個位置應該是布爾值,會將該位置上現有的值自動轉為布爾值。轉換規則是除了下面六個值被轉為false,其他值都視為true。
undefined
null
false
0
NaN
""(空字符串)
類型轉換所有對象的布爾值都是true,甚至連false對應的布爾對象也是true。
Boolean(new Boolean(false)) // true空類型
JavaScript中常見的空類型為undefined與null,不過typeof undefined === ‘undefined’ 而 typeof null === ‘object’。
null表示"沒有對象",即該處不應該有值。典型用法是:
作為函數的參數,表示該函數的參數是對象。
作為對象原型鏈的終點。
undefined表示"缺少值",就是此處應該有一個值,但是還未定義。典型用法是:
變量被聲明了,但沒有賦值時,就等于undefined。
調用函數時,應該提供的參數沒有提供,該參數等于undefined。
對象沒有賦值的屬性,該屬性的值為undefined。
函數沒有返回值時,默認返回undefined。
SymbolsSymbols是JavaScript的第七種原始類型,它代指一個全局唯一的不可變對象。如果需要創建一個Symbol對象,則需要調用Symbol函數:
var sym1 = Symbol(); var sym2 = Symbol("foo"); var sym3 = Symbol("foo");
如上的代碼會創建三個新的符號,注意,雖然Symbol使用了”foo”這個字符串作為輸入對象,但是每次會創建一個新的符號:
Symbol("foo") === Symbol("foo"); // false
確切地說,symbol與其它類型并不完全相像。symbol被創建后就不可變更,你不能為它設置屬性(在嚴格模式下嘗試設置屬性會得到TypeError的錯誤)。他們可以用作屬性名稱,這些性質與字符串類似。
另一方面,每一個symbol都獨一無二,不與其它symbol等同,即使二者有相同的描述也不相等;你可以輕松地創建一個新的symbol。這些性質與對象類似。
ES6中的symbol與Lisp和Ruby這些語言中更傳統的symbol類似,但不像它們集成得那么緊密。在Lisp中,所有的標識符都是symbol;在JS中,標識符和大多數的屬性鍵仍然是字符串,symbol只是一個額外的選項。
關于symbol的忠告:symbol不能被自動轉換為字符串,這和語言中的其它類型不同。嘗試拼接symbol與字符串將得到TypeError錯誤。
> var sym = Symbol("<3"); > "your symbol is " + sym // TypeError: can"t convert symbol to string > `your symbol is ${sym}` // TypeError: can"t convert symbol to string
有三種獲取symbol的方法。
調用Symbol()。正如我們上文中所討論的,這種方式每次調用都會返回一個新的唯一symbol。
調用Symbol.for(string)。這種方式會訪問symbol注冊表,其中存儲了已經存在的一系列symbol。這種方式與通過Symbol()定義的獨立symbol不同,symbol注冊表中的symbol是共享的。如果你連續三十次調用Symbol.for("cat"),每次都會返回相同的symbol。注冊表非常有用,在多個web頁面或同一個web頁面的多個模塊中經常需要共享一個symbol。
使用標準定義的symbol,例如:Symbol.iterator。標準根據一些特殊用途定義了少許的幾個symbol。
字符串類型 創建增刪 插值ES6中開始支持較為復雜的模板字符串方式:
// Basic literal string creation `In JavaScript " " is a line-feed.` // Multiline strings `In JavaScript this is not legal.` // String interpolation var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?` // Construct an HTTP request prefix is used to interpret the replacements and construction GET`http://foo.org/bar?a=${a}&b=${b} Content-Type: application/json X-Credentials: ${credentials} { "foo": ${foo}, "bar": ${bar}}`(myOnReadyStateChangeHandler);隨機字符串 UUID
node-uuid
類型編碼 類型轉換使用String函數,可以將任意類型的值轉化成字符串。規則如下:
數值:轉為相應的字符串。
字符串:轉換后還是原來的值。
布爾值:true轉為“true”,false轉為“false”。
undefined:轉為“undefined”。
null:轉為“null”。
而對于較復雜一點的對象類型,轉換規則如下:
先調用toString方法,如果toString方法返回的是原始類型的值,則對該值使用String方法,不再進行以下步驟。
如果toString方法返回的是復合類型的值,再調用valueOf方法,如果valueOf方法返回的是原始類型的值,則對該值使用String方法,不再進行以下步驟。
如果valueOf方法返回的是復合類型的值,則報錯。
HTML編碼function html_encode(str) { var s = ""; if (str.length == 0) return ""; s = str.replace(/&/g, ">"); s = s.replace(//g, ">"); s = s.replace(/ /g, " "); s = s.replace(/"/g, "'"); s = s.replace(/"/g, """); s = s.replace(/ /g, "其他操作 Reverse
"); return s; } function html_decode(str) { var s = ""; if (str.length == 0) return ""; s = str.replace(/>/g, "&"); s = s.replace(//g, ">"); s = s.replace(/ /g, " "); s = s.replace(/'/g, """); s = s.replace(/"/g, """); s = s.replace(/
/g, " "); return s; }
str.split("").reverse().join("");Date Time Built-in:Date
根據毫秒計算對應的天/時/分/秒
var left_time = ; function GetRTime(){ //var NowTime = new Date(); //var nMS = startTime.getTime() - NowTime.getTime() left_time = left_time - 1000; var nMS = left_time; var nD = Math.floor(nMS/(1000 * 60 * 60 * 24)); var nH = Math.floor(nMS/(1000*60*60)) % 24; var nM = Math.floor(nMS/(1000*60)) % 60; var nS = Math.floor(nMS/1000) % 60; if (nMS < 0){ }else{ } }Moment:JS的第三方時間庫 Format
moment().format("MMMM Do YYYY, h:mm:ss a"); // August 26th 2015, 9:13:04 pm moment().format("ffffdd"); // Wednesday moment().format("MMM Do YY"); // Aug 26th 15 moment().format("YYYY [escaped] YYYY"); // 2015 escaped 2015 moment().format(); // 2015-08-26T21:13:04+08:00 var dateString = moment.unix(value).format("MM/DD/YYYY");
如果需要顯示本地化時間,需要引入locale目錄下對應文件,并且使用moment.locale(String);進行設置。另外注意,Unix時間戳指的是
Difference&&Duration:時間差var a = moment([2007, 0, 29]); var b = moment([2007, 0, 28]); a.diff(b) // 86400000 milliseconds a.diff(b, "days") // 1
如果需要將duration格式化為較好的顯示,可以參考moment-duration-format。
TimeZone(時間本地化)Indexed Collection ArrayCss-Tricks:本地化時間
在JavaScript中,Array是一個全局的對象可以用來創建數組,是一個高級別的有點類似于列表的集合。可以直接使用Array.length來獲取某個數組的長度。
創建增刪JavaScript創建新的數組,可以采用如下方式:
arr = [] arr = new Array() arr = new Array([PreSize]) arr = [] arr[1] = 1 // arr = [undefined , 1] 對于不存在的自動賦值為undefined復制
slice() 方法把數組中一部分的淺復制(shallow copy)存入一個新的數組對象中,并返回這個新的數組。array.slice(begin[, end]).
slice會提取原數組中索引從 begin 到 end 的所有元素(包含begin,但不包含end)。
slice(1,4) 提取原數組中的第二個元素開始直到第四個元素的所有元素 (索引為 1, 2, 3的元素)。
例子:返回數組中的一部分
// Our good friend the citrus from fruits example var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"]; var citrus = fruits.slice(1, 3); // puts --> ["Orange","Lemon"]
例子:使用 slice
在下例中, slice從`myCar中創建了一個新數組`newCar.兩個數組都包含了一個myHonda對象的引用. 當myHonda的color屬性改變為purple, 則兩個數組中的對應元素都會隨之改變.
// 使用slice方法從myCar中創建一個newCar. var myHonda = { color: "red", wheels: 4, engine: { cylinders: 4, size: 2.2 } }; var myCar = [myHonda, 2, "cherry condition", "purchased 1997"]; var newCar = myCar.slice(0, 2); // 輸出myCar, newCar,以及各自的myHonda對象引用的color屬性. print("myCar = " + myCar.toSource()); print("newCar = " + newCar.toSource()); print("myCar[0].color = " + myCar[0].color); print("newCar[0].color = " + newCar[0].color); // 改變myHonda對象的color屬性. myHonda.color = "purple"; print("The new color of my Honda is " + myHonda.color); //輸出myCar, newCar中各自的myHonda對象引用的color屬性. print("myCar[0].color = " + myCar[0].color); print("newCar[0].color = " + newCar[0].color);
上述代碼輸出:
myCar = [{color:"red", wheels:4, engine:{cylinders:4, size:2.2}}, 2, "cherry condition", "purchased 1997"] newCar = [{color:"red", wheels:4, engine:{cylinders:4, size:2.2}}, 2] myCar[0].color = red newCar[0].color = red The new color of my Honda is purple myCar[0].color = purple newCar[0].color = purple
類數組(Array-like)對象
slice 方法可以用來將一個類數組(Array-like)對象/集合轉換成一個數組。你只需將該方法綁定到這個對象上。下述代碼中 list 函數中的 arguments 就是一個類數組對象。
function list() { return Array.prototype.slice.call(arguments); } var list1 = list(1, 2, 3); // [1, 2, 3]
除了使用 Array.prototype.slice.call(arguments),你也可以簡單的使用 [].slice.call(arguments) 來代替。另外,你可以使用 bind 來簡化該過程。
var unboundSlice = Array.prototype.slice; var slice = Function.prototype.call.bind(unboundSlice); function list() { return slice(arguments); } var list1 = list(1, 2, 3); // [1, 2, 3]插入刪除
JavaScript的Array支持從頭部插入移除,從尾部插入移除等多種方式:
var fruits = ["Apple", "Banana"]; var newLength = fruits.push("Orange");//從尾部插入,返回的數組長度 // ["Apple", "Banana", "Orange"] var last = fruits.pop(); // remove Orange (from the end),返回刪除的對象 // ["Apple", "Banana"]; var first = fruits.shift(); // remove Apple from the front // ["Banana"]; var newLength = fruits.unshift("Strawberry") // add to the front // ["Strawberry", "Banana"];遍歷索引 存在性判斷
var array = [1, 2, 3]; array.includes(1); // → true反向索引
如果在數組中需要反向索引某個元素的位置,可以使用indexOf方法
fruits.push("Mango"); // ["Strawberry", "Banana", "Mango"] var pos = fruits.indexOf("Banana"); // 1 var array = [2, 9, 9, 4, 3, 6]; var result = array.lastIndexOf(9); console.log(result); // output: 2
如果存儲的是復雜的對象,則可以使用find方法,返回數組中滿足測試條件的一個元素,如果沒有滿足條件的元素,則返回 undefined。
_.find(users, function(o) { return o.age < 40; }); // output: object for "barney" // Native var users = [ { "user": "barney", "age": 36, "active": true }, { "user": "fred", "age": 40, "active": false }, { "user": "pebbles", "age": 1, "active": true } ]; users.find(function(o) { return o.age < 40; }); // output: object for "barney" var index = users.findIndex(function(o) { return o.age >= 40; }); console.log(index); // output: 1遍歷
fruits.forEach(function (item, index, array) { console.log(item, index); }); // Apple 0 // Banana 1Set:集合
集合也是一種常見的數據結構,在ES6中添加了對于集合的支持,其基本用法如下:
// Sets var s = new Set(); s.add("hello").add("goodbye").add("hello"); s.size === 2; s.has("hello") === true;WeakSet
WeakSet提供了一種自回收的存儲方式:
// Weak Sets var ws = new WeakSet(); ws.add({ data: 42 }); // Because the added object has no other references, it will not be held in the set序列操作 map
將某個列表中的元素映射到新的列表中。
// Native var array1 = [1, 2, 3]; var array2 = array1.map(function(value, index) { return value*2; }); console.log(array2); // output: [2, 4, 6]reduce
var array = [0, 1, 2, 3, 4]; var result = array.reduce(function (previousValue, currentValue, currentIndex, array) { return previousValue + currentValue; }); console.log(result); // output: 10 //reduceRight,正好方向相反 var array = [0, 1, 2, 3, 4]; var result = array.reduceRight(function (previousValue, currentValue, currentIndex, array) { return previousValue - currentValue; }); console.log(result); // output: -2filter
// Native function isBigEnough(value) { return value >= 10; } var array = [12, 5, 8, 130, 44]; var filtered = array.filter(isBigEnough); console.log(filtered); // output: [12, 130, 44]Keyed Collections Object
Maps,Sets And Iterators in JavaScript
javascript-properties
JavaScript中Object是一個混合了類似于Dictionary與Class的用法,基本上來說也是一種鍵值類型。其中鍵的類型主要包含三種:
Identifier:包含任何有效地標識符,包括了ES的保留關鍵字。
字符串:single (") or double (") quotes. "foo", "bar","qu"ux", "" (the empty string), and "Ich u2665 BxFCcher" are all valid string literals.
數字:decimal literal (e.g. 0, 123, 123., .123, 1.23, 1e23, 1E-23, 1e+23, 12, but not 01, +123 or -123) or a hex integer literal (0[xX][0-9a-fA-F]+ in regex, e.g. 0xFFFF, 0X123,0xaBcD).
var object = { // `abc` is a valid identifier; no quotes are needed abc: 1, // `123` is a numeric literal; no quotes are needed 123: 2, // `012` is an octal literal with value `10` and thus isn’t allowed in strict mode; but if you insist on using it, quotes aren’t needed 012: 3, // `π` is a valid identifier; no quotes are needed π: Math.PI, // `var` is a valid identifier name (although it’s a reserved word); no quotes are needed var: 4, // `foo bar` is not a valid identifier name; quotes are required "foo bar": 5, // `foo-bar` is not a valid identifier name; quotes are required "foo-bar": 6, // the empty string is not a valid identifier name; quotes are required "": 7 };
注意,與object不同的是,JSON 只允許用雙引號 (") 包裹的字符串作為鍵名。而如果要根據鍵名進行索引的話,可以使用方括號,這種方式對于三種鍵值皆有效:
object["abc"]; // 1
有時候也可以使用點操作符,不過這種方式只可以被用于鍵為有效地Identifier情況:
object.abc; // 1
如果需要獲取所有的鍵名的話,可以使用Object.keys方法:
注意,所有的Object的方法只能用Object.methodName方式調用
// Native var result2 = Object.keys({one: 1, two: 2, three: 3}); console.log(result2); // output: ["one", "two", "three"] //也可以等效獲取大小 var result2 = Object.keys({one: 1, two: 2, three: 3}).length; console.log(result2); // output: 3創建添加 Object.create() :創建一個擁有指定原型和若干個指定屬性的對象。
其基本語法為:
Object.create(proto, { propertiesObject })
這里需要注意的是,propertiesObject不是一個簡單的鍵值類型,而是有固定格式的object。
var o; // 創建一個原型為null的空對象 o = Object.create(null); o = {}; // 以字面量方式創建的空對象就相當于: o = Object.create(Object.prototype); o = Object.create(Object.prototype, { // foo會成為所創建對象的數據屬性 foo: { writable:true, configurable:true, value: "hello" }, // bar會成為所創建對象的訪問器屬性 bar: { configurable: false, get: function() { return 10 }, set: function(value) { console.log("Setting `o.bar` to", value) } }}) function Constructor(){} o = new Constructor(); // 上面的一句就相當于: o = Object.create(Constructor.prototype); // 當然,如果在Constructor函數中有一些初始化代碼,Object.create不能執行那些代碼 // 創建一個以另一個空對象為原型,且擁有一個屬性p的對象 o = Object.create({}, { p: { value: 42 } }) // 省略了的屬性特性默認為false,所以屬性p是不可寫,不可枚舉,不可配置的: o.p = 24 o.p //42 o.q = 12 for (var prop in o) { console.log(prop) } //"q" delete o.p //false //創建一個可寫的,可枚舉的,可配置的屬性p o2 = Object.create({}, { p: { value: 42, writable: true, enumerable: true, configurable: true } });Object.assign:一層淺復制
Object.assign() 方法可以把任意多個的源對象所擁有的自身可枚舉屬性拷貝給目標對象,然后返回目標對象。Object.assign 方法只會拷貝源對象自身的并且可枚舉的屬性到目標對象身上。注意,對于訪問器屬性,該方法會執行那個訪問器屬性的 getter 函數,然后把得到的值拷貝給目標對象,如果你想拷貝訪問器屬性本身,請使用 Object.getOwnPropertyDescriptor() 和Object.defineProperties() 方法。
注意,字符串類型和 symbol 類型的屬性都會被拷貝。
注意,在屬性拷貝過程中可能會產生異常,比如目標對象的某個只讀屬性和源對象的某個屬性同名,這時該方法會拋出一個 TypeError 異常,拷貝過程中斷,已經拷貝成功的屬性不會受到影響,還未拷貝的屬性將不會再被拷貝。
注意, Object.assign 會跳過那些值為 null 或 undefined 的源對象。
Object.assign(target, ...sources)
例子:淺拷貝一個對象
var obj = { a: 1 }; var copy = Object.assign({}, obj); console.log(copy); // { a: 1 }
例子:合并若干個對象
var o1 = { a: 1 }; var o2 = { b: 2 }; var o3 = { c: 3 }; var obj = Object.assign(o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 } console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目標對象自身也會改變。
例子:拷貝 symbol 類型的屬性
var o1 = { a: 1 }; var o2 = { [Symbol("foo")]: 2 }; var obj = Object.assign({}, o1, o2); console.log(obj); // { a: 1, [Symbol("foo")]: 2 }
例子:繼承屬性和不可枚舉屬性是不能拷貝的
var obj = Object.create({foo: 1}, { // foo 是個繼承屬性。 bar: { value: 2 // bar 是個不可枚舉屬性。 }, baz: { value: 3, enumerable: true // baz 是個自身可枚舉屬性。 } }); var copy = Object.assign({}, obj); console.log(copy); // { baz: 3 }
例子:原始值會被隱式轉換成其包裝對象
var v1 = "123"; var v2 = true; var v3 = 10; var v4 = Symbol("foo") var obj = Object.assign({}, v1, null, v2, undefined, v3, v4); // 源對象如果是原始值,會被自動轉換成它們的包裝對象, // 而 null 和 undefined 這兩種原始值會被完全忽略。 // 注意,只有字符串的包裝對象才有可能有自身可枚舉屬性。 console.log(obj); // { "0": "1", "1": "2", "2": "3" }
例子:拷貝屬性過程中發生異常
var target = Object.defineProperty({}, "foo", { value: 1, writeable: false }); // target 的 foo 屬性是個只讀屬性。 Object.assign(target, {bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4}); // TypeError: "foo" is read-only // 注意這個異常是在拷貝第二個源對象的第二個屬性時發生的。 console.log(target.bar); // 2,說明第一個源對象拷貝成功了。 console.log(target.foo2); // 3,說明第二個源對象的第一個屬性也拷貝成功了。 console.log(target.foo); // 1,只讀屬性不能被覆蓋,所以第二個源對象的第二個屬性拷貝失敗了。 console.log(target.foo3); // undefined,異常之后 assign 方法就退出了,第三個屬性是不會被拷貝到的。 console.log(target.baz); // undefined,第三個源對象更是不會被拷貝到的。
不過需要注意的是,assign是淺拷貝,或者說,它是一級深拷貝,舉兩個例子說明:
const defaultOpt = { title: { text: "hello world", subtext: "It"s my world." } }; const opt = Object.assign({}, defaultOpt, { title: { subtext: "Yes, your world." } }); console.log(opt); // 預期結果 { title: { text: "hello world", subtext: "Yes, your world." } } // 實際結果 { title: { subtext: "Yes, your world." } }
上面這個例子中,對于對象的一級子元素而言,只會替換引用,而不會動態的添加內容。那么,其實assign并沒有解決對象的引用混亂問題,參考下下面這個例子:
const defaultOpt = { title: { text: "hello world", subtext: "It"s my world." } }; const opt1 = Object.assign({}, defaultOpt); const opt2 = Object.assign({}, defaultOpt); opt2.title.subtext = "Yes, your world."; console.log("opt1:"); console.log(opt1); console.log("opt2:"); console.log(opt2); // 結果 opt1: { title: { text: "hello world", subtext: "Yes, your world." } } opt2: { title: { text: "hello world", subtext: "Yes, your world." } }Map 創建增刪
var map = new Map(); map.set("foo", "bar"); console.log(map.get("foo")); //logs "bar" var animalSounds = new Map(); animalSounds.set("dog", "woof"); animalSounds.set("cat", "meow"); animalSounds.set("frog", "ribbit"); console.log(animalSounds.size); //logs 3 console.log(animalSounds.has("dog")); //logs true animalSounds.delete("dog"); console.log(animalSounds.size); //logs 2 console.log(animalSounds.has("dog")); //logs false animalSounds.clear(); console.log(animalSounds.size); //logs 0索引遍歷
usersMap = new Map(); usersMap.set(1, "sally"); usersMap.set(2, "bob"); usersMap.set(3, "jane"); console.log(usersMap.get(1)); //logs "sally" usersMap.forEach(function (username, userId) { console.log(userId, typeof userId); //logs 1..3, "number" if (userId === 1) { console.log("We found sally."); } }); //如果用for...of方式遍歷,每次返回的是一個Array for (data of usersMap) { console.log(data);//Array [1,"sally"] }
Map的鍵的類型可以是object、NaN等等。
var obj, map; map = new Map(); obj = {foo: "bar"}; map.set(obj, "foobar"); obj.newProp = "stuff"; console.log(map.has(obj)); //logs true console.log(map.get(obj)); //logs "foobar"Immutable.js
FaceBook-Immutable
Immutable 詳解及 React 中實踐
Immutable 對象一旦被創建之后即不可再更改,這樣可以使得應用開發工作變得簡化,不再需要大量的保護性拷貝,使用簡單的邏輯控制即可以保證內存控制與變化檢測。Immutable.js雖然和React同期出現且跟React配合很爽,但它可不是React工具集里的(它的光芒被掩蓋了),它是一個完全獨立的庫,無論基于什么框架都可以用它。意義在于它彌補了Javascript沒有不可變數據結構的問題。不可變數據結構是函數式編程中必備的。前端工程師被OOP洗腦太久了,組件根本上就是函數用法,FP的特點更適用于前端開發。
Javascript中對象都是參考類型,也就是a={a:1}; b=a; b.a=10;你發現a.a也變成10了。可變的好處是節省內存或是利用可變性做一些事情,但是,在復雜的開發中它的副作用遠比好處大的多。于是才有了淺copy和深copy,就是為了解決這個問題。舉個常見例子:
var defaultConfig = { /* 默認值 */}; var config = $.extend({}, defaultConfig, initConfig); // jQuery用法。initConfig是自定義值 var config = $.extend(true, {}, defaultConfig, initConfig); // 如果對象是多層的,就用到deep-copy了
而
var stateV1 = Immutable.fromJS({ users: [ { name: "Foo" }, { name: "Bar" } ] }); var stateV2 = stateV1.updateIn(["users", 1], function () { return Immutable.fromJS({ name: "Barbar" }); }); stateV1 === stateV2; // false stateV1.getIn(["users", 0]) === stateV2.getIn(["users", 0]); // true stateV1.getIn(["users", 1]) === stateV2.getIn(["users", 1]); // false
如上,我們可以使用===來通過引用來比較對象,這意味著我們能夠方便快速的進行對象比較,并且它能夠和React中的PureRenderMixin 兼容。基于此,我們可以在整個應用構建中使用Immutable.js。也就是說,我們的Flux Store應該是一個具有不變性的對象,并且我們通過 將具有不變性的數據作為屬性傳遞給我們的應用程序。
創建與判斷如果要創建Immutable對象,使用fromJS方法既可以將簡單的JS中的objects與arrays轉化為不可變的Maps與Lists
fromJS(json: any, reviver?: (k: any, v: Iterable) => any): any
如果reviver這個屬性被提供了,那么它會傳入一個Seq對象并且被循環調用,對于頂層對象,它的默認的鍵為""。
Immutable.fromJS({a: {b: [10, 20, 30]}, c: 40}, function (key, value) { var isIndexed = Immutable.Iterable.isIndexed(value); return isIndexed ? value.toList() : value.toOrderedMap(); }); // true, "b", {b: [10, 20, 30]} // false, "a", {a: {b: [10, 20, 30]}, c: 40} // false, "", {"": {a: {b: [10, 20, 30]}, c: 40}}
對于轉化而來的Immutable對象,可以通過Iterable.is*方法來判斷其是列表還是映射或者其他數據類型。
List 創建增刪 更新 刪除 遍歷索引 Map 創建增刪 更新要更新Map中的某個元素值,需要調用updateIn方法,該方法會根據傳入的keyPath尋找到該值,然后進行安全修正并返回一個新的對象。如果keyPath值并不存在,那么返回的Map對象會自動創建該鍵,如果KeyPath沒有提供指定值,那么會自動調用notSetValue或者賦值為undefined
updateIn(keyPath: Array, updater: (value: any) => any): Map updateIn( keyPath: Array , notSetValue: any, updater: (value: any) => any ): Map updateIn(keyPath: Iterable , updater: (value: any) => any): Map updateIn( keyPath: Iterable , notSetValue: any, updater: (value: any) => any ): Map
var data = Immutable.fromJS({ a: { b: { c: 10 } } }); data = data.updateIn(["a", "b", "c"], val => val * 2); // { a: { b: { c: 20 } } }刪除
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78719.html
摘要:模板語法四種詞法定義二空白符號空白符號分類或稱是,是縮進符,字符串中寫的。注意換行符會影響的兩個重要語法特性自動插入分號和規則。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的可以加入winter的專欄學習【原文有winter的語音】,如有侵權請聯系我,郵箱...
摘要:模板語法四種詞法定義二空白符號空白符號分類或稱是,是縮進符,字符串中寫的。注意換行符會影響的兩個重要語法特性自動插入分號和規則。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的可以加入winter的專欄學習【原文有winter的語音】,如有侵權請聯系我,郵箱...
摘要:模板語法四種詞法定義二空白符號空白符號分類或稱是,是縮進符,字符串中寫的。注意換行符會影響的兩個重要語法特性自動插入分號和規則。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的可以加入winter的專欄學習【原文有winter的語音】,如有侵權請聯系我,郵箱...
閱讀 903·2021-11-22 13:53
閱讀 2533·2021-10-15 09:40
閱讀 1001·2021-10-14 09:42
閱讀 3475·2021-09-22 15:59
閱讀 888·2021-09-02 09:47
閱讀 2368·2019-08-30 15:54
閱讀 1438·2019-08-29 17:14
閱讀 400·2019-08-29 15:15