[JS] 正規運算式 (regular expression)

此文章摘錄自’JavaScript優良部分’一書,純粹個人備忘之用。如欲看全文,請參閱該書。

先來個比對URL的正規式吧:

var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

var url = "http://www.ora.com:80/goodparts?q#fragment";

//讓我們呼叫parse_url的exec方法。如果我們傳入的字串在其中比對成功,則回傳擷取自url的零碎字串的陣列:
var result = parse_url.exec(url);

var names = ['url', 'scheme', 'slash', 'host', 'port', 'path', 'query', 'hash'];

var blanks = '     ';
var i;

for(i = 0; i < names.length; i += 1){
  document.writeln(names[i] + ':' + blanks.substring(names[i].length), result[i]);
}

// 產生結果如下:
// url:     http://www.ora.com:80/goodparts?q#fragment
// scheme:  http
// slash:   //
// host:    www.ora.com
// port:    80
// path:    goodparts
// query:   q
// hash:    fragment

以上解析:

^

^:此字元指出字串的開始。它是個定位點,此例來說,是要阻止exec跳過不像URL的字首。

(?:([A-Za-z]+):)?

此段落比對scheme名稱,但名稱後必須接著:。
(?:…):表示本段落為非記憶集結。(通常,使用非記憶集結,比使用較美觀的記憶集結更好,因為記憶會有效能懲罰。)
(…):代表一個記憶集結
字尾詞?表示該集結為選用集結。

(\/{0,3})

此記憶集結。
\/表示應該比對是否有/(斜線)字元。它被\(反斜線)字元轉義。所以不會被翻譯為正規式實字的結尾。
字尾{0,3}意指它前面的/可以出現0到3次。

([0-9.\-A-Za-z]+)

也是一個記憶集結。用於比對主機名稱(host):由一或多個英數字元,加上「.」或「」字元組成。
需做轉義,表示為\-,以免被誤解為表示範圍的連字號。

(?::(\d+))?

非記憶連結。選用的埠(port)比對,一串接在:字元後的數字序列(一或多個數字)。
\d表示數字字元。

(?:\/([^?#]*))?

開端是/字元。
字元類組[^?#]的起始為^,代表類組包含所有字元,,但?與#除外。
*則表示前面的字元類組應出現零或多次。

(?:\?([^#]*))?

負責比對零或多個字元(不包括#)。

(?:#(.*))?

最後一個選用集結,以#起始。
.」用於比對出任何字元,但行末字元除外。

$

$表示字串的結尾。確保在URL的結尾後,沒有其他多餘的材料。

來看另一個例子:

var parse_number = /^-?\d+(?:\.\d)?(?:e[+\-]?\d+)?$/i;

var test = function(num){
  document.writeln(parse_number.test(num));
];

test('1');              // true
test('number');         // false
test('98.6');           // true
test('132.21.86.100');  // false
test('123.45e-67');     // true
test('123.45D-67');     // false

以下解析:

/^…$/i

再度看到標示正規式的定位點^$,用於要求字串中所有字元,都要與正規式比對。
最後的旗標i,使得找到相符文字時,忽略大小寫的差異。樣式中唯一一個字元就是e;但我們希望e也能找出E。把e這個部分寫成[Ee](?:E|e)也能達成相同效果,但使用了旗標i後,就沒有這種需要了。

-?

負號()後的字尾?,表示負號為選用

\d+

\d[0-9]的意思相同,用於比對數字。字尾+則要求前面的數字需出現一或多次。

(?:\.\d*)?

(?:…)表示一個選用的非記憶集結。本第的集結將比對出浮點數(.後接零或多個數字)。

(?:e[+\-]?\d+)?

再來又是另一個選用的非記憶集結。可比對出e或(E),加上選用的正負號,以及一或多個數字。

建構

有兩種製作RegExp的方式,較建議的是採用正規式實字

一、採用正規式實字

由一對斜線(/)圍起。使用時可能有點麻煩,因為斜線既是除法的字算字,也會用於註解。

對RegExp實用可設置三個旗標,分別以g、i、m代表,旗標直接附加在RegExp實字後:

// 製作比對JavaScript字串的正規式物件
var my_regexp = /"(?:\\.|[^\\\"])*"/g;
旗標 說明
g 全域(比對多次;其精確意義隨不同方法而改變)
i 鈍感(忽略字元的大小寫)
m 多行(^與$能比對行末字元)

二、採用RegExp建構式

此建構式接受傳入的字串,並編譯成RegExp物件,建立字串時需要額外注意,因為在正規式與字串實字裡,反斜線(\)各自具有不意義。通常需要設計成兩條反斜線,並為引號轉義:

// 製作比對JavaScript字串的正規式物件
var my_regexp = new RegExp("\"(?:\\.|[^\\\\\\\"])*\"", 'g');

第二個參數,乃是指定旗標(本例為g)的字串。

特性 使用說明
global 如果使用旗標g時則為true
ignoreCase 如果使用旗標i則為true
lastIndex 開始下一輪exec比對的索引。初始值為0。
multiline 如果使用旗標m時則為true。
source 正規式的來源文字。

零件

正規式選項

正規式選項包含一或多個正規式序列。序列間以|字元分隔。如果任何序列比對出結果,表示選擇項比對成功。

// 從into中比對出in後,不會再找出int;因為in的比對已經成功了。
"into".match(/in|int/)

正規式序列

正規式序列包點一或多個正規式要素,每個正規式要素都能選用性地後接量詞,量語決定正規式要素可出現的次數。如果沒有量詞,素示該部分只需比對出一次。

正規式要素

\ / [ ] ( ) { } ? + * | . ^ $

如果想比對出上述特殊字元,必須加上\來轉譯

未轉譯的.可用於比對任何字元,但行末字元除外。
未轉義的^可用於比對字串的起始處,但lastIndex特性需為0。若指定了旗標m,亦可把行末字元納入比對。

未轉義的$可用於比對字串的結尾處。若指定了旗標m,亦可把行末字元納入比對。

正規式轉義

\f:與在字串裡一樣,代表換頁字元;
\n:則是換行字元;
\r:是游標歸位字元(又稱回列首);
\t:是tab字元;
\u:則能用16位元的十六進位常數,來指定Unicode字元;
\b:在正規式要素中,\b並非代表倒退字元。

\d:與[0-9]的意思相同,用於比對數字;
\D:則是前者的反義,[^0-9]比對非數字字元;
\s:是Unicode空白字元的一部分;
\S:則是前者的反義;
\w:與[0-9A-Z_a-z]意思相同,能比對出所有英數字元;
\W:則是前者的反義,與[^0-9A-Z_a-z]相同;
\1:參照到被第一個集結所記憶的文字,讓它可以再被比對,\2、\3…依此類推。

正規式集結

共有四種集結:

記憶集結:記憶集結是種以括號圍起的正規式選項。符合集結的字元將被記憶。每個記憶集結都有一個編號。正規式裡第一個(,是第一號集結;第二個(是第二號集結。

非記憶集結:非記憶集結多了(?字首。非記憶集結只單純做比對,不記憶符合的文段。因此具有效能稍快的優勢。非記憶集結不會干擾記憶集結的編號。

右合樣集結:右合樣集結多了(?=字首。它與非記憶集結有點像,但在比對出符合集結的文段後,又再回到集結開始的地方,很有效地比對不出任何東西。它不是個優良的部分。

右不合樣集結:右不合樣集結多了(?!字首。它與右合樣集結有點像,但只在找不到比對項目時成功。它也不是個優良的部分

正規式量詞

以大括號圍起的數字,表示正規式要素應該比對的次數。所以:
/www//w{3}/同義;
{3, 6}將用於比對3、4、5、6次;
{3,}可讓比對進行三次以上(含3次)。
?{0, 1}的效果相同;
*{0,}的效果相同;
+{1,}的效果相同。

[JS] 陣列

此文章摘錄自’JavaScript優良部分’一書,純粹個人備忘之用。如欲看全文,請參閱該書。

陣列實字

// 陣列實字
var empty = [];
var numbers = [
  'zero', 'one', 'two', 'three', 'four',
  'five', 'six', 'seven', 'eight', 'nine'
];

empty[1];   // undefined
numbers[1]; // 'one'

//物件實字
var numbers_object = {
  '0': 'zero' , '1': 'one'  , '2': 'two',
  '3': 'three', '4': 'four' , '5': 'five',
  '6': 'six'  , '7': 'seven', '8': 'eight',
  '9': 'nine'
};

以上numbers與number_object都是包含十項特性的物件,這些特性也具有完全相同的名稱與值;但numbers繼承自Array.prototype,而number_object卻繼承自Object.prototype;所以,numbers繼承了較多有用的方法。還有numbers也具有length特性,這是number_object所沒有的特性。

在多數語言中,陣列元素都必須為相同型別。JavaScript則允許陣列包含各種型別混合的值:

var misc = [
  'string', 98.6, true, false, null, undefined,
  ['nested', 'array'], {object: true}, NaN,
  Infinity
];

misc.length  // 10

length特性

每個陣列都有一個length特性。是陣列中最大的整數特性名稱加1

var myArray = [];
myArray.length  // 0

myArray[1000000] = true;
myArray.length  // 1000001
// myArray只包含一個特性

// 假設numbers現在改成['zero', 'one', 'two']
numbers.length = 3;

// 新的陣列元素,可藉由指派給陣列目前的length而被附加至陣列尾端:
numbers[numbers.length] = 'shi';
// numbers成為['zero', 'one', 'two', 'shi']

// 有時候,使用push方法更易於達成相同效果:
numbers.push('go');
// numbers成為['zero', 'one', 'two', 'shi', 'go']

刪除

// numbers目前是['zero', 'one', 'two', 'shi', 'go']

delete numbers[2];
// number成為['zero', 'one', undefined, 'shi', 'go']
// 但通常我們會想把右側每個元素的名稱同步減1,所以最好改成以下:
numbers.splice(2, 1);
// number成為['zero', 'one', 'shi', 'go']

splice方法,它可以對陣列動手術,刪除某些元素,並以其它元素代替。第一個引數是陣列裡的序號;第二個引數是欲刪除的元素數量。任何附加的引數,都將被插入陣列中。

列舉

var i;
for(i = 0; i < myArray.length; i += 1){
  document.writeln(myArray[i]);
}

困惑之處

JavaScript程式的常見錯誤之一,是在該用陣列時用了物件,或是該用物件時用了陣列。規則很簡單:當特性名稱是連續小整數時,就應該用陣列;否則應該用物件。

JavaScript對於陣列與物件的差別,其實也搞不太清礎。typeof運算子回報的陣列型別是’object’,更是大幫倒忙。

JavaScript沒有很好的陣列與物件區分機制。所以我們必須自訂is_array函式,可繞過這項缺陷:

var is_array = function(value){
  return value &&
         typeof value === 'object' &&
         typeof value.length === 'number' &&
         typeof value.splice === 'function'  &&
         !(value.propertyIsEnumerable('length'));
};

方法

JavaScript提供一組對陣列行動的方法。這些方法就是儲存在Array.prototype裡的函式,假設要新增一個array方法至Array.prototype來擴充:

Array.method('reduce', function(f, value){
  var i;
  for(i = 0; i < this.length; i += 1){
    value = f(this[i], value);
  }
  return value;
});

//建立一個數值陣列
var data = [4, 8, 15, 16, 23, 42];

//建立兩個簡易函式。其中一個相加兩個數字;另一個相乘兩個數字
var add = function(a, b){
  return a + b;
};

var mult = function(a, b){
  return a * b;
};

// 呼叫資料的reduce方法,傳給add函式
var sum = data.reduce(add, 0);  // sum為108

// 再度呼叫reduce方法,這次傳入mult函式
var product = data.reduce(mult, 1);  // 乘積為7418880

[JS] 關於函式

此文章摘錄自’JavaScript優良部分’一書,純粹個人備忘之用。如欲看全文,請參閱該書。

函式物件

JavaScript的函式都是物件。函式物件聯繫到Function.prototype(本身再聯繫到Object.prototype)。既然函式是物件,就能像其他任何值一樣使用。函式能儲存在變數、物件或陣列裡。函式能被當成引數傳給另一個函式、函式也能被另一個函式回傳。最後,函式既然是物件,它也可以有方法。函式能被呼叫。

函式實字

函式物件以函式實字建立:

//建立變數add,其中儲存一個相加兩數的函式
var add = function (a, b){
  return a + b;
}

函式實字有四個部份

  • 保留字function
  • 函式名稱(選用),以上述範例來說,並沒有函式名稱。所以稱為匿名函式(anonymous function)
  • 函式參數的集合
  • 一組以大括號圍起的敘述,此敘述是函式的本體,在函式被呼叫時執行

函式實字建立的函式物件,包含對外圍環境的聯繫,稱為closure(閉包),這是種具有無窮表達能力的資源。

方法呼叫模式

// 建立 myObject 物件,其中有個值value
// 以及一個執行遞增計算的方法
// 遞增方法可接收一個選用參數
// 如果引數不是數值,則使用預設值1。
var myOjbect = {
  value: 0;
  increment: function(inc){
    this.value += typeof inc === 'number' ? inc : 1;
  }
};

myObject.increment();
document.writeln(myObject.value);  //輸出1

myObject.increment(2);
document.writeln(myObject.value);  //輸出3

函式呼叫模式

當函式不是物件的特性時,則像函式一般被呼叫:

var sum = add(3, 4);

使用此種呼叫模式時,this將結合至全域變數,這點是語言設計上的錯誤。如果設計正確,呼叫內層函式時,this應該仍會結合至外層函式的this。這項錯誤的後果之一,使得方法無法利用內層函式協助工作,因為內層函式並未享有方法對物件的存取,它的this結合到錯誤的地方了。有個輕鬆的解決之道:如果方法定義一個變數,並指派它的值為this,內層函式將透過這個變數存取this。一般習慣把這個變數命名為that:

// 引數myObject具有double方法
myObject.double = function(){
  var that = this;

  var helper = function(){
    that.value = add(that.value, that.value);
  };

  helper();  // 把helper視為函式而呼叫
};

// 把double視為函式而呼叫
myObject.double();
document.writeln(myObject.getValue());  // 輸出6

建構式呼叫模式(不建議使用)

// 建立稱為Quo的建構式
// 它讓物件具有status的特性
var Quo = function(string){
  this.status = string;
};

//為所有Quo的實例,賦與一個稱為get_status的public方法
Quo.prototype.get_status = function(){
  return this.status;
};

//製作一個Quo實例
var myQuo = new Quo("confused");
document.writeln(myQuo.get_status());  // 輸出confused

與字首詞new合用的函式,稱為建構式(constructor)。

apply呼叫模式

apply方法讓我們建構一個引數陣列,用於呼叫函式,也能讓我們使用this的值。apply方法接受兩個參數,第一個是應該與this聯繫的值,第二個則是參數的陣列。

// 製作一個包含兩個數字的陣列,並予以相加
var array = [3, 4];
var sum = add.apply(null, array);  // 總和為7

// 製作一個具有status成員的物件
var statusObject = {
  status: 'A-OK'
};

// statusObject並非從Quo.prototype繼承而來
// 但我們可以在statusObject上呼叫get_status,
// 儘管 statusObject 並不擁有get_status方法
var status = Quo.prototype.get_status.apply(statusObject);  // status是A-OK

arguments陣列

var sum = function(){
  var i, sum = 0;
  for(i = 0; i < arguments.length; i += 1){
    sum += arguments[i];
  }
  return sum;
};

document.writeln(sum(4, 8, 15, 16, 23, 42));  // 108

上例並非特別好用的模式,因為設計上的錯誤,arguments並非真正的陣列,它是個像陣列的物件。arguments具有length特性,但缺乏所有陣列方法。

回傳

函式一定回傳某個值。如果未指定return值,則回傳undefined。
如果函式呼叫時附有字首詞new,而且return值不是物件,則改為回傳this(新物件)。

例外狀況

try敘述只有一個捕捉所有例外事件的catch區塊。如果你的處理方式需依照例外類型而定,則例外處理器必須檢視name,以判斷例外類型。

// throw敘述負責中斷函式的執行。它應該要拿到一個exception物件,物件中包含辨識例外類別的name特性,
// 以及描述例外性質的message特性。你也可以再多加其它特性:
var add = function(a, b){
  if(typeof a !== 'number' || typeof b !== 'number'){
    throw {
      name: 'TypeError',
      message: 'add needs numbers'
    }
  }
  return a + b;
};

// 製作try_it函式,不正確地呼叫新的加法函式,
// exception物件將被傳遞給try敘述的catch字句:
var try_it = function(){
  try{
    add('seven');
  } catch(e){
    document.writeln(e.name + ': ' + e.message);
  }
};

try_it();

擴充型別

以擴充Function.prototype為例,增加一個方法到所有函式下:

// 添method方法後,我們可不再需要鍵入prototype特性的名稱
Function.prototype.method = function(name, func){
  if(!this.prototype[name]){
    this.prototype[name] = func;
  }
  return this;
};

JavaScript沒有獨立的整數型別(integer),所以有時需要單獨抽離數值中的整數部分。我們可以為Number.prototype添加一個integer方法:該方法可能使用Math.ceiling或Math.floor產生整數,根據數值的正負號而決定:

Number.method('integer' function(){
  return Math[this < 0 ? 'ceiling': 'floor'](this);
});

// 例:
document.writeln((-10 / 3).integer());  // -3

JavaScript缺少移除字串尾端空格的方法,以下可解決:

String.method('trim', function(){
  return this.replace(/^\s+|\s+$/g, '');
});

// 例:
document.writeln('"' + "  neat  ".trim() + '"');

範圍

var foo = function(){
  var a = 3, b = 5;

  var bar = function(){
    var b = 7, c = 11;

    //此處:a是3,b是7,c是11

    a += b + c;

    //此處:a是21,b是7,c是11
  };

  //此處:a是3,b是5,c未定義

  bar();

  //此處:a是21,b是5
};

closure(閉包)

示範如何保護值不被未授權的來源隨意改變。

此時不再以物件實字做myObject的初始化,我們將透過呼叫一個回傳物件實字的函式而初始化myObject。這個函式定義了value變數,變數仍可被increment與getValue方法取用,但函式範圍把它隱藏起來,不讓程式的其餘部分見到:

// 我們並非把函式本身指派給myObject,而是把函式呼叫的結果指派過去。請注意最後一行的()。
// 函式回傳一個包含兩個方法的物件,這兩個方法則繼續享有取用value變數的特權。
var myObject = function(){
  var value = 0;

  return {
    increment: function(inc){
      value += typeof inc === 'number' ? inc : 1;
    },
    getValue: function(){
      return value;
    }
  }
}();

稍早有提過Quo建構式,它會產生具有status特性與get_status方法的物件。只是這沒意思。可以直接取用的特性時,何必繞遠路呼叫getter方法?如果status是個private特性將更為有用,讓我們定義一個不同的quo函式:

// 建立一個稱為quo的函式
// 製作具有get_status方法
// 與private status特性的物件

var quo = function(status){
  return {
    get_status: function(){
      return status;
    }
  };
};

// 製作一個quo實例
var myQuo = quo("amazed");
document.writeln(myQuo.get_status());

上述的quo函式,設計為不需使用字首詞new,所以名稱也不需首字母大寫。當我們呼叫quo,它回傳包含get_status方法的新物件。對該物件的參考則儲存在myQuo。get_status方法仍有對quo的status特性的優先存取權,儘管quo已經被回傳了。get_status對參數複本沒有存取權,而是對參數本身有存取權。都是因為函式能取用建造它本身的背景情境,才有這種可能。這種狀況稱為closure(閉包)。

模組

我們可以使用函式與closure製造模組。模組是個函式或物件,用於呈現一個介面,但隱藏起它的狀態與實作。

// 為String擴充一個deentityify方法,它的功能是在字串裡尋找html實體(html entity),並以意義相等的字元取代。
// 在物件裡保存實體名稱及其相等事物頗為合理,但物件又該放在哪裡?最理想的方式,是把物件放入closure,或許再提供
// 一個新增實體的方法

String.method('deentityify', function(){
  // 實體表。實體的名稱與字元的對照。
  var entity = {
    quot: '"',
    lt:   '&lt;',
    gt:   '&gt;'
  };

  // 回傳deentityify方法
  return function(){
    // 以下為deentityify方法。它呼叫負責字串取代的方法。
    // 尋找以&amp;起始、以;結尾的子字串。如果其間的字元
    // 儲存在實體表中,則以相等字元取代實體。
    // 這個方法用到正規運算式
    return this.replace(/&amp;([^&amp;;]+);/g,
      function(a, b){
        var r = entity[b];
        return typeof r === 'string' ? r : a;
      });
  };
})();
// 以上請注意最後一行。我們立即呼叫使用()運算子製造的函式。此時的呼叫將建立並回傳變旁deentityify的函式。
document.writeln('&lt;"&gt;'.deentityify());

模組模式,一個「定義private變數與函式」的函式;它建立可(透過closure)取用private變數與函式的特許函式;而後把特許函式回傳或儲存於可存取的地方。

使用模組模式,可削減全域變數的使用。如此一來推廣了資訊隱藏及其它良好的設計習慣;在壓縮應用程式和其他singletons時非常有效。

模組也能用於製作具有安全性的件。假設想製作一個產生連續流水號的物件:

var serial_maker = function(){
  // 製作一個產生具唯一性字串的物件。
  // 唯一性字串由兩個分組成:字首、與連續流水號。
  // 物件包括設定字首與流水號的方法
  // 還有一個產生唯一字串的gensym方法

  var prefix = '';
  var seq = 0;
  return {
    set_prefix: function(p){
      prefix = String()p;
    },
    set_seq: function(s){
      seq = s;
    },
    gensym: function(){
      var result = prefix + seq;
      seq += 1;
      return result;
    }
  };
}();

var seqer = serial_maker();
seqer.set_prefix = 'Q';
seqer.set_seq = 1000;
var unique = seqer.gensym();  //字串unique為"Q1000"

上例方法並未使用this與that。結果,則是不會外洩的seqer;除了同樣獲得方法的允許,否則無法取得或改變prefix或seq。seqer物件是可變的,所以方法雖可代取,但仍然不會開放對其秘密資料的存取。seqer不過是函式的集合,這些函式則保證具有使用或調整秘密資料的特殊能力。

如果我們把seqer.gensym傳送給第三方函式,函式將能產生唯一字串,但無法改變prefix或seq。

[JS] 關於物件

JavaScript的簡單型別裡,包括了數值字串booleannullundefined。其它所有的值都是物件(object)。
陣列是物件、函式是物件、正規式是物件…都是物件。

物件是特性(Property)的容器,具有名稱與值。

物件實字(object literal)

物件實字提供一種建立新物件值的便利註記方式。是以一對大括號圍起的零或多對名稱/值(name/value)。

//例:
var your_name = {};

var your _name2 = {
  "first_name": "Carlos",
  "last_name": "Chang"
};

var flight = {
  airline: "Oceanic",
  number: 815,
  departure:{
    IATA: "SYD",
    time: "2004-09-22 14:55",
    city: "Sydney"
  },
  arrival:{
    IATA: "LAX",
    time: "2004-09-23 10:42",
    city: "Los Angeles"
  }
};

擷取

擷取的方式可用[](中括號)或.(點號)的方式來取值:

//例:
your_name2['first_name']  // "Carlos"
flight.departure.IATA     // "SYD"

//例:||字算子能用於填入預設值:
var middle = your_name2["middle_name"] || "(none)";
var status = flight.status || "unknown";

更新

物件裡的值,可透過指派來直接更新。

//例:如果物件裡已有特性名稱,則會取代特性值:
your_name2["first_name"] = "Duncan";

//例:如果物件裡還沒有特性名稱,則會增加到物件裡:
your_name2["middle_name"] = "Lester";
your_name2.nickname = "Curly";

flight.equipment = {
  model: 'Boeing 777'
};
flight.status = 'overdue';

參考

物件是透過參考(reference)而被傳遞。

//例:a、b、c各自參考到不同物件
var a = {}, b = {}, c = {};

//例:a、b、c均參考到同一個空物件
var a = b = c {};

原型

每個物件都聯繫到一個prototype物件,由此可繼承特性。所有從物件實字建立的物件,都聯繫到Object.prototype(JavaScript的標準物件)

反映

typeof運算子在判斷特性的型別時很有幫助。

typeof flight.number    // 'number'
typeof flight.status    // 'string'
typeof flight.arrival   // 'object'
typeof flight.manifest  // 'undefined'

//因為任何原型鍊上的特性都可產生值,例:
typeof flight.toString     // 'function',由prototype來的
typeof flight.constructor  // 'function',由prototype來的

//可用hasOwnProperty方法,在物件擁有指定特性時回傳true:
flight.hasOwnProperty('number')       // true
flight.hasOwnProperty('constructor')  // false

列舉

for in敘述,可用迴圈處理所有物件中的特性名稱。列舉(enumeration)將包括所有特性–甚至包括你沒興趣的函式特性、原型特性,所以需要過濾不需要的值。最常見的過濾器是hasOwnProperty方法,加上排除函式的typeof

var name;
for(name in another_your_name2){
  if(typeof another_your_name2[name] !== 'function'){
    document.writeln(name + ': ' + your_name2[name]);
  }
}

//名稱的順序沒有保證,如果想確保特性依照特定順序出現,最好完全別用for in敘述;請改為使用陣列,依照正確順序儲存特性名稱,並用for取代for in,如此也不用在原型鏈上大海撈針:
var i;
var properties = [
  'first_name',
  'middle_name',
  'last_name',
  'profession'
];
for(i = 0; i &lt; properties.length: i += 1){
  document.writeln(properties[i] + ': ' your_name2[properties[i]]);
}

刪除

delete運算子可從物件中移除特性。如果物件裡有該特性,則移除,且不會影響原型聯繫(prototype linkage)裡的其它物件。

your_name2.nickname // 'Curly'
delete your_name2.nickname;

減少全域變數

減少全域變數的使用至最小化的其中一種方式,就是為應用程式建立唯一一個全域變數:

var MYAPP = {};

MYAPP.your_name2 = {
  "first_name": "Carlos",
  "last-name": "Chang"
};

MYAPP.flight = {
  airline: "Oceanic",
  number: 815,
  departure:{
    IATA: "SYD",
    time: "2004-09-22 14:55",
    city: "Sydney"
  },
  arrival:{
    IATA: "LAX",
    time: "2004-09-23 10:42",
    city: "Los Angeles"
  }
};

全域變數減少到單一名稱後,也顯著地降低與其他應用程式發生錯誤互動的風險了。

 

參考資料:”JavaScript優良部份”一書

[jQuery] $(document).ready(fn)與$(window).load(fn)的差別

在使用jQuery時,有時會看到$(document).ready(fn)$(window).load(fn),那兩者有何分別呢?
$(document).ready(fn)會發生在html全部下載完成時,才會去執行fn或相關程式碼。
$(window).load(fn)會發生在html標籤中所引用的圖檔、內嵌物件(如:flash)、iframe等都載入完成後,才會執行fn或相關程式碼。
 

[JS] getDir() 取得目前目錄的完整路徑

使用javascript來取得目前所在目錄的完整路徑:

function getDir(){
  var locHref = location.href;      //取得完整路徑,包含檔案名稱
  var locArr = locHref.split("/");  //用"/"切分,並存至locArr陣列
  delete locArr[locArr.length - 1];//刪除locArr陣列的最後一個元素,即檔案名稱
  return locArr.join("/");      //用"/"毀locArr陣列中的元素合併,傳回字串值
}