ES6 (一) Promise ( const,let,塊級(jí)作用域,頂層對(duì)象屬性 ) + ( 變量的解構(gòu)賦值 ) + ( 數(shù)組的擴(kuò)展 ) + ( this關(guān)鍵字 )

(一) 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)值

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市几莽,隨后出現(xiàn)的幾起案子迅办,更是在濱河造成了極大的恐慌,老刑警劉巖章蚣,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件站欺,死亡現(xiàn)場離奇詭異,居然都是意外死亡纤垂,警方通過查閱死者的電腦和手機(jī)矾策,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峭沦,“玉大人贾虽,你說我怎么就攤上這事『鹩悖” “怎么了蓬豁?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長菇肃。 經(jīng)常有香客問我地粪,道長,這世上最難降的妖魔是什么琐谤? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任蟆技,我火速辦了婚禮,結(jié)果婚禮上斗忌,老公的妹妹穿的比我還像新娘质礼。我一直安慰自己,他們只是感情好飞蹂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布几苍。 她就那樣靜靜地躺著,像睡著了一般陈哑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伸眶,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天惊窖,我揣著相機(jī)與錄音,去河邊找鬼厘贼。 笑死界酒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嘴秸。 我是一名探鬼主播毁欣,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼庇谆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凭疮?” 一聲冷哼從身側(cè)響起饭耳,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎执解,沒想到半個(gè)月后寞肖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衰腌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年新蟆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片右蕊。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡琼稻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饶囚,到底是詐尸還是另有隱情欣簇,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布坯约,位于F島的核電站熊咽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏闹丐。R本人自食惡果不足惜横殴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望卿拴。 院中可真熱鬧衫仑,春花似錦、人聲如沸堕花。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缘挽。三九已至瞄崇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間壕曼,已是汗流浹背苏研。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腮郊,地道東北人摹蘑。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像轧飞,于是被迫代替她去往敵國和親衅鹿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子撒踪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容