????????昨天學長說可以放緩一下學習進度,剛好最近期末復習也不是很緊張,所以來重新復習一下js的一些知識點。
一:變量
(1)變量聲明
來簡單看一下變量的一些知識點。首先是變量聲明:變量聲明盡量使用數組字母下劃線 來舉幾個例子:
//聲明變量盡量使用 字母數字下劃線
const web ='1',name='2';
const qianduan =houduan = quanhzan='money'
console.log(qianduan);
console.log(houduan);
console.log(quanhzan);
//js是弱數據類型語言
// typeof 可以查看變量的類型
console.log(typeof web);
我們可以連續聲明多個變量 也可以使用typeof來檢查變量的數據類型,來看看輸出結果
?(2)變量提升
變量提升的問題出現在使用var關鍵詞聲明的變量,來看個例子
//通過var聲明的變量會變量提升
console.log(web)//在運行代碼,會執行一個解析,會將var聲明的變量做一個提前 但是提前的只是聲明
var web='houdunren'
運行之后我們發現結果沒有報錯 也是undefined 這說明var造成的變量提升 只是提升了聲明過程,但是忽略了賦值過程。那么下面我將展示一段錯誤代碼:
function hd(){if(false){var web='111'}console.log(web)
}
hd()
來運行一下看看??
發現結果是undefined 本質上就是變量提升造成的問題,在執行代碼之前var就將代碼的聲明提前到最上方 所以函數直接就將未賦值的web打印出來了。所以現在的前端代碼為了避免變量提升造成的困擾,建議使用let或const來聲明變量或常數。
(3)let與const聲明變量與TDZ時間死區
鑒于var聲明所帶來的缺陷 現在使用let與const 這類具有臨時性死區(tdz)的關鍵詞避免變量提升的問題。(知道就行 不過還是建議了解一下時間死區)時間死區:是指變量從進入作用域到被實際聲明的一段時間,在這段時間里面 訪問該變量會導致錯誤從而不被提前使用。簡單來說就是變量提升被一個錯誤(ReferenceError)阻斷了。這里我就不舉實例了。
(4)全局污染
我們先來看一個例子:
function show() {web='houdunren'
}
show()
console.log(web);
來看看運行結果:
神奇的發現我們并未實際聲明web這個變量 但是web變量確實被輸出了 并且輸出的作用域是全局作用域 這就是全局污染。那么這就會造成一些問題 比如 我們在編寫代碼之前已經提前引入了第三方庫或者其他js文件 這些文件中如果存在同名的變量就會被污染
所以js中所有的變量一定要先聲明后使用。
(5) 變量凍結
繼續先看一個例子:
const HOST={url:'https://www.houdunren.com/api',port:443
};
// 對象凍結 凍結之后代碼不再可以修改
Object.freeze(HOST);
HOST.port=80;
console.log(HOST);
聲明了一個HOST對象變量 使用了Object.freeze方法之后對象里面的數據就不可被修改了
(6)傳值與傳址
對于簡單的數據類型 在傳遞值的時候是直接把數值傳遞過去的;但是對于復雜數據類型(對象,數組等),在傳遞值的時候是將自身數據的內存地址賦值過去的 問題就是當其中一個值發生變動的時候 同地址的另一個變量的值也會變化
//這里是傳值 因為簡單數據類型占用空間小 所以會直接開辟一塊空間
let a=1;
let b=a;
//這里是傳址 因為對象等復雜數據類型占用空間很大 所以會賦值前者的地址給后者
// 造成的問題是兩者之間有一個人出現改動 后者的值也會被改動
// 解決方法是使用深淺拷貝來避免
let e={};
let f=e;
解決辦法也很好解決 就是使用深淺拷貝。對于深淺拷貝另見我的對象那篇文章(附圖)
(7)null與undefined
對于簡單數據類型來說 聲明未賦值 輸出的就是undefined 說明值的沒有定義的;對于復雜數據(數組 對象)來說聲明之后沒有具體值 輸出之后就是null 可以理解成復雜數據類型就是一個書包 書包沒東西那肯定就是null了
二:運算符與邏輯控制
(1)短路邏輯
短路邏輯常備用來設定默認值 與簡單的條件執行語句 我們直接來看看例子吧
let a=0;
let b=1
if(a||b){console.log('nihao');
}
只有a或b同時為0 的時候才不會輸出。
let f=a||b;
console.log(f);
當a是0的時候 f 就被賦值成 b
function star(num){return '*'.repeat(num||1)//repaat(重復次數)
}
console.log(star(123));
如果沒有輸入參數的時候 函數只返回一個 *
(2)do while循環
do while循環是一定會執行一次循環體的 簡單來看幾個例子
打印三角形
let start=0;
do{let n=0;let line = ""; // 創建一個空字符串來存儲當前行的星號do{line += "*"; // 將星號添加到字符串中}while(++n<=start)console.log(line); // 打印完整的行
}while(++start<=5)
// 但是以我個人主見 使用for循環來完成這個效果是更好的
?
(3)打印正三角型
for(let i=0;i<10;i++){for(let j=10-i;j>0;j--){process.stdout.write(' ')}for(let j=i*2-1;j>0;j--){process.stdout.write('*')}process.stdout.write('\n')}
?這里的?process.stdout.write()是在控制臺單行輸出不自動換行的一種方法 不過基本被棄用了
(4)label(類似于goto 但不同)
當函數運行到break outerLoop的時候 直接跳出指定的循環 直接看例子吧
// 標簽(label)配合break使用的例子
outerLoop:
for(let i = 0; i < 3; i++) {innerLoop:for(let j = 0; j < 3; j++) {console.log(`i=${i}, j=${j}`);if(i === 1 && j === 1) {break outerLoop; // 直接跳出外層循環}}
}
?因為inner標簽來第一層循環里面 所以當運行到breakj outerloop的時候就直接跳出了第一層循環(被標記的循環)? ,來看看輸出結果
?(5)for in 與 for of
for in可以遍歷數組和對象 同樣的for of也可以遍歷數組 也可以遍歷字符串 如果使用for of來遍歷對象 必須先獲取鍵名或者值 可能說的很抽象 那直接來看例子
定義一個數組和對象
let obj={uname:'123',age:12,gender:'女'
}
const arr=[1,2,3,45,5,6]
1.使用for in 遍歷對象的屬性
// 遍歷對象屬性
for(let k in obj){console.log(obj[k]);
}
2.使用for in遍歷數組的值
// 遍歷數組
for (const key in arr) {console.log(arr[key]);
}
3.推薦使用for of遍歷數組
for (const value of arr) {console.log(value); // 輸出: 1, 2, 3, 45, 5, 6
}
4.使用for of遍歷字符串
const str = "hello";
for (const char of str) {console.log(char); // 輸出: h, e, l, l, o
}
5.使用for of遍歷Map集合(鍵值對集合)
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {console.log(key, value); // 輸出: a 1, b 2
}
6.for of遍歷對象的三種類型
// 6. 如果要遍歷對象,可以先獲取其鍵、值或條目
// 方法1: 遍歷對象的鍵
for (const key of Object.keys(obj)) {console.log(key, obj[key]);
}// 方法2: 遍歷對象的值
for (const value of Object.values(obj)) {console.log(value);
}// 方法3: 遍歷對象的鍵值對
for (const [key, value] of Object.entries(obj)) {console.log(key, value);
}
o而k之,感謝閱讀,拜拜