基礎知識
文章目錄
基本數據類型
String, Number, Boolean, Undefined, Null, Symbol(ES6)
- 使用typeof校驗的值
typeof 的運算數未定義,返回的就是 "undefined".
運算數爲數字 typeof(x) = "number"
字符串 typeof(x) = "string"
布爾值 typeof(x) = "boolean"
null typeof(x) = "object"
布爾值 typeof(Symbol) = "function"
-
數據存儲結構
簡單數據類型是存儲在棧中的
引用數據類型
Object, Function, Array, Date, Regexp
- 使用typeof校驗的值
typeof(Object) = "object"
typeof(Function) = "function"
typeof(Array) = "object"
typeof(Date) = "function"
typeof(RegExp) = "function"
對象,數組和null `typeof(x) = "object" `
-
數據存儲結構
複雜數據類型跟簡單數據類型的不同點就是在於, 簡單數據類型的變量指向的是內存中的數據, 而複雜數據類型指向的是其在棧的地址,通過這個地址, 從而拿到堆內存中的數據, 因此, 如果將一個對象賦值給另一個對象的時候, 其實是把這個對象在棧中的地址傳遞給了另一個對象, 此時, 他們共享內存中的同一塊空間以及空間裏的數據, 如果對其中一個對象的一個屬性進行修改的話, 那麼因爲兩個對象是共享一塊地址一個數據的, 因此另一個對象中的屬性也會被改變. 如果對其中一個對象重新賦值的話, 那麼這個對象就會指向另一塊內存空間, 就不在與另一個對象共享同一塊內存了
總結:
- 在堆中存儲的是對象,棧存儲的是對象的地址
- new關鍵字,會在堆中生成一個空對象
- 對象的誕生,地址也會生成
數據類型的真假值
JavaScript中只有6個假值:undefined, null, NaN, 0, '' (empty string), false
函數構造函數,如new +
和new Boolean
, true
、1
、”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]"
這種方法對於所有基本的數據類型都能進行判斷,即使是 null
和 undefined
。
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]';
};
}
數據類型轉換
對象到字符串
- 如果對象有toString()方法,javascript調用它。如果返回一個原始值(primitive value如:string number boolean),將這個值轉換爲字符串作爲結果
- 如果對象沒有toString()方法或者返回值不是原始值,javascript尋找對象的valueOf()方法,如果存在就調用它,返回結果是原始值則轉爲字符串作爲結果
- 否則,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)
對象到數字的轉換步驟
- 如果對象有valueOf()方法並且返回元素值,javascript將返回值轉換爲數字作爲結果
- 否則,如果對象有toString()並且返回原始值,javascript將返回結果轉換爲數字作爲結果
- 否則,throws a TypeError
json字符串轉對象
const str = '{"id":0,"name":"張三","age":12}'
const strToObj = JSON.parse(str)
console.log('str:', str)
console.log('strToObj:', strToObj)
===運算符判斷相等
- 如果兩個值不是相同類型,它們不相等
- 如果兩個值都是null或者都是undefined,它們相等 null === undefined
- 如果兩個值都是布爾類型true或者都是false,它們相等
- 如果其中有一個是NaN,它們不相等
- 如果都是數值型並且數值相等,他們相等, -0等於0
- 如果他們都是字符串並且在相同位置包含相同的16位值,他它們相等;如果在長度或者內容上不等,它們不相等;兩個字符串顯示結果相同但是編碼不同和=都認爲他們不相等
- 如果他們指向相同對象、數組、函數,它們相等;如果指向不同對象,他們不相等
==運算符判斷相等
假如我們需要對比 x 和 y 是否相同,就會進行如下判斷流程:
-
首先會判斷兩者類型是否相同。相同的話就是比大小了
-
類型不相同的話,那麼就會進行類型轉換
-
會先判斷是否在對比 null 和 undefined,是的話就會返回 true
-
判斷兩者類型是否爲 string 和 number,是的話就會將字符串轉換爲 number
1 == ‘1’
↓
1 == 1 -
判斷其中一方是否爲 boolean,是的話就會把 boolean 轉爲 number 再進行判斷
'1' == true ↓ '1' == 1 ↓ 1 == 1
-
判斷其中一方是否爲 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
變量(函數)提升
-
函數提升優先於變量提升,函數提升會把整個函數挪到作用域頂部,變量提升只會把聲明挪到作用域頂部
-
存在提升,我們能在聲明之前使用。let、const 因爲暫時性死區的原因,不能在聲明前使用
-
var 在全局作用域下聲明變量會導致變量掛載在 window 上,其他兩者不會
-
let 和 const 作用基本一致,但是後者聲明的變量不能再次賦值
-
Javascript在執行前會對整個腳本文件的聲明部分做完整分析(包括局部變量),從而確定實變量的作用域。
-
Javascript的變量的範圍是根據方法塊來劃分的(也就是說以function的一對大括號{ }來劃分)。切記,是function塊,而for、while、if塊並不是作用域的劃分標準。
-
當全局變量跟局部變量重名時,局部變量的範圍會覆蓋掉全局變量的範圍,當離開局部變量的範圍後,又重回到全局變量的範圍。(若想指定是全局變量可以使用 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兩個變量聲明命令,他們都具有如下特性:
- 塊局作用域;
- 不存在變量提升,一定聲明後才能使用;
- 暫時性死區,在代碼塊內使用let命令聲明變量之前,該變量都是不可用的,不受外部變量影響;
- 在相同作用域範圍內不允許重複聲明;
const與let不同點在於:
- const如果聲明的變量是簡單的值,則不能改變變量的值,修改會報錯;
- const如果聲明的是複合類型的變量,則只保證變量地址不變,值可以變;