JavaScript的子集和擴充套件
JavaScript的子集
- 精華:
- 提倡使用函式定義表示式而不是函式定義語句
- 迴圈體和條件分支都是用花括號括起來,不允許在迴圈體和條件分支中只包含一條語句時省略花括號
- 不包含逗號運算子、位運算子以及++和--
- 不包含==和!=,會進行型別轉換,推薦===和!==
- var語句只能出現在函式體的頂部,將所有的變數宣告寫在一條單獨的var語句中
- 禁止使用全域性變數
- 為了讓JavaScript程式碼靜態地通過安全檢查,移除一些JavaScript特性
- 禁用eval()和Function()建構函式,無法做靜態分析
- 禁止使用this關鍵字,因為函式可以通過this訪問全域性物件
- 禁止使用with,增加靜態程式碼檢查的難度
- 禁止使用某些全域性變數:①完全禁止window或document物件;②提供一個外觀皮膚或代理
- 禁止使用某些屬性和方法。如caller和callee,函式的call()和apply()方法,以及constructor和prototype,非標準的屬性_proto_
- 靜態分析可以有效地防止帶有點.運算子的屬性存取表示式去讀寫特殊屬性。方括號[]無法做靜態分析
- 安全子集:
- ADsafe
- dojox.secure
- Caja
- FBJS
- Misrosoft Web Sandbox
常量和區域性變數
- const宣告常量,重複賦值會失敗但不報錯
- JavaScript1.7加入let
- 可以作為變數宣告,和var一樣
- 在for或for/in迴圈中,作為var替代方案
- 在語句塊中定義一個新變數並顯式指定它的作用域
- 定義一個在表示式內部作用域中的變數,這個變數只在表示式內可用
- let替換程式中的var,let宣告的變數只屬於就近的花括號括起來的語句塊
function oddsums(n) {
let total = 0, result=[]; // Defined throughout the function
for(let x = 1; x <= n; x++) { // x is only defined in the loop
let odd = 2*x-1; // odd only defined in this loop
total += odd;
result.push(total);
}
// Using x or odd here would cause a ReferenceError
return result;
}
oddsums(5); // Returns [1,4,9,16,25]
複製程式碼
o = {x:1,y:2};
for(let p in o) console.log(p); // Prints x and y
for each(let v in o) console.log(v); // Prints 1 and 2
console.log(p) // ReferenceError: p is not defined
複製程式碼
let x = 1;
for(let x = x + 1; x < 5; x++)
console.log(x); // Prints 2,3,4
{ // Begin a block to create a new variable scope
let x = x + 1; // x is undefined, so x+1 is NaN
console.log(x); // Prints NaN
}
複製程式碼
let x=1, y=2;
let (x=x+1,y=x+2) { // Note that we're shadowing variables
console.log(x+y); // Prints 5
};
console.log(x+y); // Prints 3
複製程式碼
- let語句塊的一個變體——let表示式,一對圓括號括起來的變數列表和初始化表示式
let x=1, y=2;
console.log(let (x=x+1,y=x+2) x+y); // Prints 5
複製程式碼
解構賦值
- 等號右側是一個陣列或物件,指定左側一個或多個變數的語法和右側的資料和物件直接量的語法保持格式一致
let [x,y] = [1,2]; // Same as let x=1, y=2
[x,y] = [x+1,y+1]; // Same as x = x + 1, y = y+1
[x,y] = [y,x]; // Swap the value of the two variables
console.log([x,y]); // Prints [3,2]
複製程式碼
// Convert [x,y] coordinates to [r,theta] polar coordinates
function polar(x,y) {
return [Math.sqrt(x*x+y*y), Math.atan2(y,x)];
}
// Convert polar to Cartesian coordinates
function cartesian(r,theta) {
return [r*Math.cos(theta), r*Math.sin(theta)];
}
let [r,theta] = polar(1.0, 1.0); // r=Math.sqrt(2), theta=Math.PI/4
let [x,y] = cartesian(r,theta); // x=1.0, y=1.0
複製程式碼
- 右側的陣列所包含的元素不必和左側的變數一一對應
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 = 4
複製程式碼
- 解構賦值預算的返回值是右側的整個資料結構
let first, second, all;
all = [first,second] = [1,2,3,4]; // first=1, second=2, all=[1,2,3,4]
複製程式碼
- 解構賦值可以用於陣列巢狀
let [one, [twoA, twoB]] = [1, [2,2.5], 3]; // one=1, twoA=2, twoB=2.5
複製程式碼
- 解構賦值右側可以是一個物件
let transparent = {r:0.0, g:0.0, b:0.0, a:1.0}; // A RGBA color
let {r:red, g:green, b:blue} = transparent; // red=0.0,green=0.0,blue=0.0
複製程式碼
// Same as let sin=Math.sin, cos=Math.cos, tan=Math.tan
let {sin:sin, cos:cos, tan:tan} = Math;
複製程式碼
- 巢狀物件也可以用於解構賦值
// A nested data structure: an object that contains an array of objects
let data = {
name: "destructuring assignment",
type: "extension",
impl: [{engine: "spidermonkey", version: 1.7},
{engine: "rhino", version: 1.7}]
};
// Use destructuring assignment to extract four values from the data structure
let ({name:feature, impl: [{engine:impl1, version:v1},{engine:impl2}]} = data) {
console.log(feature); // Prints "destructuring assignment"
console.log(impl1); // Prints "spidermonkey"
console.log(v1); // Prints 1.7
console.log(impl2); // Prints "rhino"
}
複製程式碼
迭代
- for/each迴圈——遍歷屬性的值,而for/in是遍歷物件的屬性
let o = {one: 1, two: 2, three: 3}
for(let p in o) console.log(p); // for/in: prints 'one', 'two', 'three'
for each (let v in o) console.log(v); // for/each: prints 1, 2, 3
複製程式碼
- 使用陣列時,for/each遍歷迴圈的元素,通常按數值順序列舉
a = ['one', 'two', 'three'];
for(let p in a) console.log(p); // Prints array indexes 0, 1, 2
for each (let v in a) console.log(v); // Prints array elts 'one', 'two', 'three'
複製程式碼
- 迭代器
// A function that returns an iterator;
function counter(start) {
let nextValue = Math.round(start); // Private state of the iterator
return { next: function() { return nextValue++; }}; // Return iterator obj
}
let serialNumberGenerator = counter(1000);
let sn1 = serialNumberGenerator.next(); // 1000
let sn2 = serialNumberGenerator.next(); // 1001
複製程式碼
- 當沒有多餘的值可迭代時,再呼叫next()會丟擲StopIteration
// A function that returns an iterator for a range of integers
function rangeIter(first, last) {
let nextValue = Math.ceil(first);
return {
next: function() {
if (nextValue > last) throw StopIteration;
return nextValue++;
}
};
}
// An awkward iteration using the range iterator.
let r = rangeIter(1,5); // Get an iterator object
while(true) { // Now use it in a loop
try {
console.log(r.next()); // Try to call its next() method
}
catch(e) {
if (e == StopIteration) break; // Exit the loop on StopIteration
else throw e;
}
}
複製程式碼
- JavaScript1.7對for/in迴圈進行了擴充套件,可以用它來遍歷可迭代物件,for/in自動呼叫它的
_iterator_()
方法
// Return an iterable object that represents an inclusive range of numbers
function range(min,max) {
return { // Return an object representing a range.
get min() { return min; }, // The range's bounds are immutable.
get max() { return max; }, // and stored in the closure.
includes: function(x) { // Ranges can test for membership.
return min <= x && x <= max;
},
toString: function() { // Ranges have a string representation.
return "[" + min + "," + max + "]";
},
__iterator__: function() { // The integers in a range are iterable.
let val = Math.ceil(min); // Store current position in closure.
return { // Return an iterator object.
next: function() { // Return next integer in the range.
if (val > max) // If we're past the end then stop.
throw StopIteration;
return val++; // Otherwise return next and increment.
}
};
}
};
}
// Here's how we can iterate over a range:
for(let i in range(1,10)) console.log(i); // Prints numbers from 1 to 10
複製程式碼
建立一個可迭代的物件和它的迭代器的時,必須寫一個_iterator_()並丟擲StopIteration異常,for/in迴圈會處理異常邏輯
6. 將Iteration()函式和解構賦值一起使用——如果傳入的物件或者陣列沒有定義_interator_()
,會返回這個物件的一個可迭代的自定義迭代器
for(let [k,v] in Iterator({a:1,b:2})) // Iterate keys and values
console.log(k + "=" + v); // Prints "a=1" and "b=2"
複製程式碼
- Iterator()函式返回的迭代器:①只對自有屬性進行遍歷而忽略繼承的屬性;②如果Iterator()傳入第二個引數true,返回的迭代器只對屬性名進行遍歷,而忽略屬性值
o = {x:1, y:2} // An object with two properties
Object.prototype.z = 3; // Now all objects inherit z
for(p in o) console.log(p); // Prints "x", "y", and "z"
for(p in Iterator(o, true)) console.log(p); // Prints only "x" and "y
複製程式碼
- 生成器——yield在函式內使用,用法和return類似,返回函式中的一個值。yield和return的區別在於,使用yield的函式產生一個可保持函式內部狀態的值,這個值是可恢復的。
- 生成器是一個物件,用以表示生成器函式的當前執行狀態。它定義了一個next()方法,後者可恢復生成器函式的執行,直到遇到嚇一跳yield語句。
// Define a generator function for iterating over a range of integers
function range(min, max) {
for(let i = Math.ceil(min); i <= max; i++) yield i;
}
// Invoke the generator function to obtain a generator, then iterate it.
for(let n in range(3,8)) console.log(n); // Prints numbers 3 through 8.
複製程式碼
-
只要一個物件包含可丟擲StopIteration的next()方法,它就是一個迭代器物件。它們是可迭代的迭代器。
-
生成器函式不需要返回
// A generator function that yields the Fibonacci sequence function fibonacci() { let x = 0, y = 1; while(true) { yield y; [x,y] = [y,x+y]; } } // Invoke the generator function to obtain a generator. f = fibonacci(); // Use the generator as an iterator, printing the first 10 Fibonacci numbers. for(let i = 0; i < 10; i++) console.log(f.next()); 複製程式碼
-
可以呼叫生成器的close()方法,中止執行生成器函式。
-
生成器經常用來處理序列化的資料
// A generator to yield the lines of the string s one at a time. // Note that we don't use s.split(), because that would process the entire // string at once, allocating an array, and we want to be lazy instead. function eachline(s) { let p; while((p = s.indexOf('\n')) != -1) { yield s.substring(0,p); s = s.substring(p+1); } if (s.length > 0) yield s; } // A generator function that yields f(x) for each element x of the iterable i function map(i, f) { for(let x in i) yield f(x); } // A generator function that yields the elements of i for which f(x) is true function select(i, f) { for(let x in i) { if (f(x)) yield x; } } // Start with a string of text to process let text = " #comment \n \n hello \nworld\n quit \n unreached \n"; // Now build up a pipeline of generators to process it. // First, break the text into lines let lines = eachline(text); // Next, trim whitespace from the start and end of each line let trimmed = map(lines, function(line) { return line.trim(); }); // Finally, ignore blank lines and comments let nonblank = select(trimmed, function(line) { return line.length > 0 && line[0] != "#" }); // Now pull trimmed and filtered lines from the pipeline and process them, // stopping when we see the line "quit". for (let line in nonblank) { if (line === "quit") break; console.log(line); } 複製程式碼
-
send()和throw()可以為正在執行的生成器提供更多輸入
// A generator function that counts from an initial value. // Use send() on the generator to specify an increment. // Use throw("reset") on the generator to reset to the initial value. // This is for example only; this use of throw() is bad style. function counter(initial) { let nextValue = initial; // Start with the initial value while(true) { try { let increment = yield nextValue; // Yield a value and get increment if (increment) // If we were sent an increment... nextValue += increment; // ...then use it. else nextValue++; // Otherwise increment by 1 } catch (e) { // We get here if someone calls if (e==="reset") // throw() on the generator nextValue = initial; else throw e; } } } let c = counter(10); // Create the generator at 10 console.log(c.next()); // Prints 10 console.log(c.send(2)); // Prints 12 console.log(c.throw("reset")); // Prints 10 複製程式碼
-
陣列推導——利用另外一個陣列或可迭代物件來初始化陣列元素的技術
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); } 語法: [ expression for ( variable in object ) if ( condition ) ] 複製程式碼
- 一個沒有迴圈提的for/in或for/each迴圈
- 在執行便利店物件之後,是圓括號中的關鍵字if和條件表示式
- 在關鍵字for之前是expression,可以認為這個表示式是迴圈體
data = [2,3,4, -5]; // An array of numbers squares = [x*x for each (x in data)]; // Square each one: [4,9,16,25] // Now take the square root of each non-negative element roots = [Math.sqrt(x) for each (x in data) if (x >= 0)] // Now we'll create arrays of property names of an object o = {a:1, b:2, f: function(){}} let allkeys = [p for (p in o)] let ownkeys = [p for (p in o) if (o.hasOwnProperty(p))] let notfuncs = [k for ([k,v] in Iterator(o)) if (typeof v !== "function")] 複製程式碼
-
JavaScript 1.8中,將陣列推導中的方括號替換成圓括號,就成了一個生成器表示式
- 可以惰性求值,在需要的時候求值而不是每次都計算求值;
- 生成器沒有索引,為了得到第n個值,必須遍歷之前的n-1個值
-
生成器表示式生成map()函式
function map(i, f) { // A generator that yields f(x) for each element of i for(let x in i) yield f(x); } let h = (f(x) for (x in g)); 複製程式碼
let lines = eachline(text); let trimmed = (l.trim() for (l in lines)); let nonblank = (l for (l in trimmed) if (l.length > 0 && l[0]!='#')); 複製程式碼
函式簡寫
-
表示式閉包,如果函式只計算一個表示式並返回它的值,關鍵字return和花括號可以省略,並將帶計算的表示式緊接著放在引數列表之後
let succ = function(x) x+1, yes = function() true, no = function() false; // Sort an array in reverse numerical order data.sort(function(a,b) b-a); // Define a function that returns the sum of the squares of an array of data let sumOfSquares = function(data) Array.reduce(Array.map(data, function(x) x*x), function(x,y) x+y); 複製程式碼
多catch從句
-
JavaScript 1.5開始,try/catch可以使用多catch從句
try { // multiple exception types can be thrown here throw 1; } catch(e if e instanceof ReferenceError) { // Handle reference errors here } catch(e if e === "quit") { // Handle the thrown string "quit" } catch(e if typeof e === "string") { // Handle any other thrown strings here } catch(e) { // Handle anything else here } finally { // The finally clause works as normal } 複製程式碼
如果無表示式,認為是true
如果都不滿足,該異常未被捕獲
e4x:ecmascript for xml
skip...