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

以前看到這篇文章, 前端網老姚淺談:怎麼學JavaScript?,說到怎麼學習JavaScript,那就是 看書、分析源碼。
10本書讀2遍的好處,應該大於一本書讀20遍。
看書主動學習,看視頻是被動學習。
看書和分析源碼的時機。但已經工做一年半載時,正是提升的好時候,此時能夠去看書了。全面系統的梳理知識點,掃清本身的盲區。若是隻是靠項目經驗是不夠的,經過項目來學習,那>確定是必須的,工做自己就是一個學習的過程。
怎麼把一本書看完呢?很簡單,敲。文字加代碼都敲。
比較認同老姚的說法。去年畢業到如今,我也算是工做一年了,是時候看書查缺補漏了。

因而我就先把這本薄的經典書《JavaScript語言精粹 修訂版》豆瓣讀書本書簡介(總共10章,除去附錄,才100頁),讀完並記錄了一些筆記。基本算是摘抄書本的,本身聯想到了一些知識和資料也擴展了一下。整體寫下來近一萬字。讀書筆記還能夠分享給別人看。回顧時,書不在身邊還能夠看看本身的筆記。想一想這類經典書記一遍動手敲一遍也是很值得的。不過這讀書筆記中可能會有一些錯別字,閱讀時若是發現歡迎指正。html

第1章 精華

大多數語言都有精華和糟粕。JavaScript使人詭異的事情是,在對這門語言沒有的太多瞭解,甚至對編程都沒有太多瞭解的狀況下,你也能用它來完成工做。
看到這裏不由想起:前端

張鑫旭大牛在 《我對知乎前端相關問題的十問十答》
非計算機專業背景學習JS要點有這一條:
全部繼承和原型相關內容跳過,注意,是跳過,不要看!沒有這些JS同樣能夠活得很好,你的平常工做同樣玩得飛起,固然,你沒忍住看了相關知識也不要緊,由於你會發現本身看不懂的;

JavaScript的函數是(主要)基於詞法做用域的頂級對象。node

譯註: JavaScript中的函數是根據詞法來劃分做用域的,而不是動態劃分做用域的。具體內容參見《 JavaScript權威指南》中譯第5版相關章節「8.8.1 詞法做用域」。
JavaScript有很是強大的對象字面量表示法。這種表示法是JSON的靈感來源。
原型繼承是JavaScript中一個有爭議的特性。

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

一個簡單的例子:es6

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

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

第2章 語法

本章主要用鐵路圖(語法圖)表示語法。
主要有:空白、標識符、數字、字符串、語句、表達式、字面量、函數。
typeof 運算符產生的值有'number', 'string','boolean','undefined','function','object'。若是運算數是一個數組或者是null,那麼結果是'object',這實際上是不對的。正則表達式

第3章 對象

JavaScript簡單數據類型包括數字、字符串、布爾值,null值和undefined值。其餘全部值都是對象。
數組、字符串和布爾值「貌似」對象,由於它們擁有方法(包裝對象),但它們是不可變的。
對象是屬性的容器,其中每一個屬性都擁有名字和值。屬性名能夠是包括空字符串在內的全部字符串,屬性值能夠是除了undefined值以外的任何值。編程

JavaScript包含一種原型鏈的特性,容許對象繼承到另外一個對象的屬性。正確地使用它能減小對象初始化時的消耗的時間和內存。
檢索
.,[]兩種檢索方式,推薦點.表示法。
嘗試重undefined的成員屬性中取值將會致使TypeError異常,這時能夠經過&&來避免錯誤。
更新
若是屬性名已經存在對象裏。那麼屬性的值會被替換。若是以前沒有擁有那個屬性名,那麼該屬性將被擴充到對象中。
引用
對象經過引用來傳遞。它們永遠不會被複制。
原型
全部經過對象字面量建立的對象都連接到Object.prototype
建立新對象時,能夠選擇某個對象做爲它的原型。segmentfault

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

原型鏈接只有在檢索值的時候才被用到。若是嘗試去獲取對象的某個屬性值,但對象沒有此屬性名,那麼JavaScript會試着從原型對象中獲取屬性值。若是那個原型對象也沒有該屬性,那麼再從它的原型中尋找,依此類推,直到該過程最後達到終點Object.prototype。若是想要的屬性徹底不存在原型鏈中,那麼結果就是 undefined值。這個過程稱爲委託
原型關係是一種動態的關係。
反射
原型鏈上的全部屬性都會產生值。有兩種方案能夠處理掉對象上不須要的屬性。
①程序檢查時丟棄值爲函數的屬性。但有可能有些值確實是函數,因此該方法不可靠。
②使用hasOwnProperty方法,若是是對象擁有獨有的屬性,則返回true。該方法不會檢查原型鏈。
枚舉
for in能夠遍歷一個對象中全部的屬性名。但包含函數和一些不關心的原型中屬性。並且順序不肯定,能夠用 hasOwnProperty方法和typeof排除函數。
for 循環不會出現for in那些狀況。
刪除
delete運算符能夠用來刪除對象的屬性。
減小全局變量的污染
能夠把全局性的資源歸入一個名稱空間之下。這樣作能減小衝突。數組

第4章 函數

函數用於①代碼複用②信息隱藏③組合調用。通常來講,所謂編程,就是將一組需求分節成一組函數與數據結構的技能。
JavaScript的函數就是對象。
函數對象鏈接到Function.prototype(該原型對象自己鏈接到Object.prototype)。
每一個函數在建立時會附加兩個隱藏屬性,函數的上下文和實現函數行爲的代碼。
每一個函數對象在建立時也隨配有一個prototype屬性。它的值是一個擁有constructor屬性且值爲該函數的對象。
函數字面量
函數字面量包括4個部分。①保留字function②函數名,能夠省略,③一組參數④一組語句。
函數字面量能夠出如今任何容許表達式出現的地方。一個內部函數除了能夠訪問本身的參數和變量,同時也能夠自由訪問把它嵌套在其中的父函數的參數和變量。經過函數字面量建立的函數對象包含一個鏈接到外部上下文的鏈接。這被稱爲閉包
調用
除了聲明時定義的形式參數,每個函數還接收兩個附加的參數:thisargument。在JavaScript中一共有四種調用模式。①方法調用模式,②函數調用模式③構造器調用模式④apply調用模式。

this指向問題一直困擾不少人。我通常是這樣記的,誰調用this就指向誰。)

方法調用模式
對象的方法執行,this指向該對象。好比:

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

函數調用模式

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,

構造器調用模式
JavaScript是一門基於原型繼承的語言。
若是在一個函數前面帶上new 來調用。那麼背地利將會建立一個鏈接到該函數的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'

一個函數,若是建立的目的就是但願結合new 前綴來調用。那麼它就被稱爲構造器函數。按照約定,它們保存在以大寫函數命名的變量裏。若是調用構造器函數時沒有在前面加上new,可能會發生很是糟糕的事情,既沒有編譯時的警告,也沒有運行時廣告,因此大寫約定很是重要。
做者不推薦這種形式的構造器函數。有更好的替代方式。
Apply調用模式
JavaScript是一門函數式的面向對象編程語言,因此對象能夠擁有方法。
apply方法讓咱們構建一個參數數組傳遞給調用函數,它也容許咱們選擇this的值。
參數
arguments,雖然擁有length屬性,但不是真正的數組。而是相似數組(array-like)的對象。
返回
return 可用來是函數提早返回。當return 被執行時,函數當即返回而再也不執行餘下的語句。
一個函數總會返回一個值,若是沒指定,那就是返回undefined值。
若是函數調用時在前面加上了new 前綴,且返回值不是一個對象,則返回this(該新對象)。
異常
JavaScript提供了一套異常處理機制。
throw語句和try catch,try catchfinally是可選的。
擴展類型的功能
JavaScript容許給語言的基本類型擴充功能。在第3章中咱們已經看到,能夠經過Object.prototype添加方法,可讓該方法對全部對象均可用。這樣的方式對函數、數組、字符串、數字、正則表達式和布爾值一樣適用。

例如:

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

基本類型的原型是公用結構,因此在類庫混用時務必當心。一個保險的作法就是隻在確認沒有該方法時才添加它。

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

遞歸
遞歸函數就是會直接或間接地調用自身的一種函數。遞歸是一種強大的編程技術,遞歸是用通常的方式去解決每個子問題。書中舉了一個漢諾塔的例子,是程序設計中經典遞歸問題。詳細說明能夠參見 百度百科「漢諾塔」詞條
一些語言提供了尾遞歸優化。尾遞歸是一種在函數的最後執行調用語句的特殊形式的遞歸。參見Tail call。 ES6版本擴展了尾遞歸。參見阮一峯老師的《ES6標準入門》中的尾調用優化
做用域
在編程語言中,做用域控制着變量與參數的可見性和聲明週期。
書中指出當前JavaScript沒有塊級做用域。由於沒有塊級做用域,因此最好的作法是在函數體的頂部聲明函數中可能用到的全部變量。不過ES6擴展了有塊級做用域。
閉包
做用域的好處是內部函數能夠訪問定義它們的外部函數的參數和變量(除了thisarguments)。
例子:

<ul class="list">
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
// 點擊相應節點時,顯示對應的序號。可使用閉包來解決。
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類數組轉成數組等方案實現。
// 閉包特性:一、函數內再嵌套函數,二、內部函數能夠調用外層的參數和變量,三、參數和變量不會被垃圾回收機制回收。
// 閉包優勢 靈活和方便,便於封裝。缺點:空間浪費、內存泄露、性能消耗。

回調
發起異步請求,提供一個當服務器響應到達時隨即出發的回調函數。異步函數當即返回,這樣客戶端就不會被阻塞。
模塊
咱們可使用函數和閉包來構造模塊。模塊是一個提供接口卻隱藏狀態與實現的函數或對象。
舉例:給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;
        }
    };
}());

模塊模式利用了函數做用域和閉包來建立被綁定對象與私有成員的關聯,在上面例子中,只有deentityify方法有權訪問字符實體表這個數據對象。
模塊模式的通常形式是:一個定義了私有變量和函數的函數;利用閉包建立能夠訪問私有變量和函數的特權函數;最後返回這個特權函數,或者把它們保存到一個能夠訪問的地方。
使用模塊模式就能夠摒棄全局變量的使用。它促進了信息隱藏和其餘優秀的設計實踐。對於應用程序的封裝,或者構造其餘單例對象,模塊模式很是有效。

單例譯註
模塊模式一般結合單例模式使用。 JavaScript的單例就是用對象字面量表示法建立的對象,對象的屬性值能夠是數值或函數,而且屬性值在該對象的生命週期中不會發生變化。更多內容參見: 單例模式

級聯
有一些方法沒有返回值。若是咱們讓這些方法返回this而不是undefined,就能夠啓用級聯。
在一個級聯中,咱們能夠在單獨一條語句中依次調用同一個對象的不少方法。好比jQuery獲取元素、操做樣式、添加事件、添加動畫等。
柯里化
柯里化,是把多參數函數轉換爲一系列單參數函數並進行調用的技術。更多詳情可參見:柯里化
函數也是值。柯里化容許咱們把函數與傳遞給它的參數相結合,產生一個新的函數。

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

JavaScript並無curry方法,但能夠擴展該功能。
arguments不是真正的數組,因此使用了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)));
    }
});

記憶
函數能夠將先前操做的結果記錄在某個對象裏,從而避免無謂的重複運算。這種優化稱做記憶。
好比說,咱們想要一個遞歸函數來計算Fibonacci(斐波那契)數列,它的特色是,前面相鄰兩項之和等於後一項的值。更多參考:斐波那契。最前面兩個數字是0和1。

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

這樣雖然能完成工做,但它作了不少無謂的工做。
構造一個帶有記憶功能的函數:

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函數來定義fibonacci函數,提供其初始的memo數組和formula函數。

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

極大的減小了咱們的工做量。例如要產生一個記憶的階乘函數,只須要提供基本的階乘公式便可:

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

第5章 繼承

僞類
JavaScript的原型存在諸多矛盾。它不直接讓對象從其餘對象繼承,反而插入了一個多餘的間接層:經過構造器函數產生對象。
Function構造器產生的函數對象會運行相似這樣的一些代碼:

this.prototype =  {constructor:this}

新函數對象被賦予一個prototype屬性,這個prototype對象是存放繼承特徵的地方。

當採用構造器調用模式,即用new前綴去調用一個函數時,函數執行的方式會被修改。若是new 運算符是一個方法而不是一個運算符,它可能像這樣執行:

Function.method('new',function(){
    // 建立一個新對象,它繼承自構造器函數的原型對象。
    var that = Object.create(this.prototype);
    // 調用構造器函數,綁定 -this- 到新對象上。
    var other = this.apply(that,arguments);
    // 若是它的返回值不是一個對象,就返回該新對象。
    return (typeof other === 'object' && other) || that;
});

全部構造器函數都約定命名成大寫字母。一種更好的備選方案就是根本不使用new
對象說明符
就是指傳多個參數時,能夠直接傳遞一個對象。
原型
能夠用Object.create方法構造出更多實例來。
函數化
迄今爲止,咱們所看到的繼承模式的一個弱點就是無法保護隱私。對象的全部屬性都是可見的。咱們沒法獲得私有變量和私有函數。
幸運的是,咱們有一個更好的選擇,那就是應用模塊模式
咱們從構造一個生成對象的函數開始。咱們以小寫字母開頭來命名。
該函數包括如下四個步驟
一、建立一個新對象。
二、有選擇地私有實例變量和方法。
三、給這個新對象擴充方法。
四、返回那個新對象。
如下是一個函數化構造器的僞代碼模板

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

函數化模式有很大的靈活性。它相比僞類模式不只帶來的工做更少,還讓咱們更好的封裝和信息隱藏,以及訪問父類方法的能力。
部件
咱們能夠從一套部件中把對象組裝出來。

第6章 數組

數組是一段線性分配的內存,它經過整數計算偏移並訪問其中的元素。
數組是一種性能出色的數據結構。不幸的是,JavaScript沒有像此類數組同樣的數據結構。
數組字面量
對象字面量
數組繼承了Array.prototype大量有用的方法。而對象字面量是繼承自Object.prototype
數組有length屬性,而對象沒有。
長度
每一個數組都有一個length屬性。
能夠直接設置length的值。設置更大的length不會給數組分配更多的空間,而設小致使全部下標大於等於新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的數組也是對象,因此delete運算符能夠用來從數組中移出元素。移除後,長度不變,原位置上變成了undefined
可使用Array.prototype.splice方法刪除數組。
枚舉
JS數組就是對象,因此for in語句能夠用來遍歷數據的全部屬性。
不過for in沒法保證屬性順序。而且可能從原型鏈中獲得意外的屬性。
for循環能夠避免以上問題。
容易混淆的地方

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

識別是不是數組。

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

但它在識別從不一樣窗口(window)或幀(frame)裏的構造的數組時會失敗。
有一個更好的方式:

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

擴展
ES5 提供了Array.isArray()的方法。不過兼容性是IE9+。
要作到兼容,能夠用以下方法。MDN上提供的。MDN Array.isArray

// 方法3、
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;

方法4、instanceof 運算符用來測試一個對象在其原型鏈中是否存在一個構造函數的 prototype 屬性。
方法5、isPrototypeOf() 方法用於測試一個對象是否存在於另外一個對象的原型鏈上。
方法6、[Object.getPrototypeOf() 方法返回指定對象的原型(即, 內部[[Prototype]]屬性的值)。](https://developer.mozilla.org...
小結:除了方法2、三外,面對複雜的環境,其餘的都不能準確的判斷是不是數組。
方法
JavaScript提供了一套數組可用的方法,這些方法是被存儲在Array.prototype中的函數。
Object.prototype是能夠擴充的。
Array.prototype也是能夠擴充的。
ES5中提供的Object.create方法。這方法用在數組是沒有意義的,由於它產生的是一個對象,而不是一個數組,產生的對象將繼承這個數組的值和方法,但它沒有length特殊屬性。
指定初始值
JavaScript的數組一般不會預設值。書中寫了一個循環來擴展,生成初始值。
擴展:ES6中提供了fill來填充。好比:

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

// fill方法還能夠接受第2、第三個參數,用於指定填充的起始位置和結束位置(不包含)。
new Array(3).fill(0,1,2); // [ ,0, ,]  空位不是undefined。空位沒有任何值。ES6則是明確將空位轉爲undefined。

第7章 正則表達式

正則表達式對字符串中的信息實現查找、替換和提取操做。
可處理正則表達式的方法有regexp.execregexp.teststring.matchstring.searchstring.split。一般來講,正則相較於等效的字符串處理有着顯著的性能優點。

一個例子

// 正則表達式必須寫在一行中
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'

我的擴展:這裏推薦 在線測試正則表達式的網站regex101,默認是PHP語言,選擇JavaScript語言。
在線圖形化RegExp工具
MDN RegExp.prototype.exec()
大概解釋下這個正則,
這裏的^ 起始位置,$結束位置
() 分組捕獲 ?:不捕獲
.表示除換行之外的任意單個字符,對於碼點大於0xFFFFUnicode字符,點(.)不能識別(ES6中加u修飾符纔可識別),+表示一個或多個,*表示零個或多個,?表示0個或一個。[]表示或者,裏面符合一個便可。
\d表示數字0-9
不嚴謹的正則表達式是一個常見的安全漏洞的發源地。在執行某些匹配時,嵌套的正則表達式也能致使極其惡劣的性能問題。所以簡單是最好的策略。

再看一個 匹配數字的例子。

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

結構
有兩個方法來建立一個RegExp對象。優先考慮的是正則表達式字面量,還有一種方式是new RegExp('','g')
正則表達式標識:g全局(匹配屢次,不一樣的方法對g標識的處理防範各不相同),i忽略大小寫。m多行
元素
正則表達式分支
|表示或,也表示分支 好比:

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

正則表達式序列
一個正則表達式序列飽和一個或多個正則表達式因子。每個因子能選擇是否跟隨一個量詞,這個量詞決定着這個因子被容許出現的次數,若沒指定,這個因子則只匹配一次。
正則表達式因子

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

正則表達式轉義
\ 錶轉義 \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的引用,以此類推。
正則表達式分組
捕獲型 ()
非捕獲型?:
向前正向匹配?=
有一個(?=前綴。它相似於非捕獲類型分組,但在這個組匹配後,文本會倒回到它它開始的地方,實際上並不匹配任何東西。也能夠理解爲匹配位置。
向後負向匹配
有一個(?!前綴。它相似於向前正向匹配分組,但只有當它匹配失敗時它才繼續向前進行匹配。這不是一個好的特性。
正則表達式字符集
正則表達式字符集是一種指定一組字符的便利方式。例如,要匹配一個元音字母,(?:a|e|i|o|u),能夠方便的寫成[aeiou]
類提供另外兩個便利:①指定字符範圍
因此,一組由32ASCII的特殊組合,能夠寫成[!-\/:-@\[-{-~]`
②類的取反
取反

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

正則表達式字符轉義
字符類內部的轉義規則和正則表達式因子的相比稍有不一樣。下面是在字符類中須要被轉義的特殊字符。

- / [ \ ]

正則表達式量詞
量詞後綴決定正則表達式因子應該被匹配的次數。
{3}三次
{3,6} 三、四、五、6次
{3,}3次或更屢次
?等同於{0,1}*等同於{0,}+等同於{1,}

第8章 方法

Array

array.concat(item...)
concat 方法產生一個新數組,它包含一份array的淺複製並把一個或多個參數item附加在其後。若是item是數組,那麼每一個元素分別被添加。後面有和它功能相似的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構形成一個字符串。
separator 默認值就是逗號','
若是你想把大量的字符串片斷組裝成一個字符串,把這些片斷放在一個數組中,並用join方法鏈接起來一般比用+元素運算符鏈接起來要快。

譯註:對於 IE6/7,使用join鏈接大量字符串效率確實優於加號運算符。但目前主流瀏覽器,包括 IE8之後的版本,都對 +元素運算符鏈接字符串作了優化,性能已經顯著高於 Array.join()。因此目前大多數狀況下,建議首選使用+ 鏈接字符串。更多參看《高性能網站建設進階指南》中字符串優化相關章節。

array.pop()
pop方法移除array中的最後一個元素,並返回這個元素。若是array爲空,則返回undefined

var a = ['a','b','c'];
var c = a.pop(); // a 是 ['a','b']  c是 'c'
// pop 能夠像這樣實現。
// 這裏的 Array.method()在第一章例子中已經定義了,而且貫穿全書。其實就是至關於Array.prototype
Array.method('pop', function () {
    return this.splice(this.length-1,1)[0];
});

array.push(item...)
concat不一樣的是,它會修改array,若是參數item是數組,它會把參數數組做爲單個元素整個添加到數組中。並返回這個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能夠像這樣實現:

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

array.reverse()
reverse反轉array元素順序,並返回array自己。

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

array.shift()
shift移除array的第一個元素並返回這個元素。若是array爲空,則返回undefinedshift一般比pop慢的多。

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

shift能夠這樣實現:

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

array.slice(start[, end])
slice是對array中的一段作淺複製。end是可選的。默認是array.length,若是兩個參數任何一個是負數,array.length會和相加。若是start大於array.length,得到一個[],字符串也有Sting.slice這個同名方法。
array.sort
默認不能給一組數字排序。默認把要被排序的元素都視爲字符串。
幸運的是,可使用本身的比較函數替換默認的比較函數。
比較函數應該接受兩個參數,而且若是這兩個參數相等則返回0,若是第1個參數應該排列在前面,則返回一個負數,若是第二個參數應該排列在前面,則返回一個正數。
sort方法是不穩定的。JavaScriptsort方法的穩定性根據不一樣瀏覽器的實現而不一致。
可參見MDN sort
array.splice(start, deleteCount,item...)
splice方法從array中移除一個或多個元素,並用新的item替換它們。

// splice 能夠像這樣實現
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方法同樣,不過是用於把元素添加到數組的開始部分,返回新arraylength

// unshift 能夠像這樣實現
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方法調用function,傳遞一個會被綁定到this上的對象和一個可選的數組做爲參數。

Number

number.toExponential(fractionDigits)
toExponential方法 把這個number轉換成一個指數形式的字符串。可選參數控制其小數點後的數字位數。它的值必須在0~20

number.toFixed(fractionDigits)
toFixed方法把這個number轉換成一個十進制數形式的字符串。可選參數控制其小數點後的數字位數。它的值必須在0~20。

number.toPrecision(precision)
toPrecision方法把這個number轉換成一個十進制數形式的字符串。可選參數控制數字的精度。它的值必須在0~21

number.toString(radix)
number轉換成字符串。可選參數控制基數。它的值必須是2~36。默認的radix是以10爲基數的。radix參數最經常使用的是整數,可是它能夠用任意的數字。

Object

object.hasOwnProperty(name)
若是這個object包含名爲name的屬性,那麼返回true。原型鏈中的同名方法不會被檢測。這個方法對name就是「hasOwnProperty」時不起做用。

RegExp

regexp.exec(string)
exec是正則中最強大(和最慢)的方法。
若是成功匹配,它會返回一個數組。下標爲0 的元素包含正則匹配的子字符串。下標爲1的則是分組1捕獲的文本。下標爲2的則是分組2捕獲的文本。以此類推。若是匹配失敗則返回null
regexp.test(string)
test是最簡單(和最快)的方法。匹配成功,返回true,不然返回false。不要對這個方法使用g標識。
好比:

var reg = /\w+/g;
reg.test('ab'); // true
// 再執行一遍就是false了。
reg.test('ab'); // false
// 再執行一遍就是true了。
reg.test('ab'); // true
// 再執行一遍又是false了,如此反覆,因此用g標識後,看起來很詭異。由於每次匹配開始位置變了。
reg.test('ab'); // false

test能夠像這樣實現:

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

String

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

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

string.concat(string)
不多用,用+號運算符更方便。

string.indexOf(searchString,position)
string中查找第一個參數,若是被找到返回該字符的位置,不然返回-1position可設置指定位置開始查找。

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

string.localeCompare(that)
比較兩個字符串。相似於array.sort

string.match(regexp)
若是沒有g標識,那麼調用string.match(regexp)和調用regexp.exec(string)結果相同。若是帶有g標識,那麼它生成一個包含全部匹配(除捕獲分組以外)的數組。

string.replace(searchValue,replaceValue)
string進行查找和替換操做,並返回一個新的字符串。參數searchvalue能夠是一個字符串也能夠是一個正則表達式對象。參數replaceValue能夠是一個字符串或一個函數。

string.search(regexp)
indexOf相似,不過它接收正則爲參數。

string.slice(start, end)
slice方法複製string的一部分來構造一個新的字符串。若是start參數是負數,它將與string.length相加。end參數是可選的。

string.split(separator,limit)
string分割成片斷來建立一個字符串數組。可選參數limit能夠限制分割的片斷數量。separator參數能夠是字符串或者正則。
string.substring(start,end)
slice方法同樣,不過它不能處理負數參數。
string.toLocaleLowerCase()
它使用本地化的規則把這個string中的字母轉換成小寫格式。這個方法主要用在土耳其語上。
string.toLocaleUpperCase()
它使用本地化的規則把這個string中的字母轉換成大寫格式。這個方法主要用在土耳其語上。
string.toLowerCase()
返回新字符串,全部字母轉成小寫格式。
string.toUpperCase()
返回新字符串,全部字母轉成大寫格式。
String.fromCharCode(char...)
根據一串數字編碼返回一個字符串。

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

第9章 代碼風格

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

第10章 優美的特性

精簡的JavaScript裏都是好東西。
包括:一、函數是頂級對象;二、基於原型繼承的動態做用域;三、對象字面量和數組字面量。

到此,讀書筆記已完結。文章有什麼不妥之處,歡迎指出~

關於

做者:常以軒轅Rowboat若川爲名混跡於江湖。前端路上 | PPT愛好者 | 所知甚少,惟善學。
我的博客
segmentfault前端視野專欄,開通了前端視野專欄,歡迎關注
掘金專欄,歡迎關注
知乎前端視野專欄,開通了前端視野專欄,歡迎關注
github,歡迎follow~

相關文章
相關標籤/搜索