精讀JavaScript模式(四)
一、前言
放了個元旦,休息了三天,加上春運搶票一系列事情的沖擊,我感覺我的心已經飛了。確實應該收收心,之前計劃的學習任務也嚴重脫節了;我恨不得打死我自己。
在上篇博客中,筆記記錄到了關於構造函數方面的一些寫法和用法,這篇博客,會從原書中數組直接量開始,自己讀了下之前的博客,還是有點照搬概念的意思,想了下,還是得簡化概念,按照自己的思路去寫,那麽開始。
二、數組直接量
概要:創建數組的兩種方式,Array()創建的隱性問題
在JS中,數組也是對象,我們可以通過內置構造函數Array()創建數組,也可以通過直接量方式創建。
使用Array()創建:
var arr = new Array(‘echo‘, ‘時間跳躍‘, "聽風是風");
數組直接量創建:
var arr = [‘echo‘, ‘時間跳躍‘, "聽風是風"];
很明顯,直接量的寫法更為簡單,所以在實際開發中,對於創建數組,是絕對推薦直接量寫法。
為什麽不推薦new Array()的寫法呢,因為當我們為Array構造器傳入一個數字,這個數字不會成為數組的元素,而是成了設置數組的length。
var arr = new Array(3); console.dir(arr[0]);//undefined
我們知道,數組的length是不存在浮點數的,當我們為Array()中設置一個浮點數時會直接導致錯誤。
var arr = newArray(3.14);//報錯
三、判斷是不是數組
概括:判斷是不是數組的幾種方式
判斷是不是數組,是不是對象,首先想到的是typeof,但是很遺憾,typeof的判斷較為粗糙,得到的只是object的結果。
var arr = [1,2,3]; typeof arr;//object
在ES5中提供了一個好用的數組檢驗方法Array.isArray(),它返回一個布爾值,如果你檢驗的對象是一個數組,它將返回true。
var arr = [1,2,3]; Array.isArray(arr);//true
但如果你的開發環境不支持ES5,最保險的做法就是使用Object.prototype.toString()
var arr = [1,2,3]; Object.prototype.toString.call(arr) === ‘[object Array]‘//true
四、正則表達式直接量
概括:正則創建的2種方式,使用RegExp構造函數創建的情景
在js中正則表達式也是對象,可以通過正則直接量與RegExp()構造函數兩種方式創建:
創建正則匹配一個字母a,使用直接量:
var re = /a/gm
使用構造函數創建:
var re = new RegExp(‘a‘,‘gm‘);
與構造函數寫法相比,正則表達式直接量使用兩個斜線包裹起來,正則匹配的主體部分不包括兩端的斜線,修飾符gm不需要使用引號。
假設我們需要匹配一個斜線\,兩者寫法區別如下:
var re = /\\/gm //構造函數 var re = new RegExp(‘\\\\‘,‘gm‘);
在直接量中我們使用\對\進行轉譯,而在構造函數寫法中,轉譯兩次的行為導致使用了四個\,很明顯對於閱讀來說增加了復雜性。
js中提供了三個修飾符,且修飾符順序隨意:
g:全局匹配
m:多行匹配
i:忽略大小寫
雖然推薦直接量的方式創建正則,但如果你創建的正則包含變量,則不得不使用構造函數創建。
const str = "abc"; const re = new RegExp(str, ‘gm‘);
比較有趣的是,在使用構造函數創建正則時,不帶new調用RegExp()和帶new調用的結果是完全一樣的。這與我在上篇文章中說不帶new調用對象指向window有些不同。
其實寫到這裏,原書中第三章基本介紹完了,第四章開始介紹函數,這一章主要介紹函數幾種聲明方式,函數提前,以及一些模式,性能優化等。上面篇幅感覺有點少,順帶把第四章記錄一點。
五、函數基礎概念
概括:函數的一些特性
JS中的函數也是對象,且函數有兩個主要的特性,第一,函數是一等對象(一等公民),第二是函數提供作用域支持。
函數的第一特性函數是對象,所以我們可以對函數做以下操作
1.在程序執行時動態創建函數。
2.可以將函數作為值賦予給一個變量,可以將函數的引用拷貝到另一個變量。
3.可以將函數作為參數傳入到另一函數,也能作為函數返回值。
3.函數可以擁有自己的屬性和方法。
當我們通過構造函數的形式來創建一個函數,很明顯,這個函數屬於構造器的一個實例,屬於一個對象。
const func1 = new Function(‘a‘, ‘b‘, ‘return a+b‘); func1(1,2);//3
這裏func1就是一個對象,但和前面創建各類對象,字符串,正則一樣,並不推薦構造器的形式創建,一是可讀性差,其次執行代碼塊return a + b本質上屬於一段字符串,在執行時也做了類似於eval()的操作。
函數的第二個特性是可以提供作用域,js中原本是沒有塊級作用域概念的(現在let可以申明塊級作用域),也就是不能通過花括號來創建作用域,而創建一個函數,則會生成一個獨立的作用域。
在函數體內凡是通過var申明的變量(let const都一樣,畢竟書有點老)相對函數而言,都是局部作用域,在函數外是不可見的。
需要註意的是,雖然說花括號提供作用域,但是對於if while for這一類的花括號,在裏面通過var創建的變量也不是局部作用域,除非外層有函數,否則就是一個全局變量。(有個疑問,如果if裏面使用let const申明,外面依舊讀不到,我暫時沒了解,查閱了補全此問題)
if(true){ var a = 1 }; console.log(a);//1 function func1() { var b = 1 }; console.log(b);//報錯
六、函數術語與函數的幾種創建方式
概括:什麽是函數表達式,什麽是函數申明?函數name屬性與函數申明提前
我們可以通過構造函數,函數表達式,函數申明三種形式來創建一個函數。構造函數的創建方式在上面已有舉例,記住,不推薦這種寫法。
通過函數表達式創建函數:
//帶有命名的函數表達式 var func1 = function func(a, b){ return a +b; }; func1(1,2)//3 func1.name;//func //匿名函數表達式 var func2 = function (a, b){ return a +b; }; func2(1,2); func2.name;//func2
上述代碼中函數一的name屬性為func,但是調用它使用的是func1(),函數二雖然匿名,但依然可以找到name屬性,是我們賦予的變量名func2。
兩種函數作用完全相同,調用均為使用函數賦予的變量名進行調用,我個人在表達式創建上常用匿名表達式的寫法。
通過函數聲明創建函數:
function add(a, b){ return a + b; }; add(1,2)//3
可以說函數聲明與帶有命名的函數表達式的區別就是有沒有將這個函數賦予給一個變量。
我們在前面的創建對象,字符串,正則時都有提到一個直接量創建的概念,其實函數也有‘函數直接量的說法‘,它對應的就是函數表達式,但這個術語有歧義的,所以不推薦這麽稱呼它;但如果有人說函數直接量,你還是得知道對方說的是函數表達式的創建方式,這裏順帶提一下。
函數申明提前:
關於函數申明與函數表達式的使用取舍,一般推薦在全局,或者函數體內創建函數時,使用函數申明的寫法;因為函數申明是存在函數提升的,在全局作用域創建後,你可以在任意一個地方調用它。
同樣全局作用域,使用函數表達式創建的函數,你只能在創建之後調用,因為函數表達式提升是被賦予的變量,函數賦值的操作停留在原地,所以在創建前調用函數會報錯,這是也兩者的區別所在。
//函數申明,創建一次,隨處可用 add(1,2);//3 function add(a, b){ return a + b; }; add(1,2);//3 //函數表達式,只能在創建後使用 func(a,b);//報錯 var func = function (a, b){ return a + b; }; func(1,2);//3 //上述代碼等同於 var func; func(a,b)//此時func並不是一個函數,報錯。 func = function (a, b){ return a + b; }; func(1,2);//3
函數name屬性:
原書中提到,匿名函數表達式的name屬性在火狐,safari中為‘‘,也就是沒有賦予具體的值,在IE中是無定義的。我本人測試name屬性是被賦予的變量名,我的瀏覽器是谷歌,可能是因為瀏覽器版本的不同,對此差異簡單提一下。
最後強調一下JS函數兩大特性,函數是對象,函數提供局部變量作用域。
先記到這裏吧。下一篇將從函數回調模式開始說起。
精讀JavaScript模式(四)