Javascript Function與封裝

Posted by Benjamin Lu on 2017-08-22

Function與封裝

預設值

ES5
此種做法有個缺點,當param1或param2傳入的值對應的boolean值為false時,此種設定就會失效,如:param1為空字串或false時,就會被轉型成false則程式就會使用1當作預設值而失去本來傳入參數的意義。

1
2
3
4
5
6
7
8
9
function testParams (param1, param2) {
param1 = param1 || 1
param2 = param2 || 2
console.log(param1)
console.log(param2)
}
testParams(3, 5)

ES6

1
2
3
4
5
6
function testParams (param1 = 1, param2 = 2) {
console.log(param1)
console.log(param2)
}
testParams(3, 5)

Arrow Function

ES5

1
2
3
4
5
6
7
var hook = function (callback) {
callback(5)
}
hook(function (value) {
console.log(value) // 5
})

ES6

1
2
3
4
5
6
7
let hook = (callback) => {
callback(5)
}
hook((value) => {
console.log(value) // 5
})

Arrow Function與IIFE

IIFE (Immediately Invoked Function Expression),稱為立即函示
表示立即執行宣告的函式,常用在把功能獨立的程式碼給包裹起來
減少全域變數數量,建立模組化物件

ES5的IIFE

1
2
3
(function () {
console.log(this.constructor.name) // Window or undefined in strict mode
}())

ES5中透過IIFE全域變數變為區域變數

JQuery和JQuery plugins常用這種技巧來減少變數在全域的污染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(function ($) {
$.fn.greenify = function(options) {
// This is the easiest way to have default options.
var settings = $.extend({
// These are the defaults.
color: "#556b2f",
backgroundColor: "white"
}, options)
// Greenify the collection based on the settings variable.
return this.css({
color: settings.color,
backgroundColor: settings.backgroundColor
})
}
}(jQuery))
$("div").greenify({
color: "orange"
})

利用IIFE函式物件當作模組

獨體模式 (Singleton Pattern)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var Singleton = (function () {
var instance
function createInstance () {
var object = new Object("I am the instance")
return object
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance()
}
return instance
}
}
})()

ES6中的IIFE

1
2
3
(() => {
console.log(this.constructor.name) // Window or undefined in strict mode
})()

如果沒有要傳入的context有更簡短的寫法

1
2
3
{
console.log(this.constructor.name) // Window or undefined in strict mode
}

需要指定的話

1
2
3
((context) => {
console.log(context) // {a: 'a'}
})({a: 'a'})

Arrow Function對this的影響

ES5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var Printer = function () {
// constructor
console.log(this.constructor.name) // Printer
// public function
this.print = function () {
var array = ['a']
array.forEach(function (el) {
console.log(this.constructor.name) // Window or undefined in strict mode
})
}
setTimeout(function () {
console.log(this.constructor.name) // Window
}, 0)
}
var p = new Printer()
p.print()

ES6 + Arrow Function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Printer {
constructor () {
console.log(this.constructor.name) // Printer
setTimeout(() => console.log(this.constructor.name), 0) // Printer
}
print () {
var array = ['a']
array.forEach((el) => {
console.log(this.constructor.name) // Printer
})
}
}
let p = new Printer()
p.print()

箭頭函數當中的 this 是定義時的對象,而不是使用時的對象

資料封裝

問題: 當一個方法參數太多時怎麼處理?

ES5的例子
參數太長,程式碼不好維護,參數順序不容易記憶

1
2
3
4
5
6
7
8
9
10
11
12
13
var longParamsMethod = function (param1, param2, param3, param4) {
param1 = param1 || 'defaultParam1'
param2 = param2 || 'defaultParam2'
param3 = param3 || 'defaultParam3'
param4 = param4 || 'defaultParam4'
console.log(param1)
console.log(param2)
console.log(param3)
console.log(param4)
}
longParamsMethod('a', 'b', 'c', 'd')

ES5 利用arguments解決參數太長

1
2
3
4
5
6
7
8
9
10
11
12
13
var longParamsMethod = function () {
param1 = arguments[0] || 'defaultParam1'
param2 = arguments[1] || 'defaultParam2'
param3 = arguments[2] || 'defaultParam3'
param4 = arguments[3] || 'defaultParam4'
console.log(param1)
console.log(param2)
console.log(param3)
console.log(param4)
}
longParamsMethod('a', 'b', 'c', 'd')

但使用者仍然要記得參數的順序,當參數太長依然不方便使用,何解?
回想前面JQuery的作法,傳入options物件當參數的集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(function ($) {
$.fn.greenify = function(options) {
// 透過mixins的方式模擬繼承,複製有指定的options並覆蓋掉預設值
var settings = $.extend({
// These are the defaults.
color: "#556b2f",
backgroundColor: "white"
}, options)
// 使用混合後的參數做應用
return this.css({
color: settings.color,
backgroundColor: settings.backgroundColor
})
}
}(jQuery))
// 傳入options物件{color: "orange"}
$("div").greenify({
color: "orange"
})

ES6能帶來什麼好處?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(function ($) {
$.fn.greenify = function(options) {
// Object destruction
let {color, backgroundColor} = $.extend({
// These are the defaults.
color: "#556b2f",
backgroundColor: "white"
}, options)
// 直接使用參數
return this.css({
color: color,
backgroundColor: backgroundColor
})
}
}(jQuery))
// 傳入options物件{color: "orange"}
$("div").greenify({
color: "orange"
})

使用Object destruction配合function預設值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(function ($) {
// Object destruction + default parameter
$.fn.greenify = function({
color = '#556b2A',
backgroundColor = 'white'
}) {
// 直接使用參數
return this.css({
color: color,
backgroundColor: backgroundColor
})
}
}(jQuery))
$("div").greenify({
color: "orange"
})

補充 $.extend(destination, source)的mixins如何實現

兩個物件的mixins

1
2
3
4
5
6
7
8
9
10
function mixin (dest, src) {
for (var key in src) {
dest[key] = src[key]
}
return dest
}
var mix = mixin({a: 'a'}, {b: 'b'})
console.log(mix)

多個物件mixins,模擬多重繼承

1
2
3
4
5
6
7
8
9
10
11
12
function mixin (...objs) {
// reduce在Functional programming會再細講
return objs.reduce((dest, src) => {
for (var key in src) {
dest[key] = src[key]
}
return dest
})
}
var dest = mixin({}, {a: 'a'}, {b: 'b'}, {c: 'c'})
console.log(dest)