JS必須知道的基礎知識

基礎知識

基本數據類型

String, Number, Boolean, Undefined, Null, Symbol(ES6)

  1. 使用typeof校驗的值
    typeof 的運算數未定義,返回的就是 "undefined". 
    運算數爲數字 typeof(x) = "number" 
    字符串 typeof(x) = "string" 
    布爾值 typeof(x) = "boolean"
    null typeof(x) = "object"
    布爾值 typeof(Symbol) = "function"
  1. 數據存儲結構

    簡單數據類型是存儲在棧中的
    轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ksSbmHqN-1586918859454)(./imgs/數據類型-2.png)]

引用數據類型

Object, Function, Array, Date, Regexp

  1. 使用typeof校驗的值
    typeof(Object) = "object" 
    typeof(Function) = "function"
    typeof(Array) = "object"
    typeof(Date) = "function"
    typeof(RegExp) = "function"       
    對象,數組和null `typeof(x) = "object" `
  1. 數據存儲結構

    複雜數據類型跟簡單數據類型的不同點就是在於, 簡單數據類型的變量指向的是內存中的數據, 而複雜數據類型指向的是其在棧的地址,通過這個地址, 從而拿到堆內存中的數據, 因此, 如果將一個對象賦值給另一個對象的時候, 其實是把這個對象在棧中的地址傳遞給了另一個對象, 此時, 他們共享內存中的同一塊空間以及空間裏的數據, 如果對其中一個對象的一個屬性進行修改的話, 那麼因爲兩個對象是共享一塊地址一個數據的, 因此另一個對象中的屬性也會被改變. 如果對其中一個對象重新賦值的話, 那麼這個對象就會指向另一塊內存空間, 就不在與另一個對象共享同一塊內存了

    總結:

    1. 在堆中存儲的是對象,棧存儲的是對象的地址
    2. new關鍵字,會在堆中生成一個空對象
    3. 對象的誕生,地址也會生成
      在這裏插入圖片描述

數據類型的真假值

JavaScript中只有6個假值:undefined, null, NaN, 0, '' (empty string), false

函數構造函數,如new +new Boolean, true1”somestring”[Object], !![] 都是真值。

If([]) true

!! [] 或 !!"string" =》 true

總結:

1. 最基本是null,undefined,if判斷都是假;
2. 對於數值類型,0是假,其他爲真;
3. 對於字符類型空字符串是假,其他爲真,
4.對於方法屬性,如果定義了就是真,否則就是假,其他所有都可以看做是這些的變相應用。

一些特例

NaN

NaN,即非數值(Not a Number)是一個特殊的值,這個數值表示本來要返回數值 的操作數未返回數值的情況(這樣就不會拋出錯誤了)

可以通過+ .NaN得到NaN的值,任何與NaN進行運算的結果均會爲NaN

NaN與自身不相等(NaN不與任何值相等

    alert(  + .NaN);  //NaN
    alert(NaN+1);            //NaN
    alert(NaN==NaN);   //false

null和undefined

相同點:

  • 在 if判斷語句中,值都默認爲 false
  • 大體上兩者都是代表無,具體看差異

差異:

  • null轉爲數字類型值爲0,而undefined轉爲數字類型爲 NaN(Not a + )
  • undefined是代表調用一個值而該值卻沒有賦值,這時候默認則爲undefined
  • null是一個很特殊的對象,最爲常見的一個用法就是作爲參數傳入(說明該參數不是對象)
  • 設置爲null的變量或者對象會被內存收集器回收

數據類型的判斷以及比較

typeof

    typeof的運算數未定義,返回的就是 "undefined". 
    運算數爲數字 typeof(x) = "number" 
    字符串 typeof(x) = "string" 
    布爾值 typeof(x) = "boolean" 
    對象,數組和null typeof(x) = "obj“

instanceof

instanceof 運算符用來檢測 constructor.prototype 是否存在於參數 object 的原型鏈上。
instanceof 的內部機制是通過判斷對象的原型鏈中是不是能找到類型的 prototype
使用 instanceof判斷一個對象是否爲數組,instanceof會判斷這個對象的原型鏈上是否會找到對應的 Array 的原型,找到返回 true,否則返回 false。

[]  instanceof Array; // true

instanceof 只能用來判斷對象類型,原始類型不可以。並且所有對象類型 instanceof Object 都是 true

[]  instanceof Object; // true

Object.prototype.toString.call()

每一個繼承 Object 的對象都有 toString 方法,如果 toString 方法沒有重寫的話,會返回 [Object type],其中 type 爲對象的類型。但當除了 Object 類型的對象外,其他類型直接使用 toString 方法時,會直接返回都是內容的字符串,所以我們需要使用call或者apply方法來改變toString方法的執行上下文。

    const an = ['Hello','An'];
    an.toString(); // "Hello,An"
    Object.prototype.toString.call(an); // "[object Array]"

這種方法對於所有基本的數據類型都能進行判斷,即使是 nullundefined

    Object.prototype.toString.call('An') // "[object String]"
    Object.prototype.toString.call(1) // "[object   + ]"
    Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
    Object.prototype.toString.call(null) // "[object Null]"
    Object.prototype.toString.call(undefined) // "[object Undefined]"
    Object.prototype.toString.call(function(){}) // "[object Function]"
    Object.prototype.toString.call({name: 'An'}) // "[object Object]"

Array.isArray()

功能:用來判斷對象是否爲數組

instanceof 與 isArray

當檢測Array實例時,Array.isArray優於 instanceof ,因爲 Array.isArray 可以檢測出 iframes

    var iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    xArray = window.frames[window.frames.length-1].Array;
    var arr = new xArray(1,2,3); // [1,2,3]

    // Correctly checking for Array
    Array.isArray(arr);  // true
    Object.prototype.toString.call(arr); // true
    // Considered harmful, because doesn't work though iframes
    arr instanceof Array; // false

Array.isArray() 與 Object.prototype.toString.call()

Array.isArray()是ES5新增的方法,當不存在 Array.isArray() ,可以用 Object.prototype.toString.call() 實現。

    if (!Array.isArray) {
        Array.isArray = function(arg) {
            return Object.prototype.toString.call(arg) === '[object Array]';
        };
    }

數據類型轉換

對象到字符串

  1. 如果對象有toString()方法,javascript調用它。如果返回一個原始值(primitive value如:string number boolean),將這個值轉換爲字符串作爲結果
  2. 如果對象沒有toString()方法或者返回值不是原始值,javascript尋找對象的valueOf()方法,如果存在就調用它,返回結果是原始值則轉爲字符串作爲結果
  3. 否則,javascript不能從toString()或者valueOf()獲得一個原始值,此時throws a TypeError
const obj = {
     id: 0,
     name: '張三',
     age: 12
}
const objToStr = JSON.stringify(obj)
console.log('obj:', obj)
console.log('objToStr:', objToStr)

對象到數字的轉換步驟

  1. 如果對象有valueOf()方法並且返回元素值,javascript將返回值轉換爲數字作爲結果
  2. 否則,如果對象有toString()並且返回原始值,javascript將返回結果轉換爲數字作爲結果
  3. 否則,throws a TypeError

json字符串轉對象

const str = '{"id":0,"name":"張三","age":12}'
const strToObj = JSON.parse(str)
console.log('str:', str)
console.log('strToObj:', strToObj)

===運算符判斷相等

  1. 如果兩個值不是相同類型,它們不相等
  2. 如果兩個值都是null或者都是undefined,它們相等 null === undefined
  3. 如果兩個值都是布爾類型true或者都是false,它們相等
  4. 如果其中有一個是NaN,它們不相等
  5. 如果都是數值型並且數值相等,他們相等, -0等於0
  6. 如果他們都是字符串並且在相同位置包含相同的16位值,他它們相等;如果在長度或者內容上不等,它們不相等;兩個字符串顯示結果相同但是編碼不同=都認爲他們不相等
  7. 如果他們指向相同對象、數組、函數,它們相等;如果指向不同對象,他們不相等

==運算符判斷相等

假如我們需要對比 x 和 y 是否相同,就會進行如下判斷流程:

  1. 首先會判斷兩者類型是否相同。相同的話就是比大小了

  2. 類型不相同的話,那麼就會進行類型轉換

  3. 會先判斷是否在對比 null 和 undefined,是的話就會返回 true

  4. 判斷兩者類型是否爲 string 和 number,是的話就會將字符串轉換爲 number
    1 == ‘1’

    1 == 1

  5. 判斷其中一方是否爲 boolean,是的話就會把 boolean 轉爲 number 再進行判斷

     '1' == true
     ↓
     '1' ==  1
     ↓
     1  ==  1
    
  6. 判斷其中一方是否爲 object 且另一方爲 string、number 或者 symbol,是的話就會把 object 轉爲原始類型再進行判斷

     '1' == { name: 'yck' }
     ↓
     '1' == '[object Object]'
    

[] == ![] (true)解析

  • 根據運算符優先級 ,! 的優先級是大於 == 的,所以先會執行 ![]

    • !可將變量轉換成boolean類型,null、undefined、NaN以及空字符串(’’)取反都爲true,其餘都爲false。

    • 所以 ! [] 運算後的結果就是 false

    • 也就是 [] == ! [] 相當於 [] == false

  • 根據上面提到的規則(如果有一個操作數是布爾值,則在比較相等性之前先將其轉換爲數值——false轉換爲0,而true轉換爲1),則需要把 false 轉成 0

    • 也就是 [] == ! [] 相當於 [] == false 相當於 [] == 0
  • 根據上面提到的規則(如果一個操作數是對象,另一個操作數不是,則調用對象的valueOf()方法,用得到的基本類型值按照前面的規則進行比較,如果對象沒有valueOf()方法,則調用 toString())

    • 而對於空數組,[].toString() -> ‘’ (返回的是空字符串)

    • 也就是 [] == 0 相當於 ‘’ == 0

  • 根據上面提到的規則(如果一個操作數是字符串,另一個操作數是數值,在比較相等性之前先將字符串轉換爲數值)

    • Number(’’) -> 返回的是 0

    • 相當於 0 == 0 自然就返回 true了

  • 總結一下:

    • [] == ! [] -> [] == false -> [] == 0 -> ‘’ == 0 -> 0 == 0 -> true

{} == !{} (false)也是同理的

關鍵在於 {}.toString() -> NaN(返回的是NaN)

根據上面的規則(如果有一個操作數是NaN,則相等操作符返回 false)

總結一下:

{} == ! {} -> {} == false -> {} == 0 -> NaN == 0 -> false

變量(函數)提升

  1. 函數提升優先於變量提升,函數提升會把整個函數挪到作用域頂部,變量提升只會把聲明挪到作用域頂部

  2. 存在提升,我們能在聲明之前使用。let、const 因爲暫時性死區的原因,不能在聲明前使用

  3. var 在全局作用域下聲明變量會導致變量掛載在 window 上,其他兩者不會

  4. let 和 const 作用基本一致,但是後者聲明的變量不能再次賦值

  5. Javascript在執行前會對整個腳本文件的聲明部分做完整分析(包括局部變量),從而確定實變量的作用域。

  6. Javascript的變量的範圍是根據方法塊來劃分的(也就是說以function的一對大括號{ }來劃分)。切記,是function塊,而for、while、if塊並不是作用域的劃分標準。

  7. 當全局變量跟局部變量重名時,局部變量的範圍會覆蓋掉全局變量的範圍,當離開局部變量的範圍後,又重回到全局變量的範圍。(若想指定是全局變量可以使用 window.globalVariableName)
    4 js中創建函數有兩種方式:函數聲明式和函數字面量式。只有函數聲明才存在函數提升!如:


	console.log(f1); // function f1() {} 
	console.log(f2); // undefined 
	function f1() {}      // 函數聲明
    var f2 = function() {} 

輸出結果: function f1() {}
undefined

之所以會有以上的打印結果,是由於js中的函數提升導致代碼實際上是按照以下來執行的:


	function f1() {} // 函數提升,整個代碼塊提升到文件的最開始<br>
	console.log(f1)
	console.log(f2); 
	var f2 = function() {}


<script type="text/javascript">
	var a = 100;
	function test() {
		alert(a);
		var a = 10;
		alert(a);
	}
	test();
	alert(a);
// 答案 undefined 10 100
// 相當於
	var a = 100;
	function test() {
		var a
		alert(a);
		a = 10;
		alert(a);
	}
	test();
	alert(a);
	
</script>
<script type="text/javascript">
	while(true){
		var i = 1;
		break;
	}
	alert(i);
	// 答案 1
	
</script>
<script type="text/javascript">
	var i = 100;
	function test() {
		alert(i);
		i = 50;
		alert(i)
		var i = 10;
		alert(i);
	}
	test();
	alert(i);
	// 答案 undefined 50  10 100
</script>
<script type="text/javascript">
	var i = 100;
	function test() {
		alert(window.i);
		var i = 10;
		alert(i);
	}
	test();
	alert(i);
	// 答案 100  10 100
	
</script>

var、let 和 const

var、let 和 const 區別的實現原理是什麼?

變量生命週期

聲明(作用域註冊一個變量)、初始化(分配內存,初始化爲undefined)、賦值

  • var:遇到有var的作用域,在任何語句執行前都已經完成了聲明和初始化,也就是變量提升而且拿到undefined的原因由來
  • function: 聲明、初始化、賦值一開始就全部完成,所以函數的變量提升優先級更高
  • let:解析器進入一個塊級作用域,發現let關鍵字,變量只是先完成聲明,並沒有到初始化那一步。此時如果在此作用域提前訪問,則報錯xx is not defined,這就是暫時性死區的由來。等到解析到有let那一行的時候,纔會進入初始化階段。如果let的那一行是賦值操作,則初始化和賦值同時進行

const、class都是同let一樣的道理

let和const

let和const兩個變量聲明命令,他們都具有如下特性:

  1. 塊局作用域;
  2. 不存在變量提升,一定聲明後才能使用;
  3. 暫時性死區,在代碼塊內使用let命令聲明變量之前,該變量都是不可用的,不受外部變量影響;
  4. 在相同作用域範圍內不允許重複聲明;

const與let不同點在於:

  1. const如果聲明的變量是簡單的值,則不能改變變量的值,修改會報錯;
  2. const如果聲明的是複合類型的變量,則只保證變量地址不變,值可以變;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章