摘要:于是我就先把這本薄的經(jīng)典書語言精粹修訂版豆瓣讀書本書簡介總共章,除去附錄,才頁,讀完并記錄了一些筆記。讀書筆記還可以分享給別人看。編程語言第版定義了的標(biāo)準(zhǔn)。程序檢查時丟棄值為函數(shù)的屬性。
之前看到這篇文章,前端網(wǎng)老姚淺談:怎么學(xué)JavaScript?,說到怎么學(xué)習(xí)JavaScript,那就是看書、分析源碼。
10本書讀2遍的好處,應(yīng)該大于一本書讀20遍。
看書主動學(xué)習(xí),看視頻是被動學(xué)習(xí)。
看書和分析源碼的時機(jī)。但已經(jīng)工作一年半載時,正是提高的好時候,此時可以去看書了。全面系統(tǒng)的梳理知識點(diǎn),掃清自己的盲區(qū)。如果只是靠項(xiàng)目經(jīng)驗(yàn)是不夠的,通過項(xiàng)目來學(xué)習(xí),那>肯定是必須的,工作本身就是一個學(xué)習(xí)的過程。
怎么把一本書看完呢?很簡單,敲。文字加代碼都敲。
比較認(rèn)同老姚的說法。去年畢業(yè)到現(xiàn)在,我也算是工作一年了,是時候看書查缺補(bǔ)漏了。
于是我就先把這本薄的經(jīng)典書《JavaScript語言精粹 修訂版》豆瓣讀書本書簡介(總共10章,除去附錄,才100頁),讀完并記錄了一些筆記。基本算是摘抄書本的,自己聯(lián)想到了一些知識和資料也擴(kuò)展了一下??傮w寫下來近一萬字。讀書筆記還可以分享給別人看?;仡檿r,書不在身邊還可以看看自己的筆記。想想這類經(jīng)典書記一遍動手敲一遍也是很值得的。不過這讀書筆記中可能會有一些錯別字,閱讀時如果發(fā)現(xiàn)歡迎指正。
第1章 精華大多數(shù)語言都有精華和糟粕。JavaScript令人詭異的事情是,在對這門語言沒有的太多了解,甚至對編程都沒有太多了解的情況下,你也能用它來完成工作。
看到這里不禁想起:
張鑫旭大牛在《我對知乎前端相關(guān)問題的十問十答》
非計(jì)算機(jī)專業(yè)背景學(xué)習(xí)JS要點(diǎn)有這一條:
所有繼承和原型相關(guān)內(nèi)容跳過,注意,是跳過,不要看!沒有這些JS一樣可以活得很好,你的日常工作一樣玩得飛起,當(dāng)然,你沒忍住看了相關(guān)知識也沒關(guān)系,因?yàn)槟銜l(fā)現(xiàn)自己看不懂的;
JavaScript的函數(shù)是(主要)基于詞法作用域的頂級對象。
譯注:JavaScript中的函數(shù)是根據(jù)詞法來劃分作用域的,而不是動態(tài)劃分作用域的。具體內(nèi)容參見《JavaScript權(quán)威指南》中譯第5版相關(guān)章節(jié)“8.8.1 詞法作用域”。
JavaScript有非常強(qiáng)大的對象字面量表示法。這種表示法是JSON的靈感來源。
原型繼承是JavaScript中一個有爭議的特性。
《ECMAScript編程語言》第3版定義了JavaScript的標(biāo)準(zhǔn)。
ES3標(biāo)準(zhǔn)
擴(kuò)展:顏海鏡大牛整理的ES3中文版
顏海鏡大牛整理的ES5中文版
W3c ES5中文版
阮一峰大牛的書籍《ES6標(biāo)準(zhǔn)入門2》
更多內(nèi)容可參見這篇文章:ECMAScript 2018 標(biāo)準(zhǔn)導(dǎo)讀
一個簡單的例子:
Function.prototype.method = function(name, func) { this.prototype[name] = func; return this; }
書中貫徹始終都會用到這個method方案,作者將會在第4章解釋它。
第2章 語法本章主要用鐵路圖(語法圖)表示語法。
主要有:空白、標(biāo)識符、數(shù)字、字符串、語句、表達(dá)式、字面量、函數(shù)。
typeof 運(yùn)算符產(chǎn)生的值有"number", "string","boolean","undefined","function","object"。如果運(yùn)算數(shù)是一個數(shù)組或者是null,那么結(jié)果是"object",這其實(shí)是不對的。
JavaScript簡單數(shù)據(jù)類型包括數(shù)字、字符串、布爾值,null值和undefined值。其他所有值都是對象。
數(shù)組、字符串和布爾值“貌似”對象,因?yàn)樗鼈儞碛蟹椒ǎòb對象),但它們是不可變的。
對象是屬性的容器,其中每個屬性都擁有名字和值。屬性名可以是包括空字符串在內(nèi)的所有字符串,屬性值可以是除了undefined值之外的任何值。
JavaScript包含一種原型鏈的特性,允許對象繼承到另一個對象的屬性。正確地使用它能減少對象初始化時的消耗的時間和內(nèi)存。
檢索
.,[]兩種檢索方式,推薦點(diǎn).表示法。
嘗試重undefined的成員屬性中取值將會導(dǎo)致TypeError異常,這時可以通過&&來避免錯誤。
更新
如果屬性名已經(jīng)存在對象里。那么屬性的值會被替換。如果之前沒有擁有那個屬性名,那么該屬性將被擴(kuò)充到對象中。
引用
對象通過引用來傳遞。它們永遠(yuǎn)不會被復(fù)制。
原型
所有通過對象字面量創(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會試著從原型對象中獲取屬性值。如果那個原型對象也沒有該屬性,那么再從它的原型中尋找,依此類推,直到該過程最后達(dá)到終點(diǎn)Object.prototype。如果想要的屬性完全不存在原型鏈中,那么結(jié)果就是 undefined值。這個過程稱為委托。
原型關(guān)系是一種動態(tài)的關(guān)系。
反射
原型鏈上的所有屬性都會產(chǎn)生值。有兩種方案可以處理掉對象上不需要的屬性。
①程序檢查時丟棄值為函數(shù)的屬性。但有可能有些值確實(shí)是函數(shù),所以該方法不可靠。
②使用hasOwnProperty方法,如果是對象擁有獨(dú)有的屬性,則返回true。該方法不會檢查原型鏈。
枚舉
① for in可以遍歷一個對象中所有的屬性名。但包含函數(shù)和一些不關(guān)心的原型中屬性。而且順序不確定,可以用 hasOwnProperty方法和typeof排除函數(shù)。
②for 循環(huán)不會出現(xiàn)for in那些情況。
刪除
delete運(yùn)算符可以用來刪除對象的屬性。
減少全局變量的污染
可以把全局性的資源納入一個名稱空間之下。這樣做能減少沖突。
函數(shù)用于①代碼復(fù)用②信息隱藏③組合調(diào)用。一般來說,所謂編程,就是將一組需求分節(jié)成一組函數(shù)與數(shù)據(jù)結(jié)構(gòu)的技能。
JavaScript的函數(shù)就是對象。
函數(shù)對象連接到Function.prototype(該原型對象本身連接到Object.prototype)。
每個函數(shù)在創(chuàng)建時會附加兩個隱藏屬性,函數(shù)的上下文和實(shí)現(xiàn)函數(shù)行為的代碼。
每個函數(shù)對象在創(chuàng)建時也隨配有一個prototype屬性。它的值是一個擁有constructor屬性且值為該函數(shù)的對象。
函數(shù)字面量
函數(shù)字面量包括4個部分。①保留字function②函數(shù)名,可以省略,③一組參數(shù)④一組語句。
函數(shù)字面量可以出現(xiàn)在任何允許表達(dá)式出現(xiàn)的地方。一個內(nèi)部函數(shù)除了可以訪問自己的參數(shù)和變量,同時也可以自由訪問把它嵌套在其中的父函數(shù)的參數(shù)和變量。通過函數(shù)字面量創(chuàng)建的函數(shù)對象包含一個連接到外部上下文的連接。這被稱為閉包。
調(diào)用
除了聲明時定義的形式參數(shù),每一個函數(shù)還接收兩個附加的參數(shù):this和argument。在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ā)生非常糟糕的事情,既沒有編譯時的警告,也沒有運(yùn)行時廣告,所以大寫約定非常重要。
作者不推薦這種形式的構(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ù)提前返回。當(dāng)return 被執(zhí)行時,函數(shù)立即返回而不再執(zhí)行余下的語句。
一個函數(shù)總會返回一個值,如果沒指定,那就是返回undefined值。
如果函數(shù)調(diào)用時在前面加上了new 前綴,且返回值不是一個對象,則返回this(該新對象)。
異常
JavaScript提供了一套異常處理機(jī)制。
throw語句和try catch,try catch中finally是可選的。
擴(kuò)展類型的功能
JavaScript允許給語言的基本類型擴(kuò)充功能。在第3章中我們已經(jīng)看到,可以通過Object.prototype添加方法,可以讓該方法對所有對象都可用。這樣的方式對函數(shù)、數(shù)組、字符串、數(shù)字、正則表達(dá)式和布爾值同樣適用。
例如:
Function.prototype.method = function () { this.prototype[name] = func; return this; }
基本類型的原型是公用結(jié)構(gòu),所以在類庫混用時務(wù)必小心。一個保險的做法就是只在確認(rèn)沒有該方法時才添加它。
Function.prototype.methods = function(name, func) { if (!this.prototype[name]) { this.prototype[name] = func; } return this; }
遞歸
遞歸函數(shù)就是會直接或間接地調(diào)用自身的一種函數(shù)。遞歸是一種強(qiáng)大的編程技術(shù),遞歸是用一般的方式去解決每一個子問題。書中舉了一個漢諾塔的例子,是程序設(shè)計(jì)中經(jīng)典遞歸問題。詳細(xì)說明可以參見 百度百科“漢諾塔”詞條。
一些語言提供了尾遞歸優(yōu)化。尾遞歸是一種在函數(shù)的最后執(zhí)行調(diào)用語句的特殊形式的遞歸。參見Tail call。 ES6版本擴(kuò)展了尾遞歸。參見阮一峰老師的《ES6標(biāo)準(zhǔn)入門》中的尾調(diào)用優(yōu)化
作用域
在編程語言中,作用域控制著變量與參數(shù)的可見性和聲明周期。
書中指出當(dāng)前JavaScript沒有塊級作用域。因?yàn)闆]有塊級作用域,所以最好的做法是在函數(shù)體的頂部聲明函數(shù)中可能用到的所有變量。不過ES6擴(kuò)展了有塊級作用域。
閉包
作用域的好處是內(nèi)部函數(shù)可以訪問定義它們的外部函數(shù)的參數(shù)和變量(除了this和arguments)。
例子:
// 點(diǎn)擊相應(yīng)節(jié)點(diǎn)時,顯示對應(yīng)的序號??梢允褂瞄]包來解決。 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); } } // 擴(kuò)展 另外可以用let i = 0,或者把nodes類數(shù)組轉(zhuǎn)成數(shù)組等方案實(shí)現(xiàn)。 // 閉包特性:1、函數(shù)內(nèi)再嵌套函數(shù),2、內(nèi)部函數(shù)可以調(diào)用外層的參數(shù)和變量,3、參數(shù)和變量不會被垃圾回收機(jī)制回收。 // 閉包優(yōu)點(diǎn) 靈活和方便,便于封裝。缺點(diǎn):空間浪費(fèi)、內(nèi)存泄露、性能消耗。
回調(diào)
發(fā)起異步請求,提供一個當(dāng)服務(wù)器響應(yīng)到達(dá)時隨即出發(fā)的回調(diào)函數(shù)。異步函數(shù)立即返回,這樣客戶端就不會被阻塞。
模塊
我們可以使用函數(shù)和閉包來構(gòu)造模塊。模塊是一個提供接口卻隱藏狀態(tài)與實(shí)現(xiàn)的函數(shù)或?qū)ο蟆?br>舉例:給String添加一個deentityify方法。它的任務(wù)是尋找字符串中的HTML字符實(shí)體并把它們替換成對應(yīng)的字符。
String.method("deentityify", function () { // 字符實(shí)體表。它映射字符實(shí)體的名字到對應(yīng)的字符。 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í)體表這個數(shù)據(jù)對象。
模塊模式的一般形式是:一個定義了私有變量和函數(shù)的函數(shù);利用閉包創(chuàng)建可以訪問私有變量和函數(shù)的特權(quán)函數(shù);最后返回這個特權(quán)函數(shù),或者把它們保存到一個可以訪問的地方。
使用模塊模式就可以摒棄全局變量的使用。它促進(jìn)了信息隱藏和其他優(yōu)秀的設(shè)計(jì)實(shí)踐。對于應(yīng)用程序的封裝,或者構(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ù)并進(jìn)行調(diào)用的技術(shù)。更多詳情可參見:柯里化
函數(shù)也是值??吕锘试S我們把函數(shù)與傳遞給它的參數(shù)相結(jié)合,產(chǎn)生一個新的函數(shù)。
var add1 = add.curry(1); document.writeln(add1(6));
JavaScript并沒有curry方法,但可以擴(kuò)展該功能。
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é)果記錄在某個對象里,從而避免無謂的重復(fù)運(yùn)算。這種優(yōu)化稱作記憶。
比如說,我們想要一個遞歸函數(shù)來計(jì)算Fibonacci(斐波那契)數(shù)列,它的特點(diǎn)是,前面相鄰兩項(xiàng)之和等于后一項(xiàng)的值。更多參考:斐波那契。最前面兩個數(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ù)對象會運(yùn)行類似這樣的一些代碼:
this.prototype = {constructor:this}
新函數(shù)對象被賦予一個prototype屬性,這個prototype對象是存放繼承特征的地方。
當(dāng)采用構(gòu)造器調(diào)用模式,即用new前綴去調(diào)用一個函數(shù)時,函數(shù)執(zhí)行的方式會被修改。如果new 運(yùn)算符是一個方法而不是一個運(yùn)算符,它可能像這樣執(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ù)化
迄今為止,我們所看到的繼承模式的一個弱點(diǎn)就是沒法保護(hù)隱私。對象的所有屬性都是可見的。我們無法得到私有變量和私有函數(shù)。
幸運(yùn)的是,我們有一個更好的選擇,那就是應(yīng)用模塊模式。
我們從構(gòu)造一個生成對象的函數(shù)開始。我們以小寫字母開頭來命名。
該函數(shù)包括以下四個步驟
1、創(chuàng)建一個新對象。
2、有選擇地私有實(shí)例變量和方法。
3、給這個新對象擴(kuò)充方法。
4、返回那個新對象。
以下是一個函數(shù)化構(gòu)造器的偽代碼模板
var constructor = function (spec, my) { var that, 其他的私有實(shí)例變量; my = my || {}; 把共享的變量和函數(shù)添加到my中 that = 一個新對象 添加給 that 的特權(quán)方法 return that; }
函數(shù)化模式有很大的靈活性。它相比偽類模式不僅帶來的工作更少,還讓我們更好的封裝和信息隱藏,以及訪問父類方法的能力。
部件
我們可以從一套部件中把對象組裝出來。
數(shù)組是一段線性分配的內(nèi)存,它通過整數(shù)計(jì)算偏移并訪問其中的元素。
數(shù)組是一種性能出色的數(shù)據(jù)結(jié)構(gòu)。不幸的是,JavaScript沒有像此類數(shù)組一樣的數(shù)據(jù)結(jié)構(gòu)。
數(shù)組字面量
對象字面量
數(shù)組繼承了Array.prototype大量有用的方法。而對象字面量是繼承自Object.prototype。
數(shù)組有length屬性,而對象沒有。
長度
每個數(shù)組都有一個length屬性。
可以直接設(shè)置length的值。設(shè)置更大的length不會給數(shù)組分配更多的空間,而設(shè)小導(dǎo)致所有下標(biāo)大于等于新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運(yùn)算符可以用來從數(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]"; }
擴(kuò)展:
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 運(yùn)算符用來測試一個對象在其原型鏈中是否存在一個構(gòu)造函數(shù)的 prototype 屬性。
方法五、isPrototypeOf() 方法用于測試一個對象是否存在于另一個對象的原型鏈上。
方法六、[Object.getPrototypeOf() 方法返回指定對象的原型(即, 內(nèi)部[[Prototype]]屬性的值)。](https://developer.mozilla.org...
小結(jié):除了方法二、三外,面對復(fù)雜的環(huán)境,其他的都不能準(zhǔn)確的判斷是否是數(shù)組。
方法
JavaScript提供了一套數(shù)組可用的方法,這些方法是被存儲在Array.prototype中的函數(shù)。
Object.prototype是可以擴(kuò)充的。
Array.prototype也是可以擴(kuò)充的。
ES5中提供的Object.create方法。這方法用在數(shù)組是沒有意義的,因?yàn)樗a(chǎn)生的是一個對象,而不是一個數(shù)組,產(chǎn)生的對象將繼承這個數(shù)組的值和方法,但它沒有length特殊屬性。
指定初始值
JavaScript的數(shù)組通常不會預(yù)設(shè)值。書中寫了一個循環(huán)來擴(kuò)展,生成初始值。
擴(kuò)展: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章 正則表達(dá)式
正則表達(dá)式對字符串中的信息實(shí)現(xiàn)查找、替換和提取操作。
可處理正則表達(dá)式的方法有regexp.exec、regexp.test、string.match、string.search和string.split。通常來說,正則相較于等效的字符串處理有著顯著的性能優(yōu)勢。
一個例子
// 正則表達(dá)式必須寫在一行中 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: "http://" host: "www.ora.com" port:"80" path:"goodparts" query: "q" hash: "fragment"
個人擴(kuò)展:這里推薦 在線測試正則表達(dá)式的網(wǎng)站regex101,默認(rèn)是PHP語言,選擇JavaScript語言。
在線圖形化RegExp工具
MDN RegExp.prototype.exec()
大概解釋下這個正則,
這里的^ 起始位置,$結(jié)束位置
() 分組捕獲 ?:不捕獲
.表示除換行以外的任意單個字符,對于碼點(diǎn)大于0xFFFF的Unicode字符,點(diǎn)(.)不能識別(ES6中加u修飾符才可識別),+表示一個或多個,*表示零個或多個,?表示0個或一個。[]表示或者,里面符合一個即可。
d表示數(shù)字0-9。
不嚴(yán)謹(jǐn)?shù)恼齽t表達(dá)式是一個常見的安全漏洞的發(fā)源地。在執(zhí)行某些匹配時,嵌套的正則表達(dá)式也能導(dǎo)致極其惡劣的性能問題。因此簡單是最好的策略。
再看一個 匹配數(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)先考慮的是正則表達(dá)式字面量,還有一種方式是new RegExp("","g")。
正則表達(dá)式標(biāo)識:g全局(匹配多次,不同的方法對g標(biāo)識的處理防范各不相同),i忽略大小寫。m多行
元素
正則表達(dá)式分支
|表示或,也表示分支 比如:
"info".match(/in|int/) // 匹配成功,["in", index: 0, input: "info"]
正則表達(dá)式序列
一個正則表達(dá)式序列飽和一個或多個正則表達(dá)式因子。每一個因子能選擇是否跟隨一個量詞,這個量詞決定著這個因子被允許出現(xiàn)的次數(shù),若沒指定,這個因子則只匹配一次。
正則表達(dá)式因子
/ [ ] () { } ? + * | . ^ $
正則表達(dá)式轉(zhuǎn)義
表轉(zhuǎn)義 f 分頁
換行
回車 制表
u 允許制定一個 Unicode 字符來表示一個十六進(jìn)制的常量。
d 等同于[0-9] D 取反等同于 [^0-9]
s Unicode 空白符一個不完全子集。 S 與s相反
w [0-9A-Z_a-z] W 與其相反 [^0-9A-Z_a-z]
表示 字邊界
1 表示 分組1所捕獲的文本的一個引用,所以它能被再次匹配。
2 表示 指向分組2的引用,3 是表示分組3的引用,以此類推。
正則表達(dá)式分組
捕獲型 ()
非捕獲型?:
向前正向匹配?=
有一個(?=前綴。它類似于非捕獲類型分組,但在這個組匹配后,文本會倒回到它它開始的地方,實(shí)際上并不匹配任何東西。也可以理解為匹配位置。
向后負(fù)向匹配
有一個(?!前綴。它類似于向前正向匹配分組,但只有當(dāng)它匹配失敗時它才繼續(xù)向前進(jìn)行匹配。這不是一個好的特性。
正則表達(dá)式字符集
正則表達(dá)式字符集是一種指定一組字符的便利方式。例如,要匹配一個元音字母,(?:a|e|i|o|u),可以方便的寫成[aeiou]。
類提供另外兩個便利:①指定字符范圍
所以,一組由32個ASCII的特殊組合,可以寫成[!-/:-@[-{-~]`
②類的取反
取反
[^!-/:-@[-`{-~]
正則表達(dá)式字符轉(zhuǎn)義
字符類內(nèi)部的轉(zhuǎn)義規(guī)則和正則表達(dá)式因子的相比稍有不同。下面是在字符類中需要被轉(zhuǎn)義的特殊字符。
- / [ ]
正則表達(dá)式量詞
量詞后綴決定正則表達(dá)式因子應(yīng)該被匹配的次數(shù)。
{3}三次
{3,6} 3、4、5、6次
{3,}3次或更多次
?等同于{0,1},*等同于{0,},+等同于{1,}。
array.concat(item...)
concat 方法產(chǎn)生一個新數(shù)組,它包含一份array的淺復(fù)制并把一個或多個參數(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]
擴(kuò)展: ES6 有更便捷的擴(kuò)展運(yùn)算符...
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 默認(rèn)值就是逗號","。
如果你想把大量的字符串片段組裝成一個字符串,把這些片段放在一個數(shù)組中,并用join方法連接起來通常比用+元素運(yùn)算符連接起來要快。
譯注:對于IE6/7,使用join連接大量字符串效率確實(shí)優(yōu)于加號運(yùn)算符。但目前主流瀏覽器,包括IE8以后的版本,都對+元素運(yùn)算符連接字符串做了優(yōu)化,性能已經(jīng)顯著高于Array.join()。所以目前大多數(shù)情況下,建議首選使用+ 連接字符串。更多參看《高性能網(wǎng)站建設(shè)進(jìn)階指南》中字符串優(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 可以像這樣實(shí)現(xiàn)。 // 這里的 Array.method()在第一章例子中已經(jīng)定義了,并且貫穿全書。其實(shí)就是相當(dā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可以像這樣實(shí)現(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可以這樣實(shí)現(xiàn):
Array.method("shift", function(){ return this.splice(0,1)[0]; });
array.slice(start[, end])
slice是對array中的一段做淺復(fù)制。end是可選的。默認(rèn)是array.length,如果兩個參數(shù)任何一個是負(fù)數(shù),array.length會和相加。如果start大于array.length,獲得一個[],字符串也有Sting.slice這個同名方法。
array.sort
默認(rèn)不能給一組數(shù)字排序。默認(rèn)把要被排序的元素都視為字符串。
幸運(yùn)的是,可以使用自己的比較函數(shù)替換默認(rèn)的比較函數(shù)。
比較函數(shù)應(yīng)該接受兩個參數(shù),并且如果這兩個參數(shù)相等則返回0,如果第1個參數(shù)應(yīng)該排列在前面,則返回一個負(fù)數(shù),如果第二個參數(shù)應(yīng)該排列在前面,則返回一個正數(shù)。
sort方法是不穩(wěn)定的。JavaScript的sort方法的穩(wěn)定性根據(jù)不同瀏覽器的實(shí)現(xiàn)而不一致。
可參見MDN sort
array.splice(start, deleteCount,item...)
splice方法從array中移除一個或多個元素,并用新的item替換它們。
// splice 可以像這樣實(shí)現(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ù)組的開始部分,返回新array的length。
// unshift 可以像這樣實(shí)現(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.toExponential(fractionDigits)
toExponential方法 把這個number轉(zhuǎn)換成一個指數(shù)形式的字符串??蛇x參數(shù)控制其小數(shù)點(diǎn)后的數(shù)字位數(shù)。它的值必須在0~20。
number.toFixed(fractionDigits)
toFixed方法把這個number轉(zhuǎn)換成一個十進(jìn)制數(shù)形式的字符串??蛇x參數(shù)控制其小數(shù)點(diǎn)后的數(shù)字位數(shù)。它的值必須在0~20。
number.toPrecision(precision)
toPrecision方法把這個number轉(zhuǎn)換成一個十進(jìn)制數(shù)形式的字符串。可選參數(shù)控制數(shù)字的精度。它的值必須在0~21。
number.toString(radix)
把number轉(zhuǎn)換成字符串。可選參數(shù)控制基數(shù)。它的值必須是2~36。默認(rèn)的radix是以10為基數(shù)的。radix參數(shù)最常用的是整數(shù),但是它可以用任意的數(shù)字。
object.hasOwnProperty(name)
如果這個object包含名為name的屬性,那么返回true。原型鏈中的同名方法不會被檢測。這個方法對name就是“hasOwnProperty”時不起作用。
regexp.exec(string)
exec是正則中最強(qiáng)大(和最慢)的方法。
如果成功匹配,它會返回一個數(shù)組。下標(biāo)為0 的元素包含正則匹配的子字符串。下標(biāo)為1的則是分組1捕獲的文本。下標(biāo)為2的則是分組2捕獲的文本。以此類推。如果匹配失敗則返回null。
regexp.test(string)
test是最簡單(和最快)的方法。匹配成功,返回true,否則返回false。不要對這個方法使用g標(biāo)識。
比如:
var reg = /w+/g; reg.test("ab"); // true // 再執(zhí)行一遍就是false了。 reg.test("ab"); // false // 再執(zhí)行一遍就是true了。 reg.test("ab"); // true // 再執(zhí)行一遍又是false了,如此反復(fù),所以用g標(biāo)識后,看起來很詭異。因?yàn)槊看纹ヅ溟_始位置變了。 reg.test("ab"); // false
test可以像這樣實(shí)現(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)
很少用,用+號運(yùn)算符更方便。
string.indexOf(searchString,position)
在string中查找第一個參數(shù),如果被找到返回該字符的位置,否則返回-1。position可設(shè)置指定位置開始查找。
string.lastIndexOf(searchString,position)
lastIndexOf 方法和indexOf方法類似,不過它是從末尾開始查找,不是從頭開始。
string.localeCompare(that)
比較兩個字符串。類似于array.sort。
string.match(regexp)
如果沒有g標(biāo)識,那么調(diào)用string.match(regexp)和調(diào)用regexp.exec(string)結(jié)果相同。如果帶有g標(biāo)識,那么它生成一個包含所有匹配(除捕獲分組之外)的數(shù)組。
string.replace(searchValue,replaceValue)
對string進(jìn)行查找和替換操作,并返回一個新的字符串。參數(shù)searchvalue可以是一個字符串也可以是一個正則表達(dá)式對象。參數(shù)replaceValue可以是一個字符串或一個函數(shù)。
string.search(regexp)
和indexOf類似,不過它接收正則為參數(shù)。
string.slice(start, end)
slice方法復(fù)制string的一部分來構(gòu)造一個新的字符串。如果start參數(shù)是負(fù)數(shù),它將與string.length相加。end參數(shù)是可選的。
string.split(separator,limit)
把string分割成片段來創(chuàng)建一個字符串?dāng)?shù)組。可選參數(shù)limit可以限制分割的片段數(shù)量。separator參數(shù)可以是字符串或者正則。
string.substring(start,end)
與slice方法一樣,不過它不能處理負(fù)數(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ù)一串?dāng)?shù)字編碼返回一個字符串。
var a = String.fromCharCode(67,97,116) // a是"Cat"第9章 代碼風(fēng)格
這一章中,簡短的說了一些代碼風(fēng)格。事實(shí)證明代碼風(fēng)格在編程中是很重要的。
第10章 優(yōu)美的特性精簡的JavaScript里都是好東西。
包括:1、函數(shù)是頂級對象;2、基于原型繼承的動態(tài)作用域;3、對象字面量和數(shù)組字面量。
到此,讀書筆記已完結(jié)。文章有什么不妥之處,歡迎指出~
關(guān)于作者:常以軒轅Rowboat若川為名混跡于江湖。前端路上 | PPT愛好者 | 所知甚少,唯善學(xué)。
個人博客
segmentfault前端視野專欄,開通了前端視野專欄,歡迎關(guān)注
掘金專欄,歡迎關(guān)注
知乎前端視野專欄,開通了前端視野專欄,歡迎關(guān)注
github,歡迎follow~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/84245.html
摘要:語言精粹讀書筆記第四章函數(shù)函數(shù)字面量函數(shù)字面量包含個部分第一部分,保留字第二部分,函數(shù)名,它可以被忽略。這個超級延遲綁定使得函數(shù)對高度復(fù)用。構(gòu)造器調(diào)用模式一個函數(shù),如果創(chuàng)建的目的就是希望結(jié)合的前綴來調(diào)用,那它就被稱為構(gòu)造器構(gòu)造。 《JavaScript 語言精粹》 讀書筆記 第四章 函數(shù) Functions 函數(shù)字面量 函數(shù)字面量包含4個部分: 第一部分, 保留字 function...
摘要:對象被傳遞到從句中被捕獲。一些語言提供了尾遞歸優(yōu)化。這意味著如果一個函數(shù)返回自身遞歸調(diào)用的結(jié)果,那么調(diào)用的過程會被替換為一個循環(huán),可以顯著提高速度。構(gòu)建一個帶尾遞歸的函數(shù)。語言精粹讀書筆記函數(shù) 第四章 函數(shù) Functions (二) 參數(shù) arguments arguments數(shù)組: 函數(shù)可以通過此參數(shù)訪問所有它被調(diào)用時傳遞給它的參數(shù)列表,包括哪些沒有被分配給函數(shù)聲明時定義的形式參數(shù)...
摘要:使用構(gòu)造器有個嚴(yán)重的危害,如果在調(diào)用構(gòu)造器函數(shù)的時候忘記使用前綴,不僅不會綁定到新對象,還會污染全局變量原型模式原型模式中,我們采用對象來繼承。 構(gòu)造器調(diào)用模式 當(dāng)一個函數(shù)對象被創(chuàng)建時,F(xiàn)unction構(gòu)造器會運(yùn)行類似這樣的代碼: this.prototype = {constructor: this} new一個函數(shù)事會發(fā)生: Function.method(new, functio...
摘要:本文最早為雙十一而作,原標(biāo)題雙大前端工程師讀書清單,以付費(fèi)的形式發(fā)布在上。發(fā)布完本次預(yù)告后,捕捉到了一個友善的吐槽讀書清單也要收費(fèi)。這本書便從的異步編程講起,幫助我們設(shè)計(jì)快速響應(yīng)的網(wǎng)絡(luò)應(yīng)用,而非簡單的頁面。 本文最早為雙十一而作,原標(biāo)題雙 11 大前端工程師讀書清單,以付費(fèi)的形式發(fā)布在 GitChat 上。發(fā)布之后在讀者圈群聊中和讀者進(jìn)行了深入的交流,現(xiàn)免費(fèi)分享到這里,不足之處歡迎指教...
摘要:本文最早為雙十一而作,原標(biāo)題雙大前端工程師讀書清單,以付費(fèi)的形式發(fā)布在上。發(fā)布完本次預(yù)告后,捕捉到了一個友善的吐槽讀書清單也要收費(fèi)。這本書便從的異步編程講起,幫助我們設(shè)計(jì)快速響應(yīng)的網(wǎng)絡(luò)應(yīng)用,而非簡單的頁面。 本文最早為雙十一而作,原標(biāo)題雙 11 大前端工程師讀書清單,以付費(fèi)的形式發(fā)布在 GitChat 上。發(fā)布之后在讀者圈群聊中和讀者進(jìn)行了深入的交流,現(xiàn)免費(fèi)分享到這里,不足之處歡迎指教...
閱讀 995·2021-11-23 09:51
閱讀 3488·2021-11-22 12:04
閱讀 2730·2021-11-11 16:55
閱讀 2960·2019-08-30 15:55
閱讀 3239·2019-08-29 14:22
閱讀 3363·2019-08-28 18:06
閱讀 1253·2019-08-26 18:36
閱讀 2139·2019-08-26 12:08