标签:ons size gen 添加 字符串连接 初学者 window 自动 函数调用
一。 let和const关键字
let出现之前,js所有的作用域都是以函数为单位的,只要在一个function里声明的var,
无论是for循环等块里面声明的还是在块外面声明的,整个function都可以使用这个var,比如:
function foo() { for (var i=0; i<100; i++) { // } i += 100; // 仍然可以引用变量i }
我个人的理解是js的提升特性,函数会将里面声明的所有var都提升到函数开始的地方,所以整个函数内都共享这些var。
let的出现解决了块级作用域的问题:
function foo() { var sum = 0; for (let i=0; i<100; i++) { sum += i; } i += 1; // SyntaxError }
但是要注意的是,let并没有提升特性,也就是说不允许在声明之前就使用,而且在某一个块中声明的let变量,会屏蔽掉同名的var变量:
var c = ‘test‘; if (true) { c = ‘new‘; //ReferenceError let c; console.log(c); }
const的是”常量let“,也就是说是有块级作用域的常量值。
二。 箭头函数
箭头函数使用的最重要一点就是this的使用,一不小心就会出错。
let a = { foo: 1, bar: () => console.log(this.foo) }; a.bar(); // undefined
代码中的this指向调用a的对象,而不是a本身,如果为a外面没有定义foo的话,this会一直追溯到window对象,所以this.foo就是undefined了。
如果想调用a,只能使用普通函数:
let a = { foo: 1, bar: function() { console.log(this.foo); } }; a.bar(); // 1
再来看一个例子:
var obj = { birth: 1990, getAge: function () { var b = this.birth; // 1990 var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象 return fn(); } }; obj.getAge(); // 25
这里的this指向调用getAge的对象obj,所以this.birth能取到obj中的birth属性,该写法等价于:
var obj = { birth: 1990, getAge: function () { var b = this.birth; // 1990 var that = this; var fn = function () { return new Date().getFullYear() - that.birth; // this指向window或undefined }; return fn(); } }; obj.getAge(); // 25
这里必须使用that来捕获对象,否则在fn中的this并不是指向obj而是指向调用fn的对象。相比之下,该例中的箭头函数的使用就很清晰简洁了。
依我看来,并不应该把箭头函数归类为函数,而更应该使用lambda演算或lambda表达式这一称谓,
这一点在java中做的比较好,不知道为什么在js中很多人喜欢成为箭头函数,容易引起混淆。
三。 字符串的使用
(1)多行字符串
用反引号 ` ... ` 表示:
alert(`多行
字符串
测试`);
(2)模板字符串
var name = ‘小明‘; var age = 20; var message = `你好, ${name}, 你今年${age}岁了!`; alert(message);
模板字符串在一定程度上简化了+号的字符串连接作用。
四。 for…of…结构
for…in…
循环有很多问题,但由于历史遗留问题至今还在使用。
for...in...遍历的实际上是对象的属性名称。一个Array
数组实际上也是一个对象,它的每个元素的索引被视为一个属性,
所以当使用for…in…来遍历数组时,会感觉很别扭,而且当我们手动给Array
对象添加了额外的属性后,for...in
循环将带来意想不到的意外效果,比如:
var a = [‘A‘, ‘B‘, ‘C‘];
a.name = ‘Hello‘;
for (var x in a) {
alert(x); // ‘0‘, ‘1‘, ‘2‘, ‘name‘
}
这对于很多初学者来说很不习惯,for…of…就避免了这个问题,循环则完全修复了这些问题,它只循环集合本身的元素:
var a = [‘A‘, ‘B‘, ‘C‘];
a.name = ‘Hello‘;
for (var x of a) {
alert(x); // ‘A‘, ‘B‘, ‘C‘
}
这就是为什么要引入新的for ... of
循环。
五。 Map和Set
Map是一种key-value结构,注意要区分和对象的写法,不同的key和value通过[ ]来划定而不是逗号,用逗号连接key和value而不是冒号。
var m = new Map([[‘Michael‘, 95], [‘Bob‘, 75], [‘Tracy‘, 85]]); m.get(‘Michael‘); // 95
Set
是一组key的集合,不存储value,注意和数组的区别,Set中值都是唯一的,重复的值被自动过滤,这也是和普通数组最大的区别。
var s = new Set([1, 2, 3, 3, ‘3‘]); s; // Set {1, 2, 3, "3"}
六。 iterable类型
遍历
Array
可以采用下标循环,遍历Map
和Set
就无法使用下标。
为了统一集合类型,ES6标准引入了新的
iterable
类型。
Array
、Map
和Set
都属于iterable
类型。具有iterable
类型的集合可以通过新的for ... of
循环来遍历。
然而,更好的方式是直接使用iterable
内置的forEach
方法(注意,forEach()
方法是ES5.1标准引入的,也就是说时在引入iterable类型之前就引入了),
forEach接收一个函数,每次迭代就自动回调该函数,比如以Array为例。
var a = [‘A‘, ‘B‘, ‘C‘]; a.forEach(function (element, index, array) { // element: 指向当前元素的值 // index: 指向当前索引 // array: 指向Array对象本身 alert(element); });
Set
与Array
类似,但Set
没有索引,因此回调函数的前两个参数都是元素本身:
var s = new Set([‘A‘, ‘B‘, ‘C‘]); s.forEach(function (element, sameElement, set) { alert(element); });
Map
的回调函数参数依次为value
、key
和map
本身:
var m = new Map([[1, ‘x‘], [2, ‘y‘], [3, ‘z‘]]); m.forEach(function (value, key, map) { alert(value); });
如果对某些参数不感兴趣,由于JavaScript的函数调用不要求参数必须一致,因此可以忽略它们。例如,只需要获得Set
的element
:
var s = new Set([‘A‘, ‘B‘, ‘C‘]); s.forEach(function (element) { console.log(element); });
七。 generator
(由于generator的概念比较复杂,详情请参考廖雪峰老师的网站)
generator由function*
定义(注意多出的*
号),并且,除了return
语句,还可以用yield
返回多次。
直接调用一个generator和调用函数不一样,f()
仅仅是创建了一个generator对象,还没有去执行它。
真正要执行generator对象有两个方法,一是不断地调用generator对象的next()
方法,第二个方法是直接用for ... of
循环迭代。
(1)调用next
next()
方法会执行generator的代码,然后,每次遇到yield x;
就返回一个对象{value: x, done: true/false}
,然后“暂停”。
返回的value
就是yield
的返回值,done
表示这个generator是否已经执行结束了。如果done
为true
,则value
就是return
的返回值。
当执行到done
为true
时,这个generator对象就已经全部执行完毕,不要再继续调用next()
了。下面来看个例子:
要生成一个自增的ID,可以编写一个next_id()
函数,由于函数无法保存状态,故需要一个全局变量current_id
来保存数字:
var current_id = 0; function next_id() { current_id ++; return current_id; }
为了避免全局变量的污染,使用generator来保存运行状态:
function* next_id () { var current_id = 1; while (true) { yield current_id; current_id ++; } return current_id; // 可以不写 }
由于这里的while并没有跳出的可能,所以永远也不会执行return,可以一直调用next方法:
g = next_id(); // 只是创建了一个generator对象,还没有执行 g.next(); // 1 g.next(); // 2 ...
再来看一个可以跳出循环的例子:
function* fib(max) { var t, a = 0, b = 1, n = 1; while (n < max) { yield a; t = a + b; a = b; b = t; n ++; } return a; }
该例子是创建斐波那契数列数列,调用情况如下:
var f = fib(5); // 创建generator f.next(); // {value: 0, done: false} f.next(); // {value: 1, done: false} f.next(); // {value: 1, done: false} f.next(); // {value: 2, done: false} f.next(); // {value: 3, done: true} f.next(); // {value: undefined, done: true}
注意最后一行的value是undefined,由于在上一行已经return了,所以这一行没有意义。
(2)使用for…of
直接用for ... of
循环迭代generator对象,这种方式不需要我们自己判断done
:
for (var x of fib(5)) { console.log(x); // 依次输出0, 1, 1, 2, 3 }
标签:ons size gen 添加 字符串连接 初学者 window 自动 函数调用
原文地址:http://www.cnblogs.com/stephen666/p/6749439.html