- คู่มือฉบับสมบูรณ์มีอยู่ใน Amazon

let const และบล็อกการกำหนดขอบเขต let คุณสามารถสร้างการประกาศที่ถูกผูกไว้กับบล็อกใด ๆ เรียกว่าการกำหนดขอบเขตบล็อก แทนที่จะใช้ var ซึ่งให้ขอบเขตฟังก์ชันขอแนะนำให้ใช้ตัวแปรบล็อกบล็อก ( let หรือ const ) ใน ES6
var a = 2 ;
{
let a = 3 ;
console . log ( a ) ; // 3
let a = 5 ; // TypeError: Identifier 'a' has already been declared
}
console . log ( a ) ; // 2 อีกรูปแบบหนึ่งของการประกาศแบบปิดบล็อกคือ const ซึ่งสร้างค่าคงที่ ใน ES6 const แสดงถึงการอ้างอิงคงที่กับค่า กล่าวอีกนัยหนึ่งเนื้อหาของ Object และ Array อาจเปลี่ยนแปลงได้เฉพาะการมอบหมายใหม่ของตัวแปรจะถูกป้องกัน นี่คือตัวอย่างง่ายๆ:
{
const b = 5 ;
b = 10 ; // TypeError: Assignment to constant variable
const arr = [ 5 , 6 ] ;
arr . push ( 7 ) ;
console . log ( arr ) ; // [5,6,7]
arr = 10 ; // TypeError: Assignment to constant variable
arr [ 0 ] = 3 ; // value is mutable
console . log ( arr ) ; // [3,6,7]
}บางสิ่งที่ต้องจำไว้:
let and const แตกต่างจากการยกตัวแปรและฟังก์ชั่นแบบดั้งเดิม ทั้ง let และ const ถูกยกขึ้น แต่ไม่สามารถเข้าถึงได้ก่อนการประกาศของพวกเขาเนื่องจากเขตตายชั่วคราวlet และ const กำหนดขอบเขตไปยังบล็อกล้อมรอบที่ใกล้ที่สุดconst PI = 3.14 )const จะต้องถูกกำหนดด้วยการประกาศconst มากกว่า let ให้เว้นแต่คุณวางแผนที่จะกำหนดตัวแปรอีกครั้ง ฟังก์ชั่นลูกศรเป็นสัญลักษณ์ระยะสั้นสำหรับการเขียนฟังก์ชั่นใน ES6 คำจำกัดความของฟังก์ชั่นลูกศรประกอบด้วยรายการพารามิเตอร์ ( ... ) ตามด้วยเครื่องหมาย => ตัวทำเครื่องหมายและร่างกายฟังก์ชั่น สำหรับฟังก์ชั่นการโต้แย้งเดียวอาจใช้วงเล็บ
// Classical Function Expression
function addition ( a , b ) {
return a + b ;
} ;
// Implementation with arrow function
const addition = ( a , b ) => a + b ;
// With single argument, no parentheses required
const add5 = a => 5 + a ; โปรดทราบว่าในตัวอย่างข้างต้นฟังก์ชั่นการ addition ลูกศรจะถูกนำไปใช้กับ "ร่างกายที่กระชับ" ซึ่งไม่จำเป็นต้องมีคำสั่งคืนที่ชัดเจน หมายเหตุที่ละเว้น { } หลังจาก =>
นี่คือตัวอย่างที่มี "บล็อกบล็อก" ปกติ รวมถึงเสื้อคลุมโค้งงอ
const arr = [ 'apple' , 'banana' , 'orange' ] ;
const breakfast = arr . map ( fruit => {
return fruit + 's' ;
} ) ;
console . log ( breakfast ) ; // ['apples', 'bananas', 'oranges']ดูเถิด มีอีกมาก ...
ฟังก์ชั่นลูกศรไม่เพียง แต่ทำให้รหัสสั้นลง พวกเขาเกี่ยวข้องอย่างใกล้ชิดกับพฤติกรรมการผูกมัด this
ฟังก์ชั่นของลูกศรพฤติกรรมด้วยคำหลัก this แตกต่างจากฟังก์ชั่นปกติ แต่ละฟังก์ชั่นใน JavaScript กำหนดบริบท this ของตัวเอง แต่ฟังก์ชั่นลูกศรจับค่า this ของบริบทที่ใกล้ที่สุด ตรวจสอบรหัสต่อไปนี้:
function Person ( ) {
// The Person() constructor defines `this` as an instance of itself.
this . age = 0 ;
setInterval ( function growUp ( ) {
// In non-strict mode, the growUp() function defines `this`
// as the global object, which is different from the `this`
// defined by the Person() constructor.
this . age ++ ;
} , 1000 ) ;
}
var p = new Person ( ) ; ใน ECMASCRIPT 3/5 ปัญหานี้ได้รับการแก้ไขโดยการกำหนดค่าใน this ให้กับตัวแปรที่สามารถปิดได้
function Person ( ) {
const self = this ;
self . age = 0 ;
setInterval ( function growUp ( ) {
// The callback refers to the `self` variable of which
// the value is the expected object.
self . age ++ ;
} , 1000 ) ;
}ดังที่ได้กล่าวไว้ข้างต้นฟังก์ชั่นลูกศรจับค่านี้ของบริบทที่ปิดล้อมที่ใกล้ที่สุดดังนั้นรหัสต่อไปนี้จึงทำงานตามที่คาดไว้แม้จะมีฟังก์ชั่นลูกศรซ้อนกัน
function Person ( ) {
this . age = 0 ;
setInterval ( ( ) => {
setTimeout ( ( ) => {
this . age ++ ; // `this` properly refers to the person object
} , 1000 ) ;
} , 1000 ) ;
}
let p = new Person ( ) ;อ่านเพิ่มเติมเกี่ยวกับ 'คำศัพท์นี้' ในฟังก์ชั่นลูกศรที่นี่
ES6 อนุญาตให้คุณตั้งค่าพารามิเตอร์เริ่มต้นในคำจำกัดความของฟังก์ชั่น นี่คือภาพประกอบง่ายๆ
const getFinalPrice = ( price , tax = 0.7 ) => price + price * tax ;
getFinalPrice ( 500 ) ; // 850 ... ผู้ประกอบการถูกเรียกว่าสเปรดหรือตัวดำเนินการ REST ขึ้นอยู่กับวิธีการใช้งานและสถานที่
เมื่อใช้กับการวนซ้ำใด ๆ มันจะทำหน้าที่เป็น "แพร่กระจาย" เป็นองค์ประกอบของแต่ละบุคคล:
const makeToast = ( breadType , topping1 , topping2 ) => {
return `I had ${ breadType } toast with ${ topping1 } and ${ topping2 } ` ;
} ; const ingredients = [ 'wheat' , 'butter' , 'jam' ] ;
makeToast ( ... ingredients ) ;
// "I had wheat toast with butter and jam"
makeToast ( ... [ 'sourdough' , 'avocado' , 'kale' ] ) ;
// "I had sourdough toast with avocado and kale"การแพร่กระจายยังเหมาะสำหรับการสร้างวัตถุใหม่จากวัตถุอื่น ๆ :
const defaults = { avatar : 'placeholder.jpg' , active : false }
const userData = { username : 'foo' , avatar : 'bar.jpg' }
console . log ( { created : '2017-12-31' , ... defaults , ... userData } )
// {created: "2017-12-31", avatar: "bar.jpg", active: false, username: "foo"}อาร์เรย์ใหม่สามารถสร้างรูปแบบได้อย่างชัดเจน:
const arr1 = [ 1 , 2 , 3 ] ;
const arr2 = [ 7 , 8 , 9 ] ;
console . log ( [ ... arr1 , 4 , 5 , 6 , ... arr2 ] ) // [1, 2, 3, 4, 5, 6, 7, 8, 9] การใช้งานทั่วไปอื่น ๆ ของ ... กำลังรวบรวมข้อโต้แย้งทั้งหมดเข้าด้วยกันเป็นอาร์เรย์ สิ่งนี้ถูกเรียกว่าเป็นตัวดำเนินการ "REST"
function foo ( ... args ) {
console . log ( args ) ;
}
foo ( 1 , 2 , 3 , 4 , 5 ) ; // [1, 2, 3, 4, 5]ES6 อนุญาตให้ประกาศตัวอักษรวัตถุโดยการจัดหาไวยากรณ์ชวเลขสำหรับการเริ่มต้นคุณสมบัติจากตัวแปรและวิธีการกำหนดวิธีการทำงาน นอกจากนี้ยังช่วยให้ความสามารถในการคำนวณคีย์คุณสมบัติในคำจำกัดความตามตัวอักษรของวัตถุ
function getCar ( make , model , value ) {
return {
// with property value shorthand
// syntax, you can omit the property
// value if key matches variable
// name
make , // same as make: make
model , // same as model: model
value , // same as value: value
// computed values now work with
// object literals
[ 'make' + make ] : true ,
// Method definition shorthand syntax
// omits `function` keyword & colon
depreciate ( ) {
this . value -= 2500 ;
}
} ;
}
let car = getCar ( 'Kia' , 'Sorento' , 40000 ) ;
console . log ( car ) ;
// {
// make: 'Kia',
// model:'Sorento',
// value: 40000,
// makeKia: true,
// depreciate: function()
// } ES6 ได้รับการสนับสนุนใหม่สำหรับตัวอักษรคู่และไบนารี การเตรียมตัวเลขด้วย 0o หรือ 0O จะแปลงเป็นค่าแปดเท่า ดูรหัสต่อไปนี้:
let oValue = 0o10 ;
console . log ( oValue ) ; // 8
let bValue = 0b10 ; // 0b or 0B for binary
console . log ( bValue ) ; // 2การทำลายโครงสร้างช่วยในการหลีกเลี่ยงความต้องการตัวแปรอุณหภูมิเมื่อจัดการกับวัตถุและอาร์เรย์
function foo ( ) {
return [ 1 , 2 , 3 ] ;
}
let arr = foo ( ) ; // [1,2,3]
let [ a , b , c ] = foo ( ) ;
console . log ( a , b , c ) ; // 1 2 3 function getCar ( ) {
return {
make : 'Tesla' ,
model : 'g95' ,
metadata : {
vin : '123abc' ,
miles : '12000'
}
} ;
}
const { make , model } = getCar ( ) ;
console . log ( make , model ) ; // Tesla g95
const { make , metadata : { miles } } = getCar ( ) ;
console . log ( make , miles ) ; // Tesla 12000
ES6 อนุญาตให้ใช้ super Method ในวัตถุ (ไม่มีคลาส) ที่มีต้นแบบ ต่อไปนี้เป็นตัวอย่างง่ายๆ:
const parent = {
foo ( ) {
console . log ( "Hello from the Parent" ) ;
}
}
const child = {
foo ( ) {
super . foo ( ) ;
console . log ( "Hello from the Child" ) ;
}
}
Object . setPrototypeOf ( child , parent ) ;
child . foo ( ) ; // Hello from the Parent
// Hello from the ChildES6 แนะนำวิธีที่ง่ายขึ้นในการเพิ่มการแก้ไขซึ่งประเมินโดยอัตโนมัติ
`${ ... }` ใช้สำหรับการแสดงผลตัวแปร` backtick ถูกใช้เป็นตัวคั่น let user = 'Kevin' ;
console . log ( `Hi ${ user } !` ) ; // Hi Kevin!for...of การวนซ้ำผ่านวัตถุที่วนซ้ำเช่นอาร์เรย์ const nicknames = [ 'di' , 'boo' , 'punkeye' ] ;
nicknames . size = 3 ;
for ( let nickname of nicknames ) {
console . log ( nickname ) ;
}
// di
// boo
// punkeyefor...in การวนซ้ำมากกว่าคุณสมบัติทั้งหมดของวัตถุ const nicknames = [ 'di' , 'boo' , 'punkeye' ] ;
nicknames . size = 3 ;
for ( let nickname in nicknames ) {
console . log ( nickname ) ;
}
// 0
// 1
// 2
// size ES6 แนะนำชุดข้อมูลชุดข้อมูลใหม่ที่เรียกว่า Map และ WeakMap ตอนนี้เราใช้แผนที่ใน JavaScript ตลอดเวลา ในความเป็นจริงทุกวัตถุสามารถพิจารณาได้ว่าเป็น Map
วัตถุทำจากคีย์ (สตริงเสมอ) และค่าในขณะที่ใน Map ค่าใด ๆ (ทั้งวัตถุและค่าดั้งเดิม) อาจใช้เป็นคีย์หรือค่า ดูรหัสชิ้นนี้:
const myMap = new Map ( ) ;
const keyString = "a string" ,
keyObj = { } ,
keyFunc = ( ) => { } ;
// setting the values
myMap . set ( keyString , "value associated with 'a string'" ) ;
myMap . set ( keyObj , "value associated with keyObj" ) ;
myMap . set ( keyFunc , "value associated with keyFunc" ) ;
myMap . size ; // 3
// getting the values
myMap . get ( keyString ) ; // "value associated with 'a string'"
myMap . get ( keyObj ) ; // "value associated with keyObj"
myMap . get ( keyFunc ) ; // "value associated with keyFunc"ความอ่อนแอ
WeakMap เป็นแผนที่ที่มีการอ้างอิงคีย์ที่อ่อนแอซึ่งไม่ได้ป้องกันไม่ให้กุญแจถูกเก็บรวบรวมขยะ นั่นหมายความว่าคุณไม่ต้องกังวลเกี่ยวกับการรั่วไหลของหน่วยความจำ
อีกสิ่งที่ควรทราบที่นี่- ใน WeakMap ซึ่งตรงข้ามกับ Map ทุกคีย์จะต้องเป็นวัตถุ
WeakMap มีเพียงสี่วิธี delete(key) , has(key) , get(key) และ set(key, value)
const w = new WeakMap ( ) ;
w . set ( 'a' , 'b' ) ;
// Uncaught TypeError: Invalid value used as weak map key
const o1 = { } ,
o2 = ( ) => { } ,
o3 = window ;
w . set ( o1 , 37 ) ;
w . set ( o2 , "azerty" ) ;
w . set ( o3 , undefined ) ;
w . get ( o3 ) ; // undefined, because that is the set value
w . has ( o1 ) ; // true
w . delete ( o1 ) ;
w . has ( o1 ) ; // falseชุด วัตถุคือคอลเลกชันของค่าที่ไม่ซ้ำกัน ค่าที่ซ้ำกันจะถูกละเว้นเนื่องจากคอลเลกชันต้องมีค่าที่ไม่ซ้ำกันทั้งหมด ค่าอาจเป็นประเภทดั้งเดิมหรือการอ้างอิงวัตถุ
const mySet = new Set ( [ 1 , 1 , 2 , 2 , 3 , 3 ] ) ;
mySet . size ; // 3
mySet . has ( 1 ) ; // true
mySet . add ( 'strings' ) ;
mySet . add ( { a : 1 , b : 2 } ) ; คุณสามารถวนซ้ำตามลำดับโดยการแทรกโดยใช้วิธี forEach หรือ for...of ลูป
mySet . forEach ( ( item ) => {
console . log ( item ) ;
// 1
// 2
// 3
// 'strings'
// Object { a: 1, b: 2 }
} ) ;
for ( let value of mySet ) {
console . log ( value ) ;
// 1
// 2
// 3
// 'strings'
// Object { a: 1, b: 2 }
} ชุดยังมีวิธีการ delete() และ clear()
ความอ่อนแอ
เช่นเดียวกับ WeakMap วัตถุ WeakSet ช่วยให้คุณจัดเก็บ วัตถุ ที่เก็บไว้อย่างอ่อนแอในคอลเลกชัน วัตถุใน WeakSet เกิดขึ้นเพียงครั้งเดียว มันเป็นเอกลักษณ์ในคอลเลกชันของ Weakset
const ws = new WeakSet ( ) ;
const obj = { } ;
const foo = { } ;
ws . add ( window ) ;
ws . add ( obj ) ;
ws . has ( window ) ; // true
ws . has ( foo ) ; // false, foo has not been added to the set
ws . delete ( window ) ; // removes window from the set
ws . has ( window ) ; // false, window has been removedES6 แนะนำไวยากรณ์คลาสใหม่ สิ่งหนึ่งที่ควรทราบที่นี่คือคลาส ES6 ไม่ใช่แบบจำลองการสืบทอดวัตถุใหม่ พวกเขาเพิ่งทำหน้าที่เป็นน้ำตาลในการสืบทอดมรดกที่มีอยู่เดิมของ JavaScript
วิธีหนึ่งในการดูชั้นเรียนใน ES6 เป็นเพียงไวยากรณ์ใหม่ในการทำงานกับต้นแบบและฟังก์ชั่นตัวสร้างที่เราใช้ใน ES5
ฟังก์ชั่นที่กำหนดโดยใช้คำหลัก static ที่ใช้ฟังก์ชั่นคงที่/คลาสในคลาส
class Task {
constructor ( ) {
console . log ( "task instantiated!" ) ;
}
showId ( ) {
console . log ( 23 ) ;
}
static loadAll ( ) {
console . log ( "Loading all tasks.." ) ;
}
}
console . log ( typeof Task ) ; // function
const task = new Task ( ) ; // "task instantiated!"
task . showId ( ) ; // 23
Task . loadAll ( ) ; // "Loading all tasks.."ขยายและสุดยอดในชั้นเรียน
พิจารณารหัสต่อไปนี้:
class Car {
constructor ( ) {
console . log ( "Creating a new car" ) ;
}
}
class Porsche extends Car {
constructor ( ) {
super ( ) ;
console . log ( "Creating Porsche" ) ;
}
}
let c = new Porsche ( ) ;
// Creating a new car
// Creating Porsche extends ให้ชั้นเรียนเด็กสืบทอดมาจากชั้นเรียนหลักใน ES6 เป็นสิ่งสำคัญที่จะต้องทราบว่าตัวสร้างที่ได้รับจะต้องเรียก super()
นอกจากนี้คุณสามารถโทรหาวิธีการของคลาสแม่ในวิธีการของคลาสเด็กโดยใช้ super.parentMethodName()
อ่านเพิ่มเติมเกี่ยวกับชั้นเรียนที่นี่
บางสิ่งที่ต้องจำไว้:
function เมื่อกำหนดฟังก์ชั่นภายในนิยามคลาส Symbol เป็นประเภทข้อมูลที่ไม่เหมือนใครและไม่เปลี่ยนรูปที่แนะนำใน ES6 จุดประสงค์ของสัญลักษณ์คือการสร้างตัวระบุที่ไม่ซ้ำกัน แต่คุณไม่สามารถเข้าถึงตัวระบุใด ๆ ได้
นี่คือวิธีที่คุณสร้างสัญลักษณ์:
const sym = Symbol ( "some optional description" ) ;
console . log ( typeof sym ) ; // symbol โปรดทราบว่าคุณไม่สามารถใช้สัญลักษณ์ new Symbol(…)
หากสัญลักษณ์ถูกใช้เป็นคุณสมบัติ/คีย์ของวัตถุมันจะถูกเก็บไว้ในวิธีพิเศษที่คุณสมบัติจะไม่ปรากฏในการแจงนับปกติของคุณสมบัติของวัตถุ
const o = {
val : 10 ,
[ Symbol ( "random" ) ] : "I'm a symbol" ,
} ;
console . log ( Object . getOwnPropertyNames ( o ) ) ; // val ในการดึงคุณสมบัติสัญลักษณ์ของวัตถุให้ใช้ Object.getOwnPropertySymbols(o)
ตัววนซ้ำเข้าถึงรายการจากคอลเลกชันทีละครั้งในขณะที่ติดตามตำแหน่งปัจจุบันภายในลำดับนั้น มันมีวิธี next() ซึ่งส่งคืนรายการถัดไปในลำดับ วิธีนี้ส่งคืนวัตถุที่มีคุณสมบัติสองประการ: เสร็จสิ้นและค่า
ES6 มี Symbol.iterator ซึ่งระบุตัววนซ้ำเริ่มต้นสำหรับวัตถุ เมื่อใดก็ตามที่วัตถุจะต้องวนซ้ำ (เช่นที่จุดเริ่มต้นของ A สำหรับ .. ของลูป) วิธี @@ iterator จะถูกเรียกโดยไม่มีข้อโต้แย้งและตัววนซ้ำที่ส่งคืนจะใช้เพื่อให้ได้ค่าที่จะวนซ้ำ
ลองดูอาร์เรย์ซึ่งเป็นสิ่งที่วนซ้ำและตัววนซ้ำที่สามารถผลิตเพื่อใช้ค่าของมัน:
const arr = [ 11 , 12 , 13 ] ;
const itr = arr [ Symbol . iterator ] ( ) ;
itr . next ( ) ; // { value: 11, done: false }
itr . next ( ) ; // { value: 12, done: false }
itr . next ( ) ; // { value: 13, done: false }
itr . next ( ) ; // { value: undefined, done: true } โปรดทราบว่าคุณสามารถเขียนตัววนซ้ำที่กำหนดเองได้โดยกำหนด obj[Symbol.iterator]() ด้วยคำจำกัดความของวัตถุ
ฟังก์ชั่นเครื่องกำเนิดไฟฟ้าเป็นคุณสมบัติใหม่ใน ES6 ที่อนุญาตให้ฟังก์ชั่นสร้างค่าจำนวนมากเมื่อเวลาผ่านไปโดยการส่งคืนวัตถุซึ่งสามารถวนซ้ำเพื่อดึงค่าจากฟังก์ชั่นทีละค่า
ฟังก์ชั่นเครื่องกำเนิดไฟฟ้าส่งคืน วัตถุที่วนซ้ำ เมื่อเรียกว่า มันถูกเขียนขึ้นโดยใช้ไวยากรณ์ * ใหม่รวมถึงคำหลัก yield ใหม่ที่แนะนำใน ES6
function * infiniteNumbers ( ) {
let n = 1 ;
while ( true ) {
yield n ++ ;
}
}
const numbers = infiniteNumbers ( ) ; // returns an iterable object
numbers . next ( ) ; // { value: 1, done: false }
numbers . next ( ) ; // { value: 2, done: false }
numbers . next ( ) ; // { value: 3, done: false }ทุกครั้งที่เรียกว่า อัตราผลตอบแทน ค่าที่ให้ผลตอบแทนจะกลายเป็นค่าถัดไปในลำดับ
นอกจากนี้โปรดทราบว่าเครื่องกำเนิดไฟฟ้าจะคำนวณค่าที่ให้ผลตอบแทนตามความต้องการซึ่งช่วยให้พวกเขาแสดงลำดับที่มีราคาแพงในการคำนวณหรือแม้กระทั่งลำดับที่ไม่มีที่สิ้นสุด
ES6 ได้รับการสนับสนุนจากคำสัญญา สัญญา เป็นวัตถุที่กำลังรอการดำเนินการแบบอะซิงโครนัสให้เสร็จสมบูรณ์และเมื่อการดำเนินการเสร็จสิ้นสัญญาจะสำเร็จ (แก้ไข) หรือปฏิเสธ
วิธีมาตรฐานในการสร้างสัญญาคือการใช้ตัวสร้าง new Promise() ซึ่งยอมรับตัวจัดการที่ได้รับฟังก์ชั่นสองฟังก์ชั่นเป็นพารามิเตอร์ ตัวจัดการคนแรก (โดยทั่วไปชื่อ resolve ) เป็นฟังก์ชั่นที่จะโทรด้วยค่าในอนาคตเมื่อพร้อม และตัวจัดการที่สอง (โดยทั่วไปชื่อ reject ) เป็นฟังก์ชันที่จะเรียกให้ปฏิเสธสัญญาหากไม่สามารถแก้ไขค่าในอนาคตได้
const p = new Promise ( ( resolve , reject ) => {
if ( /* condition */ ) {
resolve ( /* value */ ) ; // fulfilled successfully
} else {
reject ( /* reason */ ) ; // error, rejected
}
} ) ; ทุกคำสัญญามีวิธีการที่ชื่อ then ต้องใช้คู่ของการโทรกลับ การเรียกกลับครั้งแรกจะเรียกว่าหากสัญญาได้รับการแก้ไขในขณะที่คำที่สองจะถูกเรียกถ้าสัญญาถูกปฏิเสธ
p . then ( ( val ) => console . log ( "Promise Resolved" , val ) ,
( err ) => console . log ( "Promise Rejected" , err ) ) ; การส่งคืนค่าจาก then การโทรกลับจะผ่านค่าไปยังค่าถัดไป then โทรกลับ
const hello = new Promise ( ( resolve , reject ) => { resolve ( "Hello" ) } ) ;
hello . then ( ( str ) => ` ${ str } World` )
. then ( ( str ) => ` ${ str } !` )
. then ( ( str ) => console . log ( str ) ) // Hello World!เมื่อส่งคืนสัญญามูลค่าที่ได้รับการแก้ไขจะถูกส่งผ่านไปยังการโทรกลับครั้งต่อไปเพื่อให้พวกเขารวมตัวกันอย่างมีประสิทธิภาพ นี่เป็นเทคนิคง่ายๆในการหลีกเลี่ยง "การโทรกลับนรก"
const p = new Promise ( ( resolve , reject ) => { resolve ( 1 ) } ) ;
const eventuallyAdd1 = ( val ) => new Promise ( ( resolve , reject ) => { resolve ( val + 1 ) } ) ;
p . then ( eventuallyAdd1 )
. then ( eventuallyAdd1 )
. then ( ( val ) => console . log ( val ) ) ; // 3