(一) Promise
promise是承諾,允諾的意思危尿。(是將來發(fā)生的事)
(1) Promise是什么呐萌?
- 從用途上來說:
(1) promise主要用于異步計(jì)算。
(2) 可以將異步操作隊(duì)列化脚线,按照期望的順序執(zhí)行搁胆,返回符合預(yù)期的結(jié)果。
(3) 可以在對(duì)象之間傳遞和操作promise邮绿,幫助我們處理隊(duì)列渠旁。 - 從語法上說:
Promise 是一個(gè)對(duì)象,從它可以獲取異步操作的消息船逮。
Promise 提供統(tǒng)一的 API顾腊,各種異步操作都可以用同樣的方法進(jìn)行處理 - executor是執(zhí)行器,執(zhí)行者的意思
(2) Promise對(duì)象的特點(diǎn)挖胃?
(1) 對(duì)象的狀態(tài)不受外界影響杂靶。
Promise對(duì)象代表一個(gè)異步操作,有三種狀態(tài):(--------三種狀態(tài)--------)
pending(進(jìn)行中)
fulfilled(已成功)
rejected(已失斀囱肌)
只有異步操作的結(jié)果吗垮,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無法改變這個(gè)狀態(tài)凹髓。(2) 一旦狀態(tài)改變烁登,就不會(huì)再變,任何時(shí)候都可以得到這個(gè)結(jié)果蔚舀。
Promise對(duì)象的狀態(tài)改變饵沧,只有兩種可能:(--------兩種改變--------)
從pending變?yōu)閒ulfilled
從pending變?yōu)閞ejected
只要這兩種情況發(fā)生,狀態(tài)就凝固赌躺,不會(huì)再變狼牺,會(huì)一直保持這個(gè)結(jié)果。
如果改變已經(jīng)發(fā)生了礼患,你再對(duì)Promise對(duì)象添加回調(diào)函數(shù)是钥,也會(huì)立即得到這個(gè)結(jié)果掠归。這與事件(Event)完全不同,事件的特點(diǎn)是悄泥,如果你錯(cuò)過了它拂到,再去監(jiān)聽,是得不到結(jié)果的码泞。
(3) Promise的缺點(diǎn)兄旬?
- (1) 無法取消Promise,一旦新建它就會(huì)立即執(zhí)行余寥,無法中途取消领铐。
- (2) 如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯(cuò)誤宋舷,不會(huì)反應(yīng)到外部绪撵。
- (3) 當(dāng)處于pending狀態(tài)時(shí),無法得知目前進(jìn)展到哪一個(gè)階段(剛剛開始還是即將完成)
(4) Promise的基本用法祝蝠?
ES6 規(guī)定音诈,Promise對(duì)象是一個(gè)構(gòu)造函數(shù),用來生成Promise實(shí)例绎狭。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value); // 異步操作成功時(shí)調(diào)用细溅,并將異步操作的結(jié)果作為參數(shù)傳遞出去
} else {
reject(error); // 異步操作失敗時(shí)調(diào)用,并將異步操作的結(jié)果作為參數(shù)傳遞出去
}
});
promise.then(function(value) { // 這兩個(gè)函數(shù)都接受Promise對(duì)象傳出的值作為參數(shù)儡嘶。
// success // 成功時(shí)喇聊,調(diào)用第一個(gè)函數(shù)
}, function(error) {
// failure // 失敗時(shí),調(diào)用第二個(gè)函數(shù)
});
說明:
(1) promise構(gòu)造函數(shù)蹦狂,接受一個(gè)函數(shù)作為參數(shù)誓篱。
(2) 這個(gè)函數(shù)又有兩個(gè)參數(shù),接受 resolve 和 reject 這兩個(gè)參數(shù)凯楔。
(3) resolve 和 reject 也是函數(shù)
resolve 函數(shù)的作用:
將Promise對(duì)象的狀態(tài)從pending 變?yōu)?resolved窜骄,
在異步操作成功時(shí)調(diào)用,并將異步操作的結(jié)果摆屯,作為參數(shù)傳遞出去邻遏。!!!
reject 函數(shù)的作用:
將Promise對(duì)象的狀態(tài)從“從 pending 變?yōu)?rejected,
在異步操作失敗時(shí)調(diào)用鸥拧,并將異步操作報(bào)出的錯(cuò)誤党远,作為參數(shù)傳遞出去削解。!!!
(5) Promise 新建后就會(huì)立即執(zhí)行?
componentDidMount() {
let p = new Promise((resolve,reject) => {
console.log('1')
resolve();
})
p.then(result => {
console.log('2')
})
console.log('3')
}
// 執(zhí)行結(jié)果:
// 1
// 3
// 2
說明:
上面代碼中富弦,Promise 新建后立即執(zhí)行,所以首先輸出的是 1 氛驮。
然后腕柜,then方法指定的回調(diào)函數(shù),將在當(dāng)前腳本所有同步任務(wù)執(zhí)行完才會(huì)執(zhí)行,所以 2 最后輸出盏缤。
(6) 用Promise封裝一個(gè)ajax請(qǐng)求
Ajax
ajax是Asynchronous JavaScript and XML
的縮寫(異步的 JavaScript 和 XML)
通過在后臺(tái)與服務(wù)器進(jìn)行少量數(shù)據(jù)交換砰蠢,AJAX 可以使網(wǎng)頁實(shí)現(xiàn)異步更新。這意味著可以在不重新加載整個(gè)網(wǎng)頁的情況下唉铜,對(duì)網(wǎng)頁的某部分進(jìn)行更新台舱。XMLHttpRequest 對(duì)象
實(shí)例化對(duì)象:
const xmlhttp= new XMLHttpRequest()
- open(method,url,async)
xmlhttp.open("GET","test1.txt",true);
open(method,url,async) // 規(guī)定請(qǐng)求的類型、URL 潭流、是否異步處理請(qǐng)求
// method:請(qǐng)求的類型骨饿;GET 或 POST
// url:文件在服務(wù)器上的位置
// async:true(異步)或 false(同步)
- send(string)
xmlhttp.send();
send(string) // 將請(qǐng)求發(fā)送到服務(wù)器烂瘫。
string:僅用于 POST 請(qǐng)求
- setRequestHeader(header,value)
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
// xmlhttp.setRequestHeader("Accept", "application/json");
setRequestHeader(header,value) // 向請(qǐng)求添加 HTTP 頭。
// header: 規(guī)定頭的名稱
// value: 規(guī)定頭的值
- responseText
- responseXML
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
responseText // 獲得字符串形式的響應(yīng)數(shù)據(jù)。
responseXML // 獲得 XML 形式的響應(yīng)數(shù)據(jù)脐往。
- onreadystatechange 事件
onreadystatechange: 存儲(chǔ)函數(shù)(或函數(shù)名),每當(dāng) readyState 屬性改變時(shí)再菊,就會(huì)調(diào)用該函數(shù)摹闽。
readyState存有 XMLHttpRequest 的狀態(tài)。從 0 到 4 發(fā)生變化根盒。
// 0: 請(qǐng)求未初始化
// 1: 服務(wù)器連接已建立
// 2: 請(qǐng)求已接收
// 3: 請(qǐng)求處理中
// 4: 請(qǐng)求已完成钳幅,且響應(yīng)已就緒
status
// 200: "OK"
// 404: 未找到頁面
完整例子:
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) { // 沒完成就返回,readyState有0炎滞,1贡这,2,3厂榛,4幾種狀態(tài)
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
//reject函數(shù)的參數(shù)通常是Error對(duì)象的實(shí)例盖矫,表示拋出的錯(cuò)誤;
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出錯(cuò)了', error);
});
(二) this關(guān)鍵字
(1) 涵義
(1) 簡單說击奶,this就是屬性或方法“當(dāng)前”所在的對(duì)象辈双。
JavaScript 語言之中,一切皆對(duì)象柜砾,運(yùn)行環(huán)境也是對(duì)象湃望,所以函數(shù)都是在某個(gè)對(duì)象之中運(yùn)行,this就是函數(shù)運(yùn)行時(shí)所在的對(duì)象(環(huán)境)痰驱。
(2) this總是返回一個(gè)對(duì)象
- this可以用在構(gòu)造函數(shù)之中证芭,表示實(shí)例對(duì)象
- this還可以用在別的場合
- 不管是什么場合,this都有一個(gè)共同點(diǎn):它總是返回一個(gè)對(duì)象担映。
var person = {
name: '張三',
describe: function () {
return '姓名:'+ this.name;
}
};
person.describe()
// "姓名:張三"
// 說明:
(1) this.name表示name屬性當(dāng)前所在的那個(gè)對(duì)象
(2) this.name在describe方法中調(diào)用废士,describe方法當(dāng)前所在的對(duì)象是person
(3) 所以this指向person對(duì)象,this.name就是person.name
(3) 由于對(duì)象的屬性可以賦給另一個(gè)對(duì)象蝇完,所以屬性所在的當(dāng)前對(duì)象是可變的官硝,即this的指向是可變的矗蕊。
var A = {
name: '張三',
describe: function () {
return '姓名:'+ this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe;
B.describe()
// "姓名:李四"
// 說明:
(1) A.describe屬性 賦值給 B對(duì)象后 this的指向發(fā)生變化
(2) describe方法中的this由 指向A對(duì)象 變?yōu)?指向B對(duì)象 (B.describe就表示describe方法當(dāng)前所在的對(duì)象是B)
(3) 所以 this.name 指向 B.name
(4) 只要函數(shù)被賦給另一個(gè)變量,this的指向就會(huì)變氢架。
- JavaScript 語言之中傻咖,一切皆對(duì)象,運(yùn)行環(huán)境也是對(duì)象岖研,所以函數(shù)都是在某個(gè)對(duì)象之中運(yùn)行卿操,this就是函數(shù)運(yùn)行時(shí)所在的對(duì)象(環(huán)境)。
var A = {
name: '張三',
describe: function () {
return '姓名:'+ this.name;
}
};
var name = '李四';
var f = A.describe; // 只要函數(shù)被賦值給一個(gè)變量孙援,函數(shù)內(nèi)部的this指向 變量 運(yùn)行時(shí)所在的 對(duì)象
f() // "姓名:李四"
// 說明:
上面代碼中硬纤,A.describe被賦值給變量f,內(nèi)部的this就會(huì)指向f運(yùn)行時(shí)所在的對(duì)象(本例是頂層對(duì)象)赃磨。
(2) this 關(guān)鍵字使用場合
(1) 全局環(huán)境
this === window // true
function f() {
console.log(this === window); // this是函數(shù)運(yùn)行時(shí)所在的對(duì)象
}
f() // true // 函數(shù)在這里運(yùn)行筝家,所在的對(duì)象是頂層對(duì)象 window
// 不管是不是在函數(shù)內(nèi)部,只要是在全局環(huán)境下運(yùn)行邻辉,this就是指頂層對(duì)象window
(2) 構(gòu)造函數(shù)
- 構(gòu)造函數(shù)中的this溪王,指的是實(shí)例對(duì)象。
(3)對(duì)象的方法
- 如果對(duì)象的方法里面包含this值骇,this的指向就是方法運(yùn)行時(shí)所在的對(duì)象莹菱。該方法賦值給另一個(gè)對(duì)象,就會(huì)改變this的指向
- 如果this所在的方法不在對(duì)象的第一層吱瘩,這時(shí)this只是指向當(dāng)前一層的對(duì)象道伟,而不會(huì)繼承更上面的層。
var a = {
p: 'Hello',
b: {
m: function() { // this指向 m方法所在的對(duì)象 b使碾, b對(duì)象中沒有 p屬性
console.log(this.p);
}
}
};
a.b.m() // undefined
(3) this 注意點(diǎn)
(1) 避免多層 this
- 解決 多層this指向改變的辦法:
事實(shí)上蜜徽,使用一個(gè)變量固定this的值,然后內(nèi)層函數(shù)調(diào)用這個(gè)變量票摇,是非常常見的做法
var o = {
f1: function () {
console.log(this); // 該this指向方法運(yùn)行時(shí)所在的對(duì)象拘鞋,0.f1(),即指向 o對(duì)象
var f2 = function () {
console.log(this); // 賦值給變量矢门,指向var定義的變量所在的對(duì)象 window
}();
}
}
o.f1()
// Object
// Window
----------------------------------------------------------------------------
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = function() {
console.log(that);
}();
}
}
o.f1()
// Object
// Object
// 定義變量that盆色,固定指向外層的this,然后在內(nèi)層使用that祟剔,就不會(huì)發(fā)生this指向的改變
(4) 綁定 this 的方法
JavaScript 提供了call隔躲、apply、bind這三個(gè)方法物延,來切換/固定this的指向宣旱。
call 方法
Function.prototype.call()
- 函數(shù)實(shí)例的call方法,可以指定函數(shù)內(nèi)部this的指向(即函數(shù)執(zhí)行時(shí)所在的作用域)教届,然后在所指定的作用域中响鹃,調(diào)用該函數(shù)。
- call方法的參數(shù)案训,是一個(gè)對(duì)象买置。如果參數(shù)為空、null和undefined强霎,則默認(rèn)傳入全局對(duì)象忿项。
- call方法的第一個(gè)參數(shù)就是this所要指向的那個(gè)對(duì)象,后面的參數(shù)則是函數(shù)調(diào)用時(shí)所需的參數(shù)城舞。
例一:
var obj = {};
var f = function () {
return this;
};
f() === window // true
f.call(obj) === obj // true
-----------------------------------------------
例二:
var av = {
name: 'wang',
age: function() {
return this.name
}
}
var ac = {
name: 'li'
}
av.age.call(ac) // li
--------------------------------------------------
call方法的參數(shù)轩触,應(yīng)該是一個(gè)對(duì)象。如果參數(shù)為空家夺、null和undefined脱柱,則默認(rèn)傳入全局對(duì)象。
var n = 123;
var obj = { n: 456 };
function a() {
console.log(this.n);
}
a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456
--------------------------------------------------
function add(a, b) {
return a + b;
}
add.call(this, 1, 2) // 3
上面代碼中拉馋,call方法指定函數(shù)add內(nèi)部的this綁定當(dāng)前環(huán)境(對(duì)象)榨为,并且參數(shù)為1和2,
因此函數(shù)add運(yùn)行后得到3煌茴。
apply 方法
Function.prototype.apply()
- apply方法的作用與call方法類似随闺,也是改變this指向,然后再調(diào)用該函數(shù)蔓腐。唯一的區(qū)別就是矩乐,它接收一個(gè)數(shù)組作為函數(shù)執(zhí)行時(shí)的參數(shù),使用格式如下回论。
- apply方法的第一個(gè)參數(shù)也是this所要指向的那個(gè)對(duì)象散罕,如果設(shè)為null或undefined,則等同于指定全局對(duì)象傀蓉。
- 第二個(gè)參數(shù)則是一個(gè)數(shù)組笨使,該數(shù)組的所有成員依次作為參數(shù),傳入原函數(shù)僚害。原函數(shù)的參數(shù)硫椰,在call方法中必須一個(gè)個(gè)添加,但是在apply方法中萨蚕,必須以數(shù)組形式添加靶草。
- apply() 只有兩個(gè)參數(shù),第一個(gè)是this所要指向的對(duì)象岳遥,第二個(gè)是數(shù)組
func.apply(thisValue, [arg1, arg2, ...])
- 利用Function.prototype.applay() 方法 做一些算法
1.計(jì)算數(shù)組中最大的值奕翔?
var arr = [1,333,444,34,56,3];
Math.max.apply(null,arr) // Math.max方法返回參數(shù)之中最大的那值 Math.max(2, -1, 5) => 5
444
2.將數(shù)組的空元素變?yōu)閡ndefined
Array.apply(null, ['a', ,'b'])
// [ 'a', undefined, 'b' ]
-----------------------------------------------------
空元素與undefined的差別在于,數(shù)組的forEach方法會(huì)跳過空元素浩蓉,但是不會(huì)跳過undefined派继。
var a = ['a', , 'b'];
function print(i) {
console.log(i);
}
a.forEach(print)
// a
// b
Array.apply(null, a).forEach(print)
// a
// undefined
// b
bind 方法
Function.prototype.bind()
- bind方法用于將函數(shù)體內(nèi)的this綁定到某個(gè)對(duì)象宾袜,然后返回一個(gè)新函數(shù)。
- bind方法的參數(shù)就是所要綁定this的對(duì)象
- 區(qū)別:
bind方法只是返回新的函數(shù)驾窟,但是函數(shù)不是自己執(zhí)行庆猫。而外兩個(gè)方法call和apply都會(huì)綁定this指向后,調(diào)用該函數(shù)绅络。 - 如果bind方法的第一個(gè)參數(shù)是null或undefined月培,等于將this綁定到全局對(duì)象,函數(shù)運(yùn)行時(shí)this指向頂層對(duì)象(瀏覽器為window)恩急。
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var func = counter.inc.bind(counter);
func();
counter.count // 1
上面代碼中杉畜,counter.inc方法被賦值給變量func。
這時(shí)必須用bind方法將inc內(nèi)部的this衷恭,綁定到counter此叠,否則就會(huì)出錯(cuò)。
-------------------------------------------------------------------------------
this綁定到其他對(duì)象也是可以的随珠。
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var obj = {
count: 100
};
var func = counter.inc.bind(obj);
func();
- bind還可以接受更多的參數(shù)拌蜘,將這些參數(shù)綁定原函數(shù)的參數(shù)。
var add = function (x, y) {
return x * this.m + y * this.n; // x是4牙丽, this.m是2简卧,y是1, this.n是2
}
var obj = {
m: 2,
n: 2
};
var newAdd = add.bind(obj, 4);
newAdd(1) // 10
上面代碼中烤芦,bind方法除了綁定this對(duì)象举娩,還將add函數(shù)的第一個(gè)參數(shù)x綁定成5,
然后返回一個(gè)新函數(shù)newAdd构罗,這個(gè)函數(shù)只要再接受一個(gè)參數(shù)y就能運(yùn)行了铜涉。
- bind結(jié)合回調(diào)函數(shù)使用
var counter = {
count: 0,
inc: function () {
'use strict';
this.count++;
}
};
function callIt(callback) {
callback();
}
callIt(counter.inc.bind(counter));
counter.count // 1
使用bind需要注意的點(diǎn)
- 有一種情況比較隱蔽,就是某些數(shù)組方法可以接受一個(gè)函數(shù)當(dāng)作參數(shù)遂唧。這些函數(shù)內(nèi)部的this指向芙代,很可能也會(huì)出錯(cuò)。
var obj = {
name: '張三',
times: [1, 2, 3],
print: function () { // 對(duì)象內(nèi)的方法中的this盖彭,指向方法運(yùn)行時(shí)所在的對(duì)象纹烹,this.times 指向 obj
this.times.forEach(function (n) {
console.log(this.name); // 但是里層的this.name 指向的是 window對(duì)象
});
}
};
obj.print()
// 沒有任何輸出
上面代碼中,obj.print內(nèi)部this.times的this是指向obj的召边,這個(gè)沒有問題铺呵。
但是,forEach方法的回調(diào)函數(shù)內(nèi)部的this.name卻是指向全局對(duì)象隧熙,導(dǎo)致沒有辦法取到值片挂。
--------------------------------------------------------------------
解決辦法:
var obj = {
name: '張三',
times: [1, 2, 3],
print: function () {
// 或者在這里定義一個(gè) var that = this,然后that.times.forEach(.....that.name.....)
this.times.forEach(
function (n) {
console.log(this.name);
}.bind(this) // 對(duì)里層的函數(shù)綁定this要指向的對(duì)象。
);
}
};
obj.print()
// 張三
// 張三
// 張三
- 結(jié)合call方法使用!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!(重要)
[1, 2, 3].slice(0, 1) // [1]
// 等同于
Array.prototype.slice.call([1, 2, 3], 0, 1) // [1]
上面的代碼中音念,數(shù)組的slice方法從[1, 2, 3]里面沪饺,按照指定位置和長度切分出另一個(gè)數(shù)組。
這樣做的本質(zhì)是:
在[1, 2, 3]上面調(diào)用Array.prototype.slice方法闷愤,因此可以用call方法表達(dá)這個(gè)過程整葡,得到同樣的結(jié)果。
-------------------------------------
call方法實(shí)質(zhì)上是調(diào)用Function.prototype.call方法肝谭,因此上面的表達(dá)式可以用bind方法改寫掘宪。
var slice = Function.prototype.call.bind(Array.prototype.slice);
slice([1, 2, 3], 0, 1) // [1]
// 上面代碼的含義就是蛾扇,將Array.prototype.slice變成Function.prototype.call方法所在的對(duì)象攘烛,
// 調(diào)用時(shí)就變成了Array.prototype.slice.call。
類似的寫法還可以用于其他數(shù)組方法镀首。
(1) let 命令
ES6 新增了let命令坟漱,用來聲明變量。
- 它的用法類似于var更哄,但是所聲明的變量芋齿,只在let命令所在的代碼塊內(nèi)有效,外部無效成翩。
{
let a = 10; // let聲明的變量觅捆,只在let命令所在的代碼塊內(nèi)有效,塊級(jí)作用域
var b = 1;
}
a // ReferenceError: a is not defined. // 代碼塊外部無法訪問let變量
b // 1 // var可以
-------------------------------------------------------------
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i);
// ReferenceError: i is not defined
說明:計(jì)數(shù)器i只在for循環(huán)體內(nèi)有效麻敌,在循環(huán)體外引用就會(huì)報(bào)錯(cuò)栅炒。
-------------------------------------------------------------
for循環(huán)還有一個(gè)特別之處,就是設(shè)置循環(huán)變量的那部分是一個(gè)父作用域术羔,而循環(huán)體內(nèi)部是一個(gè)單獨(dú)的子作用域赢赊。
for (let i = 0; i < 3; i++) { // 循環(huán)變量部分,父作用域
let i = 'abc'; // 循環(huán)體部分內(nèi)部是级历,一個(gè)單獨(dú)的子作用域
console.log(i);
}
// abc
// abc
// abc
上面代碼正確運(yùn)行释移,輸出了 3 次abc。
這表明函數(shù)內(nèi)部的變量i與循環(huán)變量i不在同一個(gè)作用域寥殖,有各自單獨(dú)的作用域玩讳。
- let不存在變量提升
(1) var聲明的變量存在變量提升,變量可以在聲明前使用嚼贡,值為undefined
(2) let命令所聲明的變量一定要在聲明后使用锋边,否則報(bào)錯(cuò)。
- 暫時(shí)性死區(qū)
在代碼塊內(nèi)编曼,使用let命令聲明變量之前豆巨,該變量都是不可用的。這在語法上掐场,稱為“暫時(shí)性死區(qū)”(temporal dead zone往扔,簡稱 TDZ)贩猎。
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp; // 塊級(jí)作用域內(nèi),let聲明tmp變量前萍膛,先給tmp變量賦值吭服,報(bào)錯(cuò)
}
存在全局變量tmp,但是塊級(jí)作用域內(nèi)let又聲明了一個(gè)局部變量tmp蝗罗,導(dǎo)致后者綁定這個(gè)塊級(jí)作用域艇棕,
所以在let聲明變量前,對(duì)tmp賦值會(huì)報(bào)錯(cuò)串塑。
------------------------------------------------------------
“暫時(shí)性死區(qū)”也意味著typeof不再是一個(gè)百分之百安全的操作沼琉。
typeof x; // ReferenceError
let x;
------------------------------------------------------------
function bar(x = y, y = 2) { // y在聲明前就賦值給x,報(bào)錯(cuò)
return [x, y];
}
bar(); // 報(bào)錯(cuò)
------------------------------------------------------------
function bar(x = 2, y = x) { // 先聲明了x值是2桩匪, 再賦值給y打瘪,正確
return [x, y];
}
bar(); // [2, 2]
------------------------------------------------------------
// 不報(bào)錯(cuò)
var x = x;
// 報(bào)錯(cuò)
let x = x;
- 不允許重復(fù)聲明
let不允許在相同作用域內(nèi),重復(fù)聲明同一個(gè)變量傻昙。
// 報(bào)錯(cuò)
function func() {
let a = 10;
var a = 1;
}
// 報(bào)錯(cuò)
function func() {
let a = 10;
let a = 1;
}
---------------------------------------------------------
因此闺骚,不能在函數(shù)內(nèi)部重新聲明參數(shù)。
function func(arg) {
let arg; // 報(bào)錯(cuò)
}
function func(arg) { // arg屬于不同的作用域妆档,和for類似
{
let arg; // 不報(bào)錯(cuò)
}
}
(2) 塊級(jí)作用域
- es5有全局作用域和函數(shù)作用域:
函數(shù)外聲明的變量是全局變量僻爽,函數(shù)內(nèi)聲明的變量是局部變量,函數(shù)內(nèi)部可以使用全局變量贾惦,但函數(shù)外部無法使用函數(shù)內(nèi)定義的局部變量胸梆,(可以用閉包解決) - 只有全局作用域和函數(shù)作用域帶來的不合理場景:
一是:內(nèi)層變量可能會(huì)覆蓋外層變量。
二是:用來計(jì)數(shù)的循環(huán)變量泄露為全局變量纤虽。比如for循環(huán)中的計(jì)數(shù)變量i - 塊級(jí)作用域
function f1() {
let n = 5; // let命令乳绕,外層代碼塊不受內(nèi)層代碼塊的影響,在外層代碼塊內(nèi)逼纸,打印變量洋措,仍然是5
// 如果兩次都是var命令,則里層會(huì)覆蓋外層杰刽,最后結(jié)果輸出是10
if (true) {
let n = 10;
}
console.log(n); // 5
}
上面的函數(shù)有兩個(gè)代碼塊菠发,都聲明了變量n,運(yùn)行后輸出 5贺嫂。
這表示外層代碼塊不受內(nèi)層代碼塊的影響滓鸠。如果兩次都使用var定義變量n,最后輸出的值才是 10
- ES6 允許塊級(jí)作用域的任意嵌套第喳。
(3) const命令
- const聲明一個(gè)只讀的常量糜俗。一旦聲明,常量的值就不能改變。
const a = 10;
a = 20; // const聲明的常量不能被改變
console.log(a) // 報(bào)錯(cuò)
- const聲明的變量不得改變值悠抹,這意味著珠月,const一旦聲明變量,就必須立即初始化楔敌,不能留到以后賦值啤挎。
const foo;
// SyntaxError: Missing initializer in const declaration
上面代碼表示,對(duì)于const來說卵凑,只聲明不賦值庆聘,就會(huì)報(bào)錯(cuò)。
- const的作用域與let命令相同:只在聲明所在的塊級(jí)作用域內(nèi)有效勺卢。
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined
// 在const聲明所在的塊級(jí)作用域外無法使用伙判。
- const命令聲明的常量也是不提升,同樣存在暫時(shí)性死區(qū)值漫,只能在聲明的位置后面使用澳腹。
- const聲明的常量织盼,也與let一樣不可重復(fù)聲明杨何。
const命令 本質(zhì)
- const實(shí)際上保證的,并不是變量的值不得改動(dòng)沥邻,而是變量指向的那個(gè)內(nèi)存地址不得改動(dòng)危虱。對(duì)于簡單類型的數(shù)據(jù)(數(shù)值、字符串唐全、布爾值)埃跷,值就保存在變量指向的那個(gè)內(nèi)存地址,因此等同于常量邮利。但對(duì)于復(fù)合類型的數(shù)據(jù)(主要是對(duì)象和數(shù)組)弥雹,變量指向的內(nèi)存地址,保存的只是一個(gè)指針延届,const只能保證這個(gè)指針是固定的剪勿,至于它指向的數(shù)據(jù)結(jié)構(gòu)是不是可變的,就完全不能控制了方庭。因此厕吉,將一個(gè)對(duì)象聲明為常量必須非常小心。
(4) 頂層對(duì)象的屬性
頂層對(duì)象械念,在瀏覽器環(huán)境指的是window對(duì)象头朱,在 Node 指的是global對(duì)象。
ES5 之中龄减,頂層對(duì)象的屬性與全局變量是等價(jià)的项钮。
window.a = 1;
a // 1
a = 2;
window.a // 2
上面代碼中,頂層對(duì)象的屬性賦值與全局變量的賦值,是同一件事烁巫。
- ES6 為了改變這一點(diǎn)鳖敷,一方面規(guī)定,為了保持兼容性程拭,var命令和function命令聲明的全局變量定踱,依舊是頂層對(duì)象的屬性;另一方面規(guī)定恃鞋,let命令崖媚、const命令、class命令聲明的全局變量恤浪,不屬于頂層對(duì)象的屬性畅哑。也就是說,從 ES6 開始水由,全局變量將逐步與頂層對(duì)象的屬性脫鉤荠呐。
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
var a = 1;
// 如果在 Node 的 REPL 環(huán)境,可以寫成 global.a
// 或者采用通用方法砂客,寫成 this.a
window.a // 1
let b = 1;
window.b // undefined
(一) 解構(gòu)賦值
(1) 解構(gòu)
ES6 允許按照一定模式泥张,從數(shù)組和對(duì)象中提取值,對(duì)變量進(jìn)行賦值鞠值,這被稱為解構(gòu)(Destructuring)媚创。
(2) 解構(gòu)賦值的用途
- (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è)對(duì)象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
- (3)函數(shù)參數(shù)的定義
解構(gòu)賦值可以方便地將一組參數(shù)與變量名對(duì)應(yīng)起來芒炼。
// 參數(shù)是一組有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 參數(shù)是一組無次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
- (4)提取 JSON 數(shù)據(jù)
解構(gòu)賦值對(duì)提取 JSON 對(duì)象中的數(shù)據(jù),尤其有用术徊。
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData; // data:number本刽,屬性和變量名不一致,number是變量
console.log(id, status, number);
// 42, "OK", [867, 5309]
- (5)函數(shù)參數(shù)的默認(rèn)值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
}) {
// ... do stuff
};
指定參數(shù)的默認(rèn)值弧关,就避免了在函數(shù)體內(nèi)部再寫var foo = config.foo || 'default foo';這樣的語句盅安。
- (6)遍歷 Map 結(jié)構(gòu)
任何部署了 Iterator 接口的對(duì)象,都可以用for...of循環(huán)遍歷世囊。Map 結(jié)構(gòu)原生支持 Iterator 接口别瞭,配合變量的解構(gòu)賦值,獲取鍵名和鍵值就非常方便株憾。
const 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
-
(7)輸入模塊的指定方法
加載模塊時(shí)蝙寨,往往需要指定輸入哪些方法晒衩。解構(gòu)賦值使得輸入語句非常清晰。
const { SourceMapConsumer, SourceNode } = require("source-map");
import {connect} from 'react-redux';
(3) 數(shù)組的解構(gòu)賦值
let [a, b, c] = [1, 2, 3];
上面代碼表示墙歪,可以從數(shù)組中提取值听系,按照對(duì)應(yīng)位置,對(duì)變量賦值虹菲。
本質(zhì)上靠胜,這種寫法屬于“模式匹配”,只要等號(hào)兩邊的模式相同毕源,左邊的變量就會(huì)被賦予對(duì)應(yīng)的值浪漠。
-----------------------------------------
let [head, ...tail] = [1, 2, 3, 4]; // 解構(gòu)賦值: 從數(shù)組或者對(duì)象中取值,對(duì)變量進(jìn)行賦值
head // 1
tail // [2, 3, 4]
-----------------------------------------
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
- 如果解構(gòu)不成功霎褐,變量的值就等于undefined址愿。
let [foo] = [];
let [bar, foo] = [1];
以上兩種情況都屬于解構(gòu)不成功,foo的值都會(huì)等于undefined冻璃。
- 不完全解構(gòu)
不完全解構(gòu)响谓,即等號(hào)左邊的模式,只匹配一部分的等號(hào)右邊的數(shù)組省艳。這種情況下娘纷,解構(gòu)依然可以成功。
let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4 // 注意:這里的d是4拍埠,而不是3
上面兩個(gè)例子失驶,都屬于不完全解構(gòu)土居,但是可以成功枣购。
--------------------------------------------------------------
如果等號(hào)的右邊不是數(shù)組(或者嚴(yán)格地說,不是可遍歷的結(jié)構(gòu)擦耀,那么將會(huì)報(bào)錯(cuò)棉圈。
// 報(bào)錯(cuò)
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
(4) 默認(rèn)值
解構(gòu)賦值允許指定默認(rèn)值。
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
注意眷蜓,ES6 內(nèi)部使用嚴(yán)格相等運(yùn)算符(===)分瘾,判斷一個(gè)位置是否有值。
所以吁系,只有當(dāng)一個(gè)數(shù)組成員嚴(yán)格等于undefined德召,默認(rèn)值才會(huì)生效。
-------------------------------------------------------------------------
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
上面代碼中汽纤,如果一個(gè)數(shù)組成員是null上岗,默認(rèn)值就不會(huì)生效,因?yàn)閚ull不嚴(yán)格等于undefined蕴坪。
(二) 對(duì)象的解構(gòu)賦值
對(duì)象的解構(gòu)與數(shù)組有一個(gè)重要的不同肴掷。
- 數(shù)組的元素是按次序排列的敬锐,變量的取值由它的位置決定;
- 而對(duì)象的屬性沒有次序呆瞻,變量必須與屬性同名台夺,才能取到正確的值。
let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" }; // 對(duì)象的解構(gòu)賦值痴脾,變量必須與屬性同名才能取到正確的值
baz // undefined
- 如果變量名與屬性名不一致颤介,必須寫成下面這樣。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
- 也就是說赞赖,對(duì)象的解構(gòu)賦值的內(nèi)部機(jī)制买窟,是先找到同名屬性,然后再賦給對(duì)應(yīng)的變量薯定。真正被賦值的是后者始绍,而不是前者。
let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined
foo是匹配的模式话侄,baz才是變量亏推。真正被賦值的是變量baz,而不是模式foo年堆。
說明:對(duì)象的解構(gòu)賦值吞杭,是先找到同名屬性,在賦值給對(duì)應(yīng)的變量变丧,被賦值的是屬性的值芽狗,而不是屬性
(被賦值的是后者)
- 對(duì)象的解構(gòu)也可以指定默認(rèn)值。
var {x, y = 5} = {x: 1};
x // 1
y // 5
var {x: y = 3} = {}; // (重要)
y // 3
var {x: y = 3} = {x: 5};
y // 5
var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"
--------------------------------------------------------------------
默認(rèn)值生效的條件是痒蓬,對(duì)象的屬性值嚴(yán)格等于undefined童擎。
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
- 如果解構(gòu)失敗,變量的值等于undefined攻晒。
let {foo} = {bar: 'baz'};
foo // undefined
(三) 字符串的解構(gòu)賦值
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
-------------------------
類似數(shù)組的對(duì)象都有一個(gè)length屬性顾复,因此還可以對(duì)這個(gè)屬性解構(gòu)賦值。
let {length : len} = 'hello';
len // 5
(四) 函數(shù)參數(shù)的解構(gòu)賦值
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
上面代碼中鲁捏,函數(shù)add的參數(shù)表面上是一個(gè)數(shù)組芯砸,但在傳入?yún)?shù)的那一刻,數(shù)組參數(shù)就被解構(gòu)成變量x和y给梅。
對(duì)于函數(shù)內(nèi)部的代碼來說,它們能感受到的參數(shù)就是x和y动羽。
- 函數(shù)參數(shù)的解構(gòu)也可以使用默認(rèn)值包帚。---------------------------------------------對(duì)比
function move({x = 0, y = 0} = {}) { // 為變量x,y指定了默認(rèn)值,同時(shí)為參數(shù)指定了默認(rèn)值
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
-
為函數(shù)參數(shù)指定默認(rèn)值曹质,和為變量指定默認(rèn)值不一樣--------------對(duì)比
function move({x, y} = { x: 0, y: 0 }) {
// 上面 這樣是為函數(shù)參數(shù)指定默認(rèn)值婴噩,而不是為變量x,y指定默認(rèn)值
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
上面代碼是為函數(shù)move的參數(shù)指定默認(rèn)值擎场,而不是為變量x和y指定默認(rèn)值