標籤: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(); // 1g.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); // 建立generatorf.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}
js的ES6特性