?1.變量聲明
? ? ? ? 首先js變量聲明有三種,var,const,let,這三種變量聲明中我們第一優先使用const,需要改變這個值的時候我們用ley,var是盡量不去使用。
? ? ? ? 那么我們現在來總結一下三種聲明變量的區別。首先是var let 都是定義變量。
????????
{var a = 20}console.log(a);
? ? ? ?首先是var聲明變量這里我們輸出控制臺看到了20。因為我們var沒有塊級作用域,使用var定義的變量會直接作為全局變量。
????????
{let b =20}console.log(b);
? ? ? ? 這里的輸出結果是會報錯, b是局部變量,只能在{}中使用。也就是說let聲明的變量有塊級作用域,而var沒有。那么作用域有什么用呢?
????????
for (var i = 0; i < 5; i++) {console.log('循環內部', i);//能訪問到i}console.log('循環內部', i);
? ? ? ?這里還是拿到了i 因為var讓i變成了全局變量,這很明顯不符合常理,因為我們循環體中我們僅僅希望i去控制循環,并不希望去設置一個全局變量i。
????????
for (let i = 0; i < 5; i++) {console.log('循環內部', i);//能訪問到i}console.log('循環內部', i);
? ? ? ? 使用let就只能在作用域里面訪問到i,在全局是訪問不到的。
? ? ? ? 我們在去嘗試下面的代碼。
????????
if (true) {var b = 33}console.log(b);
? ? ? 控制臺輸出了33,很正常因為全局變量b=33.我們改一下。
????????
if (true) {let b = 33}console.log(b);
? ? ? ?這里我們就無法訪問到b,因為只在{}這個塊級作用域里面才能訪問到b,我們在來看一種情況。
????????
if (false) {/ var b = 33}console.log(b);
? ? ? ?這里沒有報錯,甚至輸出的是undefined,這里是因為var會引發 變量提升,就是當我們用var去聲明一個變量的時候,瀏覽器在解析代碼的時候,會優先把var聲明的變量在一開始就var聲明,所以盡管沒有賦值成功,也會默認聲明,這顯然非常不符合邏輯。所以我們開發時不去使用var。
????????
const obj = {name: '隨你'}
obj.age = 18
obj.name = "www"console.log(obj.name);console.log('obj.age', obj.age)
? ? ? ? 這里我們用const去定義一個obj對象,我們不可以直接去更改obj對象,// obj = { name: '猴子石' }不可以這樣直接對obj進行賦值,但是我們可以改變對象的內容,去添加新的對象或者覆蓋掉已經有的屬性。
? ? ? ? 總結1.var 沒有塊級作用域2.let 有塊級作用域3.const 和let類似 具有塊級作用域,但是只能賦值一次4.const 使用場景 1.對一些常量可以使用const 一般大寫字母 const PI = 3.15
????????5.對一些對象函數也可以使用const 可以避免對象和函數被意外修改 可以讓obj永遠指向后面的對象。
2.解構賦值
? ? ? ? 我們從數組里面獲取里面的元素的時候,我們最開始是用變量賦值的方式。
let a, blet arr = ['w', 'n']a = arr[0]b = arr[1]console.log(a, b);
? ? ? ? 好像很簡單的邏輯就很麻煩寫起來。我們可以用解構賦值的語法去獲取。
let a, blet arr = ['w', 'n'][a, b] = arr //解構賦值console.log(a, b);
? ? ? ? 這樣就非常簡便,我們甚至可以在解構賦值的時候去聲明變量。
const [a, b] = arr
? ? ? ? 這就是數組的解構賦值,但是如果有很多數組,我們希望去用數組解構的時候,就需要一一對應,或者用...b這種去讓b去接住后面所有的數組元素。
const wrr = ['wwwww', '傻孩子', 'bailong', 'aaaa', 'ppp']// const [a, b, , c] = wrr// console.log(a, b, c);//多個解構就是一一對應,多余的就不顯示想跳過一個就,就可以// const [a, b, ...c] = wrr// console.log(a, b, c);//也可以直接...c(數組)去拿取后面所有的元素 而且這里的...c會接所有元素所以只能放最后
? ? 這就是數組的解構賦值現在我們來看對象的解構。
????????
const obj = {name: 'wwww',age: 12,gender: '難'}let a, b, c;({ name: a, age: b, gender: c } = obj) //這里用括號因為不()直接等號左邊{}不合理//要注意的是被賦值的變量在右邊,對象的屬性在左邊 比如第一個將name賦值被aconsole.log(a, b, c);//通常我們把變量名和對象的屬性名一致// const { name: name, gender: gender, age: age } = obj// console.log(name, gender, age);//然后對象可以簡寫因為左右一致,const { name, age, gender } = objconsole.log(name, age, gender);// 解構賦值還可以交換數組中變量的位置arr = [1, 2, 3]console.log(arr);[arr[1], arr[2]] = [arr[2], arr[1]]console.log(arr);// 這里把下標1,2換了位置
?和數組的解構賦值思路一樣,只不過對象需要用{}包裹,然后賦值的時候,左邊是我們對象里面的屬性,右邊是我們想要進行賦值的變量!!!,然后我們可以簡寫(往往解構的時候變量名和對象的屬性名一致),就可以直接。
????????
這樣去解構賦值,也可以用解構賦值來換取位置,方法如圖。
3.展開
????????
function fn(a, b, c) {return (a + b + c)}const arr = [1, 2, 3]//計算三個數字的和// let result = fn(arr[0], arr[1], arr[2])let result = fn(...arr)//...展開數組 ...可以展開數組console.log(result);const arr2 = [7, 8, ...arr, 4, 5, 6]//將arr淺復制給arr2console.log(arr2);//數組可以展開對象也可以const obj = {name: 'www',age: 12,gender: 'nan'}const obj2 = { gogog: 'www', ...obj, address: 'wopa', name: 'zzzz' } //把obj在新對象中展開相當于淺復制//對象是無序的如果重復了后來的覆蓋前面的console.log(obj2);
? ? ? ? ?首先我們在前面的數組結構用到了...a去表示一個數組變量,那么我們就可以用...arr去表示一個數組展開后的內容,也就是...展開數組。我們可以用...arr去傳參,相當于傳過去展開的數組,對象也可以這樣,相當于展開了對象。然后我們這里用...obj去給obj2這個對象賦值,展開之后,這里name重復后來的會覆蓋掉前面的。
4.箭頭函數
????????
const fn = function () {return 'hello'}const fn2 = a => aconsole.log(fn2(123));箭頭函數只有一個參數的函數,參數等于返回值如果沒有參數或者多個參數用()括起來()=>返回值 (a,b,c)=>{}const sum = (a, b, c) => a + b + cconst sum = (a, b, c) => ({ name: 'sun' })let result = sum(1, 2, 3)console.log(result);箭頭后面的叫做返回值 返回值必須是表達式?表達式(有返回值的語句)如果返回的是對象需要加()如果需要在箭頭函數定義邏輯,在箭頭后跟一個代碼塊代碼塊語法和普通函數一致const fn4 = (a, b) => {if (a === 10) {a += 5} else if (a === 20) {a += 10}return a + b}console.log('fn4', fn4(1, 2))
? ? ? ? ?箭頭函數是我們定義函數的另一種簡寫方式,當只有一個參數的時候,而且返回值等于參數的時候可以這樣去書寫,但是如果有多個參數的話,我們就需要給參數加一個()
而且返回值必須是表達式,也就是必須要有值作為返回值,如果我們希望通過一個邏輯去返回值的話,我們就在箭頭后面跟一個代碼塊。這就是箭頭函數的使用方法。
5.箭頭函數和普通函數的區別
????????
//箭頭函數是傳統函數表達式的簡寫方式,但是也有一些限制導致一些場景無法使用//沒有自己的this//沒有arguments//不能作為構造函數調用//無法通過call apply bind指定函數的thisfunction fn(a, b, ...args) {//用來保存函數實參arguments 類數組不能用操作數組的方法// console.log(arguments.length);//輸出的2// console.log(...arguments);//輸出的1,2console.log(...args);//用...args代替arguments更好用是真正的數組}fn(1, 2, 3, 4, 5)const fn2 = (...argus) => {// console.log(arguments);//報錯argument is undefinedconsole.log(...argus);}fn2(1, 2, 3)//箭頭函數沒有arguments(類數組)但是也可以用...argus去保存實參//箭頭函數沒有自己的thisconst fn3 = () => {console.log(this);//這里的this是window}fn3()//函數中的this是指向外層作用域(運行環境中的this)。這里fn3的作用域是全局 所以說windowconst obj = {fn4: () => {console.log(this);}}obj.fn4()//這里仍然是window因為箭頭函數沒有自己的this,它的this總是外層作用域的thisconst obj2 = {ss: function () {console.log(this);}}obj2.ss()//這里的this指向的就是obj2因為普通函數調用時看“誰點的它”(誰調用)誰調用就指向誰const obj3 = {ss: function () {const test = () => {console.log(this);}test()}}obj3.ss()//這里指向的是obj3,因為這個this就是外層作用域也就是普通函數的this,普通函數的this指向obj3所以這里指向obj3
? ? ? ? 首先箭頭函數雖然是傳統函數的簡寫方式,但是也是有一些場景無法使用的,比如沒有自己的this沒有argument保存實參,也不能作為構造函數調用,因為不能用this,也無法通過call,bind方法去指定this。如圖fn4箭頭函數中的this是外層作用域也就是obj的this,而obj是在全局中定義的,所有this指向的window。而ss這個普通函數obj2.ss輸出的this指向的obj2因為普通函數的this是說調用指向誰。