【重學前端】JS基礎-變數和型別
變數和型別
-
1.JavaScript規定了幾種語言型別
1.Undefined 2.Null(js中的資料在底層是以二進位制儲存,如果前三位為0,那麼就會判定為object,而null的所有都為0) 3.Boolean 4.String 5.Number 6.Symbol(ECMAScript 6 新定義,例項是唯一且不可改變的) 7.Object
-
2.JavaScript物件的底層資料結構是什麼
-
3.Symbol型別在實際開發中的應用、可手動實現一個簡單的Symbol
-
應用場景1:使用Symbol來作為物件屬性名(key)
const PROP_NAME = Symbol(); const PROP_AGE = Symbol(); let obj = { [PROP_NAME]: "一斤程式碼" } obj[PROP_AGE] = 18; console.log(obj[PROP_NAME]); // '一斤程式碼' console.log(obj[PROP_AGE]); // 18
【注意】Symbol型別的key是不能通過
Object.keys()
或者for...in
來列舉的,利用該特性,我們可以把一些不需要對外操作和訪問的屬性使用Symbol來定義。實現當使用JSON.stringify()
將物件轉換成JSON字串的時候,Symbol屬性也會被排除在輸出內容之外。獲取以Symbol方式定義的物件屬性 // 使用Object的API Object.getOwnPropertySymbols(obj); // [Symbol(name)] // 使用新增的反射API Reflect.ownKeys(obj); // [Symbol(name), 'age', 'title']
-
應用場景2:使用Symbol來替代常量
//由於定義const變數的時候需要立即賦值,這樣定義,直接就保證了三個常量的值是唯一的了 const TYPE_AUDIO = Symbol(); const TYPE_VIDEO = Symbol(); const TYPE_IMAGE = Symbol();
-
應用場景3:使用Symbol定義類的私有屬性/方法
利用Symbol的唯一性以及模組化機制,實現類的私有屬性和方法。
//a.js const PASSWORD = Symbol() class Login { constructor(username, password) { this.username = username this[PASSWORD] = password } checkPassword(pwd) { return this[PASSWORD] === pwd } } export default Login
//b.js import Login from './a' const login = new Login('admin', '123456') login.checkPassword('123456') // true login.PASSWORD // oh!no! login[PASSWORD] // oh!no! login["PASSWORD"] // oh!no!
-
-
4.JavaScript中的變數在記憶體中的具體儲存形式
JavaScript中的變數分為基本型別和引用型別
-
基本型別是儲存在棧記憶體中的簡單資料段,它們的值都有固定的大小,儲存在棧空間,通過按值訪問
-
引用型別是儲存在堆記憶體中的物件,值大小不固定,棧記憶體中存放的該物件的訪問地址指向堆記憶體中的物件,JavaScript不允許直接訪問堆記憶體中的位置,因此操作物件時,實際操作物件的引用。
-
//例子: let a1 = 0; // 棧記憶體 let a2 = "this is string" // 棧記憶體 let a3 = null; // 棧記憶體 let b = { x: 10 }; // 變數b存在於棧中,{ x: 10 }作為物件存在於堆中 let c = [1, 2, 3]; // 變數c存在於棧中,[1, 2, 3]作為物件存在於堆中
複製行為
- 在棧記憶體中的資料發生複製行為時,系統會自動為新的變數分配一個新值,最後這些變數都是相互獨立互不影響的
- 引用型別的複製,為新的變數分配相同的地址指標,儲存在棧記憶體中,該地址指標在堆記憶體中訪問到的具體物件實際上是同一個,因此一改變數值,雙方都改。
生存期
- 當一個方法執行時,會建立自己的棧記憶體,在方法內定義的變數都會放入這塊棧記憶體裡,方法執行結束後,這個記憶體棧也將自然銷燬。
- 堆記憶體中的物件不會隨方法的結束而銷燬,即使方法結束後,這個物件還可能被另一個引用變數所引用(方法的引數傳遞時很常見)。只有當一個物件沒有任何引用變數引用它時,系統的垃圾回收機制才會在核實的時候回收它。
-
-
5.基本型別對應的內建物件,以及他們之間的裝箱拆箱操作
-
內建物件
-
資料封裝類物件:Object、Array、Boolean、Number 、String
-
其他物件:Function、Math、Date、RegExp、Error
-
Error6種錯誤型別(前4個較常見)
1.SyntaxError: 語法錯誤 2.ReferenceError: 引用錯誤 要用的東西沒找到 3.RangeError: 範圍錯誤 專指引數超範圍 4.TypeError: 型別錯誤 錯誤的呼叫了物件的方法 5.EvalError: eval()方法錯誤的使用 6.URIError: url地址錯誤
-
-
-
特殊的基本包裝型別(Boolean、Number 、String)
-
包裝型別:專門封裝原始型別的資料,並提供對資料常用操作的內建物件方法。
-
生存期:只要用原始型別的資料呼叫方法或者訪問屬性時,js引擎都會自動建立對應的包裝型別物件方法呼叫完,包裝型別物件會自動釋放。
-
無法直接給基本型別新增屬性和方法,但可以在基本包裝型別物件的原型下面新增,因為每個物件都有原型。
-
裝箱(把基本資料型別轉化為對應的引用資料型別的操作)
-
隱式裝箱
let a = 'sun'; let b = a.indexof('s'); // 上面程式碼在後臺實際的步驟為: let a = new String('sun') let b = a.indexof('s') a = null
-
顯式裝箱
let a = new String('sun '); let b = a.indexof('s')
-
-
拆箱(把引用型別轉化為對應基本資料型別的操作)
let name = new String('sun') let age = new Number(24) console.log(typeof name) // object console.log(typeof age) // object // 拆箱操作(通常通過引用型別的valueof()和toString()方法實現) console.log(typeof age.valueOf()); // number // 24 基本的數字型別 console.log(typeof name.valueOf()); // string // 'sun' 基本的字元型別 console.log(typeof age.toString()); // string // '24' 基本的字元型別 console.log(typeof name.toString()); // string // 'sun' 基本的字元型別
-
-
-
6.理解值型別和引用型別
-
值型別,即基本型別
-
string、number、boolean、undefined、null、symbol
-
1、佔用空間固定,儲存在棧中 2、儲存與複製的是值本身 3、使用typeof檢測資料的型別 4、基本型別資料是值型別
-
-
引用型別
-
Object、Array、Function
-
1、佔用空間不固定,儲存在堆中 2、儲存與複製的是指向物件的一個指標 3、使用instanceof檢測資料型別 4、使用new()方法構造出的物件是引用型
-
-
-
7.null和undefined的區別
-
用法不同
null表示 “沒有物件”,即該處不應該有值。 (1)作為函式的引數,表示該函式的引數不是物件。 (2)作為物件原型鏈的終點。 undefined表示“缺少值”,就是此處應該有一個值,但是還沒有定義。 (1)變數被聲明瞭,但沒有賦值時,就等於undefined。 (2)呼叫函式時,應該提供的引數沒有提供,該引數就等於undefined。 (3)物件沒有賦值的屬性,該屬性的值為undefined。 (4)函式沒有返回值時,預設返回undefined。
-
型別轉換
Number(undefined) // NaN Number(undefined + 10) //NaN Number(null) // 0 Number(10 + null) // 10
-
資料型別
undefined === null //false undefined與object undefined == null //true js規定
-
-
8.至少可以說出三種判斷JavaScript資料型別的方式,以及他們的優缺點,如何準確的判斷陣列型別
-
typeof
-
優點:使用簡單,能判斷出所有基本資料型別(null是object)+function
-
缺點:不能區分object型別的具體型別,比如 Array 、Date 以及自定義類。
-
var arr = []; console.log(typeof arr);//object
-
-
instanceof
-
優點:能判斷出引用型別
-
缺點:只能判斷出用物件定義的基本資料型別;不能跨iframe
-
var arr = []; alert(arr instanceof Array);//true
-
-
constructor
-
優點: 基本能檢測所有的型別(除了null和undefined,並且會報錯。因為null和undefined是無效的物件,因此是不會有constructor存在的)
-
缺點:constructor易被修改導致判斷出錯,也不能跨iframe
-
var arr = []; alert(arr.constructor == Array);//true
-
-
Object.prototype.toString.call( ) ;
-
優點:能判斷出所有的型別
-
缺點:IE6下,undefined和null均為Object
-
var arr = []; console.log(Object.prototype.toString.call(arr));//[object Array]
-
-
-
9.可能發生隱式型別轉換的場景以及轉換原則,應如何避免或巧妙應用
-
應用場景
-
場景一:算術運算子
-
字串
+
數字=字串數字console.log(10+"abc");//10abc
-
非字串
+
數字=轉數字+數字console.log(10+true);//11
-
-
、*
、/
、%
將字元轉換成數字,再進行正常的運算,結果必為數值,若字元不是純數字,則結果為NaN -
(
+
字串)轉數字
-
-
場景二:關係(比較)運算子
-
<
、<=
、>
、>=
、!=
、==
-
任意一邊出現數值,就會把另一邊轉成數值,再進行比較。
console.log([1] == 1);//true,相當於1 == 1 console.log([] == true);//false,相當於0 == 1 console.log({} > true);//false,相當於 NaN > 1 console.log('true' <= 0);//false,相當於NaN <= 0
-
一個值轉換為另一個值並不意味著兩個值相等
console.log(new Date() == 'Fri Jul 15 2016 22:12:05 GMT+0800 (中國標準時間)');//true console.log(Number(null));//0 console.log(null == 0);//false console.log(Boolean(NaN));//false console.log(NaN == false);//false,NaN與任何值都不等,包括自己
-
兩邊是物件
console.log({} == {});//false console.log([] == []);//false
-
兩邊都是字串,則是按照字串的按位比較法。
-
-
-
場景三:isNaN()
- 引數轉成數值,嚴格轉換,能識別小數點
-
場景四:判斷語句if()
- 隱式轉換,將其他轉成布林值
-
場景五:邏輯運算子
- 邏輯非 !
- 隱式轉換,將其他資料型別轉成布林值
- 優先順序高於關係運算符
- 位運算子(&、|、^、&&、||)
- 隱式轉換,將其他資料型別轉成數字
- 優先順序低於關係運算符
- 位運算子(<<等)
- 隱式轉換,將其他資料型別轉成數字
- 優先順序高於關係運算符
- 邏輯非 !
-
避免隱式轉換
- 如果兩遍的值中有 true 或者 false , 千萬不要使用 == (會被轉為數字0,1來進行判斷,會出現一些意外的情況)
- 如果兩遍的值中有[]、””或者0,儘量不要使用 ==
- 使用全等=來代替
-
巧妙應用
使用一個物件,進行隱式型別轉換 const a = (function() { let i = 1; return { valueOf: function() { return i++; } } })(); console.log(a == 1 && a == 2 && a == 3); // true
-
-
10.出現小數精度丟失的原因,JavaScript可以儲存的最大數字、最大安全數字,JavaScript處理大數字的方法、避免精度丟失的方法
-
小數精度丟失的原因:計算機的二進位制實現和位數限制有些數無法有限表示。
-
JS的基礎型別Number,遵循 IEEE 754 規範,採用雙精度儲存(double precision),佔用 64 bit。符號位(1bit),指數(11bit),尾數(52bit),因此 JS 中能精準表示的最大整數是 Math.pow(2, 53)-1,十進位制即 9007199254740991。
-
最大安全數字
console.log(Number.MAX_SAFE_INTEGER); //9007199254740991 console.log(Number.MIN_SAFE_INTEGER); //-9007199254740991
-
JS 處理大數字的方法
- json-bigint
- bignumber.js:原理是把所有數字當作字串,重新實現了計算邏輯,缺點是效能比原生的差很多。
- proposal bigint
- 大數運算:化為字串再運算
-
避免精度丟失的方法
- 小數:小數化整進行運算再化為小數。
- CalcEval.js引擎
-
本文作者:AlubNoBug