(一) 棧區和堆區
https://segmentfault.com/a/1190000002789651 (重要)
js變量有兩種不同的數據類型: ( 基本類型 ) 和 ( 引用類型 )
- 基本數據類型有:number,string,boolean,null,undefined
- 引用數據類型有:array,object。
array (是特殊的對象,屬性是數值字符串)
(1) 基本類型的變量存放在棧區 (棧區指內存里的棧內存)
- 棧區包括了( 變量的標識符 ) 和 ( 變量的值 )。
-
不能給基本類型添加屬性和方法,基本類型是不可變得;
棧區:變量的標識符,變量的值
(2) 引用類型的值是同時保存在棧內存和堆內存中的對象
- 引用類型的存儲需要內存的棧區和堆區(堆區是指內存里的堆內存)共同完成,棧區內存保存變量標識符和指向堆內存中該對象的指針,也可以說是該對象在堆內存的地址。
引用類型的存儲需要 ( 棧區 ) 和 ( 堆區 )
棧區保存 ( 變量的標識符 ) 和 ( 指向堆區中該對象的指針 )
-
引用類型可以擁有屬性和方法,屬性又可以包含基本類型和引用類型。
引用類型的值存儲需要棧區和堆區,棧區中是標識符和指針,堆區中的對象
(3) 基本類型賦值
(4) 對象的引用
(一) ES6字符串擴展
es5中,只有 indexOf() 方法可以確定一個字符串是否包含在另一個字符串中
(1) includes():返回布爾值,表示是否找到了參數字符串。
(2) startsWith():返回布爾值,表示參數字符串是否在原字符串的頭部。
(3) endsWith():返回布爾值,表示參數字符串是否在原字符串的尾部。
- 這三個方法都支持第二個參數,表示開始搜索的位置
- 使用第二個參數n時,endsWith的行為與其他兩個方法有所不同。它針對前n個字符,而其他兩個方法針對從第n個位置直到字符串結束。
let s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
--------------------------------------------------------------------
let s = 'Hello world!';
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true // 注意注意注意! 5表示 前5個字符
s.includes('Hello', 6) // false
上面代碼表示,使用第二個參數n時,endsWith的行為與其他兩個方法有所不同。
endsWith 針對前n個字符,而其他兩個方法針對從第n個位置直到字符串結束。
(4) repeat()
repeat方法返回一個新字符串,表示將原字符串重復n次。
- 參數如果是小數,會被取整,即只取整數部分。(!!!!)
- 如果repeat的參數是負數或者Infinity,會報錯
- 如果參數是 0 到-1 之間的小數,則等同于 0,這是因為會先進行取整運算。0 到-1 之間的小數,取整以后等于-0,repeat視同為 0。
- 參數NaN等同于 0。
- 如果repeat的參數是字符串,則會先轉換成數字。
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
'na'.repeat(2.9) // "nana" ---- 參數是小數,取整
'na'.repeat(Infinity) ---- 報錯
// RangeError
'na'.repeat(-1) ---- 報錯
// RangeError
'na'.repeat(-0.9) // ""
'na'.repeat(NaN) // ""
'na'.repeat('na') // ""
'na'.repeat('3') // "nanana" ---- 參數是字符串,會被轉換成數字
(5) padStart(),padEnd()
ES2017 引入了字符串補全長度的功能。如果某個字符串不夠指定長度,會在頭部或尾部補全。padStart()用于頭部補全,padEnd()用于尾部補全。
- pad:是墊,補全的意思
- padStart和padEnd一共接受兩個參數:
第一個參數用來指定字符串的長度。
第二個參數是用來補全的字符串。 - 如果原字符串的長度,等于或大于指定的最小長度,則返回原字符串。
- 如果用來補全的字符串與原字符串,兩者的長度之和超過了指定的最小長度,則會截去超出位數的補全字符串。
- 如果省略第二個參數,默認使用空格補全長度。
padStart的常見用途是為數值補全指定位數。下面代碼生成 10 位的數值字符串。
'1'.padStart(10, '0') // "0000000001" // padStart主要用于給數值補全位數
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"
padStart另一個用途是提示字符串格式。
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
-----------------------------------------------------
'xxx'.padStart(2, 'ab') // 'xxx' -- 指定的字符串長度,小于原字符串長度,則返回原字符串
'xxx'.padEnd(2, 'ab') // 'xxx'
-----------------------------------------------------
'abc'.padStart(10, '0123456789')
// '0123456abc'
-----------------------------------------------------
'x'.padStart(4) // ' x'
'x'.padEnd(4) // 'x '
(二) js錯誤的類型 ( 6種 ) ( 前4種常用 )
- SyntaxError : 語法錯誤
- ReferenceError : 引用錯誤 ( 要用的東西沒有找到 )
( reference 是引用的意思 )
- RangeError : 范圍錯誤 ( 專指參數超出范圍 )
- TypeError : 類型錯誤 ( 錯誤的調用了對象的方法 )
- EvalError: eval()方法錯誤的使用
- URIError: url地址錯誤
(1) Error對象
JavaScript 解析或運行時,一旦發生錯誤,引擎就會拋出一個錯誤對象。JavaScript 原生提供Error構造函數,所有拋出的錯誤都是這個構造函數的實例。
- js解析或運行出錯,都會拋出一個錯誤對象
- 所有拋出的錯誤都是 Error構造函數的實例
- 拋出Error實例對象以后,整個程序就中斷在發生錯誤的地方,不再往下執行。
-
Error構造函數,接受一個參數,表示錯誤提示。可以從 實例對象的 message 屬性讀到這個參數
Error實例對象屬性: ( message , name , stack )
- message:錯誤提示信息
- name:錯誤名稱(非標準屬性)
- stack:錯誤的堆棧(非標準屬性)
let err = new Error('這是錯誤信息提示') // Error構造函數,new執行構造函數,返回實例對象
console.log( err.message ); // 實例的 message 屬性,讀取Error構造函數的 參數
console.log( err.name );
console.log( err.stack );
// 拋出Error實例對象以后,整個程序就 ( 中斷 ) 在發生錯誤的地方,不再往下執行。
// Error實例對象必須有message屬性
SyntaxError對象是解析代碼時發生的語法錯誤。 // 語法錯誤
ReferenceError對象是引用一個不存在的變量時發生的錯誤。 // 引用不存在的變量
RangeError對象是一個值超出有效范圍時發生的錯誤。主要有幾種情況, // 超出范圍,數組,對象方法,堆棧
一是 : 數組長度為負數,
二是 : Number對象的方法參數超出范圍,以及函數堆棧超過最大值。
TypeError對象是變量或參數不是預期類型時發生的錯誤。 // 類型錯誤
比如,對字符串、布爾值、數值等原始類型的值使用new命令,就會拋出這種錯誤,
因為new命令的參數應該是一個構造函數。
URIError對象是 URI 相關函數的參數不正確時拋出的錯誤,主要涉及這六個函數:
encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()、 unescape()
// 以上這6種派生錯誤,連同原始的Error對象,都是構造函數
// 這些構造函數都接受一個函數,代表錯誤提示信息(message)。
(2) throw 語句
throw語句的作用是手動中斷程序執行,拋出一個錯誤。
- throw語句,手動中斷程序執行,并拋出一個錯誤
- throw可以拋出任何類型的值。也就是說,它的參數可以是任何值。
let a=9;
if (a<10) {
throw new Error('a不能小于10') // throw拋出的錯誤,就是 Error構造函數的參數,程序會中止執行
}
// 對于 JavaScript 引擎來說,遇到throw語句,程序就中止了。
// 引擎會接收到throw拋出的信息,可能是一個錯誤實例,也可能是其他類型的值。
----------------------------------------------
// 拋出一個字符串
throw 'Error!';
// Uncaught Error!
// 拋出一個數值
throw 42;
// Uncaught 42
// 拋出一個布爾值
throw true;
// Uncaught true
// 拋出一個對象
throw {
toString: function () {
return 'Error!';
}
};
// Uncaught {toString: ?}
(3) try…catch 結構
一旦發生錯誤,程序就中止執行了。JavaScript 提供了try...catch結構,允許對錯誤進行處理,選擇是否往下執行。
- try...catch結構,對錯誤進行處理,選擇是否往下執行
- catch代碼塊捕獲錯誤之后,程序不會中斷,會按照正常流程繼續執行下去。( 重要 )
- catch代碼塊之中,還可以再拋出錯誤,甚至使用嵌套的try...catch結構。
- 為了捕捉不同類型的錯誤,catch代碼塊之中可以加入判斷語句。
let a=9;
try {
if (a<10) {
throw new Error('錯誤,a不能小于10') // throw手動中止執行,并拋出錯誤
console.log('該條語句不會被執行,因為throw命令會中斷代碼執行')!!!!!!!!!
}
} catch (error) { // 錯誤被catch代碼塊捕獲,參數是try代碼塊拋出的值
console.log(error.name + ':' + error.message)
}
console.log('該條語句會執行,因為catch捕獲錯誤后,程序會按正常執行')!!!!!!!!!!
// 上面代碼中,try代碼塊拋出錯誤(throw語句),JavaScript 引擎就立即把代碼的執行,轉到catch代碼塊,
// 或者說錯誤被catch代碼塊捕獲了。catch接受一個參數,表示try代碼塊拋出的值。
(4) finally 代碼塊
try...catch結構允許在最后添加一個finally代碼塊,表示不管是否出現錯誤,都必需在最后運行的語句。
- finally 表示不管是否出現錯誤,都必須在最后執行的語句
function cleansUp() {
try { // try代碼塊
throw new Error('出錯了……'); // throw手動中止程序,并拋出錯誤
console.log('此行不會執行'); // trow會中止代碼執行
} finally { // try拋出錯誤,會立即執行catch語句。 finally語句表示不管是否出現錯誤,都必須在最后執行
console.log('完成清理工作'); // 因為沒有catch ,所以執行完 finally后,程序就會中止( 因為throw )
}
}
cleansUp()
// 完成清理工作
// Error: 出錯了……
// 上面代碼中,由于沒有catch語句塊,所以錯誤沒有捕獲。
// 執行finally代碼塊以后,程序就中斷在錯誤拋出的地方。
(重要)
function idle(x) {
try {
console.log(x);
return 'result';
} finally { // try語句未發生錯誤,finally依舊會執行。執行完finally,才會執行return !!!!
console.log("FINALLY");
}
}
idle('hello')
// hello
// FINALLY
// "result"
// 上面代碼說明,try代碼塊沒有發生錯誤,而且里面還包括return語句,但是finally代碼塊依然會執行。
// 注意,只有在其執行完畢后,才會顯示return語句的值。
-------------------------------------
try {
console.log('111111');
return console.log('222222222');
} catch (err) {
console.log('不執行');
} finally {
console.log('333333333');
}
結果是:
11111111
22222222
33333333
// 說明 return語句的執行是排在finally代碼之前,只是等finally代碼執行完畢后才返回。
------------------------------------------------
function test() {
try {
console.log('111111');
return '222222222';
} catch (err) {
console.log('不執行');
} finally {
console.log('333333333');
}
}
const returnValue = test();
console.log(returnValue)
結果是:
11111111
33333333
22222222
(重要)
var count = 0;
function countUp() {
try {
return count;
} finally {
count++;
}
}
countUp()
// 0
count
// 1
// return語句的執行是排在finally代碼之前,只是等finally代碼執行完畢后才返回。
// return語句的count的值,是在finally代碼塊運行之前就獲取了。
總結:
componentDidMount() {
let x = 10;
try {
if (x < 11) {
console.log('Error的實例中有 message,name,stack等屬性');
console.log('throw可以拋出任何類型的值');
throw new Error('錯誤信息:x不能小于11'); // throw手動中斷代碼執行,并拋出錯誤
console.log('該語句不會執行,trorw 會中止程序');
}
} catch (err) { // catch 捕獲 try 代碼塊的錯誤,catch執行完,會正常執行程序
console.log(err.message, 'err'); // err.message輸出 Error 構造函數的參數
console.log('catch 中還能拋出錯誤,也能再嵌套 try...catch...');
} finally {
console.log('不管有沒有錯誤,都會執行finally語句');
}
console.log('這句會正常執行,catch不會中止程序執行');
console.log('js錯誤類型有 SyntaxError語法錯誤');
console.log('js錯誤類型有 ReferenceError引用錯誤,引用不存在的變量');
console.log('js錯誤類型有 RangeError范圍錯誤,如數組的長度為負');
console.log('js錯誤類型有 TypeError類型錯誤,變量或參數不是預期類型');
}
執行結果:
//Error的實例中有 message,name,stack等屬性
//username.js:78 throw可以拋出任何類型的值
//username.js:83 錯誤信息:x不能小于11 err
//username.js:84 catch 中還能拋出錯誤,也能再嵌套 try...catch...
//username.js:86 不管有沒有錯誤,都會執行finally語句
//username.js:88 這句會正常執行,catch不會中止程序執行
//username.js:89 js錯誤類型有 SyntaxError語法錯誤
//username.js:90 js錯誤類型有 ReferenceError引用錯誤,引用不存在的變量
//username.js:91 js錯誤類型有 RangeError范圍錯誤,如數組的長度為負
//username.js:92 js錯誤類型有 TypeError類型錯誤,變量或參數不是預期類型
(三) 數組的擴展
(1) 擴展運算符
- 擴展運算符后面還可以放置表達式。
- 如果擴展運算符后面是一個空數組,則不產生任何效果。
表達式
const arr = [
...(x > 0 ? ['a'] : []),
'b',
];
空數組
[...[], 1]
// [1]
- 獲取數組最大的元素 (重要)
// ES5 的寫法
Math.max.apply(null, [14, 3, 77]) // apply第一個參數是需要綁定的對象,第二個參數只能是數組
// ES6 的寫法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);
擴展運算符的運用
(1) 復制數組
數組是復合類型的數據結構,直接復制,只是復制了指向底層數據結構的指針,而不是克隆一個全新的數組。
- 基本類型和引用類型 https://segmentfault.com/a/1190000002789651
- 直接復制引用類型的值(數組),其實只是復制了棧區 指向 堆區同一對象的 指針,并不是對象的克隆,改變其中一個值,另一個值就會跟著改變。
const a1 = [1, 2];
const a2 = a1;
a2[0] = 2;
a1 // [2, 2]
// 上面代碼中,a2并不是a1的克隆,而是指向同一份數據的另一個指針。修改a2,會直接導致a1的變化
// a1有指向堆區 該數組的指針
// a2只是復制了 棧區,棧區的指針也指向了 堆區的該數組
// 改變其中一個變量,另一個也會改變
真正的克隆 (復制)
(1) es5中的 concat() 方法 ------------------------ concat()方法
const a1 = [1, 2];
const a2 = a1.concat();
a2[0] = 2;
a1 // [1, 2]
// concat方法用于多個數組的合并。它將新數組的成員,添加到原數組成員的后部,然后返回一個新數組,原數組不變。
// 除了數組作為參數,concat也接受其他類型的值作為參數,添加到目標數組尾部。
------------------------------------
(2) es6 擴展運算符 ------------------------------- 數組的淺拷貝
let a1 = [1,2,3];
// let a2 = [...a1];
let [...a2] = a1;
a2.push(4);
console.log(a1);
console.log(a2);
// 上面的兩種寫法,a2都是a1的克隆。
(2) 將字符串轉為真正的數組。-------- (重要)
[...'hello']
// [ "h", "e", "l", "l", "o" ]
// 展開運算符可以用于所有具有iterator接口的數據
// 原生具有 iterator 接口的數據結構 :
(1) array,
(2) string,
(3) NodeList對象, ----- 如:document.querySellectorAll()返回值
(4) TypedArray,
(5) 函數的 arguments 對象 ----------- arguments對象包含了函數運行時的所有參數,用[下標]取值
(6) Map
(7) Set
- 注意:對于那些沒有部署 Iterator 接口的類似數組的對象,擴展運算符就無法將其轉為真正的數組。
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// TypeError: Cannot spread non-iterable object. // spread 是擴展的意思
let arr = [...arrayLike];
(2) Array.from()
Array.from方法用于將兩類對象轉為真正的數組:
(1) 類似數組的對象(array-like object),
(2) 可遍歷(iterable)的對象(包括 ES6 新增的數據結構 Set 和 Map)。
Array.from還可以接受第二個參數,作用類似于數組的map方法,用來對每個元素進行處理,將處理后的值放入返回的數組。
- Array.from()將
類似數組的對象
和可遍歷的對象
轉化成真正的數組
- 只要是部署了 Iterator 接口的數據結構,Array.from 都能將其轉為數組。!!!
- 只要是部署了 Iterator 接口的數據結構,展開運算符 都能將其轉為數組。!!!
- 如果參數是一個真正的數組,Array.from會返回一個一模一樣的新數組。
- 所謂類似數組的對象,本質特征只有一點,即必須有length屬性。因此,任何有length屬性的對象,都可以通過Array.from方法轉為數組,而此時擴展運算符就無法轉換。(重要) !!!
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5的寫法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的寫法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
// Array.from() 將類似數組的對象 轉換成真正的 數組 ( array like object )
// ( 展開運算符 ) 只能是對具有iterator接口的數據類型轉換成數組,不適用于一般的類似數組的對象
- 實際應用中,常見的類似數組的對象是 DOM 操作返回的 NodeList 集合,以及函數內部的arguments對象。Array.from都可以將它們轉為真正的數組。
- NodeList對象 和 arguments對象具有iterator接口,所以也能用 展開運算符轉換成數組
function a(x,y,z) {
console.log( arguments ); // Arguments(3) [1, 2, 3, callee: ?, Symbol(Symbol.iterator): ?]
console.log( arguments[0]); // 1
console.log( Array.from(arguments) ); // [1, 2, 3]
console.log( [...arguments]); // [1, 2, 3]
return x+y+z;
}
a(1,2,3);
總結:
(1) Array.from能將類似數組的對象 轉換成真正的數組
(2) 具有iterator接口的數據結構,都可以通過展開運算符,轉換成數組。
(3) 原生具有iterator接口的數據結構有:array,string,NodeList,arguments,map,set,TypedArray
(4) ( NodeList對象 ),( arguments對象 ) 可以通過 ( Array.from() ) 和 ( 展開運算符 )轉化為數組
- 所謂類似數組的對象,本質特征只有一點,即必須有length屬性。因此,任何有length屬性的對象,都可以通過Array.from方法轉為數組,而此時擴展運算符就無法轉換。(重要) !!!
Array.from({ length: 3 });
// [ undefined, undefined, undefined ]
Array.from還可以接受第二個參數,作用類似于數組的map方法,用來對每個元素進行處理,將處理后的值放入返回的數組。 (重要)!!!
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]
(重要)(重要)(重要)(重要)(重要)(重要)(重要)(重要)(重要)(重要)(重要)
function a(x,y,z) {
console.log( Array.from(arguments, x => (x+'').repeat(4)) ); // ["1111", "2222", "3333"]
console.log( [...arguments]);
return x+y+z;
}
a(1,2,3)
下面的例子將數組中布爾值為false的成員轉為0。
Array.from([1, , 2, , 3], (n) => n || 0)
// [1, 0, 2, 0, 3]
Array.from()可以將各種值轉為真正的數組,并且還提供map功能。這實際上意味著,只要有一個原始的數據結構,你就可以先對它的值進行處理,然后轉成規范的數組結構,進而就可以使用數量眾多的數組方法
Array.from({ length: 2 }, () => 'jack')
// ['jack', 'jack']
(3) Array.of()
Array.of方法用于將一組值,轉換為數組。
- 這個方法的主要目的,是彌補數組構造函數Array()的不足。因為參數個數的不同,會導致Array()的行為有差異。
- Array.of基本上可以用來替代Array()或new Array(),并且不存在由于參數不同而導致的重載。它的行為非常統一。
- Array.of總是返回參數值組成的數組。如果沒有參數,就返回一個空數組
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]