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...