let塊級作用域
怎么樣更好的認(rèn)識let;就是現(xiàn)有的聲明方式進(jìn)行比較;
1:var的聲明提升會在未初始化變量的時(shí)候提醒undefined;但是不報(bào)錯(cuò);未遵循先定義后使用的邏輯;
但是let是遵循的先定義后使用;未定義就是用的情況會報(bào)錯(cuò);
2:官方中的‘暫時(shí)性死區(qū)’;在代碼塊內(nèi),使用let命令聲明變量之前,該變量都是不可用的。這在語法上,稱為“暫時(shí)性死區(qū)”;但是var不會;如圖;在使用let綁定后;也就綁定了區(qū)塊;在代碼塊內(nèi);let聲明就會出現(xiàn)暫時(shí)死區(qū);
image.png
3:不允許重復(fù)聲明:前提是在統(tǒng)一代碼塊內(nèi);另外函數(shù)內(nèi)部也不可以聲明和和參數(shù)相同的變量;但是下圖的方式可以;也就是剛才說到的在統(tǒng)一代碼塊內(nèi);
image.png
2:塊級作用域存在的意義;
首先防止內(nèi)城變量覆蓋外層變量;第二場景用來計(jì)數(shù)的循環(huán)變量泄露為全局變量;
變量的解構(gòu)【比較重要和常用】
下面僅僅總結(jié)常用的一些語法;
首先截取剩余的數(shù)組;
let [head, ...tail] = [1, 2, 3, 4]; head // 1 tail // [2, 3, 4] 1)
交換變量的值 let x = 1; let y = 2; [x, y] = [y, x];
(2)從函數(shù)返回多個(gè)值 函數(shù)只能返回一個(gè)值,如果要返回多個(gè)值,只能將它們放在數(shù)組或?qū)ο罄锓祷亍S辛私鈽?gòu)賦值,取出這些值就非常方便。 // 返回一個(gè)數(shù)組 function example() { return [1, 2, 3]; } let [a, b, c] = example(); // 返回一個(gè)對象 function example() { return { foo: 1, bar: 2 }; } let { foo, bar } = example();
(4)提取 JSON 數(shù)據(jù) 解構(gòu)賦值對提取 JSON 對象中的數(shù)據(jù),尤其有用。 let jsonData = { id: 42, status: "OK", data: [867, 5309] }; let { id, status, data: number } = jsonData; console.log(id, status, number); // 42, "OK", [867, 5309] 上面代碼可以快速提取 JSON 數(shù)據(jù)的值。
字符串的擴(kuò)展
1:includes(str,開始檢索的下標(biāo));使用includes()來代替indexOf;判斷是否含有某個(gè)字符串;
2:startsWith(str,開始檢索的下標(biāo)):返回布爾值,表示參數(shù)字符串是否在原字符串的頭部。
3:endsWith(str,檢索這個(gè)下標(biāo)前的數(shù)字):返回布爾值,表示參數(shù)字符串是否在原字符串的尾部。
4:repeat方法返回一個(gè)新字符串,表示將原字符串重復(fù)n次
5:padStart()用于頭部補(bǔ)全,padEnd()用于尾部補(bǔ)全;這兩個(gè)方法在
第六章數(shù)值的擴(kuò)展
1:Number.isFinite()用來檢查一個(gè)數(shù)值是否為有限的(finite)。
Number.isFinite(15); // true
Number.isFinite(0.8); // true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isFinite(-Infinity); // false
Number.isFinite('foo'); // false
Number.isFinite('15'); // false
Number.isFinite(true); // false
2:Number.isNaN()用來檢查一個(gè)值是否為NaN。
Number.isNaN(NaN) // true
Number.isNaN(15) // false
Number.isNaN('15') // false
Number.isNaN(true) // false
Number.isNaN(9/NaN) // true
Number.isNaN('true'/0) // true
Number.isNaN('true'/'true') // true
補(bǔ)充:Number.isInteger();Number.isInteger()用來判斷一個(gè)值是否為整數(shù)。需要注意的是,在 JavaScript 內(nèi)部,整數(shù)和浮點(diǎn)數(shù)是同樣的儲存方法,所以3和3.0被視為同一個(gè)值。
對比:
它們與傳統(tǒng)的全局方法isFinite()和isNaN()的區(qū)別在于,傳統(tǒng)方法先調(diào)用Number()將非數(shù)值的值轉(zhuǎn)為數(shù)值,再進(jìn)行判斷,而這兩個(gè)新方法只對數(shù)值有效,Number.isFinite()對于非數(shù)值一律返回false, Number.isNaN()只有對于NaN才返回true,非NaN一律返回false。
isFinite(25) // true
isFinite("25") // true
Number.isFinite(25) // true
Number.isFinite("25") // false
isNaN(NaN) // true
isNaN("NaN") // true
Number.isNaN(NaN) // true
Number.isNaN("NaN") // false
Number.isNaN(1) // false
3:ES6 將全局方法parseInt()和parseFloat(),移植到Number對象上面,行為完全保持不變。
// ES5的寫法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6的寫法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
4:Number.EPSILON;ES6在Number對象上面,新增一個(gè)極小的常量Number.EPSILON。
5.551115123125783e-17 < Number.EPSILON 但是如果這個(gè)誤差能夠小于Number.EPSILON,我們就可以認(rèn)為得到了正確結(jié)果。
5:Math方法的擴(kuò)展;Math.trunc方法用于去除一個(gè)數(shù)的小數(shù)部分,返回整數(shù)部分。
Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0
對于非數(shù)值,Math.trunc內(nèi)部使用Number方法將其先轉(zhuǎn)為數(shù)值。
對于空值和無法截取整數(shù)的值,返回NaN。
Math.trunc('123.456')
Math.trunc(NaN); // NaN
Math.trunc('foo'); // NaN
Math.trunc(); // NaN
函數(shù)擴(kuò)展
1:函數(shù)參數(shù)的默認(rèn)值;
ES6之前的默認(rèn)值顯示方法
function log(x, y) {
y = y || 'World';
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World
上面代碼檢查函數(shù)log的參數(shù)y有沒有賦值,如果沒有,則指定默認(rèn)值為World。這種寫法的缺點(diǎn)在于,如果參數(shù)y賦值了,但是對應(yīng)的布爾值為false,
則該賦值不起作用。就像上面代碼的最后一行,參數(shù)y等于空字符,結(jié)果被改為默認(rèn)值。
為了避免這個(gè)問題,通常需要先判斷一下參數(shù)y是否被賦值,如果沒有,再等于默認(rèn)值。
if (typeof y === 'undefined') {
y = 'World';
}
ES6 允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫在參數(shù)定義的后面。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
注意;
參數(shù)變量是默認(rèn)聲明的,所以不能用let或const再次聲明。
function foo(x = 5) {
let x = 1; // error
const x = 2; // error
}
與結(jié)構(gòu)結(jié)合的用法;
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
基礎(chǔ)用法和注意事項(xiàng):上面代碼只使用了對象的解構(gòu)賦值默認(rèn)值,沒有使用函數(shù)參數(shù)的默認(rèn)值。
只有當(dāng)函數(shù)foo的參數(shù)是一個(gè)對象時(shí),變量x和y才會通過解構(gòu)賦值生成。如果函數(shù)foo調(diào)用時(shí)沒提供參數(shù)
,變量x和y就不會生成,從而報(bào)錯(cuò)。通過提供函數(shù)參數(shù)的默認(rèn)值,就可以避免這種情況。
function fetch(url, { body = '', method = 'GET', headers = {} }) {
console.log(method);
}
fetch('http://example.com', {})
// "GET"
fetch('http://example.com')
// 報(bào)錯(cuò)
// 寫法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 寫法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
上面兩種寫法都對函數(shù)的參數(shù)設(shè)定了默認(rèn)值,區(qū)別是寫法一函數(shù)參數(shù)的默認(rèn)值是空對象,但是設(shè)置了對象解構(gòu)賦值的默認(rèn)值;寫法二函數(shù)參數(shù)的默認(rèn)值是一個(gè)有具體屬性的對象,但是沒有設(shè)置對象解構(gòu)賦值的默認(rèn)值。
// 函數(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ù)默認(rèn)值的位置
// 例一
function f(x = 1, y) {
return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 報(bào)錯(cuò)
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) // 報(bào)錯(cuò)
f(1, undefined, 2) // [1, 5, 2]
rest 參數(shù)
ES6 引入 rest 參數(shù)(形式為...變量名),用于獲取函數(shù)的多余參數(shù),
這樣就不需要使用arguments對象了。rest 參數(shù)搭配的變量是一個(gè)數(shù)組,該變量將多余的參數(shù)放入數(shù)組中。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
//正常將argument變?yōu)閿?shù)組的辦法就是使用var arr = Array.prototype.slice.apply(arguments);
或者Array.prototype.slice.call(arguments).sort();
注意:
1 注意,rest 參數(shù)之后不能再有其他參數(shù)(即只能是最后一個(gè)參數(shù)),否則會報(bào)錯(cuò)
// 報(bào)錯(cuò)
function f(a, ...b, c) {
// ...
}//就會報(bào)錯(cuò)
2: ES2016 做了一點(diǎn)修改,規(guī)定只要函數(shù)參數(shù)使用了默認(rèn)值、解構(gòu)賦值、或者擴(kuò)展運(yùn)算符,那么函數(shù)內(nèi)部就不能顯式設(shè)定為嚴(yán)格模式,否則會報(bào)錯(cuò)。
3:這樣規(guī)定的原因是,函數(shù)內(nèi)部的嚴(yán)格模式,同時(shí)適用于函數(shù)體和函數(shù)參數(shù)。但是,函數(shù)執(zhí)行的時(shí)候,先執(zhí)行函數(shù)參數(shù),然后再執(zhí)行函數(shù)體。
這樣就有一個(gè)不合理的地方,只有從函數(shù)體之中,才能知道參數(shù)是否應(yīng)該以嚴(yán)格模式執(zhí)行,但是參數(shù)卻應(yīng)該先于函數(shù)體執(zhí)行。
// 報(bào)錯(cuò)
function doSomething(value = 070) {
'use strict';
return value;
}
上面代碼中,參數(shù)value的默認(rèn)值是八進(jìn)制數(shù)070,但是嚴(yán)格模式下不能用前綴0表示八進(jìn)制,
所以應(yīng)該報(bào)錯(cuò)。但是實(shí)際上,JavaScript 引擎會先成功執(zhí)行value = 070,然后進(jìn)入函數(shù)體內(nèi)部,發(fā)現(xiàn)需要用嚴(yán)格模式執(zhí)行,這時(shí)才會報(bào)錯(cuò)。
雖然可以先解析函數(shù)體代碼,再執(zhí)行參數(shù)代碼,但是這樣無疑就增加了復(fù)雜性。
因此,標(biāo)準(zhǔn)索性禁止了這種用法,只要參數(shù)使用了默認(rèn)值、解構(gòu)賦值、或者擴(kuò)展運(yùn)算符,就不能顯式指定嚴(yán)格模式。
兩種方法可以規(guī)避這種限制。第一種是設(shè)定全局性的嚴(yán)格模式,這是合法的。第二種是把函數(shù)包在一個(gè)無參數(shù)的立即執(zhí)行函數(shù)里面。
2:箭頭函數(shù)
ES6 允許使用“箭頭”(=>)定義函數(shù)。
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
用法1:與結(jié)構(gòu)的配合
const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {
return person.first + ' ' + person.last;
}
用法2:簡化回調(diào)函數(shù)
[1,2,3].map(function (x) {
return x * x;
});
// 箭頭函數(shù)寫法
[1,2,3].map(x => x * x);
關(guān)于箭頭函數(shù)中this指向問題的變化
var x=11;
var obj={
x:22,
say:function(){
console.log(this);
obj2={
x:33,
say:function(){
console.log(this);
}
}
obj2.say()
}
}
obj.say();//22
數(shù)組的擴(kuò)展屬性;
ES5中數(shù)組方法復(fù)習(xí):
1:擴(kuò)展運(yùn)算符(spread)是三個(gè)點(diǎn)(...)。它好比 rest 參數(shù)的逆運(yùn)算,將一個(gè)數(shù)組轉(zhuǎn)為用逗號分隔的參數(shù)序列
ES5中使用toString的方法
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
1:該運(yùn)算符主要用于函數(shù)調(diào)用
function push(array, ...items) {
array.push(...items);
}
function add(x, y) {
return x + y;
}
var numbers = [4, 38];
add(...numbers) // 42
擴(kuò)展運(yùn)算符與正常的函數(shù)參數(shù)可以結(jié)合使用,非常靈活。
function f(v, w, x, y, z) { }
var args = [0, 1];
f(-1, ...args, 2, ...[3]);
2:替代數(shù)組的 apply 方法 由于擴(kuò)展運(yùn)算符可以展開數(shù)組,所以不再需要apply方法,將數(shù)組轉(zhuǎn)為函數(shù)的參數(shù)了。
// 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);
下面是擴(kuò)展運(yùn)算符取代apply方法的一個(gè)實(shí)際的例子,應(yīng)用Math.max方法,簡化求出一個(gè)數(shù)組最大元素的寫法。
// ES5 的寫法
Math.max.apply(null, [14, 3, 77])
// ES6 的寫法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);
3:另一個(gè)例子是通過push函數(shù),將一個(gè)數(shù)組添加到另一個(gè)數(shù)組的尾部。
// ES5的 寫法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6 的寫法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);
上面代碼的 ES5 寫法中,push方法的參數(shù)不能是數(shù)組,所以只好通過apply方法變通使用push方法。有了擴(kuò)展運(yùn)算符,就可以直接將數(shù)組傳入push方法。
擴(kuò)展運(yùn)算符的應(yīng)用
1:合并數(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' ]
2:與解構(gòu)賦值結(jié)合
// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list
3:擴(kuò)展運(yùn)算符還可以將字符串轉(zhuǎn)為真正的數(shù)組。
[...'hello']
// [ "h", "e", "l", "l", "o" ]
7:數(shù)組實(shí)例的 entries(),keys() 和 values()
ES6 提供三個(gè)新的方法——entries(),keys()和values()——用于遍歷數(shù)組。
它們都返回一個(gè)遍歷器對象(詳見《Iterator》一章),可以用for...of循環(huán)進(jìn)行遍歷,
唯一的區(qū)別是keys()是對鍵名的遍歷、values()是對鍵值的遍歷,entries()是對鍵值對的遍歷。
`for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"`
如果不使用for...of循環(huán),可以手動(dòng)調(diào)用遍歷器對象的next方法,進(jìn)行遍歷。
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']
對象中新增的方法;
var obj={ fun:function(){"函數(shù)內(nèi)容"} } 原來寫法,在簡寫對應(yīng)的屬性的時(shí)候,需要名字相同,和定義的對象的屬性名字相同,
const obj={ fun(){"函數(shù)的內(nèi)容"} } ES6中函數(shù)的寫法
var target={a:1}; var source1={b:2}; var source2={c:3}; Object.assign(target,source1,source2) 這個(gè)時(shí)候target的值就是target={a:1,b:2,c:3} Object.assign屬于淺拷貝;只是拷貝的指針,如果源對象某個(gè)屬性的值是對象,那么目標(biāo)對象拷貝得到的是這個(gè)對象的引用。
:Symbol
數(shù)組的擴(kuò)展
擴(kuò)展運(yùn)算符(...);就是這三個(gè)點(diǎn)