JavaScript PATTERNS

cHAPTER 5: 物件建立模式

如何建立一個物件

範例

let Obj= {
  "name": "Ceall"
}

It's Easy !

大量使用時, 該注意甚麼

進階事項

  • 命名空間
  • 模組化
  • private屬性與靜態成員觀念

oUTLINE

  • 命名空間模式
  • Private 屬性與方法
  • 靜態成員

命名空間模式

一個原則

只建立一個全域變數, 其餘變數都是在該變數內

範例

// 全域變數
var MYAPP = {}

//變數
MYAPP.car = ''

// function
MYAPP.fn = function(){};

// Mudules
MYAPP.modules = {}
MYAP.modules.module1 = {}
MYAP.modules.module2 = {}

上述做法缺點

  • 命名衝突

如何解決

  • 採用命名空間函式

命名空間函式

var MYAPP = MYAPP || {};

MYAPP.namespace('MYAPP.a') // MYAPP.a
MYAPP.namespace('a.b.c') // MYAPP.a.b.c


MYAPP.namespace = function(ns_string){
    let parts = ns_string.split("."),
        parent = MYAPP,
        i;

    if(parts[0] === "MYAPP"){
        parts = parts.slice(1)
    }

    for(i = 0; i < parts.length; i++){
        if(typeof parent[parts[i]] === "undefined"){
            parent[parts[i]] = {}
        }

        parent = parent[parts[i]]
    }

    return parent
}

原則: 若變數名稱存在, 則保留目前資料內容,

若不存在, 則建立一個空物件給予新變數

這樣就可以了, 嗎?

如果有100個變數, 可能會遇到

  • 使用時, 需要打一長串的前綴名稱
  • 任何一段code都可更動其他功能所使用的參數
  • 變數名稱越長, 查詢時間越久

如何解決 ?

A: 沙盒模式

沙盒模式 - 核心概念

  • 建立一個沙盒物件, 並設定初始的屬性與方法(sandbox)
  • 該建構式可自行新增屬性與方法,並擴增到使用者建立的新沙盒中
  • 可傳入一個回呼函式, 回呼函式預設會傳入沙盒物件, 透過回呼函式, 自行擴充新的變數名稱, 並可保護全域命名空間

實做沙盒模式

function Sandbox(){
      //將參數列轉為陣列
  var args = Array.prototype.slice.call(arguments),
      // 最後一個參數是callback function
      callback = args.pop(),
      // 模組可以用陣列方式傳遞, 也可以用個別參數傳入
      modules = (args[0] && typeof args[0] === "string") ? args : args[0],
      i;

  // 先確保此函式是以建構式方式呼叫
  if(!(this instanceof Sandbox)){
    return new Sandbox(modules, callback);
  }

  // 依照需要為this增加屬性
  this.a = 1;
  this.b = 2;

  // 現在,將模組新增至盒新的this物件
  // 沒有指定模組, 或者"*"都表示「使用所有模組」
  if(!modules || modules === '*'){
    modules = [];
    for(i in Sandbox.modules){
      if(Sandbox.modules.hasOwnProperty(i)){
         modules.push(i); 
      }
    }
  }

  // 初始所需的模組
  for(i = 0; i < modules.length; i += 1){
     Sandbox.modules[modules[i]](this);
  }

  callback(this);
}

// 依照需要建立prototype的屬性
Sandbox.prototype = {
  name: "My Application",
  version: "1.0",
  getName: function(){
     return this.name;
  }
};

簡單範例(1/2)

//沙盒模式 看起來像是

new Sandbox(function (box){
  // 你的程式碼
  // box是最初始的沙盒物件
})

// 使用擴增模組

Sandbox ('ajax','dom',function (box){
  // 使用dom和ajax模組
  // 此時沙盒物件, box, 已可使用'ajax', 'dom'兩個模組的屬性與方法
  console.log(box);
})

Sandbox (['ajax', 'dom'], function (box){
  console.log(box);
})

簡單範例(2/2)

// 建立沙盒模組

Sandbox.modules = {};

Sandbox.modules.dom = function (box){
  box.geElement = function (){};
  box.getStyle = function (){};
  box.foo = "bar";
}

Sandbox.modules.ajax = function (box){
  box.makeRequest = function (){};    
  box.getResponse = function (){};
}

Sandbox解決了

  • 命名變數全部依賴在同一個全域變數
  • 變數名稱過長, 階層太多

PRIVATE屬性與方法

js不像java一樣

沒有PRIVATE, pROTECT, PUBLIC可表示屬性與方法

所有的方法與屬性都是PUBLIC

pUBLIC範例

// public成員

var obj = {
  prop: 1,
  method: function(){
       return "test"
  }
}

console.log(obj.prop)  //prop 可被public存取
console.log(obj.method()) //method 可被public存取

沒有private用法

可用closure實作

Private靜態成員

  • 所有由同一個建構式建立的物件, 都可互相共享
  • 建構式之外不可取用的成員

Private 建構式範例

// private建構式範例

function fn(){
  // private成員
  var data  = 0

  this.getData = function(){
    return data
  }
}

var test = new getData();

console.log(fn.data) // undefined, data是private
console.log(fn.getData) // 0, public方法可存取data

Private 物件實字範例

// private物件實字範例

var myData;

(function getData(){
  // private成員
  var data  = 0


  // 實作public
  myData = {
    // 特權方式
    getData: function(){
      return data
    }
  }
})()

myData.getData() // 0

特權方法

將PRIVATe成員RETURN到外部的變數

利用建構式產生PRIVATE變數的缺點

每次呼叫建構式產生新物件時, 都需要重新建立一次

使用頻繁的建構式, 可考慮改用Prototype

Prototype與隱私權

function Gadget(){
  //private 成員
  var name = 'iPod';

  //public 方法
  this.getName = function(){
    return name;
  }
}

Gadget.prototype = (function(){
  //private成員
  var browser = 'Mobile'
  //public的prototype成員
  return {
    getBrowser: function(){
      return browser
    }
  }  
})()

var toy = new Gadget();

console.log(toy.getName()) // '自己的'特權方法
console.log(toy.getBrowser()) //prototype的特權方法

優點: 所有相同的建構式都可以共享prototype 內的屬性與方法, 節省記憶體

靜態成員

何謂靜態成員

  • 不需建立實體即可使用
  • 不會在實體與實體之間改變

靜態成員範例

//建構式

var Gadget = function(){}

//一個靜態方法
Gadget.isShiny  = function(){
  return "you bet";
}

//加入原型的方法
Gadget.prototype.setPrice  = function(price){
 tihs.price = price;
}


// 測試

//呼叫靜態方法
Gadget.isShiny(); // "you bet"

// 建立一個實體, 然後呼叫方法
var iphone = new Gadget();
iphone.setPrice(500);

typeof Gadget.setPrice; // "undefined", 無法用靜態方式呼叫實體方法
typeof iphone.isShiny; // "undefined", 無法實體呼叫靜態方法


Made with Slides.com