JavaScript語(yǔ)言精粹摘要

編程

所謂編程,就是將一組需求分解成一組函數(shù)與數(shù)據(jù)結(jié)構(gòu)的技能。

假與真

下面列出的值被當(dāng)作假(false)

  • false
  • null
  • undefined
  • 空字符串('')
  • 數(shù)字0
  • 數(shù)字NaN

其它所有的值都被當(dāng)做真,包括true、字符串“false”、字符串“0”,以及所有的對(duì)象。

for in語(yǔ)句

for in語(yǔ)句會(huì)枚舉一個(gè)對(duì)象的所有屬性名(或鍵名)。
通常你需要檢測(cè)object.hasOwnProperty(ppty)來確定這個(gè)屬性名是該對(duì)象的成員,還是來自于原型鏈,代碼如下:

for (ppty in obj) {
    if (obj.hasOwnProperty(ppty)) {
        // do something
    }
}

注:hasOwnProperty方法不會(huì)檢查原型鏈。屬性名的出現(xiàn)順序是不確定的。

typeof運(yùn)算符

typeof運(yùn)算符產(chǎn)生的值有:'number'、'string'、'boolean'、'undefined'、'function'和'object'。

數(shù)據(jù)類型

JavaScript的簡(jiǎn)單數(shù)據(jù)類型包括數(shù)字、字符串、布爾值(true和false)、null值和undefined值。其它所有的值都是對(duì)象。即數(shù)據(jù)類型有:number, string, boolean, null, undefined和object。

對(duì)象

JavaScript的對(duì)象永遠(yuǎn)不會(huì)是真的空對(duì)象,因?yàn)樗鼈兛梢詮脑玩溨腥〉贸蓡T屬性。

對(duì)象是屬性的容器,其中每個(gè)屬性都擁有名字和值。屬性的名字可以是包括空字符串在內(nèi)的任意字符串。屬性值可以是除undefined值之外的任何值。

如果你嘗試檢索一個(gè)并不存在的成員屬性的值,將返回undefined,示例:

var obj = {a: 'av'};
obj.b // undefined

對(duì)象通過引用來傳遞,它們永遠(yuǎn)不會(huì)被復(fù)制,示例:

var x = stooge;
x.nickname = 'Curly';
var nickname = stooge.nickname; // 因?yàn)閤和stooge指向同一個(gè)對(duì)象的引用,所以nickname為'Curly'

var a = {}, b = {}, c = {}; // a、b和c每個(gè)都引用一個(gè)不同的空對(duì)象

a = b = c = {}; // a、b和c都引用同一個(gè)空對(duì)象

判斷對(duì)象:

var is_object = function(v) {
    return (v && typeof v === 'object')? true : false;
};

原型

給Object增加一個(gè)create方法,這個(gè)方法創(chuàng)建一個(gè)使用原對(duì)象作為其原型的新對(duì)象:

if (typeof Object.create !== 'function') {
    Object.create = function(o) {
        var F = function() {};
        F.prototype = o;
        return new F();    
    }
}

原型連接“只有”在檢索值的時(shí)候才被用到。如果我們嘗試去獲取對(duì)象的某個(gè)屬性值,但該對(duì)象沒有此屬性名,那么JavaScript會(huì)試著從原型對(duì)象中獲取屬性值。如果那個(gè)原型對(duì)象也沒有該屬性,那么再?gòu)乃脑椭袑ふ?,依此類推,直到該過程最后到達(dá)終點(diǎn)Object.prototype。如果想要的屬性完全不存在于原型鏈中,那么結(jié)果就是undefined值。這個(gè)過程稱為委托。

原型關(guān)系是一種動(dòng)態(tài)的關(guān)系。如果我們添加一個(gè)新的屬性到原型中,該屬性會(huì)立即對(duì)所有基于該原型創(chuàng)建的對(duì)象可見。

||運(yùn)算符

||運(yùn)算符可以用來填充默認(rèn)值,示例:

var b = obj.b || 'bv';

全局變量

最小化使用全局變量的方法之一是為你的應(yīng)用只創(chuàng)建一個(gè)唯一的全局變量:

var MYAPP = {};

函數(shù)

函數(shù)的與眾不同之處在于它們可以被調(diào)用。函數(shù)的調(diào)用模式有4種:

  • 方法調(diào)用模式
    當(dāng)一個(gè)函數(shù)被保存為對(duì)象的一個(gè)屬性時(shí),我們稱它為一個(gè)方法。當(dāng)一個(gè)方法被調(diào)用時(shí),this被綁定到該對(duì)象。

  • 函數(shù)調(diào)用模式
    當(dāng)一個(gè)函數(shù)并非一個(gè)對(duì)象的屬性時(shí),那么它就是被當(dāng)作一個(gè)函數(shù)來調(diào)用的,示例:

var sum = add(3, 4);    // sum的值為7

以此模式調(diào)用函數(shù)時(shí),this被綁定到全局對(duì)象。因此對(duì)象內(nèi)部的函數(shù),無法通過this訪問對(duì)象屬性,如果要訪問,則解決方案的示例如下:

obj.v = 2;
// 給obj對(duì)象增加一個(gè)double方法
obj.double = function() {
    var that = this;    // 解決方法
    var helper = function() {
        that.v = add(that.v, that.v);
    };
    helper();   // 以函數(shù)的形式調(diào)用helper
}

// 以方法的形式調(diào)用double
obj.double();
console.log(obj.v); // 4
  • 構(gòu)造器調(diào)用模式
    如果在一個(gè)函數(shù)前面帶上new來調(diào)用,那么背地里將會(huì)創(chuàng)建一個(gè)連接到該函數(shù)的prototype成員的新對(duì)象,同時(shí)this會(huì)被綁定到那個(gè)新對(duì)象上。

一個(gè)函數(shù),如果創(chuàng)建的目的就是希望結(jié)合new前綴來調(diào)用,那它就被稱為構(gòu)造器函數(shù)。按照約定,它們保存在以大寫格式命名的變量里。

// 創(chuàng)建一個(gè)名為Quo的構(gòu)造器函數(shù)。它構(gòu)造一個(gè)帶有status屬性的對(duì)象。
var Quo = function(status) {
    this.status = status;
};

// 給Quo的所有實(shí)例提供一個(gè)名為getStatus的公共方法
Quo.prototype.getStatus = function() {
    return this.status;
};

// 構(gòu)造一個(gè)Quo實(shí)例
var myQuo = new Quo('confused');
console.log(myQuo.getStatus()); // confused
  • Apply調(diào)用模式
    因?yàn)镴avaScript是一門函數(shù)式的面向?qū)ο缶幊陶Z(yǔ)言,所以函數(shù)可以擁有方法。

apply方法讓我們構(gòu)建一個(gè)參數(shù)數(shù)組傳遞給調(diào)用函數(shù)。它也允許我們選擇this的值。apply方法接收兩個(gè)參數(shù),第1個(gè)是要綁定給this的值,第2個(gè)就是一個(gè)參數(shù)數(shù)組。

// 構(gòu)造一個(gè)包含兩個(gè)數(shù)字的數(shù)組,并將它們相加
var add = function(a, b) {
    return a + b;
};
var arr = [3, 4];
var sum = add.apply(null, arr); // sum值為7

// 構(gòu)造一個(gè)包含status成員的對(duì)象
var statusObj = {
    status: 'A-OK'
};

// statusObj并沒有繼承自Quo.prototype,但我們可以在statusObj上調(diào)用getStatus方法,盡管statusObj并沒有一個(gè)名為getStatus的方法
var status = Quo.prototype.getStatus.apply(statusObj);  // status值為'A-OK'

參數(shù)

因?yàn)檎Z(yǔ)言的一個(gè)設(shè)計(jì)錯(cuò)誤,arguments并不是一個(gè)真正的數(shù)組。它只是一個(gè)“類似數(shù)組(array-like)”的對(duì)象。arguments擁有一個(gè)length屬性,但它沒有任何數(shù)組的方法。

返回

一個(gè)函數(shù)總是會(huì)返回一個(gè)值。如果沒有指定返回值,則返回undefined。

如果函數(shù)調(diào)用時(shí)在前面加上了new前綴,且返回值不是一個(gè)對(duì)象,則返回this(該對(duì)象)。

如果沒有指定return語(yǔ)句的返回表達(dá)式,那么返回值是undefined,即:

return;  // 返回undefined

return undefined;   // 返回undefined

return this;    // 返回this(該對(duì)象)

擴(kuò)充類型的功能

// 通過給Function.prototype增加一個(gè)method方法,我們下次給對(duì)象增加方法的時(shí)候就不必鍵入prototype這幾個(gè)字符,省掉了一點(diǎn)麻煩。
Function.prototype.method = function(name, func) {
    if (!this.prototype[name]) {
        this.prototype[name] = func;
    }
    return this;
}


// 通過給Number.prototype增加一個(gè)integer方法,它會(huì)根據(jù)數(shù)字的正負(fù)來判斷是使用Math.ceiling還是Math.floor:
Number.method('integer', function() {
    return Math[this < 0? 'ceil' : 'floor'](this);
});


// 通過給String.prototype增加一個(gè)trim方法,它會(huì)刪除字符串首尾的空白:
String.method('trim', function() {
    return this.replace(/^\s+|\s+$/g, '');
});

閉包

閉包示例:

// 創(chuàng)建一個(gè)名為quo的構(gòu)造函數(shù)。它構(gòu)造出帶有g(shù)etStatus方法和status私有屬性的一個(gè)對(duì)象。
var quo = function(status) {
    return {
        getStatus: function() {
            return status;
        }
    };
};

// 構(gòu)造一個(gè)quo實(shí)例
var myQuo = quo('amazed');
console.log(myQuo.getStatus()); // amazed

這個(gè)quo函數(shù)被設(shè)計(jì)成無須在前面加上new來使用,所以名字也沒有首字母大寫。當(dāng)我們調(diào)用quo時(shí),它返回包含getStatus方法的一個(gè)新對(duì)象。該對(duì)象的一個(gè)引用保存在myQuo中。即使quo已經(jīng)返回了,但getStatus方法仍然享有訪問quo對(duì)象的status屬性的特權(quán)。getStatus方法并不是訪問該參數(shù)的一個(gè)副本,它訪問的就是該參數(shù)本身。這是可能的,因?yàn)樵摵瘮?shù)可以訪問它被創(chuàng)建時(shí)所處的上下文環(huán)境。這被稱為閉包。

模塊

模塊模式的一般形式是:一個(gè)定義了私有變量和函數(shù)的函數(shù);利用閉包創(chuàng)建可以訪問私有變量和函數(shù)的特權(quán)函數(shù);最后返回這個(gè)特權(quán)函數(shù),或者把它們保存到一個(gè)可訪問到的地方。

模塊模式通常結(jié)合單例模式(Singleton Pattern)使用。JavaScript的單例就是用對(duì)象字面量表示法創(chuàng)建的對(duì)象,對(duì)象的屬性值可以是數(shù)值或函數(shù),并且屬性值在該對(duì)象的生命同期中不會(huì)發(fā)生變化。它通常作為工具為程序其它部分提供功能支持。單例模式的更多內(nèi)容請(qǐng)參見:https://zh.wikipedia.org/wiki/單例模式

級(jí)聯(lián)

有一些方法沒有返回值,如果我們讓這些方法返回this而不是undefined,就可以啟用級(jí)聯(lián)。

記憶

函數(shù)可以將先前操作的結(jié)果記錄在某個(gè)對(duì)象里,從而避免無謂的重復(fù)運(yùn)算。這種優(yōu)化被稱為記憶(memorization)。以下是一個(gè)幫助我們構(gòu)造帶記憶功能函數(shù)的函數(shù):

var memorizer = function(memo, formula) {
    var recur = function(n) {
        var result = memo[n];
        if (typeof result !== 'number') {
            result = formula(recur, n);
            memo[n] = result;
        }
        return result;
    };
    return recur;
};

使用以上構(gòu)造函數(shù),構(gòu)造帶記憶功能的fibonacci函數(shù):

var fibonacci = memorizer([0, 1], function(recur, n) {
    return recur(n - 1) + recur(n - 2);
});

使用以上構(gòu)造函數(shù),構(gòu)造帶記憶功能的階乘函數(shù):

var factorial = memorizer([0, 1], function(recur, n) {
    return n * recur(n - 1);
});

繼承

在基于類的語(yǔ)言中,對(duì)象是類的實(shí)例,并且類可以從另一個(gè)類繼承。JavaScript是一門基于原型的語(yǔ)言,這意味著對(duì)象直接從其它對(duì)象繼承。

函數(shù)化

構(gòu)造一個(gè)生成對(duì)象的函數(shù),以小寫字母開頭來命名,因?yàn)樗⒉恍枰褂胣ew前綴,該函數(shù)包括4個(gè)步驟:

  1. 創(chuàng)建一個(gè)新對(duì)象。有很多的方式去構(gòu)造一個(gè)對(duì)象。它可以構(gòu)造一個(gè)對(duì)象字面量,或者它可以和new前綴連用去調(diào)用一個(gè)構(gòu)造器函數(shù),或者它可以使用Object.create方法去構(gòu)造一個(gè)已經(jīng)存在的對(duì)象的新實(shí)例,或者它可以調(diào)用任意一個(gè)會(huì)返回一個(gè)對(duì)象的函數(shù)。
  2. 有選擇地定義私有實(shí)例變量和方法。這些就是函數(shù)中通過var語(yǔ)句定義的普通變量。
  3. 給這個(gè)新對(duì)象擴(kuò)充方法。這些方法擁有特權(quán)去訪問參數(shù),以及在第2步中通過var語(yǔ)句定義的變量。
  4. 返回那個(gè)新對(duì)象。

這里是一個(gè)函數(shù)化構(gòu)造器的偽代碼模板:

var constructor = function(spec, my) {
    var that;
    var 其它的私有實(shí)例變量;
    my || my || {};

    把共享的變量和函數(shù)添加到my中;

    that = 一個(gè)新對(duì)象;

    添加給that的特權(quán)方法;

    return that;
};

函數(shù)化模式還給我們提供了一個(gè)處理父類方法的方法。我們會(huì)構(gòu)造一個(gè)supervisor方法,它取得一個(gè)方法名并返回調(diào)用那個(gè)方法的函數(shù)。該函數(shù)會(huì)調(diào)用原來的方法,盡管屬性已經(jīng)變化了。

Object.method('supervisor', function(name) {
    var that = this;
    var method = that[name];

    return function() {
        return method.apply(that, arguments);
    }
});

如果我們用函數(shù)化的樣式創(chuàng)建一個(gè)對(duì)象,并且該對(duì)象的所有方法都不使用this或that,那么該對(duì)象就是持久性(durable)的。一個(gè)持久性對(duì)象就是一個(gè)簡(jiǎn)單功能函數(shù)的集合。

數(shù)組Array

數(shù)組的length屬性的值是這個(gè)數(shù)組的最大整數(shù)屬性名加上1。

設(shè)置更大的length不會(huì)給數(shù)組分配更多的空間,而把length設(shè)小將導(dǎo)致所有下標(biāo)大于等于新length的屬性被刪除。

當(dāng)屬性名是小而連續(xù)的整數(shù)時(shí),你應(yīng)該使用數(shù)組,否則,使用對(duì)象。

JavaScript沒有一個(gè)好的機(jī)制來區(qū)別數(shù)組和對(duì)象,我們可以通過定義自己的is_array函數(shù)來彌補(bǔ)這個(gè)缺陷:

var is_array = function(v) {
    return Object.prototype.toString.apply(v) === '[object Array]';
};

JavaScript提供了一套數(shù)組可用的方法,這些方法是被儲(chǔ)存在Array.prototype中的函數(shù)。

創(chuàng)建一個(gè)用于構(gòu)造數(shù)組的方法Array.dim:

Array.dim = function(dimension, initial) {
    var a = [];
    var i;
    for (i = 0; i< dimension; i += 1) {
        a[i] = initial;
    }
    return a;
};

創(chuàng)建一個(gè)包含10個(gè)0的數(shù)組:
var my_arr = Array.dim(10, 0);

創(chuàng)建一個(gè)用于構(gòu)造矩陣的方法Array.matrix:

Array.matrix = function(m, n, initial) {
    var a;
    var i;
    var j;
    var mat = [];
    for (i = 0; i< m; i += 1) {
        a = [];
        for (j = 0; j < n; j += 1) {
            a[j] = initial;
        }
        mat[i] = a;
    }
    return mat;
};

構(gòu)造一個(gè)用0填充的4 × 4矩陣:
var my_mat = Array.matrix(4, 4, 0);
console.log(my_mat[3][3]);  // 0

創(chuàng)建一個(gè)用于構(gòu)造單位矩陣的方法Array.identity:

Array.identity = function(n) {
    var i;
    var mat = Array.matrix(n, n , 0);
    for (i = 0; i < n; i += 1) {
        mat[i][i] = 1;
    }
    return mat;
};

構(gòu)造一個(gè)4 × 4的單位矩陣:
my_mat = Array.identity(4);
console.log(my_mat[3][3]);  // 1

正則表達(dá)式

在JavaScript程序中,正則表達(dá)式必須寫在一行中。

.會(huì)匹配除行結(jié)束符以外的所有字符。

一個(gè)未轉(zhuǎn)義的$將匹配文本的結(jié)束,當(dāng)指定了m標(biāo)識(shí)時(shí),它也能匹配行結(jié)束符。

一般情況下,最好堅(jiān)持使用貪婪性匹配。

方法

  • array.sort(comparefn)
    你的比較函數(shù)應(yīng)該接受兩個(gè)參數(shù),并且如果這兩個(gè)參數(shù)相等則返回0,如是第1個(gè)參數(shù)應(yīng)該排在前面,則返回一個(gè)負(fù)數(shù),如果第2個(gè)參數(shù)應(yīng)該排列在前面,則返回一個(gè)正數(shù)。

編寫一個(gè)構(gòu)造比較函數(shù)的函數(shù),支持基于多個(gè)鍵值進(jìn)行排序,如下:

// by函數(shù)接收一個(gè)成員名字符串,和一個(gè)可選的次要比較函數(shù)作為參數(shù),
// 并返回一個(gè)可以用來對(duì)包含該成員的對(duì)象數(shù)組進(jìn)行排序的比較函數(shù)。
// 當(dāng)o[name]和p[name]相等時(shí),次要比較函數(shù)被用來決出高下。
var by = function(name, minor) {
    return function(o, p) {
        var a;
        var b;
        if (o && p && typeof o === 'object' && typeof p === 'object') {
            a = o[name];
            b = p[name];
            if (a === b) {
                return typeof minor === 'function'?  minor(o, p) : 0;
            }
            if (typeof a === typeof b) {
                return a < b? -1 : 1;
            }
            return typeof a < typeof b ? -1 : 1;
        } else {
            throw {
                    name: 'Error',
                    message: 'Expected an object when sorting by ' + name;
            };
        }
    };
};

var s = [
    {first: 'Joe', last: 'Besser'},
    {first: 'Moe', last: 'Howard'},
    {first: 'Joe', last: 'DeRita'},
    {first: 'Shemp', last: 'Howard'},
    {first: 'Larry', last: 'Fine'},
    {first: 'Curly', last: 'Howard'}
];

s.sort(by('last', by('first')));
// 排序結(jié)果是s:
[
    {first: 'Joe', last: 'Besser'},
    {first: 'Joe', last: 'DeRita'},
    {first: 'Larry', last: 'Fine'},
    {first: 'Curly', last: 'Howard'},
    {first: 'Moe', last: 'Howard'},
    {first: 'Shemp', last: 'Howard'}
]

數(shù)字

判斷數(shù)字:

var is_number = function(v) {
    return typeof v === 'number' && isFinite(v);
};

function語(yǔ)句對(duì)比function表達(dá)式

一個(gè)語(yǔ)句不能以一個(gè)函數(shù)表達(dá)式開頭,因?yàn)楣俜降恼Z(yǔ)法假定以單詞function開頭的語(yǔ)句是一個(gè)function語(yǔ)句。解決方法就是把函數(shù)調(diào)用括在一個(gè)圓括號(hào)之中。

(function() {
    var hidden_variable;
    // 這個(gè)函數(shù)可能對(duì)環(huán)境有一些影響,但不會(huì)引入新的全局變量。
}());

JSON

JavaScript對(duì)象表示法(JavaScript Object Notation,簡(jiǎn)稱JSON)是一種輕量級(jí)的數(shù)據(jù)交換格式。

JSON有6種類型的值:對(duì)象、數(shù)組、字符串、數(shù)字、布爾值(true和false)和特殊值null??瞻祝崭穹?、制表符、回車符和換行符)可被插到任何值的前后。

JSON對(duì)象是一個(gè)容納“名/值”對(duì)的無序集合。名字可以是任何字符串。值可以是任何類型的JSON值,包括數(shù)組和對(duì)象。

JSON數(shù)組是一個(gè)值的有序序列。其值可以是任何類型的JSON值,包括數(shù)組和對(duì)象。

JSON字符串被包圍在一對(duì)雙引號(hào)之間。

JSON數(shù)字與JavaScript的數(shù)字相似。

代碼風(fēng)格

對(duì)于一個(gè)組織機(jī)構(gòu)來說,軟件的長(zhǎng)遠(yuǎn)價(jià)值和代碼庫(kù)的質(zhì)量成正比。

優(yōu)美的特性

一門語(yǔ)言是否具備為其自身編寫一個(gè)編譯器的能力,仍然是對(duì)這門語(yǔ)言完整性的一個(gè)測(cè)試。

我們發(fā)現(xiàn)人們想要的產(chǎn)品其實(shí)只要能工作即可。

在設(shè)計(jì)產(chǎn)品和編程語(yǔ)言時(shí),我們希望直接使用核心的精華部分,因?yàn)槭沁@些精華創(chuàng)造了大部分的價(jià)值。

如果產(chǎn)品和編程語(yǔ)言被設(shè)計(jì)得僅留下精華,那該有多好。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容