JavaScript 容許採用\uxxxx
形式表示一個字符,其中xxxx
表示字符的 Unicode 碼點。這種表示法只限於碼點在\u0000
~\uFFFF
之間的字符。超出這個範圍的字符,必須用兩個雙字節的形式表示。ES6中只要將碼點放入大括號,就能正確解讀該字符。
git
"\uD842\uDFB7" // "?" "\u{20BB7}" // "?"
大括號表示法與 UTF-16 編碼是等價的。es6
'\z' === 'z' // true '\172' === 'z' // true '\x7A' === 'z' // true '\u007A' === 'z' // true '\u{7A}' === 'z' // true
JavaScript內部,字符以UTF-16的格式儲存,每一個字符固定爲2
個字節。對於那些須要4個字節儲存的字符(Unicode碼點大於0xFFFF
的字符),JavaScript會認爲它們是兩個字符,字符串長度會誤判爲2。github
var s = "?"; s.length // 2 s.charAt(0) // '' s.charAt(1) // '' s.charCodeAt(0) // 55362 s.charCodeAt(1) // 57271 s.codePointAt(0) // 134071 codePointAt方法在第一個字符上,正確地識別了「?」 s.codePointAt(1) // 57271 第二個字符是「?」的後兩個字節 String.fromCharCode(0x20BB7) // "ஷ" 最高位2被捨棄了,最後返回碼點U+0BB7對應的字符,而不是碼點U+20BB7對應的字符。 String.fromCodePoint(0x20BB7) // "?" '?'.at(0) // "?"
對於Unicode碼點大於0xFFFF
的字符:正則表達式
charAt:沒法讀取整個字符。該方法不能識別碼點大於0xFFFF的字符。數組
charCodeAt:只能分別返回前兩個字節和後兩個字節的值。瀏覽器
fromCharCode:不能識別大於0xFFFF的碼點。安全
codePointAt:可以正確處理4個字節儲存的字符,返回一個字符的碼點。codePointAt方法是測試一個字符由兩個字節仍是由四個字節組成的最簡單方法。數據結構
fromCodePoint:能夠識別0xFFFF的字符。app
at: 能夠識別Unicode編號大於0xFFFF的字符,返回正確的字符。這個方法能夠經過墊片庫實現。ide
注意,fromCodePoint方法定義在String對象上,而codePointAt方法定義在字符串的實例對象上。
normalize():用來將字符的不一樣表示方法統一爲一樣的形式
includes():返回布爾值,表示是否找到了參數字符串。
startsWith():返回布爾值,表示參數字符串是否在源字符串的頭部。
endsWith():返回布爾值,表示參數字符串是否在源字符串的尾部。
padStart():用於頭部補全。常見用途是爲數值補全指定位數和提示字符串格式。
'1'.padStart(10, '0') // "0000000001" '12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
padEnd():用於尾部補全。
repeat():返回一個新字符串,表示將原字符串重複n次。參數若是是小數,會被取整。
參數是負數或者Infinity,會報錯。
參數是0到-1之間的小數,則等同於0,這是由於會先進行取整運算。
參數0到-1之間的小數,取整之後等於-0,repeat視同爲0。
參數NaN等同於0。
repeat的參數是字符串,則會先轉換成數字。
'na'.repeat(2.9) // "nana" 'na'.repeat(Infinity)// RangeError 'na'.repeat(NaN) // "" 'na'.repeat('na') // "" 'na'.repeat('3') // "nanana"
ES6爲字符串添加了遍歷器接口(詳見《Iterator》一章),使得字符串能夠被for...of循環遍歷。該遍歷能夠識別大於0xFFFF的碼點,傳統的for循環沒法識別這樣的碼點。
var text = String.fromCodePoint(0x20BB7); for (let i = 0; i < text.length; i++) { console.log(text[i]); } // " " // " " //for循環會認爲它包含兩個字符(都不可打印) for (let i of text) { console.log(i); } // "?" //for...of循環會正確識別出這一個字符
模板字符串用反引號(`)標識。它能夠看成普通字符串使用,也能夠用來定義多行字符串,或者在字符串中嵌入變量。在模板字符串中須要使用反引號,則前面要用反斜槓轉義。使用模板字符串表示多行字符串,全部的空格和縮進都會被保留在輸出之中。
// 普通字符串 `In JavaScript '\n' is a line-feed.` // 多行字符串 `In JavaScript this is not legal.` // 字符串中嵌入變量 `Hello ${name}, how are you ${time}?`
模板字符串中嵌入變量,須要將變量名寫在${}之中。
大括號內部能夠放入任意的JavaScript表達式,能夠進行運算,以及引用對象屬性和調用函數。若是大括號中的值不是字符串,將按照通常的規則轉爲字符串。
好比,大括號中是一個對象,將默認調用對象的toString方法。
若是模板字符串中的變量沒有聲明,將報錯。
// 變量place沒有聲明 var msg = `Hello, ${place}`; // 報錯
因爲模板字符串的大括號內部,就是執行JavaScript代碼,所以若是大括號內部是一個字符串,將會原樣輸出。
`Hello ${'World'}` // "Hello World"
模板字符串緊跟在一個函數名後面,該函數將被調用來處理這個模板字符串。即模板字符串就是該函數的參數。標籤模板是函數調用的一種特殊形式。「標籤」指的就是函數,緊跟在後面的模板字符串就是它的參數。
//模板字符裏面有變量,會將模板字符串先處理成多個參數,再調用函數。 var a = 5; var b = 10; tag`Hello ${ a + b } world ${ a * b }`; // 等同於 tag(['Hello ', ' world ', ''], 15, 50);
模板處理函數的第一個參數(模板字符串數組),還有一個raw屬性。
console.log(`123`) //123 console.log`123` // ["123", raw: Array[1]]
上面代碼中,第二個console.log接受的參數,其實是一個數組。該數組有一個raw屬性,保存的是轉義後的原字符串。
String.raw方法,每每用來充當模板字符串的處理函數,返回一個斜槓都被轉義(即斜槓前面再加一個斜槓)的字符串,對應於替換變量後的模板字符串。若是原字符串的斜槓已經轉義,那麼String.raw不會作任何處理。
String.raw`Hi\n${2+3}!`; // "Hi\\n5!" String.raw`Hi\\n` // "Hi\\n"
String.raw方法也能夠做爲正常的函數使用。這時,它的第一個參數,應該是一個具備raw屬性的對象,且raw屬性的值應該是一個數組。
String.raw({ raw: 'test' }, 0, 1, 2); // 't0e1s2t' // 等同於 String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2);
var regex = new RegExp('xyz', 'i'); // 等價於 var regex = /xyz/i; // 等價於 var regex = new RegExp(/xyz/i);
ES5中如下寫法會報錯。ES6可使用第二個參數指定修飾符,新指定的修飾符會覆蓋原有的正則表達式的修飾符。
var regex = new RegExp(/xyz/ig, 'i'); //原有正則對象的修飾符是ig,它會被第二個參數i覆蓋.
ES6對正則表達式添加了u修飾符,含義爲「Unicode模式」,用來正確處理大於\uFFFF
的Unicode字符。也就是說,會正確處理四個字節的UTF-16編碼。
/^\uD83D/u.test('\uD83D\uDC2A') // false /^\uD83D/.test('\uD83D\uDC2A') // true
上面的代碼中,uD83DuDC2A是一個四字節的UTF-16編碼,表明一個字符。不加「u」,會按 ES5 將其識別爲2個字符,加了「u」以後,會按 ES6 將其正確識別爲一個字符。
如下幾種狀況就必須加上「u」才能正確識別:
.在正則表達式中表示除行終止符(換行符(n),回車符(r),行分隔符,段分隔符)外的任意單個字符,S表示匹配全部不是空格的字符。他們均正確識別碼點大於0xFFFF的Unicode字符,必須加上u修飾符才能正確識別。
/^\S$/.test('?') // false /^\S$/u.test('?') // true
ES6新增了使用大括號表示Unicode字符,這種表示法在正則表達式中必須加上u修飾符,才能識別。不然大括號會被解讀爲量詞。
/^\u{3}$/.test('uuu') // true 被解讀爲量詞 /^\u{3}$/u.test('uuu') // false 被解讀爲Unicode表達式
有些Unicode字符的編碼不一樣,可是字型很相近,須要加u才能識別。好比,u004B與u212A都是大寫的K。
/[a-z]/i.test('\u212A') // false 該行代碼不加u修飾符,就沒法識別非規範的K字符 /[a-z]/iu.test('\u212A') // true
y修飾符的做用與g修飾符相似,也是全局匹配,後一次匹配都從上一次匹配成功的下一個位置開始。不一樣之處在於,g修飾符只要剩餘位置中存在匹配就可,而y修飾符確保匹配必須從剩餘的第一個位置開始。y修飾符號就是讓頭部匹配的標誌^在全局匹配中都有效。
var s = 'aaa_aa_a'; var r1 = /a+/g; var r2 = /a+/y; r1.exec(s) // ["aaa"] r1.exec(s) // ["aa"] r2.exec(s) // ["aaa"] r2.exec(s) // null 第一次執行後,剩餘字符串是_aa_a,y修飾符要求匹配必須從頭部開始,因此返回null
在split方法中使用y修飾符,原字符串必須以分隔符開頭。這也意味着,只要匹配成功,數組的第一個成員確定是空字符串。
ES6的正則對象多了sticky屬性,表示是否設置了y修飾符。
var r = /hello\d/y; r.sticky // true
ES6爲正則表達式新增了flags屬性,會返回正則表達式的修飾符。
// ES5的source屬性 返回正則表達式的正文 /abc/ig.source // "abc" // ES6的flags屬性 返回正則表達式的修飾符 /abc/ig.flags // 'gi'
JavaScript 語言的正則表達式,只支持先行斷言(lookahead)和先行否認斷言(negative lookahead)。
」先行斷言「指的是,x只有在y前面才匹配,必須寫成/x(?=y)/。好比,只匹配百分號以前的數字,要寫成/d+(?=%)/。」先行否認斷言「指的是,x只有不在y前面才匹配,必須寫成/x(?!y)/。好比,只匹配不在百分號以前的數字,要寫成/d+(?!%)/。
ES6 提供了二進制和八進制數值的新的寫法,分別用前綴0b(或0B)和0o(或0O)表示。可以使用Number方法將0b和0o前綴的字符串數值轉爲十進制。
Number('0b111') // 7
Number.isFinite():用來檢查一個數值
是否爲有限的(finite),對於非數值一概返回false。
Number.isFinite(0.8); // true Number.isFinite(NaN); // false Number.isFinite(Infinity); // false Number.isFinite(-Infinity); // false Number.isFinite('foo'); // false Number.isFinite('15'); // false Number.isFinite(true); // false
Number.isNaN():用來檢查一個值是否爲NaN。
Number.isNaN(NaN) // true Number.isNaN(15) // false Number.isNaN('15') // false Number.isNaN(true) // false Number.isNaN(9/NaN) // true Number.isNaN('true'/0) // true Number.isNaN('true'/'true') // true
ES6將全局方法parseInt()和parseFloat(),移植到Number對象上面,行爲徹底保持不變。
Number.parseInt === parseInt // true
Number.isInteger():用來判斷一個值是否爲整數。須要注意的是,在JavaScript內部,整數和浮點數是一樣的儲存方法,因此3和3.0被視爲同一個值。
Number.isInteger(25) // true Number.isInteger(25.0) // true Number.isInteger(25.1) // false Number.isInteger("15") // false Number.isInteger(true) // false
Number.isSafeInteger():用來判斷一個整數是否落在Number.MAX_SAFE_INTEGER與Number.MIN_SAFE_INTEGER範圍以內。使用該函數時,需注意不只要驗證運算結果是否落在安全整數的範圍內,還要同時驗證參與運算的每一個值,不然極可能獲得錯誤結果。
Number.isSafeInteger(3) // true Number.isSafeInteger(1.2) // false
Number.EPSILON:極小常量。用於爲浮點數計算,設置一個偏差範圍。
JavaScript可以準確表示的整數範圍在-2^53到2^53之間(不含兩個端點),超過這個範圍,沒法精確表示這個值。ES6使用Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER這兩個常量,用來表示這個範圍的上下限。
Math.pow(2, 53) // 9007199254740992 Number.MAX_SAFE_INTEGER === 9007199254740991 // true Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER // true Number.MIN_SAFE_INTEGER === -9007199254740991 // true
ES6在Math對象上新增了17個與數學相關的方法。全部這些方法都是靜態方法,只能在Math對象上調用。
Math.trunc:用於去除一個數的小數部分,返回整數部分。對於非數值,Math.trunc內部使用Number方法將其先轉爲數值。對於空值和沒法截取整數的值,返回NaN。
Math.trunc('123.456') // 123 Math.trunc(NaN); // NaN Math.trunc('foo'); // NaN Math.trunc(); // NaN
Math.cbrt():用於計算一個數的立方根。對於非數值,Math.cbrt方法內部先使用Number方法將其轉爲數值。
Math.sign():用來判斷一個數究竟是正數、負數、仍是零。正數返回+1,負數返回-1,0返回0,-0返回-0,其餘值,返回NaN。
Math.clz32():JavaScript的整數使用32位二進制形式。Math.clz32()返回一個數的32位無符號整數形式有多少個前導0。對於小數,只考慮其整數部分。對於空值或其餘類型的值,會將它們先轉爲數值,而後再計算。
//1000的二進制形式是0b1111101000,一共有10位,因此32位之中有22個前導0。 Math.clz32(1000) // 22
Math.imul:返回兩個數以32位帶符號整數形式相乘的結果,返回的也是一個32位的帶符號整數。
Math.fround:返回一個數的單精度浮點數形式。對於整數來講,Math.fround方法返回結果同樣。對於那些沒法用64個二進制位精確表示的小數,Math.fround方法返回最接近這個小數的單精度浮點數。
Math.fround(1) // 1 Math.fround(1.337) // 1.3370000123977661 Math.fround(1.5) // 1.5
Math.hypot:返回全部參數的平方和的平方根。若是參數不是數值,Math.hypot方法會將其轉爲數值。只要有一個參數沒法轉爲數值,就會返回NaN。
Math.sign():用來判斷一個值的正負,可是若是參數是-0,它會返回-0。
Math.expm1():
Math.log1p(x):返回1 + x的天然對數,即Math.log(1 + x)。若是x小於-1,返回NaN。
Math.log10(x):返回以10爲底的x的對數。若是x小於0,則返回NaN。
Math.log2(x):返回以2爲底的x的對數。若是x小於0,則返回NaN。
Math.sinh(x):返回x的雙曲正弦(hyperbolic sine)
Math.cosh(x):返回x的雙曲餘弦(hyperbolic cosine)
Math.tanh(x): 返回x的雙曲正切(hyperbolic tangent)
Math.asinh(x): 返回x的反雙曲正弦(inverse hyperbolic sine)
Math.acosh(x): 返回x的反雙曲餘弦(inverse hyperbolic cosine)
Math.atanh(x): 返回x的反雙曲正切(inverse hyperbolic tangent)
ES2016 新增了一個指數運算符(**)。
2 ** 3 // 8 b **= 3; // 等同於 b = b * b * b;
在 V8 引擎中,指數運算符與Math.pow的實現不相同,對於特別大的運算結果,二者會有細微的差別。
Math.pow(99, 99) // 3.697296376497263e+197 99 ** 99 // 3.697296376497268e+197
Array.from方法用於將兩類對象轉爲真正的數組:相似數組的對象(即有length屬性的對象)和可遍歷(iterable)的對象(包括ES6新增的數據結構Set和Map)。若是參數是一個真正的數組,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']
擴展運算符(...)也能夠將某些數據結構轉爲數組。擴展運算符背後調用的是遍歷器接口(Symbol.iterator)。
// arguments對象 function foo() { var args = [...arguments]; } // NodeList對象 [...document.querySelectorAll('div')]
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]
Array.from()能正確處理各類Unicode字符,所以能夠將將字符串轉爲數組,而後正確返回字符串的長度。
function countSymbols(string) { return Array.from(string).length; }
Array.of老是返回參數值組成的數組。若是沒有參數,就返回一個空數組。Array.of基本上能夠用來替代Array()或new Array()。
Array.of(3, 11, 8) // [3,11,8] Array.of(3) // [3] Array() // [] Array(3) // [, , ,] Array(3, 11, 8) // [3, 11, 8]
copyWithin() :在當前數組內部,將指定位置的成員複製到其餘位置(會覆蓋原有成員),而後返回當前數組。Array.prototype.copyWithin(target, start = 0, end = this.length)
。三個參數都應該是數值,若是不是,會自動轉爲數值。會修改當前數組。
find() :用於找出第一個符合條件的數組成員。它的參數是一個回調函數,全部數組成員依次執行該回調函數,直到找出第一個返回值爲true的成員,而後返回該成員。若是沒有符合條件的成員,則返回undefined。能夠發現NaN。
findIndex() :與find方法很是相似,返回第一個符合條件的數組成員的位置,若是全部成員都不符合條件,則返回-1。能夠發現NaN。
[NaN].indexOf(NaN) // -1 [NaN].findIndex(y => Object.is(NaN, y)) // 0 //indexOf方法沒法識別數組的NaN成員,可是findIndex方法能夠藉助Object.is方法作到。
fill() :使用給定值,填充一個數組。fill方法用於空數組的初始化很是方便。數組中已有的元素,會被所有抹去。
['a', 'b', 'c'].fill(7) // [7, 7, 7] new Array(3).fill(7) // [7, 7, 7] ['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c'] fill方法從1號位開始,向原數組填充7,到2號位以前結束。
includes():返回一個布爾值,表示某個數組是否包含給定的值,與字符串的includes方法相似。該方法屬於ES7,但Babel轉碼器已經支持。
[1, 2, NaN].includes(NaN); // true [NaN].indexOf(NaN) // -1 [NaN].includes(NaN) // true //indexof會致使對NaN的誤判,可是includes能夠正確判斷NaN。
entries(),keys()和values()均用於遍歷數組。它們都返回一個遍歷器對象(Iterator),能夠用for...of循環進行遍歷,惟一的區別是keys()是對鍵名的遍歷、values()是對鍵值的遍歷,entries()是對鍵值對的遍歷。
for (let [index, elem] of ['a', 'b'].entries()) { console.log(index, elem); } // 0 "a" // 1 "b"
不使用for...of循環,能夠手動調用遍歷器對象的next方法,進行遍歷。
let letter = ['a', 'b', 'c']; let entries = letter.entries(); console.log(entries.next().value); // [0, 'a'] console.log(entries.next().value); // [1, 'b']
數組的空位指,數組的某一個位置沒有任何值。空位不是undefined,一個位置的值等於undefined,依然是有值的。空位是沒有任何值。
Array(3) // [, , ,] Array(3)返回一個具備3個空位的數組。
ES5大多數狀況下會忽略空位。
forEach(), filter(), every() 和some()都會跳過空位。
map()會跳過空位,但會保留這個值。
join()和toString()會將空位視爲undefined,而undefined和null會被處理成空字符串。
// filter方法 ['a',,'b'].filter(x => true) // ['a','b'] // map方法 [,'a'].map(x => 1) // [,1] // join方法 [,'a',undefined,null].join('#') // "#a##"
ES6明確將空位轉爲undefined。
Array.from方法會將數組的空位,轉爲undefined。
擴展運算符(...)將空位轉爲undefined。
copyWithin()會連空位一塊兒拷貝。
fill()會將空位視爲正常的數組位置。
for...of循環也會遍歷空位。
entries()、keys()、values()、find()和findIndex()會將空位處理成undefined。
Array.from(['a',,'b']) // [ "a", undefined, "b" ] [...['a',,'b']] // [ "a", undefined, "b" ] new Array(3).fill('a') // ["a","a","a"]
因爲空位的處理規則很是不統一,因此建議避免出現空位。
ES6 容許爲函數的參數設置默認值,即直接寫在參數定義的後面。參數變量是默認聲明的,因此不能用let或const再次聲明。使用參數默認值時,函數不能有同名參數。
//參數變量x是默認聲明的,在函數體中,不能用let或const再次聲明,不然會報錯。 function foo(x = 5) { let x = 1; // error const x = 2; // error }
若是參數默認值是變量,那麼參數就不是傳值的,而是每次都從新計算默認值表達式的值。也就是說,參數默認值是惰性求值的。
let x = 99; function foo(p = x + 1) { console.log(p); } foo() // 100 x = 100; foo() // 101 //代碼中,參數p的默認值是x + 1。這時,每次調用函數foo,都會從新計算x + 1,而不是默認p等於 100。
參數默認值能夠與解構賦值的默認值結合起來使用。
function foo({x, y = 5}) { console.log(x, y); } foo({}) // undefined, 5 foo() // TypeError: Cannot read property 'x' of undefined
只有當函數foo的參數是一個對象時,變量x和y纔會經過解構賦值而生成。若是函數foo調用時參數不是對象,變量x和y就不會生成,從而報錯。若是參數對象沒有y屬性,y的默認值5纔會生效。
一般狀況下,定義了默認值的參數,應該是函數的尾參數。若是非尾部的參數設置默認值,則調用時沒法只省略該參數,而不省略它後面的參數,除非顯式輸入undefined。
function f(x = 1, y) { return [x, y]; } f(2) // [2, undefined]) f(, 1) // 報錯 f(undefined, 1) // [1, 1]
函數的length屬性,將返回沒有指定默認值的參數個數。設置了默認值的參數不是尾參數,那麼length屬性也再也不計入後面的參數。rest參數也不會計入length屬性。
(function (a, b, c = 5) {}).length // 2 (function(...args) {}).length // 0 (function (a, b = 1, c) {}).length // 1
一旦設置了參數的默認值,調用函數時,參數會造成一個單獨的做用域(context)。這種語法行爲,在不設置參數默認值時,是不會出現的。當該單獨做用域裏面默認值是變量,且變量未定義,則指向外層的全局變量,若此時該全局變量不存在,就會報錯。
let x = 1; function f(y = x) { let x = 2; console.log(y); } f() // 1
函數f調用時,參數y = x造成一個單獨的做用域。這個做用域裏面,變量x自己沒有定義,因此指向外層的全局變量x。函數調用時,函數體內部的新聲明局部變量x影響不到默認值變量x。
rest 運算符:將一個不定數量的參數表示爲一個數組。
ES6 引入 rest 參數(形式爲「...變量名」),用於獲取函數的多餘參數,rest 參數中的變量表明一個數組。注意,rest 參數以後不能再有其餘參數(即只能是最後一個參數),不然會報錯。函數的length屬性,不包括 rest 參數。
function f(a, ...b) { console.log(b); } f(2,3,4,5) //[3, 4, 5] // 報錯 function f(a, ...b, c) { // ... }
擴展運算符(spread)是三個點(...)。它比如 rest 參數的逆運算,將一個數組轉爲用逗號分隔的參數序列。在某種程度上,rest運算符和Spread運算符(即擴展運算符)相反,Spread運算符會「展開」元素使其變成多個元素,rest運算符會收集多個元素和「壓縮」成一個單一的元素。
console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5 // ES6的寫法 Math.max(...[14, 3, 77]) // 等同於 Math.max(14, 3, 77);
合併數組
// ES5 [1, 2].concat(more) // ES6 [1, 2, ...more]
將字符串轉爲真正的數組。
[...'hello'] // [ "h", "e", "l", "l", "o" ]
該寫法可以正確識別32位的Unicode字符。
'x\uD83D\uDE80y'.length // 4 [...'x\uD83D\uDE80y'].length // 3 //JavaScript會將32位Unicode字符,識別爲2個字符,採用擴展運算符就沒有這個問題。
擴展運算符內部調用的是數據結構的Iterator接口。因此任何Iterator接口的對象,均可以用擴展運算符轉爲真正的數組。
對於那些沒有部署Iterator接口的相似數組的對象,擴展運算符就沒法將其轉爲真正的數組。
從ES5開始,函數內部能夠設定爲嚴格模式。
function doSomething(a, b) { 'use strict'; // code }
《ECMAScript 2016標準》規定只要函數參數使用了默認值、解構賦值、或者擴展運算符,那麼函數內部就不能顯式設定爲嚴格模式,不然會報錯。
規定的緣由:函數內部的嚴格模式,同時適用於函數體代碼和函數參數代碼。可是,函數執行的時候,先執行函數參數代碼,而後再執行函數體代碼。這樣就有一個不合理的地方,只有從函數體代碼之中,才能知道參數代碼是否應該以嚴格模式執行,可是參數代碼卻應該先於函數體代碼執行。所以,標準如此定義。
函數的name屬性,返回該函數的函數名。將一個匿名函數賦值給一個變量,ES5 的name屬性,會返回空字符串,而 ES6 的name屬性會返回實際的函數名。若是將一個具名函數賦值給一個變量,則 ES5 和 ES6 的name屬性都返回這個具名函數本來的名字。
var f = function () {}; // ES5 f.name // "" // ES6 f.name // "f" const bar = function baz() {}; // ES5 bar.name // "baz" // ES6 bar.name // "baz"
Function構造函數返回的函數實例,name屬性的值爲anonymous。bind返回的函數,name屬性值會加上bound前綴。
(new Function).name // "anonymous" function foo() {}; foo.bind({}).name // "bound foo"
ES6容許使用「箭頭」(=>)定義函數。若是箭頭函數不須要參數或須要多個參數,就使用一個圓括號表明參數部分。若是箭頭函數的代碼塊部分多於一條語句,就要使用大括號將它們括起來。因爲大括號被解釋爲代碼塊,因此若是箭頭函數直接返回一個對象,必須在對象外面加上括號。箭頭函數能夠嵌套。
var f = v => v; //等同於 var f = function(v) { return v; }; var f = () => 5; // 等同於 var f = function () { return 5 }; var getTempItem = id => ({ id: id, name: "Temp" });
箭頭函數使用注意點:
函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。this指向的固定化,是由於箭頭函數根本沒有本身的this,致使內部的this就是外層代碼塊的this。正是由於它沒有this,因此也就不能用做構造函數。
不能夠看成構造函數,也就是說,不可使用new命令,不然會拋出一個錯誤。
不可使用arguments對象,該對象在函數體內不存在。若是要用,能夠用rest參數代替。
不可使用yield命令,所以箭頭函數不能用做Generator函數。
// ES6 function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } // ES5 function foo() { var _this = this; setTimeout(function () { console.log('id:', _this.id); }, 100); }
上面代碼中,轉換後的ES5版本清楚地說明了,箭頭函數裏面根本沒有本身的this,而是引用外層的this。
除了this,如下三個變量在箭頭函數之中也是不存在的,指向外層函數的對應變量:arguments、super、new.target
。因爲箭頭函數沒有本身的this,因此固然也就不能用call()、apply()、bind()這些方法去改變this的指向。
(function() { return [ (() => this.x).bind({ x: 'inner' })() ]; }).call({ x: 'outer' }); // ['outer'] //上面代碼中,箭頭函數沒有本身的this,因此bind方法無效,內部的this指向外部的this。
ES7提出了「函數綁定」(function bind)運算符,用來取代call、apply、bind調用。雖然該語法仍是ES7的一個提案,可是Babel轉碼器已經支持。
函數綁定運算符是並排的兩個雙冒號(::)
,雙冒號左邊是一個對象,右邊是一個函數。該運算符會自動將左邊的對象,做爲上下文環境(即this對象),綁定到右邊的函數上面。若是雙冒號左邊爲空,右邊是一個對象的方法,則等於將該方法綁定在該對象上面。雙冒號運算符返回的仍是原對象,所以能夠採用鏈式寫法。
foo::bar(...arguments); // 等同於 bar.apply(foo, arguments); var method = obj::obj.foo; // 等同於 var method = ::obj.foo;
尾調用就是指某個函數運行的最後一步是調用另外一個函數。尾調用不必定出如今函數尾部,只要是最後一步操做便可。
function f(x){ return g(x); } //函數f的最後一步是調用函數g,這就叫尾調用。 function f(x){ return g(x) + 1; } //函數調用以後還有操做,不是尾調用。 function f(x){ g(x); } //上面的函數等同於下面的代碼,所以也不是尾調用。 //function f(x){ // g(x); // return undefined; //}
函數調用會在內存造成一個「調用記錄」
,又稱「調用幀」(call frame),保存調用位置和內部變量等信息。若是在函數A的內部調用函數B,那麼在A的調用幀上方,還會造成一個B的調用幀。等到B運行結束,將結果返回到A,B的調用幀纔會消失。
若是函數B內部還調用函數C,那就還有一個C的調用幀,以此類推。全部的調用幀,就造成一個「調用棧」(call stack)。
function f() { let m = 1; let n = 2; return g(m + n); } f(); // 等同於 function f() { return g(3); } f(); // 等同於 g(3);
上面代碼中,若是函數g不是尾調用,函數f就須要保存內部變量m和n的值、g的調用位置等信息。但因爲調用g以後,函數f就結束了,因此執行到最後一步,徹底能夠刪除 f(x) 的調用幀,只保留 g(3) 的調用幀。
這就叫作「尾調用優化」(Tail call optimization),即只保留內層函數的調用幀。若是全部函數都是尾調用,那麼徹底能夠作到每次執行時,調用幀只有一項,這將大大節省內存。這就是「尾調用優化」的意義。注意,只有再也不用到外層函數的內部變量,內層函數的調用幀纔會取代外層函數的調用幀,不然就沒法進行「尾調用優化」。
函數調用自身,稱爲遞歸。若是尾調用自身,就稱爲尾遞歸。遞歸很是耗費內存,由於須要同時保存成千上百個調用幀,很容易發生「棧溢出」錯誤(stack overflow)。
但對於尾遞歸來講,因爲只存在一個調用幀,因此永遠不會發生「棧溢出」錯誤。
function factorial(n) { if (n === 1) return 1; return n * factorial(n - 1); } factorial(5) // 120 計算n的階乘,最多須要保存n個調用記錄,複雜度 O(n) //改寫爲尾遞歸 function factorial(n, total) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5, 1) // 120 改寫成了尾遞歸,只保留一個調用記錄,複雜度 O(1) 。
因而可知,「尾調用優化」對遞歸操做意義重大。ES6明確規定,全部ECMAScript的實現,都必須部署「尾調用優化」。這就是說,在ES6中,只要使用尾遞歸,就不會發生棧溢出,相對節省內存。
尾遞歸的實現,每每須要改寫遞歸函數,確保最後一步只調用自身。作到這一點的方法,就是把全部用到的內部變量改寫成函數的參數。
ES6的尾調用優化只在嚴格模式下開啓,正常模式是無效的。
這是由於在正常模式下,函數內部有兩個變量,能夠跟蹤函數的調用棧。
func.arguments:返回調用時函數的參數。
func.caller:返回調用當前函數的那個函數。
尾調用優化發生時,函數的調用棧會改寫,所以上面兩個變量就會失真。嚴格模式禁用這兩個變量,因此尾調用模式僅在嚴格模式下生效。
function restricted() { "use strict"; restricted.caller; // 報錯 restricted.arguments; // 報錯 } restricted();
正常模式下,或者那些不支持該功能的環境中,採用「循環」換掉「遞歸」,以減小調用棧。
//正常遞歸函數 function sum(x, y) { if (y > 0) { return sum(x + 1, y - 1); } else { return x; } } sum(1, 100000) //
可使用蹦牀函數(trampoline)將遞歸執行轉爲循環執行。
function trampoline(f) { while (f && f instanceof Function) { f = f(); } return f; }
上面就是蹦牀函數的一個實現,它接受一個函數f做爲參數。只要f執行後返回一個函數,就繼續執行。這裏是返回一個函數,而後執行該函數,而不是函數裏面調用函數,這樣就避免了遞歸執行,從而就消除了調用棧過大的問題。
而後,要作的就是將原來的遞歸函數,改寫爲每一步返回另外一個函數。
function sum(x, y) { if (y > 0) { return sum.bind(null, x + 1, y - 1); } else { return x; } } //sum函數的每次執行,都會返回自身的另外一個版本 trampoline(sum(1, 100000));//而後,用蹦牀函數執行sum,就不會發生調用棧溢出。
ES2017 容許函數的最後一個參數有尾逗號(trailing comma)。此前,函數定義和調用時,都不容許最後一個參數後面出現逗號。
ES6 容許直接寫入變量和函數,做爲對象的屬性和方法。簡潔寫法的屬性名老是字符串。
var foo = 'bar'; var baz = {foo}; baz // {foo: "bar"} // 等同於 var baz = {foo: foo}; //ES6 容許在對象之中,直接寫變量。這時,屬性名爲變量名, 屬性值爲變量的值。 var o = { method() { return "Hello!"; } }; // 等同於 var o = { method: function() { return "Hello!"; } };
JavaScript語言定義對象的屬性,有兩種方法。方法一是直接用標識符做爲屬性名,方法二是用表達式做爲屬性名,這時要將表達式放在方括號以內
。
// 方法一 obj.foo = true; // 方法二 obj['a' + 'bc'] = 123;
若是使用字面量方式定義對象(使用大括號),在 ES5 中只能使用方法一(標識符)定義屬性。但ES6 容許字面量定義對象時,用方法二(表達式)做爲對象的屬性名,即把表達式放在方括號內。表達式還能夠用於定義方法名。
let propKey = 'foo'; let obj = { [propKey]: true, ['a' + 'bc']: 123 };
注意,屬性名錶達式與簡潔表示法,不能同時使用,會報錯。
// 報錯 var foo = 'bar'; var bar = 'abc'; var baz = { [foo] }; // 正確 var foo = 'bar'; var baz = { [foo]: 'abc'};
注意,屬性名錶達式若是是一個對象,默認狀況下會自動將對象轉爲字符串[object Object]
,這一點要特別當心。
const keyA = {a: 1}; const keyB = {b: 2}; const myObject = { [keyA]: 'valueA', [keyB]: 'valueB' }; myObject // Object {[object Object]: "valueB"} //[keyA]和[keyB]獲得的都是[object Object],因此[keyB]會把[keyA]覆蓋掉,而myObject最後只有一個[object Object]屬性。
對象方法的name屬性返回函數名(即方法名)。若是對象的方法使用了取值函數(getter)和存值函數(setter),則name屬性不是在該方法上面,而是該方法的屬性的描述對象的get和set屬性上面,返回值是方法名前加上get和set。
const obj = { get foo() {}, set foo(x) {} }; obj.foo.name // TypeError: Cannot read property 'name' of undefined const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo'); descriptor.get.name // "get foo" descriptor.set.name // "set foo"
bind方法創造的函數,name屬性返回bound加上原函數的名字。
Function構造函數創造的函數,name屬性返回anonymous。
若是對象的方法是一個 Symbol 值,那麼name屬性返回的是這個 Symbol 值的描述。
ES5使用相等運算符(==)和嚴格相等運算符(===)比較兩個值是否相等。前者會自動轉換數據類型,後者的NaN不等於自身,以及+0等於-0。ES6中可用Object.is來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行爲基本一致。可是用Object.is比較時,+0不等於-0,NaN等於自身。
Object.is('foo', 'foo') // true Object.is({}, {}) // false +0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true
Object.assign方法用於對象的合併,將源對象(source)的全部可枚舉屬性,複製到目標對象(target)。Object.assign方法的第一個參數是目標對象,後面的參數都是源對象。注意,若是目標對象與源對象有同名屬性,或多個源對象有同名屬性,則後面的屬性會覆蓋前面的屬性。
var target = { a: 1, b: 1 }; var source1 = { b: 2, c: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
若是隻有一個參數,Object.assign會直接返回該參數。若是該參數不是對象,則會先轉成對象,而後返回。因爲undefined和null沒法轉成對象,因此若是它們做爲參數,就會報錯。若非對象參數出如今源對象的位置(即非首參數),則這些參數都會轉成對象,若是沒法轉成對象,就會跳過。這意味着,若是undefined和null不在首參數,就不會報錯。
Object.assign(null) // 報錯 let obj = {a: 1}; Object.assign(obj, null) === obj // true
Object.assign不會拷貝對象的內部屬性[[PrimitiveValue]]。布爾值、數值、字符串分別轉成對應的包裝對象時,它們的原始值都在包裝對象的內部屬性[[PrimitiveValue]]上面。只有字符串的包裝對象,會產生可枚舉屬性
,這些屬性會被拷貝。所以其餘類型的值(即數值、字符串和布爾值)不在首參數,除了字符串會以數組形式,拷貝入目標對象,其餘值都不會產生效果。
var v1 = 'abc'; var v2 = true; var v3 = 10; var obj = Object.assign({}, v1, v2, v3); console.log(obj); // { "0": "a", "1": "b", "2": "c" } Object(true) // {[[PrimitiveValue]]: true} Object(10) // {[[PrimitiveValue]]: 10} Object('abc') // {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}
Object.assign拷貝的屬性是有限制的。
只拷貝源對象的自身屬性和屬性名爲Symbol值的屬性。
不拷貝繼承屬性。
不拷貝不可枚舉的屬性(enumerable: false)。
注意點
1.Object.assign方法實行的是淺拷貝,而不是深拷貝。 也就是說,若是源對象某個屬性的值是對象,那麼目標對象拷貝獲得的是這個對象的引用。
var obj1 = {a: {b: 1}}; var obj2 = Object.assign({}, obj1); obj1.a.b = 2; obj2.a.b // 2 //Object.assign拷貝獲得的是這個對象的引用。這個對象的任何變化,都會反映到目標對象上面。
2.對於嵌套的對象,一旦遇到同名屬性,Object.assign的處理方法是替換,而不是添加。
var target = { a: { b: 'c', d: 'e' } } var source = { a: { b: 'hello' } } Object.assign(target, source) // { a: { b: 'hello' } } //target對象的a屬性被source對象的a屬性整個替換掉
3.Object.assign能夠用來處理數組,可是會把數組視爲對象。
Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3] //Object.assign把數組視爲屬性名爲0、一、2的對象,所以源數組的0號屬性4覆蓋了目標數組的0號屬性1。
布爾值、數值、字符串分別轉成對應的包裝對象時,它們的原始值都在包裝對象的內部屬性[[PrimitiveValue]]上面。只有字符串的包裝對象,會產生可枚舉屬性。 null或undefined轉換爲對象時將建立並返回一個空對象。
Object(1) // Number {[[PrimitiveValue]]: 1} Object("foo") // String {0: "f", 1: "o", 2: "o", length: 3, [[PrimitiveValue]]: "foo"} Object(null) // Object {}
對象的每一個屬性都有一個描述對象(Descriptor),用來控制該屬性的行爲。Object.getOwnPropertyDescriptor方法能夠獲取該屬性的描述對象。
let obj = { foo: 123 }; Object.getOwnPropertyDescriptor(obj, 'foo') // { // value: 123, // writable: true, // enumerable: true, // configurable: true // }
描述對象的enumerable屬性,稱爲」可枚舉性「,若是該屬性爲false,就表示某些操做會忽略當前屬性。
ES5有三個操做會忽略enumerable爲false的屬性。
for...in循環:只遍歷對象自身的和繼承的可枚舉的屬性
Object.keys():返回對象自身的全部可枚舉的屬性的鍵名
JSON.stringify():只串行化對象自身的可枚舉的屬性
Object.assign():會忽略enumerable爲false的屬性,只拷貝對象自身的可枚舉的屬性。
以上四個操做之中,最後一個是ES6新增的。只有for...in會返回繼承的屬性。
ES6規定,全部Class的原型的方法都是不可枚舉的。
for...in:循環遍歷對象自身的和繼承的可枚舉屬性(不含Symbol屬性)。
Object.keys(obj):返回一個數組,包括對象自身的(不含繼承的)全部可枚舉屬性(不含Symbol屬性)。
Object.getOwnPropertyNames(obj):返回一個數組,包含對象自身的全部屬性(不含Symbol屬性,可是包括不可枚舉屬性)。
Object.getOwnPropertySymbols(obj):返回一個數組,包含對象自身的全部Symbol屬性。
Reflect.ownKeys(obj):返回一個數組,包含對象自身的全部屬性,無論屬性名是Symbol或字符串,也無論是否可枚舉。
以上的5種方法遍歷對象的屬性,都遵照一樣的屬性遍歷的次序規則。
首先遍歷全部屬性名爲數值的屬性,按照數字排序。
其次遍歷全部屬性名爲字符串的屬性,按照生成時間排序。
最後遍歷全部屬性名爲Symbol值的屬性,按照生成時間排序。
__proto__
屬性(先後各兩個下劃線),用來讀取或設置當前對象的prototype對象。目前,全部瀏覽器(包括 IE11)都部署了這個屬性。該屬性最好不要用。在實現上,__proto__調用的是Object.prototype.__proto__。
Object.setPrototypeOf方法的做用與__proto__相同,用來設置一個對象的prototype對象,返回參數對象自己。它是 ES6 正式推薦的設置原型對象的方法。
// 格式 Object.setPrototypeOf(object, prototype) // 用法 var o = Object.setPrototypeOf({}, null);
若是第一個參數不是對象,會自動轉爲對象。可是因爲返回的仍是第一個參數,因此這個操做不會產生任何效果。因爲undefined和null沒法轉爲對象,因此若是第一個參數是undefined或null,就會報錯。
Object.setPrototypeOf(1, {}) === 1 // true Object.setPrototypeOf(undefined, {}) // TypeError: Object.setPrototypeOf called on null or undefined
該方法與Object.setPrototypeOf方法配套,用於讀取一個對象的原型對象。
function Rectangle() { // ... } var rec = new Rectangle(); Object.getPrototypeOf(rec) === Rectangle.prototype // true
若是參數不是對象,會被自動轉爲對象。若是參數是undefined或null,它們沒法轉爲對象,因此會報錯。
// 等同於 Object.getPrototypeOf(Number(1)) Object.getPrototypeOf(1) // Number {[[PrimitiveValue]]: 0} Object.getPrototypeOf(1) === Number.prototype // true
Object.getPrototypeOf(null) // TypeError: Cannot convert undefined or null to object
Object.keys:返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵名。ES5 引入。
Object.values():返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵值。ES2017 引入。返回數組的成員順序,與《屬性的遍歷》部分介紹的排列規則一致。Object.values會過濾屬性名爲 Symbol 值的屬性。若是參數不是對象,Object.values會先將其轉爲對象。
Object.values({ [Symbol()]: 123, foo: 'abc' }); // ['abc'] Object.values('foo') // ['f', 'o', 'o'] Object.values(42) // [] Object.values(true) // [] Object.values(null) //Uncaught TypeError: Cannot convert undefined or null to object
Object.entries():返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵值對數組。該方法的行爲與Object.values基本一致。原對象的屬性名是一個 Symbol 值,該屬性會被忽略。可將對象轉爲真正的Map結構。
Object.entries({ [Symbol()]: 123, foo: 'abc' }); // [ [ 'foo', 'abc' ] ] var obj = { foo: 'bar', baz: 42 }; var map = new Map(Object.entries(obj)); map // Map { foo: "bar", baz: 42 }
ES2017 將擴展運算符(...)引入了對象。
(1)解構賦值
對象的解構賦值用於從一個對象取值,至關於將全部可遍歷的、但還沒有被讀取的屬性
,分配到指定的對象上面。全部的鍵和它們的值,都會拷貝到新對象上面。
解構賦值要求等號右邊是一個對象,因此若是等號右邊是undefined或null,就會報錯
,由於它們沒法轉爲對象。
解構賦值必須是最後一個參數,不然會報錯。
解構賦值的拷貝是淺拷貝,即若是一個鍵的值是複合類型的值(數組、對象、函數)、那麼解構賦值拷貝的是這個值的引用,而不是這個值的副本。
解構賦值不會拷貝繼承自原型對象的屬性。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; x // 1 y // 2 z // { a: 3, b: 4 } //變量z是解構賦值所在的對象。它獲取等號右邊的全部還沒有讀取的鍵(a和b),將它們連同值一塊兒拷貝過來。 let { x, y, ...z } = null; // 運行時錯誤 let { ...x, y, z } = { x: 1, y: 2, a: 3, b: 4 }; // 句法錯誤 var o = Object.create({ x: 1, y: 2 }); o.z = 3; let { x, ...{ y, z } } = o; x // 1 y // undefined z // 3 //變量x是單純的解構賦值,因此能夠讀取對象o繼承的屬性;變量y和z是雙重解構賦值,只能讀取對象o自身的屬性,因此只有變量z能夠賦值成功。
(2)擴展運算符
擴展運算符(...)用於取出參數對象的全部可遍歷屬性,拷貝到當前對象之中。這等同於使用Object.assign方法。擴展運算符能夠用於合併兩個對象。若是擴展運算符的參數是null或undefined,這個兩個值會被忽略,不會報錯。
let z = { a: 3, b: 4 }; let n = { ...z }; n // { a: 3, b: 4 } let ab = { ...a, ...b }; // 等同於 let ab = Object.assign({}, a, b); let emptyObject = { ...null, ...undefined }; // 不報錯
若是用戶自定義的屬性,放在擴展運算符後面,則擴展運算符內部的同名屬性會被覆蓋掉。
let aWithOverrides = { ...a, x: 1, y: 2 }; // 等同於 let aWithOverrides = { ...a, ...{ x: 1, y: 2 } }; // 等同於 let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 }); //a對象的x屬性和y屬性,拷貝到新對象後會被覆蓋掉。
做用:可用來修改現有對象的部分屬性。
let newVersion = { ...previousVersion, name: 'New Name' // Override the name property }; //newVersion對象自定義了name屬性,其餘屬性所有複製自previousVersion對象。
Object.getOwnPropertyDescriptor:返回某個對象屬性的描述對象(descriptor)。ES5引入。
var obj = { p: 'a' }; Object.getOwnPropertyDescriptor(obj, 'p') // Object { value: "a", // writable: true, // enumerable: true, // configurable: true // }
Object.getOwnPropertyDescriptors:返回指定對象全部自身屬性(非繼承屬性)的描述對象。ES2017 引入。
const obj = { foo: 123, get bar() { return 'abc' } }; Object.getOwnPropertyDescriptors(obj) // { foo: // { value: 123, // writable: true, // enumerable: true, // configurable: true }, // bar: // { get: [Function: bar], // set: undefined, // enumerable: true, // configurable: true } } //Object.getOwnPropertyDescriptors方法返回一個對象,全部原對象的屬性名都是該對象的屬性名,對應的屬性值就是該屬性的描述對象。
主要是爲了解決Object.assign()沒法正確拷貝get屬性和set屬性的問題,由於Object.assign方法老是拷貝一個屬性的值,而不會拷貝它背後的賦值方法或取值方法。Object.getOwnPropertyDescriptors配合Object.create方法,將對象屬性克隆到一個新對象時,屬於淺拷貝。
const source = { set foo(value) { console.log(value); } }; const target1 = {}; Object.assign(target1, source); Object.getOwnPropertyDescriptor(target1, 'foo') // { value: undefined, // writable: true, // enumerable: true, // configurable: true } const clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
?.
稱爲Null 傳導運算符,該運算符若返回null或undefined,就再也不往下運算,而是返回undefined。
Null 傳導運算符有四種用法。
obj?.prop // 讀取對象屬性
obj?.[expr] // 同上
func?.(...args) // 函數或對象方法的調用
new C?.(...args) // 構造函數的調用
const firstName = (message && message.body && message.body.user && message.body.user.firstName) || 'default'; //等同於 const firstName = message?.body?.user?.firstName || 'default';
參考自:ECMAScript 6 入門