Default function parameters
1
2
3
4
5
let getScoreText = function ( name = 'Anonymous' , score = 0 ) {
return ` $ { name } Score : $ { score } `
}
let text = getScoreText ( undefined , 11 )
console . log ( text ) // Anonymous Score: 11
立即函示 IIFE(Immediately Invoked Function Expression)
可立刻執行的函示,好處不會污染到 global,在函示後面加上 ()
即可
1
2
3
4
5
6
( function () {
var a = 1
console . log ( a ) // 3
})()
a // a is not defined
Arrow Function
ES6 新增的寫法
1
2
3
4
5
6
7
8
9
10
11
12
const squareLong1 = function ( num ) {
return num * num
}
console . log ( squareLong1 ( 3 ))
const squareLong2 = ( num ) => {
return num * num
}
console . log ( squareLong2 ( 3 ))
const squareLong3 = ( num ) => num * num
console . log ( squareLong3 ( 3 ))
Arrow Function don’t bind arguments
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const add1 = function () {
return arguments [ 0 ] + arguments [ 1 ]
}
console . log ( add1 ( 1 , 2 )) // 3
const add2 = () => {
return arguments [ 0 ] + arguments [ 1 ]
}
console . log ( add2 ( 1 , 2 ))
// [object Object]function require(path) {
// try {
// exports.requireDepth += 1;
// return mod.require(path);
// } finally {
// exports.requireDepth -= 1;
// }
// }
Arrow Function don’t bind this
因為 arrow function
沒有綁定 this
因此會 undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// arrow function
const pet1 = {
name : 'Hal' ,
getGreeting : () => {
return ` Hello $ { this . name } ! `
}
}
console . log ( pet1 . getGreeting ()) // Hello undefined!
// 原本寫法
const pet2 = {
name : 'Hal' ,
getGreeting : function () {
return ` Hello $ { this . name } ! `
}
}
console . log ( pet2 . getGreeting ()) // Hello Hal!
// 原本寫法縮寫
const pet3 = {
name : 'Hal' ,
getGreeting () {
return ` Hello $ { this . name } ! `
}
}
console . log ( pet3 . getGreeting ()) // Hello Hal!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
var test = {
say : 'hi' ,
citys : [ 'a' , 'b' , 'c' ],
getGreeting : function () {
this . citys . forEach ( function ( city ) {
// 這裡的 this 會指向 window
console . log ( this . say );
});
}
};
test . getGreeting (); // undefined
var test = {
say : 'hi' ,
citys : [ 'a' , 'b' , 'c' ],
getGreeting : function () {
that = this ;
this . citys . forEach ( function ( city ) {
console . log ( that . say );
});
}
};
test . getGreeting (); // hi, hi, hi
var test = {
say : 'hi' ,
citys : [ 'a' , 'b' , 'c' ],
getGreeting : function () {
this . citys . forEach ( city => console . log ( this . say ));
}
};
test . getGreeting (); // hi, hi, hi
var test = {
say : 'hi' ,
citys : [ 'a' , 'b' , 'c' ],
getGreeting () {
this . citys . forEach ( city => console . log ( this . say ));
}
};
test . getGreeting (); // hi, hi, hi
Constructor
constructor 的用法就是 function
搭配 new
關鍵字:
可以透過 Constructor function
達成 Object-oriented programming (OOP)
,就像是 class
那樣
1
2
3
4
5
6
7
8
9
const Person = function ( firstName , lastName , age ) {
this . firstName = firstName
this . lastName = lastName
this . age = age
}
const me = new Person ( 'Leon' , 'Ji' , 27 )
// me.[[prototype]] = Person.prototype 實際上會做此連結 hidden internal property
console . log ( me ) // Person {firstName: "Leon", lastName: "Ji", age: 27}
建議使用大寫開頭
透過 new
建立新的 instance
new 會進行以下操作
建立一個物件({}
)
將這個新物件的 constructor 屬性設為另一個物件(Person
)
1
2
me . constructor == Person // true
me instanceof Person // true
1
2
Person . prototype . say = 'hi'
me . say // hi
這裡的 this
就是 Person
Classes
後來出的新語法 class
,讓原本的 function
寫法更乾淨,更像 OOP 一點
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Person {
constructor ( firstName , lastName , age , likes = []) {
this . firstName = firstName
this . lastName = lastName
this . age = age
this . likes = likes
}
getBio () {
let bio = ` $ { this . firstName } is $ { this . age }. `
this . likes . forEach ( like => {
bio += ` $ { this . firstName } likes $ { like }. `
})
return bio
}
setName ( fullName ) {
const name = fullName . split ( ' ' )
this . firstName = name [ 0 ]
this . lastName = name [ 1 ]
}
}
const me = new Person ( 'Leon' , 'Ji' , 27 , [ 'Teaching' , 'Biking' ])
console . log ( me . getBio ())
const person2 = new Person ( 'Clancey' , 'Turner' , 51 )
person2 . setName ( 'Leon Ji' )
console . log ( person2 . getBio ())
// Leon is 27. Leon likes Teaching. Leon likes Biking.
// Leon is 51.
Subclass
可以在建立一個 class,去繼承其他的 class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// stu -> Student.prototype -> Person.prototype -> Object.prototype -> null
class Student extends Person {
constructor ( firstName , lastName , age , grade , likes ) {
// super 用於 call 上層的 constructor
super ( firstName , lastName , age , likes )
this . grade = grade
}
updateGrade ( change ) {
this . grade += change
}
getBio () {
const status = this . grade >= 70 ? 'passing' : 'failing'
return ` $ { this . firstName } is $ { status } the class . `
}
}
const stu = new Student ( 'Leon' , 'Ji' , 27 , 50 , [ 'Teaching' , 'Biking' ])
console . log ( stu . getBio ())
student . updateGrade ( 50 )
console . log ( stu . getBio ())
// Leon is failing the class.
// Leon is passing the class.
getter & setter
The get
syntax binds an object property to a function that will be called when that property is looked up.
The set
syntax binds an object property to a function to be called when there is an attempt to set that property.
執行時,就跟變數一樣,不需加上 ()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const human = {
firstName : 'Leon' ,
lastName : 'Ji' ,
get name () {
return ` $ { this . firstName } $ { this . lastName } `
},
set name ( name ) {
const names = name . trim (). split ( ' ' )
this . firstName = names [ 0 ]
this . lastName = names [ 1 ]
}
}
human . name = ' Leon Ji '
console . log ( human . firstName ) // Leon
console . log ( human . lastName ) // Ji
this 識別字(this Identifier)
this 是 function 執行時所屬的物件,而 this 是在執行時期做繫結,其值和函式在哪裡被呼叫(call-site)有關。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function foo () {
console . log ( this . bar )
}
var bar = 'global'
var obj1 = {
bar : 'obj1' ,
foo : foo
}
var obj2 = {
bar : 'obj2'
}
foo () // 'global'
obj1 . foo () // 'obj1'
foo . call ( obj2 ) // 'obj2'
new foo () // undefined
匹配的優先順序由高至低排列
new 繫結:this
會指向 new
出來的物件。
new foo()
sets this
to a brand new empty object.
明確繫結:使用 call
、apply
、bind
,明確指出要繫結給 this
的物件。
foo.call(obj2)
sets this
to the obj2
object.
隱含繫結:當函式為物件的方法(method)時,在執行階段 this
就會被繫結至該物件。
obj1.foo()
sets this
to the obj1
object.
預設繫結:當其他規則都不適用時,意即沒有使用 call
、apply
、bind
或不屬於任何物件的 method,就套用預設繫結,在非嚴格模式下,瀏覽器環境 this
的值就是預設值全域物件 window,而在嚴格模式下,this
的值就是 undefined
。
重點是誰呼叫它
1
2
3
4
5
6
7
8
9
10
11
function callName () {
console . log ( this . name );
}
var name = 'global leon' ;
var say = {
name : 'leon' ,
callName : callName
// 這裡的 function 指向全域的 function,但不重要
}
callName () // global leon
say . callName () // leon
透過 bind 綁定正確的 context
1
2
3
4
5
6
7
8
9
10
11
12
const obj = {
name : 'foobar' ,
getName () {
return this . name ;
}
};
console . log ( obj . getName ()); // foobar
const getName = obj . getName ;
console . log ( getName ()); // error
const getNameWithBind = obj . getName . bind ( obj );
console . log ( getNameWithBind ()); // foobar
Closures 閉包
A closure is the combination of a function and the lexical environment within which that function was declared.
閉包 (Closure) 是一種特殊的函式,他能夠存取被宣告當下的環境中的變數。
1
2
3
4
5
6
7
8
const createCounter = () => {
let count = 0
return () => {
return count
}
}
const counter = createCounter ()
console . log ( counter ()) // 0
1
2
3
4
5
6
7
8
9
10
11
12
// return 一個 function,baseTip 已被存取
const createTipper = baseTip => {
return amount => {
return baseTip * amount
}
}
const tip20 = createTipper ( 0.2 )
const tip30 = createTipper ( 0.3 )
console . log ( tip20 ( 100 )) // 20
console . log ( tip20 ( 80 )) // 16
console . log ( tip30 ( 100 )) // 30
1
2
3
4
5
6
7
8
9
10
11
var a = 100
function echo () {
console . log ( a )
}
function test () {
var a = 200
echo ()
}
test () // 100
Object
1
2
3
4
5
6
7
8
9
let myBook = {
title : '1984' ,
author : 'George Orwell' ,
pageCount : 326
}
console . log ( ` $ { myBook . title } by $ { myBook . author } ` )
myBook . title = 'Leon'
console . log ( myBook )
Object Reference
output 出相同的值,因為 function 裡的 object 是 reference 到同一個 memory,
1
2
3
4
5
6
7
8
9
10
11
let myAccount = {
name : 'Leon' ,
expenses : 0 ,
income : 0
}
let addExpense = function ( account , amount ) {
account . expenses = account . expenses + amount
console . log ( account ) // {name: "Leon", expenses: 2000, income: 0}
}
addExpense ( myAccount , 2000 )
console . log ( myAccount ) // {name: "Leon", expenses: 2000, income: 0}
但是當 assign 一個新的 object 給 account,就會是不一樣的 object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Both logs print differen things
let myAccount = {
name : 'Leon' ,
expenses : 0 ,
income : 0
}
let addExpense = function ( account , amount ) {
account = {}
account . age = 1
console . log ( account ) // {age: 1}
}
addExpense ( myAccount , 2000 )
console . log ( myAccount ) // {name: "Leon", expenses: 0, income: 0}
Methods
1
2
3
4
5
6
7
8
9
10
let restaurant = {
name : 'ASB' ,
guestCapacity : 75 ,
guestCount : 0 ,
checkAvailability : function ( partySize ) {
let seatsLeft = this . guestCapacity - this . guestCount
return partySize <= seatsLeft
}
}
console . log ( restaurant . checkAvailability ( 4 ))
String methods
1
2
3
4
5
6
7
let name = 'Leon'
console . log ( name . length )
console . log ( name . toUpperCase ())
console . log ( name . toLowerCase ())
let password = 'abc123asdf098'
console . log ( password . includes ( 'password' ))
Number & Math methods
1
2
3
4
5
6
7
8
9
10
let num = 103.941
console . log ( num . toFixed ( 2 ))
console . log ( Math . round ( num ))
console . log ( Math . floor ( num ))
console . log ( Math . ceil ( num ))
let min = 0
let max = 1
let randomNum = Math . floor ( Math . random () * ( max - min + 1 )) + min
Prototype Object
Prototype 可說是物件的一種 fallback 機制,當在此物件找不到指定屬性時,就會透過原型鏈結(prototype link / prototype reference)追溯到其父物件上。
透過 prototype
可以 shared 給每個 instance
,因為 instance
都是繼承自 prototype property
以下 new
出來的 instance 都繼承自 Person prototype
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const Person = function ( firstName , lastName , age , likes = []) {
this . firstName = firstName
this . lastName = lastName
this . age = age
this . likes = likes
}
Person . prototype . getBio = function () {
let bio = ` $ { this . firstName } is $ { this . age }. `
this . likes . forEach ( like => {
bio += ` $ { this . firstName } likes $ { like }. `
})
return bio
}
Person . prototype . setName = function ( fullName ) {
const name = fullName . split ( ' ' )
this . firstName = name [ 0 ]
this . lastName = name [ 1 ]
}
const me = new Person ( 'Leon' , 'Ji' , 27 , [ 'Teaching' , 'Biking' ])
// me.[[prototype]] = Person.prototype 實際上會做此連結 hidden internal property
console . log ( me . getBio ())
const person2 = new Person ( 'Clancey' , 'Turner' , 51 )
person2 . setName ( 'Leon Ji' )
console . log ( person2 . getBio ())
所有 object 都有 hasOwnProperty
是因爲都繼承自 Object.prototype
1
2
3
4
5
6
7
// myObject --> Object.prototype --> null
const myObject = {} console . log ( myObject . hasOwnProperty ( 'doesNotExist' ))
// 因為 myObject 本身並沒有 hasOwnProperty,而是繼承而來
myObject . hasOwnProperty ( 'hasOwnProperty' ) // false
// __proto__ 上一層
myObject . __proto__ . hasOwnProperty ( 'hasOwnProperty' ) // true
基本型別 Primitives values 的 prototype
string
, number
, boolean
, undefined
, null
1
2
3
4
5
// Array: myArray --> Array.prototype --> Object.prototype --> null
// Function: myFunc --> Function.prototype --> Object.prototype --> null
// String: myString --> String.prototype --> Object.prototype --> null
// Number: myNumber --> Number.prototype --> Object.prototype --> null
// Boolean: myBoolean --> Boolean.prototype --> Object.prototype --> null
Array
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const nums = [ 1 ]
nums . push ( 12 )
nums . unshift ( 3 )
console . log ( nums ) // [3, 1, 12]
nums . pop ()
console . log ( nums ) // [3, 1]
nums . shift ()
console . log ( nums ) // [1]
nums . splice ( 1 , 0 , 2 )
console . log ( nums ) // [1, 2]
nums . splice ( 1 , 0 , 3 )
console . log ( nums ) // [1, 3, 2]
nums . splice ( 1 , 2 , 99 )
console . log ( nums ) // [1, 99]
forEach
1
2
3
4
5
6
7
8
9
10
const todos = [ 'Order cat food' , 'Clean kitchen' , 'Buy food' , 'Do work' , 'Exercise' ]
todos . forEach ( function ( todo , index ) {
const num = index + 1
console . log ( ` $ { num }. $ { todo } ` )
})
for ( let num = 1 ; num <= todos . length ; num ++ ) {
console . log ( ` $ { num }. $ { todos [ num ]} ` )
}
indexOf
1
2
3
4
5
6
7
8
9
const places = [ 'a' , 'b' , 'c' ]
const index = places . indexOf ( 'c' )
console . log ( index ) // 2
const index2 = places . indexOf ( 'd' )
console . log ( index2 ) // -1
// 無法用在 object 因為 {} === {} false,兩個 object 會是不同的 memory
const testObject = [{}]
console . log ( testObject . indexOf ({})) // -1
findIndex
for object should use findIndex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const notes = [
{
title : 'My next trip' ,
body : 'I would like to go to Spain'
},
{
title : 'Habbits to work on' ,
body : 'Exercise. Eating a bit better.'
},
{
title : 'Office modification' ,
body : 'Get a new seat'
}
]
const index = notes . findIndex ( function ( note , index ) {
return note . title === 'Habbits to work on'
})
console . log ( index ) // 1
find
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const notes = [
{
title : 'My next trip' ,
body : 'I would like to go to Spain'
},
{
title : 'Habbits to work on' ,
body : 'Exercise. Eating a bit better.'
},
{
title : 'Office modification' ,
body : 'Get a new seat'
}
]
const findNote = function ( notes , noteTitle ) {
return notes . find ( function ( note , index ) {
return note . title . toLowerCase () === noteTitle . toLowerCase ()
})
}
const note = findNote ( notes , 'my next trip' )
console . log ( note ) // Will print the first object from our array above
filter
1
2
3
4
5
6
var words = [ 'spray' , 'limit' , 'elite' , 'exuberant' , 'destruction' , 'present' ];
const result = words . filter ( word => word . length > 6 );
console . log ( result );
// expected output: Array ["exuberant", "destruction", "present"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const todos = [
{
text : 'Order cat food' ,
completed : false
},
{
text : 'Clean kitchen' ,
completed : true
},
{
text : 'Do work' ,
completed : false
}
]
const getThingsToDo = function ( todos ) {
return todos . filter ( function ( todo ) {
return ! todo . completed
})
}
console . log ( getThingsToDo ( todos ))
sorting
1
2
3
4
5
6
7
8
9
var months = [ 'March' , 'Jan' , 'Feb' , 'Dec' ]
months . sort ()
console . log ( months )
// expected output: Array ["Dec", "Feb", "Jan", "March"]
var array1 = [ 1 , 30 , 4 , 21 , 100000 ]
array1 . sort ()
console . log ( array1 )
// expected output: Array [1, 100000, 21, 30, 4]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const todos = [
{
text : 'Buy food' ,
completed : true
},
{
text : 'Do work' ,
completed : false
},
{
text : 'Exercise' ,
completed : true
}
]
const sortTodos = function ( todos ) {
todos . sort ( function ( a , b ) {
if ( ! a . completed && b . completed ) {
return - 1
} else if ( ! b . completed && a . completed ) {
return 1
} else {
return 0
}
})
}
sortTodos ( todos )
console . log ( todos )
Rest Parameter
1
2
3
4
5
6
const calculateAverage = (... numbers ) => {
let sum = 0
numbers . forEach ( num => ( sum += num ))
return sum / numbers . length
}
console . log ( calculateAverage ( 0 , 100 , 88 , 64 )) // Will print: 63
1
2
3
4
5
6
const printTeam = ( teamName , coach , ... players ) => {
console . log ( ` Team : $ { teamName } ` )
console . log ( ` Coach : $ { coach } ` )
console . log ( ` Players : $ { players . join ( ', ' )} ` )
}
printTeam ( 'Liberty' , 'Casey Penn' , 'Marge' , 'Aiden' , 'Herbert' , 'Sherry' )
Spread Syntax
1
2
3
4
5
6
7
8
9
10
11
const printTeam = ( teamName , coach , ... players ) => {
console . log ( ` Team : $ { teamName } ` )
console . log ( ` Coach : $ { coach } ` )
console . log ( ` Players : $ { players . join ( ', ' )} ` )
}
const team = {
name : 'Libery' ,
coach : 'Casey Penn' ,
players : [ 'Marge' , 'Aiden' , 'Herbert' , 'Sherry' ]
}
printTeam ( team . name , team . coach , ... team . players )
1
2
3
4
5
6
7
let cities = [ 'Barcelona' , 'Cape Town' , 'Bordeaux' ]
let citiesClone = [... cities , 'Santiago' ]
console . log ( cities )
console . log ( citiesClone )
// [ 'Barcelona', 'Cape Town', 'Bordeaux' ]
// [ 'Barcelona', 'Cape Town', 'Bordeaux', 'Santiago' ]