《JavaScript語言精粹 修訂版》 讀書筆記

首發(fā)于:segmentfault《JavaScript語言精粹 修訂版》 讀書筆記

之前看到這篇文章,前端網(wǎng)老姚淺談:怎么學JavaScript?最筒,說到怎么學習JavaScript绸栅,那就是看書劫哼、分析源碼。
10本書讀2遍的好處,應該大于一本書讀20遍。
看書主動學習樱溉,看視頻是被動學習。
看書和分析源碼的時機纬凤。但已經(jīng)工作一年半載時福贞,正是提高的好時候,此時可以去看書了停士。全面系統(tǒng)的梳理知識點挖帘,掃清自己的盲區(qū)。如果只是靠項目經(jīng)驗是不夠的恋技,通過項目來學習拇舀,那>肯定是必須的,工作本身就是一個學習的過程蜻底。
怎么把一本書看完呢骄崩?很簡單,敲。文字加代碼都敲刁赖。
比較認同老姚的說法搁痛。去年畢業(yè)到現(xiàn)在长搀,我也算是工作一年了宇弛,是時候看書查缺補漏了。

于是我就先把這本薄的經(jīng)典書《JavaScript語言精粹 修訂版》豆瓣讀書本書簡介(總共10章源请,除去附錄枪芒,才100頁),讀完并記錄了一些筆記谁尸【俗伲基本算是摘抄書本的,自己聯(lián)想到了一些知識和資料也擴展了一下良蛮〕槁担總體寫下來近一萬字。讀書筆記還可以分享給別人看决瞳』踽悖回顧時,書不在身邊還可以看看自己的筆記皮胡。想想這類經(jīng)典書記一遍動手敲一遍也是很值得的痴颊。不過這讀書筆記中可能會有一些錯別字,閱讀時如果發(fā)現(xiàn)歡迎指正屡贺。

第1章 精華

大多數(shù)語言都有精華和糟粕蠢棱。JavaScript令人詭異的事情是,在對這門語言沒有的太多了解甩栈,甚至對編程都沒有太多了解的情況下泻仙,你也能用它來完成工作。
看到這里不禁想起:

張鑫旭大牛在《我對知乎前端相關(guān)問題的十問十答》
非計算機專業(yè)背景學習JS要點有這一條:
所有繼承和原型相關(guān)內(nèi)容跳過量没,注意玉转,是跳過,不要看允蜈!沒有這些JS一樣可以活得很好冤吨,你的日常工作一樣玩得飛起,當然饶套,你沒忍住看了相關(guān)知識也沒關(guān)系漩蟆,因為你會發(fā)現(xiàn)自己看不懂的;

JavaScript的函數(shù)是(主要)基于詞法作用域的頂級對象妓蛮。

譯注:JavaScript中的函數(shù)是根據(jù)詞法來劃分作用域的怠李,而不是動態(tài)劃分作用域的。具體內(nèi)容參見《JavaScript權(quán)威指南》中譯第5版相關(guān)章節(jié)“8.8.1 詞法作用域”。
JavaScript有非常強大的對象字面量表示法捺癞。這種表示法是JSON的靈感來源夷蚊。
原型繼承是JavaScript中一個有爭議的特性。

ECMAScript編程語言》第3版定義了JavaScript的標準髓介。
ES3標準
擴展:顏海鏡大牛整理的ES3中文版
顏海鏡大牛整理的ES5中文版
W3c ES5中文版
阮一峰大牛的書籍《ES6標準入門2》
更多內(nèi)容可參見這篇文章:ECMAScript 2018 標準導讀

一個簡單的例子:

Function.prototype.method = function(name, func) {
  this.prototype[name] = func;
  return this;
}

書中貫徹始終都會用到這個method方案惕鼓,作者將會在第4章解釋它。

第2章 語法

本章主要用鐵路圖(語法圖)表示語法唐础。
主要有:空白箱歧、標識符、數(shù)字一膨、字符串呀邢、語句、表達式豹绪、字面量价淌、函數(shù)。
typeof 運算符產(chǎn)生的值有'number', 'string','boolean','undefined','function','object'瞒津。如果運算數(shù)是一個數(shù)組或者是null,那么結(jié)果是'object',這其實是不對的蝉衣。

第3章 對象

JavaScript簡單數(shù)據(jù)類型包括數(shù)字、字符串仲智、布爾值买乃,null值和undefined值。其他所有值都是對象钓辆。
數(shù)組剪验、字符串和布爾值“貌似”對象,因為它們擁有方法(包裝對象)前联,但它們是不可變的功戚。
對象是屬性的容器,其中每個屬性都擁有名字和值似嗤。屬性名可以是包括空字符串在內(nèi)的所有字符串啸臀,屬性值可以是除了undefined值之外的任何值。

JavaScript包含一種原型鏈的特性烁落,允許對象繼承到另一個對象的屬性乘粒。正確地使用它能減少對象初始化時的消耗的時間和內(nèi)存。
檢索
.,[]兩種檢索方式伤塌,推薦點.表示法灯萍。
嘗試重undefined的成員屬性中取值將會導致TypeError異常,這時可以通過&&來避免錯誤每聪。
更新
如果屬性名已經(jīng)存在對象里旦棉。那么屬性的值會被替換齿风。如果之前沒有擁有那個屬性名,那么該屬性將被擴充到對象中绑洛。
引用
對象通過引用來傳遞救斑。它們永遠不會被復制。
原型
所有通過對象字面量創(chuàng)建的對象都鏈接到Object.prototype真屯。
創(chuàng)建新對象時脸候,可以選擇某個對象作為它的原型。

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

原型連接只有在檢索值的時候才被用到讨跟。如果嘗試去獲取對象的某個屬性值纪他,但對象沒有此屬性名鄙煤,那么JavaScript會試著從原型對象中獲取屬性值晾匠。如果那個原型對象也沒有該屬性,那么再從它的原型中尋找梯刚,依此類推凉馆,直到該過程最后達到終點Object.prototype。如果想要的屬性完全不存在原型鏈中亡资,那么結(jié)果就是 undefined值澜共。這個過程稱為委托
原型關(guān)系是一種動態(tài)的關(guān)系锥腻。
反射
原型鏈上的所有屬性都會產(chǎn)生值嗦董。有兩種方案可以處理掉對象上不需要的屬性。
①程序檢查時丟棄值為函數(shù)的屬性瘦黑。但有可能有些值確實是函數(shù)京革,所以該方法不可靠。
②使用hasOwnProperty方法幸斥,如果是對象擁有獨有的屬性匹摇,則返回true。該方法不會檢查原型鏈甲葬。
枚舉
for in可以遍歷一個對象中所有的屬性名廊勃。但包含函數(shù)和一些不關(guān)心的原型中屬性。而且順序不確定经窖,可以用 hasOwnProperty方法和typeof排除函數(shù)坡垫。
for 循環(huán)不會出現(xiàn)for in那些情況。
刪除
delete運算符可以用來刪除對象的屬性画侣。
減少全局變量的污染
可以把全局性的資源納入一個名稱空間之下冰悠。這樣做能減少沖突。

第4章 函數(shù)

函數(shù)用于①代碼復用②信息隱藏③組合調(diào)用棉钧。一般來說屿脐,所謂編程涕蚤,就是將一組需求分節(jié)成一組函數(shù)與數(shù)據(jù)結(jié)構(gòu)的技能。
JavaScript的函數(shù)就是對象的诵。
函數(shù)對象連接到Function.prototype(該原型對象本身連接到Object.prototype)万栅。
每個函數(shù)在創(chuàng)建時會附加兩個隱藏屬性,函數(shù)的上下文和實現(xiàn)函數(shù)行為的代碼西疤。
每個函數(shù)對象在創(chuàng)建時也隨配有一個prototype屬性烦粒。它的值是一個擁有constructor屬性且值為該函數(shù)的對象。
函數(shù)字面量
函數(shù)字面量包括4個部分代赁。①保留字function②函數(shù)名扰她,可以省略,③一組參數(shù)④一組語句芭碍。
函數(shù)字面量可以出現(xiàn)在任何允許表達式出現(xiàn)的地方徒役。一個內(nèi)部函數(shù)除了可以訪問自己的參數(shù)和變量,同時也可以自由訪問把它嵌套在其中的父函數(shù)的參數(shù)和變量窖壕。通過函數(shù)字面量創(chuàng)建的函數(shù)對象包含一個連接到外部上下文的連接忧勿。這被稱為閉包
調(diào)用
除了聲明時定義的形式參數(shù)瞻讽,每一個函數(shù)還接收兩個附加的參數(shù):thisargument鸳吸。在JavaScript中一共有四種調(diào)用模式。①方法調(diào)用模式速勇,②函數(shù)調(diào)用模式③構(gòu)造器調(diào)用模式④apply調(diào)用模式晌砾。

this指向問題一直困擾很多人。我一般是這樣記的烦磁,誰調(diào)用this就指向誰。)

方法調(diào)用模式
對象的方法執(zhí)行,this指向該對象个初。比如:

var myObj = {
  value: 0,
  showValue: function() {
    console.log('value:', this.value);
  }
}
myObj.showValue();  // value: 0

函數(shù)調(diào)用模式

var add = function(a,b) {
    return a + b;
}
add(3,4);  //7
window.add(3,4);  //7
// 這種this被綁定到全局對象(window)乖寒。
// 可以理解是window.add(3,4);

有種簡單的辦法就是var that = this;this存儲下。
例:

var myObj = {
  value: 0,
  age: 20,
  showValue: function() {
    console.log('value:',this.value);
    var  that = this;
    var showAge = function() {
        // window上沒有age院溺,所以是undefined
        console.log('這里的this是window ---age:', this.age);  // undefined
        console.log('age:', that.age);  // 20
     }
     showAge();
  }
}
myObj.showValue();  // 0楣嘁, undefined,

構(gòu)造器調(diào)用模式
JavaScript是一門基于原型繼承的語言珍逸。
如果在一個函數(shù)前面帶上new 來調(diào)用逐虚。那么背地利將會創(chuàng)建一個連接到該函數(shù)的prototype成員的新對象,同時this會被綁定到那個新對象上谆膳。
new 前綴也會改變return 語句的行為叭爱。
例:

var Quo = function (string) {
  this.status = string;
}
Quo.prototype.get_status = function () {
  return this.status; 
}
var myQuo = new Quo('confused'); // 'confused'

一個函數(shù),如果創(chuàng)建的目的就是希望結(jié)合new 前綴來調(diào)用漱病。那么它就被稱為構(gòu)造器函數(shù)买雾。按照約定把曼,它們保存在以大寫函數(shù)命名的變量里。如果調(diào)用構(gòu)造器函數(shù)時沒有在前面加上new,可能會發(fā)生非常糟糕的事情漓穿,既沒有編譯時的警告嗤军,也沒有運行時廣告,所以大寫約定非常重要晃危。
作者不推薦這種形式的構(gòu)造器函數(shù)叙赚。有更好的替代方式。
Apply調(diào)用模式
JavaScript是一門函數(shù)式的面向?qū)ο缶幊陶Z言僚饭,所以對象可以擁有方法震叮。
apply方法讓我們構(gòu)建一個參數(shù)數(shù)組傳遞給調(diào)用函數(shù),它也允許我們選擇this的值鳍鸵。
參數(shù)
arguments苇瓣,雖然擁有length屬性,但不是真正的數(shù)組权纤。而是類似數(shù)組(array-like)的對象钓简。
返回
return 可用來是函數(shù)提前返回。當return 被執(zhí)行時汹想,函數(shù)立即返回而不再執(zhí)行余下的語句。
一個函數(shù)總會返回一個值撤蚊,如果沒指定古掏,那就是返回undefined值。
如果函數(shù)調(diào)用時在前面加上了new 前綴侦啸,且返回值不是一個對象槽唾,則返回this(該新對象)。
異常
JavaScript提供了一套異常處理機制光涂。
throw語句和try catch,try catchfinally是可選的庞萍。
擴展類型的功能
JavaScript允許給語言的基本類型擴充功能。在第3章中我們已經(jīng)看到忘闻,可以通過Object.prototype添加方法钝计,可以讓該方法對所有對象都可用。這樣的方式對函數(shù)齐佳、數(shù)組私恬、字符串、數(shù)字炼吴、正則表達式和布爾值同樣適用本鸣。

例如:

Function.prototype.method = function () {
  this.prototype[name]  = func;
  return this;
}

基本類型的原型是公用結(jié)構(gòu),所以在類庫混用時務必小心硅蹦。一個保險的做法就是只在確認沒有該方法時才添加它荣德。

Function.prototype.methods = function(name, func) {
  if (!this.prototype[name]) {
      this.prototype[name] = func;
  }
  return this;
}

遞歸
遞歸函數(shù)就是會直接或間接地調(diào)用自身的一種函數(shù)闷煤。遞歸是一種強大的編程技術(shù),遞歸是用一般的方式去解決每一個子問題涮瞻。書中舉了一個漢諾塔的例子曹傀,是程序設計中經(jīng)典遞歸問題。詳細說明可以參見 百度百科“漢諾塔”詞條饲宛。
一些語言提供了尾遞歸優(yōu)化皆愉。尾遞歸是一種在函數(shù)的最后執(zhí)行調(diào)用語句的特殊形式的遞歸。參見Tail call艇抠。 ES6版本擴展了尾遞歸幕庐。參見阮一峰老師的《ES6標準入門》中的尾調(diào)用優(yōu)化
作用域
在編程語言中,作用域控制著變量與參數(shù)的可見性和聲明周期家淤。
書中指出當前JavaScript沒有塊級作用域异剥。因為沒有塊級作用域,所以最好的做法是在函數(shù)體的頂部聲明函數(shù)中可能用到的所有變量絮重。不過ES6擴展了有塊級作用域冤寿。
閉包
作用域的好處是內(nèi)部函數(shù)可以訪問定義它們的外部函數(shù)的參數(shù)和變量(除了thisarguments)。
例子:

<ul class="list">
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
// 點擊相應節(jié)點時青伤,顯示對應的序號督怜。可以使用閉包來解決狠角。
var add_the_handlers = function() {
    var helper = function(i) {
        return function(e) {
            alert(i);
        }
    }
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = helper(i);
    }
}
// 擴展 另外可以用let i = 0号杠,或者把nodes類數(shù)組轉(zhuǎn)成數(shù)組等方案實現(xiàn)。
// 閉包特性:1丰歌、函數(shù)內(nèi)再嵌套函數(shù)姨蟋,2、內(nèi)部函數(shù)可以調(diào)用外層的參數(shù)和變量立帖,3眼溶、參數(shù)和變量不會被垃圾回收機制回收。
// 閉包優(yōu)點 靈活和方便晓勇,便于封裝堂飞。缺點:空間浪費、內(nèi)存泄露宵蕉、性能消耗酝静。

回調(diào)
發(fā)起異步請求,提供一個當服務器響應到達時隨即出發(fā)的回調(diào)函數(shù)羡玛。異步函數(shù)立即返回别智,這樣客戶端就不會被阻塞。
模塊
我們可以使用函數(shù)和閉包來構(gòu)造模塊稼稿。模塊是一個提供接口卻隱藏狀態(tài)與實現(xiàn)的函數(shù)或?qū)ο蟆?br> 舉例:給String添加一個deentityify方法薄榛。它的任務是尋找字符串中的HTML字符實體并把它們替換成對應的字符讳窟。

String.method('deentityify', function () {
    // 字符實體表。它映射字符實體的名字到對應的字符敞恋。
    var entity = {
        quot: '"',
        lt: '<',
        gt: '>'
    };
    // 返回 deentityify方法
    return function () {
        return this.replace(/&([^&;]+);)/g,
        function (a,b) {
            var r = entity[b];
            return typeof r === 'string'? r : a;
        }
    };
}());

模塊模式利用了函數(shù)作用域和閉包來創(chuàng)建被綁定對象與私有成員的關(guān)聯(lián)丽啡,在上面例子中,只有deentityify方法有權(quán)訪問字符實體表這個數(shù)據(jù)對象硬猫。
模塊模式的一般形式是:一個定義了私有變量和函數(shù)的函數(shù)补箍;利用閉包創(chuàng)建可以訪問私有變量和函數(shù)的特權(quán)函數(shù);最后返回這個特權(quán)函數(shù)啸蜜,或者把它們保存到一個可以訪問的地方坑雅。
使用模塊模式就可以摒棄全局變量的使用。它促進了信息隱藏和其他優(yōu)秀的設計實踐衬横。對于應用程序的封裝裹粤,或者構(gòu)造其他單例對象,模塊模式非常有效蜂林。

單例譯注
模塊模式通常結(jié)合單例模式使用遥诉。JavaScript的單例就是用對象字面量表示法創(chuàng)建的對象,對象的屬性值可以是數(shù)值或函數(shù)噪叙,并且屬性值在該對象的生命周期中不會發(fā)生變化矮锈。更多內(nèi)容參見:單例模式

級聯(lián)
有一些方法沒有返回值。如果我們讓這些方法返回this而不是undefined构眯,就可以啟用級聯(lián)愕难。
在一個級聯(lián)中,我們可以在單獨一條語句中依次調(diào)用同一個對象的很多方法惫霸。比如jQuery獲取元素、操作樣式葱弟、添加事件壹店、添加動畫等。
柯里化
柯里化芝加,是把多參數(shù)函數(shù)轉(zhuǎn)換為一系列單參數(shù)函數(shù)并進行調(diào)用的技術(shù)硅卢。更多詳情可參見:柯里化
函數(shù)也是值〔卣龋柯里化允許我們把函數(shù)與傳遞給它的參數(shù)相結(jié)合将塑,產(chǎn)生一個新的函數(shù)。

var add1 = add.curry(1);
document.writeln(add1(6));

JavaScript并沒有curry方法蝌麸,但可以擴展該功能点寥。
arguments不是真正的數(shù)組,所以使用了Array.prototype.slice方法来吩。

Function.method('curry',function(){
    var slice = Array.prototype.slice,
    args = slice.apply(arguments),
    that = this;
    return function() {
        return that.apply(null, args.concat(slice.apply(arguments)));
    }
});

記憶
函數(shù)可以將先前操作的結(jié)果記錄在某個對象里敢辩,從而避免無謂的重復運算蔽莱。這種優(yōu)化稱作記憶。
比如說戚长,我們想要一個遞歸函數(shù)來計算Fibonacci(斐波那契)數(shù)列盗冷,它的特點是,前面相鄰兩項之和等于后一項的值同廉。更多參考:斐波那契仪糖。最前面兩個數(shù)字是0和1。

var fibonacci = function() {
    return n < 2? n : fibonacci(n-1) + fibonacci(n-2);
}

這樣雖然能完成工作迫肖,但它做了很多無謂的工作锅劝。
構(gòu)造一個帶有記憶功能的函數(shù):

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

再用這個memoizer函數(shù)來定義fibonacci函數(shù),提供其初始的memo數(shù)組和formula函數(shù)咒程。

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

極大的減少了我們的工作量鸠天。例如要產(chǎn)生一個記憶的階乘函數(shù),只需要提供基本的階乘公式即可:

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

第5章 繼承

偽類
JavaScript的原型存在諸多矛盾帐姻。它不直接讓對象從其他對象繼承稠集,反而插入了一個多余的間接層:通過構(gòu)造器函數(shù)產(chǎn)生對象。
Function構(gòu)造器產(chǎn)生的函數(shù)對象會運行類似這樣的一些代碼:

this.prototype =  {constructor:this}

新函數(shù)對象被賦予一個prototype屬性饥瓷,這個prototype對象是存放繼承特征的地方剥纷。

當采用構(gòu)造器調(diào)用模式,即用new前綴去調(diào)用一個函數(shù)時呢铆,函數(shù)執(zhí)行的方式會被修改晦鞋。如果new運算符是一個方法而不是一個運算符,它可能像這樣執(zhí)行:

Function.method('new',function(){
    // 創(chuàng)建一個新對象棺克,它繼承自構(gòu)造器函數(shù)的原型對象悠垛。
    var that = Object.create(this.prototype);
    // 調(diào)用構(gòu)造器函數(shù),綁定 -this- 到新對象上娜谊。
    var other = this.apply(that,arguments);
    // 如果它的返回值不是一個對象确买,就返回該新對象。
    return (typeof other === 'object' && other) || that;
});

所有構(gòu)造器函數(shù)都約定命名成大寫字母纱皆。一種更好的備選方案就是根本不使用new湾趾。
對象說明符
就是指傳多個參數(shù)時,可以直接傳遞一個對象派草。
原型
可以用Object.create方法構(gòu)造出更多實例來搀缠。
函數(shù)化
迄今為止,我們所看到的繼承模式的一個弱點就是沒法保護隱私近迁。對象的所有屬性都是可見的艺普。我們無法得到私有變量和私有函數(shù)。
幸運的是独泞,我們有一個更好的選擇府适,那就是應用模塊模式
我們從構(gòu)造一個生成對象的函數(shù)開始官份。我們以小寫字母開頭來命名缴罗。
該函數(shù)包括以下四個步驟
1助琐、創(chuàng)建一個新對象。
2面氓、有選擇地私有實例變量和方法兵钮。
3、給這個新對象擴充方法舌界。
4掘譬、返回那個新對象。
以下是一個函數(shù)化構(gòu)造器的偽代碼模板

var constructor = function (spec, my) {
    var that, 其他的私有實例變量呻拌;
    my = my || {};
    把共享的變量和函數(shù)添加到my中
    that = 一個新對象
    添加給 that 的特權(quán)方法
    return that;
}

函數(shù)化模式有很大的靈活性葱轩。它相比偽類模式不僅帶來的工作更少,還讓我們更好的封裝和信息隱藏藐握,以及訪問父類方法的能力靴拱。
部件
我們可以從一套部件中把對象組裝出來。

第6章 數(shù)組

數(shù)組是一段線性分配的內(nèi)存猾普,它通過整數(shù)計算偏移并訪問其中的元素袜炕。
數(shù)組是一種性能出色的數(shù)據(jù)結(jié)構(gòu)。不幸的是初家,JavaScript沒有像此類數(shù)組一樣的數(shù)據(jù)結(jié)構(gòu)偎窘。
數(shù)組字面量
對象字面量
數(shù)組繼承了Array.prototype大量有用的方法。而對象字面量是繼承自Object.prototype溜在。
數(shù)組有length屬性陌知,而對象沒有。
長度
每個數(shù)組都有一個length屬性掖肋。
可以直接設置length的值纵诞。設置更大的length不會給數(shù)組分配更多的空間,而設小導致所有下標大于等于新length的屬性被刪除培遵。

var arr = [1,2,3];
arr.length = 1;
console.log(arr) // [1]

也可以通過length來通過添加值

var arr = [1,2,3];
arr[arr.length] = 4;
console.log(arr) // [1,2,3,4]

有時用push方法更方便。
刪除
由于JavaScript的數(shù)組也是對象登刺,所以delete運算符可以用來從數(shù)組中移出元素籽腕。移除后,長度不變纸俭,原位置上變成了undefined皇耗。
可以使用Array.prototype.splice方法刪除數(shù)組。
枚舉
JS數(shù)組就是對象揍很,所以for in語句可以用來遍歷數(shù)據(jù)的所有屬性郎楼。
不過万伤,for in無法保證屬性順序。并且可能從原型鏈中得到意外的屬性呜袁。
for循環(huán)可以避免以上問題敌买。
容易混淆的地方

typeof [] === "object"; // true
typeof {} === "object"; // true

識別是否是數(shù)組。

// 方法一阶界、
var is_array = function (value) {
  return value && typeof value === 'object' && value.constructor === Array;
};

但它在識別從不同窗口(window)或幀(frame)里的構(gòu)造的數(shù)組時會失敗虹钮。
有一個更好的方式:

// 方法二、
var is_array = function (value) {
  return Object.prototype.toString.apply(value) === '[object Array]';
}

擴展
ES5 提供了Array.isArray()的方法膘融。不過兼容性是IE9+芙粱。
要做到兼容,可以用如下方法氧映。MDN上提供的春畔。MDN Array.isArray

// 方法三、
if (!Array.isArray){
  Array.isArray = function(arg){
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}
var arr = [];
// 方法四
arr instanceof Array;
// 方法五
Array.prototype.isPrototypeOf(arr);
// 方法六
Object.getPrototypeOf(arr) === Array.prototype;

方法四岛都、instanceof 運算符用來測試一個對象在其原型鏈中是否存在一個構(gòu)造函數(shù)的 prototype 屬性律姨。
方法五、isPrototypeOf() 方法用于測試一個對象是否存在于另一個對象的原型鏈上疗绣。
方法六线召、Object.getPrototypeOf() 方法返回指定對象的原型(即, 內(nèi)部[[Prototype]]屬性的值)。
小結(jié):除了方法二多矮、三外缓淹,面對復雜的環(huán)境,其他的都不能準確的判斷是否是數(shù)組塔逃。
方法
JavaScript提供了一套數(shù)組可用的方法讯壶,這些方法是被存儲在Array.prototype中的函數(shù)。
Object.prototype是可以擴充的湾盗。
Array.prototype也是可以擴充的伏蚊。
ES5中提供的Object.create方法。這方法用在數(shù)組是沒有意義的格粪,因為它產(chǎn)生的是一個對象躏吊,而不是一個數(shù)組,產(chǎn)生的對象將繼承這個數(shù)組的值和方法帐萎,但它沒有length特殊屬性比伏。
指定初始值
JavaScript的數(shù)組通常不會預設值。書中寫了一個循環(huán)來擴展疆导,生成初始值赁项。
擴展:ES6中提供了fill來填充。比如:

['a','b','c'].fill(0);   // [0,0,0]
new Array(3).fill(0);   // [0,0,0]

// fill方法還可以接受第二、第三個參數(shù)悠菜,用于指定填充的起始位置和結(jié)束位置(不包含)舰攒。
new Array(3).fill(0,1,2); // [ ,0, ,]  空位不是undefined』诖祝空位沒有任何值摩窃。ES6則是明確將空位轉(zhuǎn)為undefined。

第7章 正則表達式

正則表達式對字符串中的信息實現(xiàn)查找篙顺、替換和提取操作偶芍。
可處理正則表達式的方法有regexp.execregexp.test德玫、string.match匪蟀、string.searchstring.split。通常來說宰僧,正則相較于等效的字符串處理有著顯著的性能優(yōu)勢材彪。

一個例子

// 正則表達式必須寫在一行中
var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
var url = "http://www.ora.com:80/goodparts?q#fragment";
var result = parse_url.exec(url);
// ……

依次匹配到的是:

url: 'http://www.ora.com:80/goodparts?q#fragment',
scheme: 'http',
slash: '//'
host: 'www.ora.com'
port:'80'
path:'goodparts'
query: 'q'
hash: 'fragment'

個人擴展:這里推薦 在線測試正則表達式的網(wǎng)站regex101,默認是PHP語言琴儿,選擇JavaScript語言段化。
在線圖形化RegExp工具
MDN RegExp.prototype.exec()
大概解釋下這個正則,
這里的^ 起始位置造成,$結(jié)束位置
() 分組捕獲 ?:不捕獲
.表示除換行以外的任意單個字符显熏,對于碼點大于0xFFFFUnicode字符,點(.)不能識別(ES6中加u修飾符才可識別)晒屎,+表示一個或多個喘蟆,*表示零個或多個,?表示0個或一個鼓鲁。[]表示或者蕴轨,里面符合一個即可。
\d表示數(shù)字0-9骇吭。
不嚴謹?shù)恼齽t表達式是一個常見的安全漏洞的發(fā)源地橙弱。在執(zhí)行某些匹配時,嵌套的正則表達式也能導致極其惡劣的性能問題燥狰。因此簡單是最好的策略棘脐。

再看一個 匹配數(shù)字的例子。

var parse_number = /^-?\d+(?:\.\d*)?(?:e[+\-]?\d+)?$/i;
parse_number.test('1'); // true
parse_number.test('number'); // false
parse_number.test('98.6'); // true
parse_number.test('132.21.86.100'); // false
parse_number.test('123.45E-67'); // true
parse_number.test('123.45D-67'); // false

結(jié)構(gòu)
有兩個方法來創(chuàng)建一個RegExp對象龙致。優(yōu)先考慮的是正則表達式字面量荆残,還有一種方式是new RegExp('','g')
正則表達式標識:g全局(匹配多次净当,不同的方法對g標識的處理防范各不相同),i忽略大小寫。m多行
元素
正則表達式分支
|表示或像啼,也表示分支 比如:

'info'.match(/in|int/)  // 匹配成功俘闯,["in", index: 0, input: "info"]

正則表達式序列
一個正則表達式序列飽和一個或多個正則表達式因子。每一個因子能選擇是否跟隨一個量詞忽冻,這個量詞決定著這個因子被允許出現(xiàn)的次數(shù)真朗,若沒指定,這個因子則只匹配一次僧诚。
正則表達式因子

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

正則表達式轉(zhuǎn)義
\ 表轉(zhuǎn)義 \f 分頁 \n 換行 \r回車 \t 制表
\u 允許制定一個 Unicode 字符來表示一個十六進制的常量遮婶。
\d 等同于[0-9] \D 取反等同于 [^0-9]
\s Unicode 空白符一個不完全子集。 \S 與\s相反
\w [0-9A-Z_a-z] \W 與其相反 [^0-9A-Z_a-z]
\b 表示 字邊界
\1 表示 分組1所捕獲的文本的一個引用湖笨,所以它能被再次匹配旗扑。
\2 表示 指向分組2的引用,\3 是表示分組3的引用慈省,以此類推臀防。
正則表達式分組
捕獲型()
非捕獲型?:
向前正向匹配?=
有一個(?=前綴。它類似于非捕獲類型分組边败,但在這個組匹配后袱衷,文本會倒回到它它開始的地方,實際上并不匹配任何東西笑窜。也可以理解為匹配位置致燥。
向后負向匹配
有一個(?!前綴。它類似于向前正向匹配分組排截,但只有當它匹配失敗時它才繼續(xù)向前進行匹配嫌蚤。這不是一個好的特性。
正則表達式字符集
正則表達式字符集是一種指定一組字符的便利方式匾寝。例如搬葬,要匹配一個元音字母,(?:a|e|i|o|u),可以方便的寫成[aeiou]艳悔。
類提供另外兩個便利:①指定字符范圍
所以急凰,一組由32ASCII的特殊組合,可以寫成[!-\/:-@\[-{-~]`
②類的取反
取反

[^!-\/:-@\[-`{-~]

正則表達式字符轉(zhuǎn)義
字符類內(nèi)部的轉(zhuǎn)義規(guī)則和正則表達式因子的相比稍有不同猜年。下面是在字符類中需要被轉(zhuǎn)義的特殊字符抡锈。

- / [ \ ]

正則表達式量詞
量詞后綴決定正則表達式因子應該被匹配的次數(shù)。
{3}三次
{3,6} 3乔外、4床三、5、6次
{3,}3次或更多次
?等同于{0,1}杨幼,*等同于{0,}撇簿,+等同于{1,}聂渊。

第8章 方法

Array

array.concat(item...)
concat 方法產(chǎn)生一個新數(shù)組,它包含一份array的淺復制并把一個或多個參數(shù)item附加在其后四瘫。如果item是數(shù)組汉嗽,那么每個元素分別被添加。后面有和它功能類似的array.push(item...)方法找蜜。

var a = ['a','b','c'];
var b = ['x','y','z'];
var c = a.concat(b, true);
// c => ['a','b','c','x','y','z',true]

擴展: ES6 有更便捷的擴展運算符...

var a = ['a','b','c'];
var b = ['x','y','z'];
var c = [...a,true,...b];   // ["a", "b", "c", true, "x", "y", "z"]

array.join(separator)
join方法把一個array構(gòu)造成一個字符串饼暑。
separator 默認值就是逗號','
如果你想把大量的字符串片段組裝成一個字符串洗做,把這些片段放在一個數(shù)組中弓叛,并用join方法連接起來通常比用+元素運算符連接起來要快。

譯注:對于IE6/7诚纸,使用join連接大量字符串效率確實優(yōu)于加號運算符撰筷。但目前主流瀏覽器,包括IE8以后的版本咬清,都對+元素運算符連接字符串做了優(yōu)化闭专,性能已經(jīng)顯著高于Array.join()。所以目前大多數(shù)情況下旧烧,建議首選使用+ 連接字符串影钉。更多參看《高性能網(wǎng)站建設進階指南》中字符串優(yōu)化相關(guān)章節(jié)。

array.pop()
pop方法移除array中的最后一個元素掘剪,并返回這個元素平委。如果array為空,則返回undefined夺谁。

var a = ['a','b','c'];
var c = a.pop(); // a 是 ['a','b']  c是 'c'
// pop 可以像這樣實現(xiàn)廉赔。
// 這里的 Array.method()在第一章例子中已經(jīng)定義了,并且貫穿全書匾鸥。其實就是相當于Array.prototype
Array.method('pop', function () {
    return this.splice(this.length-1,1)[0];
});

array.push(item...)
concat不同的是蜡塌,它會修改array,如果參數(shù)item是數(shù)組勿负,它會把參數(shù)數(shù)組作為單個元素整個添加到數(shù)組中馏艾。并返回這個array的新長度值。

var a = [1,2,3];
var b = [4,5,6];
var c = a.push(b,true);
// a 是 [1,2,3,[4,5,6],true]
// c 是 5

push可以像這樣實現(xiàn):

Array.method('push', function () {
  this.splice.apply(
  this,
  [this.length,0].
  concat(Array.prototype.slice.apply(arguments)));
  return this.length;
});

array.reverse()
reverse反轉(zhuǎn)array元素順序奴愉,并返回array本身琅摩。

var a = [1,2,3];
var b = a.reverse();
// a 和 b都是 [3,2,1]

array.shift()
shift移除array的第一個元素并返回這個元素。如果array為空锭硼,則返回undefined房资。shift通常比pop慢的多。

var a = [1,2,3];
var c = a.shift(); // a 是[2,3] , c 是1

shift可以這樣實現(xiàn):

Array.method('shift', function(){
    return this.splice(0,1)[0];
});

array.slice(start[, end])
slice是對array中的一段做淺復制檀头。end是可選的轰异。默認是array.length,如果兩個參數(shù)任何一個是負數(shù)岖沛,array.length會和相加。如果start大于array.length,獲得一個[],字符串也有Sting.slice這個同名方法溉浙。
array.sort
默認不能給一組數(shù)字排序烫止。默認把要被排序的元素都視為字符串。
幸運的是戳稽,可以使用自己的比較函數(shù)替換默認的比較函數(shù)。
比較函數(shù)應該接受兩個參數(shù)期升,并且如果這兩個參數(shù)相等則返回0惊奇,如果第1個參數(shù)應該排列在前面,則返回一個負數(shù)播赁,如果第二個參數(shù)應該排列在前面颂郎,則返回一個正數(shù)。
sort方法是不穩(wěn)定的容为。JavaScriptsort方法的穩(wěn)定性根據(jù)不同瀏覽器的實現(xiàn)而不一致乓序。
可參見MDN sort
array.splice(start, deleteCount,item...)
splice方法從array中移除一個或多個元素,并用新的item替換它們坎背。

// splice 可以像這樣實現(xiàn)
Array.method('splice',function (start, deleteCount) {
    var max = Math.max,
        min = Math.min,
        delta,
        element,
        insertCount = max(arguments.length - 2, 0),
        k = 0,
        len = this.length,
        new_len,
        result = [],
        shift_count;
    start = start || 0;
    if (start < 0) {
        start += len;
    }
    start = max(min(start, len), 0);
    deleteCount = max(min(typeof deleteCount === 'number' ? deleteCount : len, len - start), 0);
    delta = insertCount - deleteCount;
    new_len = len + delta;
    while (k < deleteCount) {
        element = this[start + k];
        if (element !== undefined) {
            result[k] = element;
        }
        k += 1;
    }
    shift_count = len - start - deleteCount;
    if (delta < 0) {
        k = start + insertCount;
        while (shift_count) {
            this[k] = this[k - delta];
            k += 1;
            shift_count -= 1;
        }
        this.length = new_len;
    } else if (delta > 0) {
        k = 1;
        while (shift_count) {
            this[new_len - k] = this[len - k];
            k += 1;
            shift_count -= 1;
        }
        this.length = new_len;
    }
    for (k = 0; k < insertCount; k += 1) {
        this[start + k] = arguments[k + 2];
    }
    return result;
});

array.unshift(item...)
unshift 方法像push方法一樣替劈,不過是用于把元素添加到數(shù)組的開始部分,返回新arraylength得滤。

// unshift 可以像這樣實現(xiàn)
Array.method('unshift', function(){
    this.splice.apply(this,
    [0,0].concat(Array.prototype.slice.apply(arguments)));
    return this.length;
});

Function

function.apply(thisArg,argArray)
apply方法調(diào)用function,傳遞一個會被綁定到this上的對象和一個可選的數(shù)組作為參數(shù)陨献。

Number

number.toExponential(fractionDigits)
toExponential方法 把這個number轉(zhuǎn)換成一個指數(shù)形式的字符串《可選參數(shù)控制其小數(shù)點后的數(shù)字位數(shù)眨业。它的值必須在0~20

number.toFixed(fractionDigits)
toFixed方法把這個number轉(zhuǎn)換成一個十進制數(shù)形式的字符串沮协×浼瘢可選參數(shù)控制其小數(shù)點后的數(shù)字位數(shù)。它的值必須在0~20慷暂。

number.toPrecision(precision)
toPrecision方法把這個number轉(zhuǎn)換成一個十進制數(shù)形式的字符串聘殖。可選參數(shù)控制數(shù)字的精度呜呐。它的值必須在0~21就斤。

number.toString(radix)
number轉(zhuǎn)換成字符串∧⒓可選參數(shù)控制基數(shù)洋机。它的值必須是2~36。默認的radix是以10為基數(shù)的洋魂。radix參數(shù)最常用的是整數(shù)绷旗,但是它可以用任意的數(shù)字喜鼓。

Object

object.hasOwnProperty(name)
如果這個object包含名為name的屬性,那么返回true衔肢。原型鏈中的同名方法不會被檢測庄岖。這個方法對name就是“hasOwnProperty”時不起作用。

RegExp

regexp.exec(string)
exec是正則中最強大(和最慢)的方法角骤。
如果成功匹配隅忿,它會返回一個數(shù)組。下標為0 的元素包含正則匹配的子字符串邦尊。下標為1的則是分組1捕獲的文本背桐。下標為2的則是分組2捕獲的文本。以此類推蝉揍。如果匹配失敗則返回null链峭。
regexp.test(string)
test是最簡單(和最快)的方法。匹配成功又沾,返回true,否則返回false弊仪。不要對這個方法使用g標識。
比如:

var reg = /\w+/g;
reg.test('ab'); // true
// 再執(zhí)行一遍就是false了杖刷。
reg.test('ab'); // false
// 再執(zhí)行一遍就是true了励饵。
reg.test('ab'); // true
// 再執(zhí)行一遍又是false了,如此反復挺勿,所以用g標識后曲横,看起來很詭異。應該每次匹配開始位置變了不瓶。
reg.test('ab'); // false

test可以像這樣實現(xiàn):

RegExp.method('test', function(string){
    return this.exec(string) !== null;
});

String

string.charAt(pos)
返回在string中的pos位置處的字符禾嫉。

string.charCodeAt(pos)
charAt一樣,不過返回整數(shù)形式表示字符碼位蚊丐。

string.concat(string)
很少用熙参,用+號運算符更方便。

string.indexOf(searchString,position)
string中查找第一個參數(shù)麦备,如果被找到返回該字符的位置孽椰,否則返回-1position可設置指定位置開始查找凛篙。

string.lastIndexOf(searchString,position)
lastIndexOf 方法和indexOf方法類似黍匾,不過它是從末尾開始查找,不是從頭開始呛梆。

string.localeCompare(that)
比較兩個字符串锐涯。類似于array.sort

string.match(regexp)
如果沒有g標識填物,那么調(diào)用string.match(regexp)和調(diào)用regexp.exec(string)結(jié)果相同纹腌。如果帶有g標識霎终,那么它生成一個包含所有匹配(除捕獲分組之外)的數(shù)組。

string.replace(searchValue,replaceValue)
string進行查找和替換操作升薯,并返回一個新的字符串莱褒。參數(shù)searchvalue可以是一個字符串也可以是一個正則表達式對象。參數(shù)replaceValue可以是一個字符串或一個函數(shù)涎劈。

string.search(regexp)
indexOf類似广凸,不過它接收正則為參數(shù)。

string.slice(start, end)
slice方法復制string的一部分來構(gòu)造一個新的字符串蛛枚。如果start參數(shù)是負數(shù)炮障,它將與string.length相加。end參數(shù)是可選的坤候。

string.split(separator,limit)
string分割成片段來創(chuàng)建一個字符串數(shù)組∑蟛洌可選參數(shù)limit可以限制分割的片段數(shù)量白筹。separator參數(shù)可以是字符串或者正則。
string.substring(start,end)
slice方法一樣谅摄,不過它不能處理負數(shù)參數(shù)徒河。
string.toLocaleLowerCase()
它使用本地化的規(guī)則把這個string中的字母轉(zhuǎn)換成小寫格式。這個方法主要用在土耳其語上送漠。
string.toLocaleUpperCase()
它使用本地化的規(guī)則把這個string中的字母轉(zhuǎn)換成大寫格式顽照。這個方法主要用在土耳其語上。
string.toLowerCase()
返回新字符串闽寡,所有字母轉(zhuǎn)成小寫格式代兵。
string.toUpperCase()
返回新字符串,所有字母轉(zhuǎn)成大寫格式爷狈。
String.fromCharCode(char...)
根據(jù)一串數(shù)字編碼返回一個字符串植影。

var a = String.fromCharCode(67,97,116) // a是'Cat'

第9章 代碼風格

這一章中,簡短的說了一些代碼風格涎永。事實證明代碼風格在編程中是很重要的思币。

第10章 優(yōu)美的特性

精簡的JavaScript里都是好東西。
包括:1羡微、函數(shù)是頂級對象谷饿;2、基于原型繼承的動態(tài)作用域妈倔;3博投、對象字面量和數(shù)組字面量。

到此启涯,讀書筆記已完結(jié)贬堵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恃轩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子黎做,更是在濱河造成了極大的恐慌叉跛,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒸殿,死亡現(xiàn)場離奇詭異筷厘,居然都是意外死亡,警方通過查閱死者的電腦和手機宏所,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門酥艳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人爬骤,你說我怎么就攤上這事充石。” “怎么了霞玄?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵骤铃,是天一觀的道長。 經(jīng)常有香客問我坷剧,道長惰爬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任惫企,我火速辦了婚禮撕瞧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狞尔。我一直安慰自己丛版,他們只是感情好,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布沪么。 她就那樣靜靜地躺著硼婿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪禽车。 梳的紋絲不亂的頭發(fā)上寇漫,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音殉摔,去河邊找鬼州胳。 笑死,一個胖子當著我的面吹牛逸月,可吹牛的內(nèi)容都是我干的栓撞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瓤湘!你這毒婦竟也來了瓢颅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤弛说,失蹤者是張志新(化名)和其女友劉穎挽懦,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體木人,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡信柿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了醒第。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片渔嚷。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖稠曼,靈堂內(nèi)的尸體忽然破棺而出形病,到底是詐尸還是另有隱情,我是刑警寧澤霞幅,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布窒朋,位于F島的核電站,受9級特大地震影響蝗岖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜榔至,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一抵赢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唧取,春花似錦铅鲤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至淡诗,卻和暖如春骇塘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背韩容。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工款违, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人群凶。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓插爹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缎讼。 傳聞我的和親對象是個殘疾皇子矮燎,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,233評論 0 4
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)局义,斷路器气嫁,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 直接貼代碼当窗。
    fever105閱讀 340評論 1 1
  • 夏夜里黑色的天幕,好似一張巨大的網(wǎng)杉编,遮蓋住了浩渺神秘的蒼穹超全,也籠罩住了這漸已安靜下來的人世間。夜空中邓馒,星辰寥寥嘶朱,屈...
    小小坤_閱讀 261評論 0 0
  • 今天早上智華老師講了企業(yè)自動化運轉(zhuǎn)的法門 第一就是優(yōu)化內(nèi)部人員組織,讓優(yōu)秀的人更優(yōu)秀光酣,不主動的人干起來疏遏,混日子的人...
    十碗汁閱讀 104評論 0 0