一. let 和 const
1 let
基本用法
ES6新增let
命令,與var
用法類似,但所聲明的變量只在聲明的代碼塊中使用
{
let a=10;
var b=1;
}
a//a is not defined
b//1
var 聲明的變量像是 static 的
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // a[1]->a[10]的值都是10
var a = [];
for (let i = 0; i < 10; i++) {//變量 i 使用 let 聲明只在本輪循環(huán)有效,每次循環(huán) 值都是一個新的變量
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
a[5]();//5
不存在變量提升
let 聲明的變量一定要在聲明后使用,否則報錯
console.log(foo); // 輸出undefined,foo 在此時已經(jīng)存在
console.log(bar); // 報錯ReferenceError
var foo = 2;
let bar = 2;
temporal dead zone
只要塊級作用于存在let
和const
命令, 凡是在聲明之前使用這些變量就會報錯
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError: tmp is not defined
let tmp;
}
不允許重復(fù)聲明
//let不允許在相同作用域內(nèi),重復(fù)聲明同一個變量庶喜。
// 報錯
function () {
let a = 10;
var a = 1;
}
// 報錯
function () {
let a = 10;
let a = 1;
}
//因此,不能在函數(shù)內(nèi)部重新聲明參數(shù)救鲤。d[s
function func(arg) {
let arg; // 報錯
}
function func(arg) {
{
let arg; // 不報錯
}
}
2 塊級作用域
為什么需要塊級作用域久窟?
第一種場景,內(nèi)層變量可能會覆蓋外層變量本缠。
var tmp = new Date();
function f(){
console.log(tmp);
if (false){
var tmp = "hello world";
}
}
f() // undefined 內(nèi)層的tmp變量覆蓋了外層的tmp變量斥扛。
第二種場景,用來計數(shù)的循環(huán)變量泄露為全局變量丹锹。
var s = 'hello';
for (var i = 0; i < s.length; i++){
console.log(s[i]);
}
console.log(i); // 5
ES6的塊級作用域
let實際上為JavaScript新增了塊級作用域稀颁。
function f1() {
let n = 5;
if (true) {
let n = 10;
console.log(n); // 10
}
console.log(n); // 5
}
{
let a = 'secret';
function f() {
return a;
}
}
f() // 報錯
//上面代碼中,塊級作用域外部楣黍,無法調(diào)用塊級作用域內(nèi)部定義的函數(shù)峻村。如果確實需要調(diào)用,就要像下面這樣處理锡凝。
let f;
{
let a = 'secret';
f = function () {
return a;
}
}
f() // "secret"
3 const 命令
const 聲明常量,類似于 java 中的 final,聲明后無法修改其值
'use strict';
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: "PI" is read-only
//常規(guī)模式下不會報錯,但是修改無效
const PI = 3.1415;
PI = 3; // 常規(guī)模式時,重新賦值無效垢啼,但不報錯
PI // 3.1415
const 聲明的變量不得改變值, const 一旦聲明變量則需要給其賦值,不能在后面賦值,這點與 java 不同.
'use strict';
const foo;
// SyntaxError: missing = in const declaration
const的作用域與let命令相同:只在聲明所在的塊級作用域內(nèi)有效窜锯。
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined
不可重復(fù)聲明
var message = "Hello!";
let age = 25;
// 以下兩行都會報錯
const message = "Goodbye!";
const age = 30;
將對象聲明為常量,特性與 java 一樣
const foo = {};
foo.prop = 123;
foo.prop
// 123
foo = {} // TypeError: "foo" is read-only
const a = [];
a.push("Hello"); // 可執(zhí)行
a.length = 0; // 可執(zhí)行
a = ["Dave"]; // 報錯
如果想將對象凍結(jié),使用Object.freeze
方法
const foo = Object.freeze({});
// 常規(guī)模式時,下面一行不起作用芭析;
// 嚴格模式時锚扎,該行會報錯
foo.prop = 123;
4 跨模塊常量
// constants.js 模塊
export const A = 1;
export const B = 3;
export const C = 4;
// test1.js 模塊
import * as constants from './constants';
console.log(constants.A); // 1
console.log(constants.B); // 3
// test2.js 模塊
import {A, B} from './constants';
console.log(A); // 1
console.log(B); // 3
5 全局對象屬性
全局對象是最頂層的對象,在瀏覽器環(huán)境指的是window對象馁启,在Node.js指的是global對象驾孔。ES5之中芍秆,全局對象的屬性與全局變量是等價的。
二. 變量的解構(gòu)賦值
ES6允許按照一定模式翠勉,從數(shù)組和對象中提取值妖啥,對變量進行賦值,這被稱為解構(gòu)(Destructuring)对碌。
1 數(shù)組的解構(gòu)賦值
//以前荆虱,為變量賦值,只能直接指定值朽们。
var a = 1;
var b = 2;
var c = 3;
//ES6允許寫成下面這樣怀读。
var [a, b, c] = [1, 2, 3];
只要等號兩邊的模式相同,左邊的變量就會被賦予對應(yīng)的值
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
不完全解構(gòu)骑脱,即等號左邊的模式菜枷,只匹配一部分的等號右邊的數(shù)組
let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
對于Set
結(jié)構(gòu),也可以使用數(shù)組的解構(gòu)賦值叁丧。
let [x, y, z] = new Set(["a", "b", "c"])
x // "a"
結(jié)構(gòu)賦值允許指定默認值
var [foo = true] = [];
foo // true
[x, y = 'b'] = ['a'] // x='a', y='b'
[x, y = 'b'] = ['a', undefined] // x='a', y='b'
惰性賦值啤誊,在用到的時候才會求值
function f(){
console.log('aaa');
}
let [x = f()] = [1];
//經(jīng)過babel翻譯后的代碼
var _ = 1;
var x = _ === undefined ? f() : _;
默認值可以引用解構(gòu)賦值的其他變量,但該變量必須已經(jīng)聲明歹袁。
let [x = 1, y = x] = []; // x=1; y=1
let [x = 1, y = x] = [2]; // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = []; // ReferenceError
2. 對象的解構(gòu)賦值(重要?姥堋!)
與數(shù)組不同的是条舔,對象屬性沒有次序枫耳,變量必須與屬性同名才能取到正確值
var { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
///相對應(yīng)下面代碼
var _foo$bar = { foo: "aaa", bar: "bbb" };
var bar = _foo$bar.bar;
var foo = _foo$bar.foo;
var { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined
let { log, sin, cos } = Math;
/////////
var log = Math.log;
var sin = Math.sin;
var cos = Math.cos;
變量可以重命名
var { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
////
var _foo$bar = { foo: "aaa", bar: "bbb" };
var baz = _foo$bar.foo;
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
//////////
var obj = { first: 'hello', last: 'world' };
var f = obj.first;
var l = obj.last;
3. 函數(shù)參數(shù)的解構(gòu)賦值
function move({x = 0, y = 0} = {}) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
////--轉(zhuǎn)義后--
function move() {
var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var _ref$x = _ref.x;
var x = _ref$x === undefined ? 0 : _ref$x;
var _ref$y = _ref.y;
var y = _ref$y === undefined ? 0 : _ref$y;
return [x, y];
}
另一種寫法
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
////////
function move() {
var _ref = arguments.length <= 0 || arguments[0] === undefined ? { x: 0, y: 0 } : arguments[0];
var x = _ref.x;
var y = _ref.y;
return [x, y];
}
6.其他解構(gòu)
字符串解構(gòu)
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
let {length : len} = 'hello';
len // 5
數(shù)值解構(gòu)
let {toString: s} = 123;
//////
var _ = 123;
var s = _.toString;
7 用途
交換變量值
[x, y] = [y, x];
/////
var _ref2 = [y, x];
x = _ref2[0];
y = _ref2[1];
函數(shù)返回多個值
// 返回一個數(shù)組
function example() {
return [1, 2, 3];
}
var [a, b, c] = example();
// 返回一個對象
function example() {
return {
foo: 1,
bar: 2
};
}
var { foo, bar } = example();
函數(shù)參數(shù)定義
// 參數(shù)是一組有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3])
// 參數(shù)是一組無次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1})
提取Json數(shù)據(jù)
var jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
}
let { id, status, data: number } = jsonData;
console.log(id, status, number)
// 42, "OK", [867, 5309]
//////
var id = jsonData.id;
var status = jsonData.status;
var number = jsonData.data;
遍歷MAP結(jié)構(gòu)
var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
模塊導(dǎo)入
const { SourceMapConsumer, SourceNode } = require("source-map");
三. 類型
ECMAScript 有 5 種原始類型,即 Undefined孟抗、Null迁杨、Boolean、Number 和 String凄硼。
1. Undefined
如前所述铅协,Undefined 類型只有一個值,即 undefined
摊沉。當聲明的變量未初始化時狐史,該變量的默認值是undefined
。
2. Null
Null 類型只有一個值null
说墨,值 undefined 實際上是從值 null 派生來的
null == undefined;//true
null === undefined;//false
3. Number
八進制數(shù)和十六進制數(shù),ES5 之后八進制用0o
表示
var iNum = 0o70; //070 等于十進制的 56
var iNum = 0x1f; //0x1f 等于十進制的 31
如果要將0b和0x前綴的字符串數(shù)值轉(zhuǎn)為十進制骏全,要使用Number方法。
Number('0b111') // 7
Number('0o10') // 8
4. String
5. 類型轉(zhuǎn)換
轉(zhuǎn)換成字符串 toString()
方法
arrayObject.toString()
booleanObject.toString()
dateObject.toString()
NumberObject.toString()
stringObject.toString()
轉(zhuǎn)換成數(shù)字parseInt() 尼斧、 parseFloat()
var iNum1 = parseInt("12345red"); //返回 12345
var iNum1 = parseInt("0xA"); //返回 10
var iNum1 = parseInt("56.9"); //返回 56
var iNum1 = parseInt("red"); //返回 NaN
var fNum1 = parseFloat("12345red"); //返回 12345
var fNum2 = parseFloat("0xA"); //返回 NaN
var fNum3 = parseFloat("11.2"); //返回 11.2
var fNum4 = parseFloat("11.22.33"); //返回 11.22
var fNum5 = parseFloat("0102"); //返回 102
var fNum1 = parseFloat("red"); //返回 NaN
6.強制類型轉(zhuǎn)換
ECMAScript 中可用的 3 種強制類型轉(zhuǎn)換如下:
- Boolean(value) - 把給定的值轉(zhuǎn)換成 Boolean 型姜贡;
- Number(value) - 把給定的值轉(zhuǎn)換成數(shù)字(可以是整數(shù)或浮點數(shù));
- String(value) - 把給定的值轉(zhuǎn)換成字符串棺棵;
四. 數(shù)組
1. Array.from()
Array.from方法用于將兩類對象轉(zhuǎn)為真正的數(shù)組:類似數(shù)組的對象(array-like object)和可遍歷(iterable)的對象(包括ES6新增的數(shù)據(jù)結(jié)構(gòu)Set和Map)所謂類似數(shù)組的對象楼咳,本質(zhì)特征只有一點熄捍,即必須有l(wèi)ength屬性,因此,任何有l(wèi)ength屬性的對象母怜,都可以通過Array.from方法轉(zhuǎn)為數(shù)組余耽。
Array.from('hello')
// ['h', 'e', 'l', 'l', 'o']
let namesSet = new Set(['a', 'b'])
Array.from(namesSet) // ['a', 'b']
...
運算符也可以將某些結(jié)構(gòu)轉(zhuǎn)換成數(shù)組
function foo() {
var args = [...arguments];
}
////////
function foo() {
var args = [].concat(_slice.call(arguments));
}
var t=[..."helloworld"] //["h","e","l","l","o","w","o","r","l","d"]
Array.from還可以接受第二個參數(shù),用來對每個元素進行處理糙申,將處理后的值放入返回的數(shù)組宾添。
Array.from(arrayLike, x => x * x);
///////
Array.from(arrayLike, function (x) {
return x * x;
});
Array.from([1, 2, 3], (x) => x * x)// [1, 4, 9]
////////
Array.from([1, 2, 3], function (x) {
return x * x;
});
2. Array.of()
Array.of方法用于將一組值,轉(zhuǎn)換為數(shù)組柜裸。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
//Array.of方法可以用下面的代碼模擬實現(xiàn)缕陕。
function ArrayOf(){
return [].slice.call(arguments);
}
3. find() findIndex()
find方法用于找出第一個符合條件的數(shù)組成員,參數(shù)是一個回調(diào)函數(shù)
[1, 4, -5, 10].find((n) => n < 0)//-5
/////
[1, 4, -5, 10].find(function (n) {
return n < 0;
});
[1, 5, 10, 15].find(function(value, index, arr) {//依次為當前的值疙挺、當前的位置和原數(shù)組扛邑。
return value > 9;
}) // 10
findIndex方法用于返回符合條件值的位置,如果沒有符合的返回-1
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
5. fill()
填充數(shù)組
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
6. includes()
用來檢測數(shù)組里是否包含某個值
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
[1, 2, NaN].includes(NaN); // true
7. 數(shù)組推導(dǎo)
使用現(xiàn)有數(shù)組生成新數(shù)組
var a1 = [1, 2, 3, 4];
var a2 = [for (i of a1) i * 2];
a2 // [2, 4, 6, 8]
var years = [ 1954, 1974, 1990, 2006, 2010, 2014 ];
[for (year of years) if (year > 2000) year];
// [ 2006, 2010, 2014 ]
[for (year of years) if (year > 2000) if(year < 2010) year];
// [ 2006]
[for (year of years) if (year > 2000 && year < 2010) year];
// [ 2006]
[for (i of [1, 2, 3]) i * i];
// 等價于
[1, 2, 3].map(function (i) { return i * i });
[for (i of [1,4,2,3,-8]) if (i < 3) i];
// 等價于
[1,4,2,3,-8].filter(function(i) { return i < 3 });
五. 函數(shù)
1. 參數(shù)默認值
ES6可以為函數(shù)參數(shù)設(shè)置默認值
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
var p = new Point();
p // { x: 0, y: 0 }
可以結(jié)合解構(gòu)賦值使用
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined, 5
foo({x: 1}) // 1, 5
foo({x: 1, y: 2}) // 1, 2
foo() // TypeError: Cannot read property 'x' of undefined
function fetch(url, { body = '', method = 'GET', headers = {} }){
console.log(method);
}
fetch('http://example.com', {})
// "GET"
fetch('http://example.com')
// 報錯
上面這種寫法不能省略第二個參數(shù)铐然,如果給第二個參數(shù)默認值則可以省略
function fetch(url, { method = 'GET' } = {}){
console.log(method);
}
fetch('http://example.com')
// "GET"
需注意的情況
// 寫法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 寫法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
// 函數(shù)沒有參數(shù)的情況
m1() // [0, 0]
m2() // [0, 0]
// x和y都有值的情況
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]
// x有值蔬崩,y無值的情況
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]
// x和y都無值的情況
m1({}) // [0, 0];
m2({}) // [undefined, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
參數(shù)默認值的位置
非尾部參數(shù)設(shè)置默認值,這個參數(shù)是無法省略的
function f(x = 1, y) {
return [x, y];
}
///////
function f(x, y) {
if (x === undefined) x = 1;
return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 報錯,其實本身這種語法就是錯誤的
f(undefined, 1) // [1, 1]
function f(x, y = 5, z) {
return [x, y, z];
}
f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 報錯
f(1, undefined, 2) // [1, 5, 2]
函數(shù)的length屬性
指定了默認值以后搀暑,函數(shù)的length屬性沥阳,將返回沒有指定默認值的參數(shù)個數(shù)
(function(a){}).length // 1
(function(a = 5){}).length // 0
(function(a, b, c = 5){}).length // 2
默認值參數(shù)作用域
總結(jié)起來就是參數(shù)使用的變量已經(jīng)生成則作用域是函數(shù)作用域否則更上層的作用域的變量已經(jīng)生成則使用的是上層作用域的值,如果全局內(nèi)都沒有此變量存在則會報錯
情況1:
var x = 1;
function f(x, y = x) {
console.log(y);
}
f(2) // 2
情況2:
let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
情況3:
function f(y = x) {
let x = 2;
console.log(y);
}
f() // ReferenceError: x is not defined
2. rest參數(shù)
就是...變量名
形式的參數(shù),表示不定數(shù)量的參數(shù)
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
rest參數(shù)后不能再有其他參數(shù)
// 報錯
function f(a, ...b, c) {
// ...
}
注意函數(shù)的length屬性不包括rest參數(shù)
(function(a) {}).length // 1
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
3. 擴展運算符(spread)
擴展運算符用...
表示,用于將一個數(shù)組轉(zhuǎn)換為逗號分隔的參數(shù)序列朽砰。
console.log(...[1, 2, 3])//console.log(1,2,3)
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
主要用于函數(shù)調(diào)用
//多么扭曲的例子=_=
function f(v, w, x, y, z) { }
var args = [0, 1];
f(-1, ...args, 2, ...[3]);
替代apply方法
// ES5的寫法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);
// ES6的寫法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f(...args);
// ES5的寫法
Math.max.apply(null, [14, 3, 77])
// ES6的寫法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);
合并數(shù)組
// ES5
[1, 2].concat(more)
// ES6
[1, 2, ...more]
var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];
// ES5的合并數(shù)組
arr1.concat(arr2, arr3));
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6的合并數(shù)組
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
擴展解構(gòu)賦值
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
const [first, ...rest] = [];
first // undefined
rest // []:
const [first, ...rest] = ["foo"];
first // "foo"
rest // []
需要注意的是將擴展運算符用于數(shù)組賦值則只能放在參數(shù)最后一位潮峦,否則會報錯
const [...butLast, last] = [1, 2, 3, 4, 5];
// 報錯
const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 報錯
map set結(jié)構(gòu)
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]
4. name屬性
用于返回該函數(shù)的函數(shù)名
var func1 = function () {};
// ES5
func1.name // ""
// ES6
func1.name // "func1"
5.箭頭函數(shù)
es6可以使用=>
定義函數(shù), =>
左邊代表參數(shù)名
var f=v=>v;
///等價于///
var f = function(v) {
return v;
};
如果箭頭函數(shù)不需要參數(shù)或者需要多個參數(shù),使用括號代表參數(shù)部分
var f = () => 5;
// 等同于
var f = function (){ return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
如果方法體多于一行,則需要用{}
并使用return 返回
var sum = (num1, num2) => { return num1 + num2; }
///////
var sum = function sum(num1, num2) {
return num1 + num2;
};
如果箭頭部分直接返回對象,則必須在對象外邊加上括號
var getTempItem = id => ({id:id,name:"Temp"});
///////
var getTempItem = function getTempItem(id) {
return { id: id, name: "Temp" };
};
6. 函數(shù)綁定
函數(shù)綁定運算符是::
,雙冒號左邊是對象,右邊是一個函數(shù),運算符會自動將左邊的對象作為上下文環(huán)境,綁定到右邊的函數(shù)上
foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return obj::hasOwnProperty(key);
}
如果雙冒號左邊為空爆安,右邊是一個對象的方法,則等于將該方法綁定在該對象上面。
var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;
let log = ::console.log;
// 等同于
var log = console.log.bind(console);
7. 尾調(diào)用優(yōu)化
尾調(diào)用(Tail Call)是函數(shù)式編程的一個重要概念,就是指某個函數(shù)的最后一步是調(diào)用另一個函數(shù)。
function f(x){
return g(x);
}
尾調(diào)用優(yōu)化即只保留內(nèi)層函數(shù)的調(diào)用幀.
如下例子,g函數(shù)調(diào)用后f就結(jié)束,則在最后執(zhí)行g(shù)的時候f可以不用保留
function f() {
let m = 1;
let n = 2;
return g(m + n);
}
f();
// 等同于
function f() {
return g(3);
}
f();
// 等同于
g(3);
尾遞歸
通過尾調(diào)用優(yōu)化可以節(jié)省尾遞歸產(chǎn)生的棧內(nèi)存
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
factorial(5, 1) // 120
尾調(diào)用優(yōu)化只在嚴格模式下開啟
六. 對象
七. Proxy和Reflect
八. Set和Map數(shù)據(jù)結(jié)構(gòu)
1. Set
初始化
var s = new Set();
var set = new Set([1, 2, 3, 4, 4])
添加數(shù)據(jù)
var s = new Set();
s.add(1);
兩個對象總是不相等的薪伏。
let set = new Set();
set.add({})
set.size // 1
set.add({})
set.size // 2
操作方法:
- add(value):添加某個值,返回Set結(jié)構(gòu)本身粗仓。
- delete(value):刪除某個值嫁怀,返回一個布爾值,表示刪除是否成功借浊。
- has(value):返回一個布爾值眶掌,表示該值是否為Set的成員。
- clear():清除所有成員巴碗,沒有返回值。
s.add(1).add(2).add(2);
// 注意2被加入了兩次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
遍歷操作:
- keys():返回一個鍵名的遍歷器
- values():返回一個鍵值的遍歷器
- entries():返回一個鍵值對的遍歷器
- forEach():使用回調(diào)函數(shù)遍歷每個成員
let set = new Set(['red', 'green', 'blue']);
for ( let item of set.keys() ){
console.log(item);
}
// red
// green
// blue
for ( let item of set.values() ){
console.log(item);
}
// red
// green
// blue
for ( let item of set.entries() ){
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
2. Map
初始化
var m = new Map();
var o = {p: "Hello World"};
m.set(o, "content")
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
var map = new Map([["name", "張三"], ["title", "Author"]]);
map.size // 2
map.has("name") // true
map.get("name") // "張三"
map.has("title") // true
map.get("title") // "Author"
操作方法
-
size
屬性: 返回Map成員總數(shù) -
set(key, value)
:設(shè)置key所對應(yīng)的鍵值 -
get(key)
:讀取key對應(yīng)的鍵值如果找不到key即寒,返回undefined -
has(key)
:返回一個布爾值橡淆,表示某個鍵是否在Map數(shù)據(jù)結(jié)構(gòu)中 -
delete(key)
:刪除某個鍵召噩,返回true。如果刪除失敗逸爵,返回false -
clear()
:方法清除所有成員具滴,沒有返回值
遍歷方法
- keys():返回鍵名的遍歷器。
- values():返回鍵值的遍歷器师倔。
- entries():返回所有成員的遍歷器构韵。
- forEach():遍歷Map的所有成員。
let map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
與其他結(jié)構(gòu)互轉(zhuǎn)(TODO:)
九. Class
十. 異步
JS 是單線程模型的,在瀏覽器環(huán)境下 JS和其他瀏覽器任務(wù)共享同一個線程,在一個任務(wù)執(zhí)行過程中會阻塞其他任務(wù).
我們不能阻塞主線程太長時間,異步就是用來解決這種情況的.
常見的異步編程有以下四種:
- 回調(diào)函數(shù)
- 事件監(jiān)聽
- 發(fā)布/訂閱
- Promise 對象
1. 回調(diào)函數(shù)
2. 事件監(jiān)聽
在瀏覽器下的 js 編程中我們經(jīng)常會寫到這樣的事件監(jiān)聽代碼
var image1=document.querySelector('.img-1');
image1.addEventListener('load',()={
//圖片加載完成
});
image1.addEventListener('error',()=>{
//出現(xiàn)錯誤
})
3. 發(fā)布/訂閱
4. Promise(重要!!)
Promise 是異步編程的一種解決方案
4.1 基本用法
生成 Promise 實例
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise 構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)有兩個參數(shù)resolve
和reject
.這兩個函數(shù)由 JS 引擎提供不用自己部署.
resolve
函數(shù)作用是將 Promise 對象的狀態(tài)從未完成(Pending)變成成功(Reolved)在異步操作成功時調(diào)用趋艘,并將異步操作的結(jié)果疲恢,作為參數(shù)傳遞出去;reject函數(shù)的作用是瓷胧,將Promise對象的狀態(tài)未完變?yōu)?strong>失敗显拳,在異步操作失敗時調(diào)用,并將異步操作報出的錯誤搓萧,作為參數(shù)傳遞出去杂数。
Promise實例生成以后,可以用then方法分別指定Resolved狀態(tài)和Reject狀態(tài)的回調(diào)函數(shù)瘸洛。
promise.then(function(value) {
// success
}, function(value) {
// failure
});