JavaScript子集和擴(kuò)展

JavaScript子集

大多數(shù)語言都會定義它們的子集,用以更安全地執(zhí)行不信任的第三方代碼贰剥。

精華(The Good Parts)

Douglas Crockford曾寫過一本很簿的書《JavaScript: The Good Parts》蚌成,專門介紹JavaScript中值得發(fā)揚(yáng)光大的精華部分凛捏。這個語言子集的目標(biāo)是規(guī)避語言中的怪癖葵袭、缺陷部分坡锡,最終編程更輕松鹉勒、程序更健壯。

  • 使用函數(shù)定義表達(dá)式而不是函數(shù)定義語句來定義函數(shù)锯厢。
  • 循環(huán)體和條件分支都使用花括號括起來实辑。
  • 任何語句只要不是以花括號結(jié)束都應(yīng)該使用分號做結(jié)尾剪撬。
  • 推薦使用"==="和"!=="残黑,不推薦使用"=="和"!="(因為比較時會涉及到類型轉(zhuǎn)換)梨水。
  • 由于JavaScript并不包含塊級作用域疫诽,var語句最好出現(xiàn)在函數(shù)體的頂部踊沸。
  • 禁止使用全局變量社证。

Crockford寫過一個在線代碼質(zhì)量檢測工具JSLint追葡,可通過這個工具對代碼進(jìn)行檢查宜肉。

子集的安全性

子集的設(shè)計目的是能在一個容器或"沙箱"中更安全地運行不可信的第三方JavaScript代碼谬返。所有能破壞這個沙箱并影響全局執(zhí)行環(huán)境的語言特性和API在這個安全子集中都是禁止的遣铝。
為了讓JavaScript代碼靜態(tài)地通過安全檢查,必須移除一些JavaScript特性:

  • 禁止使用this關(guān)鍵字涨冀,因為函數(shù)(在非嚴(yán)格模式中)可能通過this訪問全局對象鹿鳖。
  • 禁止使用with語句壮莹,因為with語句增加了靜態(tài)代碼檢查的難度命满。
  • 靜態(tài)分析可以有效地防止帶有點(.)運算符的屬性存取表達(dá)式去讀寫特殊屬性周荐。但我們無法對方括號([])內(nèi)的字符串表達(dá)式做靜態(tài)分析概作⊙堕牛基于這個原因愚屁,安全子集禁止使用方括號霎槐,除非括號內(nèi)是一個數(shù)字或字符串直接量送浊。
  • eval()和Function()構(gòu)造函數(shù)在任何子集里都是禁止使用的,因為它們可以執(zhí)行任意代碼丘跌,而且JavaScript無法對這些代碼做靜態(tài)分析袭景。
  • 禁止使用全局變量,因此代碼中不能有對Window對象的引用和對Document的引用闭树。
  • 禁止使用某些屬性和方法耸棒,以免在水箱中的代碼擁有過多的權(quán)限。如:arguments對象的兩個屬性caller和callee报辱、函數(shù)的call()和apply()方法、以及constructor和prototype兩個屬性。

JavaScript擴(kuò)展

常量和局部變量

在JavaScript1.5及后續(xù)版本中可以使用const關(guān)鍵字來定義常量幅疼,常量是不可重復(fù)賦值的變量米奸。

const pi = 3.14;
pi = 4;             // 對常量賦值會報"TypeError"

在JavaScript1.7中,添加了關(guān)鍵字let衣屏,可以定義帶有作用域的變量:
下面列舉各種場景使用let的作用域:

  • 全局作用域(定義在函數(shù)之外)
let me = 'go';  // 全局作用域
var i = 'able'; // 全局作用域
  • 函數(shù)作用域
function ingWithinEstablishedParameters() {
    let terOfRecommendation = 'awesome worker!';    // 函數(shù)作用域
    var sityCheerleading = 'go!';                   // 函數(shù)作用域
};
  • 塊作用域
function allyIlliterate() {
    // tuce在這里不可見

    for( let tuce = 0; tuce < 5; tuce++ ) {
        // tuce在這里可見
    };

    // tuce在這里不可見
};

function byE40() {
    // nish在這里可見

    for( var nish = 0; nish < 5; nish++ ) {
        // nish在這里可見
    };

    // nish在這里可見
};

更多可參考:“l(fā)et” keyword vs “var” keyword

例子如下:

o = {x:1, y:2};
for each(let v in o) 
  console.log(v);       // 輸出1和2
console.log(v);         // 錯誤:v is not defined

在for循環(huán)中使用let關(guān)鍵字:

// 輸出 5, 5, 5, 5, 5
for (var i = 0; i < 5; ++i) {
    setTimeout(function () {
        console.log(i);
    }, 1000);  
}

// 輸出 1, 2, 3, 4, 5
for (let i = 0; i < 5; ++i) {
    setTimeout(function () {
        console.log(i);
    }, 1000);  
}

更多可參考:Explanation of let and block scoping with for loops

注:var聲明的變量在它們所聲明的函數(shù)內(nèi)始終是存在的躏升,但直到代碼執(zhí)行行到var語句時才初始化變量辩棒,在var語句執(zhí)行之前它的值是undefined狼忱。通過let聲明的變量也與之類似。

解構(gòu)賦值(destructuring assignment)

在解構(gòu)賦值中一睁,等號右側(cè)是一個數(shù)組或?qū)ο?一個結(jié)構(gòu)化的值)钻弄,指定左側(cè)一個或多個變量的語法和右側(cè)的數(shù)組和對象直接量的語法保持格式一致。

數(shù)組解構(gòu)賦值

  • 簡單的解構(gòu)賦值
let [x, y] = [1, 2];    // 等價于let x=1, y=2
[x, y] = [x+1, y+1];    // 等價于x=x+1, y=y+1
[x, y] = [y, x];        // 交換2個變量的值
console.log([x, y]);    // => [3, 2]
  • 解構(gòu)賦值左右的變量不一定要一一對應(yīng)者吁,左側(cè)多余的變量賦值為undefined窘俺,而右側(cè)多余的值則會忽略。左側(cè)的變量列表可以包含連續(xù)的逗號用以跳過右側(cè)對應(yīng)的值复凳。
let [x, y] = [1];       // x = 1, y = undefined
[x, y] = [1, 2, 3];     // x = 1, y = 2
[, x, y] = [1, 2, 3, 4];// x = 2, y = 3

注:JavaScript并未提供將右側(cè)多余的值以數(shù)組的形式賦值給左側(cè)變量的語法瘤泪。比如,上面代碼的第2行育八,并不能將[2,3]賦值給y对途。

  • 解構(gòu)賦值的返回值是右側(cè)的整個數(shù)據(jù)結(jié)構(gòu),而不是從中取出的某個值髓棋。
let first, second, all;
all = [first, second] = [1, 2, 3, 4];   // first=1, second=2, all=[1,2,3,4]
  • 解構(gòu)賦值可使用數(shù)組嵌套的語法实檀。
let [one, [twoA, twoB]] = [1, [2, 2,5], 3]; // one=1, twoA=2, twoB=2.5

對象解構(gòu)賦值

  • 在這種情況下,解構(gòu)賦值的左側(cè)看起來是一個對象直接量按声,對象中是一個名值對的列表膳犹,冒號右側(cè)的值是變量名
let transparent = {r:0.0, g:0.0, b:0.0, a:1.0}; 
let {r:red, g:green, b:blue} = transparent;     // red=0.0, green=0.0, blue=0.0
  • 解構(gòu)賦值可使用對象嵌套的語法签则。
let data = {
    name: "destructuring assignment",
    type: "extension",
    impl: [{engine: "spidermonkey", version: 1.7},
           {engine: "rhino", version: 1.7}]
};

let ({name: feature, impl: [{engine:impl1, version: v1}, {engine:impl2}]} = data) {
    console.log(feature);   // => "destructuring assignment"
    console.log(impl1);     // => "spidermonkey"
    console.log(v1);        // => 1.7
    console.log(impl2);     // => "rhino"
} 

迭代

for VS for/each

for循環(huán)是遍歷對象的屬性须床,而for/each遍歷對象屬性的值

a = ["one", "two", "three"];
for(let p in a) console.log(p);     // => 0, 1, 2
for each(let v in a) console.log(v) // => "one", "two", "three"

迭代器

迭代器是一個對象渐裂,這個對象允許對它的值集合進(jìn)行遍歷豺旬,并保持任何必要的狀態(tài)以便能夠跟蹤到當(dāng)前遍歷的"位置"。迭代器必須包含next()方法芯义,每一次調(diào)用next()都返回集合中的下一個值哈垢。
下面的counter()返回一個迭代器對象:

function counter(start) {
    let nextValue = Math.round(start);
    return { next: function() { return nextValue++; }};     // 返回迭代器對象
}

let serialNumberGenerator = counter(1000);
let sn1 = serialNumberGenerator.next();     // => 1000
let sn2 = serialNumberGenerator.next();     // => 1001

注:當(dāng)?shù)饔糜谟邢薜募蠒r,沒有多余的值可迭代時耘分,next()會拋出StopIterator,StopIterator是全局對象的屬性,一個普通的對象(它自身沒有屬性)求泰,只是為了終結(jié)迭代而保留的一個對象央渣。

可迭代對象

  • 可迭代對象表示一組可迭代處理的值,可迭代對象必須包含一個_iterator_()的方法渴频,用以返回這個集合的迭代器對象芽丹。
  • for/in循環(huán)會自動調(diào)用它的_iterator_()方法來獲得一個迭代器對象(類型自動轉(zhuǎn)換),然后調(diào)用迭代器的next()方法卜朗。for/in循環(huán)會自動處理StopIteration異常拔第,而且處理過程對開發(fā)者是不可見的。
function range(min, max) {
    return {
        get min() { return min; },
        get max() { return max; },
        includes:  function(x) { return min <= x && x <= max; },
        toString:  function()  { return "[" + min + "," + max + "]"; },
        _iterator_: function() {
            let val = Math.ceil(min);
            return { next: function() { 
                                if(val > max) 
                                    throw StopIteration;
                                return val++;
                            }
                    };
        }
    };
}

for(let i in range(1, 10)) console.log(i);  // => 輸出1~10之間的數(shù)字

Iterator()函數(shù)

  • 如果這個函數(shù)的參數(shù)是一個可迭代對象场钉,那么它將返回這個對象的_iterator_()方法的調(diào)用結(jié)果蚊俺,返回一個迭代器對象。
  • 如果傳入的對象或者數(shù)組沒有定義_iterator_()方法逛万,它會返回這個對象的一個可迭代的自定義迭代器泳猬。每次調(diào)用這個迭代器的next()方法都會返回包含2個值的數(shù)組,第1個數(shù)組元素是屬性名宇植,第2個數(shù)組元素是屬性的值得封。
for(let [k, v] in Iterator({a:1, b:2}))
    console.log(k + "=" + v);           // => "a=1", "b=2"
  • Iterator()函數(shù)返回的迭代器還有2個重要的特性。第一指郁,它只對自有屬性進(jìn)行遍歷而忽略繼承的屬性忙上。第二,如果給Iterator()傳入第2個參數(shù)true坡氯,返回的迭代器只對屬性名進(jìn)行遍歷晨横,而忽略屬性值。
o = {x:1, y:2};
Object.prototype.z = 3;
for(p in o) console.log(p);                 // => "x", "y", "z"
for(p in Iterator(o)) console.log(p);       // => ["x", 1], ["y", 2]箫柳,注:不會遍歷繼承屬性
for(p in Iterator(o, true)) console.log(p); // => "x", "y"手形,          注:只遍歷屬性名

生成器函數(shù) Generator function

任何使用關(guān)鍵字yield的函數(shù)都稱為"生成器函數(shù)"。

  • yield和return的區(qū)別在于悯恍,使用yield的函數(shù)可保持函數(shù)內(nèi)部狀態(tài)的值库糠。
  • 生成器函數(shù)通過yield返回值,不建議使用return返回值涮毫。
  • 和普通函數(shù)一樣瞬欧,生成器函數(shù)也通過關(guān)鍵字function聲明,typeof運算符返回"function"罢防,并從Function.prototype繼承屬性和方法艘虎。
  • 對生成器函數(shù)的調(diào)用不會執(zhí)行函數(shù)體,而是返回一個生成器對象咒吐。
  • 如果不再使用生成器對象野建,可通過close()方法來釋放它属划。調(diào)用close()相當(dāng)于在函數(shù)運行掛起的位置執(zhí)行了一條return語句轧叽,如果如果當(dāng)前掛起的位置在try語句塊中贼陶,那么將首先執(zhí)行finally語句肠牲,再執(zhí)行close()返回藻治,如果finally語句塊產(chǎn)生了異常,這個異常會傳播給close()盼忌。

下面是一個普通的生成器函數(shù):

function range(min, max) {

  for(let i=Math.ceil(min); i <= max; i++) {
    yield i;
  }
    
}

var f = range(3, 8);    // 返回一個生成器對象
console.log(f.next());  // 3赘方,返回單獨值
console.log(f.next());  // 4铛楣,返回單獨值

如果在生成器函數(shù)聲明時加個"*"目溉,則yield會返回對象值:

// function后添加"*"
function *range(min, max) {
  for(let i=Math.ceil(min); i <= max; i++) {
    yield i;
  }
    
}

var f = range(3, 8);    // 返回一個生成器對象
console.log(f.next());  // { value=3,  done=false,  z=3}明肮,返回對象
console.log(f.next());  // { value=4,  done=false,  z=3},返回對象

yield不僅可以返回值停做,還可以用于接收值晤愧,可通過next()或send()傳遞yield的接收值:

function *test() {
  var i = yield 2;
  
  yield i;
};

var f = test();
console.log(f.next());      // { value=2,  done=false,  z=3}, 第1次調(diào)用時大莫,沒有執(zhí)行到y(tǒng)ield語句蛉腌,不需要接收值
console.log(f.next(5));     // { value=5,  done=false,  z=3}, yield接收變量5,并賦值給i
console.log(f.next(6));     // { done=true,  z=3,  value=undefined}只厘,最后一個值為undefined

更多可參考:
es6-generators
如何理解ES6的yield

數(shù)組推導(dǎo) array comprehension

數(shù)組推導(dǎo)是一種利用另外一個數(shù)組或可迭代對象來初始化數(shù)組元素的技術(shù)烙丛。
數(shù)組推導(dǎo)的語法如下:

[expression for ( variable in object ) if ( condition )]

定義一個數(shù)組:

let evensquares = [x*x for (x in range(0,10)) if (x % 2 === 0)]

與下面的代碼等價:

let evensquares = [];
for(x in range(0,10)) {
    if(x % 2 === 0)
        evensquares.push(x*x);
}

數(shù)組推導(dǎo)表達(dá)式特點:

  • 在變量之前沒有關(guān)鍵字var和let,其實這里使用了隱式的let羔味。
  • if語句是可選的河咽,如果省略的話,相當(dāng)于給數(shù)組推導(dǎo)補(bǔ)充一條if(true)從句赋元。

生成器表達(dá)式 generator expression

將數(shù)組推導(dǎo)中的方括號替換成圓括號忘蟹,它就成了一個生成器表達(dá)式。比如:

let h = (f(x) for(x in g));

這段代碼與下面的代碼等價:

function map(i, g) {
    for(let x in g) yield f(x);
}

函數(shù)簡寫

如果函數(shù)只計算一個表達(dá)式并返回它的值搁凸,關(guān)鍵字return和花括號都可以省略媚值。
比如,對數(shù)組按降序排列:

data.sort(function(a,b) b-a);   // 在"b-a"的前面省略了"{}"和return

多catch從句

try/catch語句中可以使用多catch從句护糖,在catch從句的參數(shù)中可以添加關(guān)鍵字if進(jìn)行異常類型判斷褥芒。

try {
    // 這里可能會拋出多種類型的異常
    throw 1;
}
catch(e if e instanceof ReferenceError) {
    // 這里處理引用錯誤
}
catch(e if e === "quit") {
    // 這里處理拋出的字符串是"quit"的情況
}
catch(e if typeof e === "string") {
    // 處理其他字符串的情況
}
catch(e) {
    // 處理余下的異常情況
}
finally {
    // finally從句正常執(zhí)行
}

注:如果catch從句沒有一個是true,那么程序會向上拋出這個未捕獲的異常嫡良。

E4X: ECMAScript for XML

E4X為處理XML文檔定義了一系列強(qiáng)大的特性锰扶。XML對象和原始的JavaScript對象不同,對它們進(jìn)行typeof運算的結(jié)果是"xml"寝受。XML對象和DOM對象沒有任何關(guān)系坷牛。

XML對象創(chuàng)建

  • 直接量創(chuàng)建
// 創(chuàng)建一個XML對象
var pt = <periodictable>
            <element id="1"><name>Hydrogen</name></element>
            <element id="2"><name>Helium</name></element>
         </periodictable>;

// 添加一個新元素
pt.element += <element id="3"><name>Lithium</name></element>;
  • 構(gòu)造函數(shù)構(gòu)造
// 創(chuàng)建單個節(jié)點 XML
pt.element += new XML('<element id="4"><name>Boron</name></element>');

// 創(chuàng)建多個節(jié)點 XMLList
pt.element += new XMLList('<element id="4"><name>Boron</name></element>' + 
                          '<element id="5"><name>Lithium</name></element>');

XML對象訪問

  • 普通訪問
// 得到所有<element>標(biāo)簽的 列表
var elements = pt.element;      

// 得到所有的<name>標(biāo)簽的 列表
var names = pt.element.name;    

// "Hydrogen",name的第0個標(biāo)簽內(nèi)容
var n = names[0];               
  • 通配符(*)訪問
// 得到所有<element>標(biāo)簽的所有子節(jié)點(即<name>標(biāo)簽列表)
var names = pt.element.*;
  • 使用字符@區(qū)分屬性和標(biāo)簽名
var pt = new XML('<periodictable><element id="1"><name>Hydrogen</name></element><element id="2"><name>Helium</name></element></periodictable>');
            
// 訪問id屬性很澄,輸出"2"
var a = pt.element[1].@id;

// 獲取所有的<element>標(biāo)簽的所有屬性
var b = pt.element.@*;
  • 過濾列表
// 對所有的<element>元素組成的列表進(jìn)行過濾
// 過濾出那些id屬性小于3的元素
var c = pt.element.(@id < 3);
  • 刪除標(biāo)簽和屬性
delete pt.element;          // 刪除所有的<element>標(biāo)簽

delete pt.element[0].@id;   // 刪除一個屬性

命名空間

E4X是支持命名空間的京闰,它為使用XML命名空間提供了語法支持和API支持锨亏。

// 聲明默認(rèn)的命名空間
default xml namespace = "http://www.w3.org/1999/xhtml";
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市忙干,隨后出現(xiàn)的幾起案子器予,更是在濱河造成了極大的恐慌,老刑警劉巖捐迫,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乾翔,死亡現(xiàn)場離奇詭異,居然都是意外死亡施戴,警方通過查閱死者的電腦和手機(jī)反浓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赞哗,“玉大人雷则,你說我怎么就攤上這事》舅瘢” “怎么了月劈?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長藤乙。 經(jīng)常有香客問我猜揪,道長,這世上最難降的妖魔是什么坛梁? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任而姐,我火速辦了婚禮,結(jié)果婚禮上划咐,老公的妹妹穿的比我還像新娘拴念。我一直安慰自己,他們只是感情好褐缠,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布政鼠。 她就那樣靜靜地躺著,像睡著了一般送丰。 火紅的嫁衣襯著肌膚如雪缔俄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天器躏,我揣著相機(jī)與錄音俐载,去河邊找鬼。 笑死登失,一個胖子當(dāng)著我的面吹牛遏佣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播揽浙,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼状婶,長吁一口氣:“原來是場噩夢啊……” “哼意敛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起膛虫,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤草姻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后稍刀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撩独,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年账月,在試婚紗的時候發(fā)現(xiàn)自己被綠了综膀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡局齿,死狀恐怖剧劝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抓歼,我是刑警寧澤讥此,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站锭部,受9級特大地震影響暂论,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拌禾,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望展哭。 院中可真熱鬧湃窍,春花似錦、人聲如沸匪傍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽役衡。三九已至茵休,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間手蝎,已是汗流浹背榕莺。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留棵介,地道東北人钉鸯。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像邮辽,于是被迫代替她去往敵國和親唠雕。 傳聞我的和親對象是個殘疾皇子贸营,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

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