原始(Primitive)值通常叫作棧數據(一旦開了個房間、不可能在這個房間裏對其進行修改)數組
原始類型存儲的都是值,是沒有函數能夠調用的,如 undefined.toString() 會報錯。通常咱們看到的 '1'.toString() 能夠調用是由於實際上它已經被強制轉換成了 String 類型也就是對象類型,因此能夠調用 toString 函數 安全
在 JS 中,存在着如下幾種原始值,分別是:markdown
number(typeof undefined === "undefined")
string(typeof '' === 'string')
boolean(typeof true === 'boolean')
null(typeof null === 'object')
undefined(typeof null === 'undefined')
symbol(typeof Symbol() === 'symbol')
bigInt(typeof 10n === 'bigint')(沒有正式發佈但即將被加入標準的原始類型)函數
undefined 和 null 的區別post
除了會在必要的狀況下類型轉換之外,原始類型還有一些坑性能
typeof null 會輸出 object,這是 JS 存在的一個悠久 Bug,在 JS 的最第一版本中使用的是 32 位系統,爲了性能考慮使用低位存儲變量的類型信息,000 開頭表明是對象,然而 null 表示爲全零,因此將它錯誤的判斷爲 object
0.1 + 0.2 !== 0.3
,此處詳見分析篇:爲何 0.1 + 0.2 !== 0.3通常叫作堆數據,包括:ui
對象(Object)(typeof {} === 'object') 數組(Array)(typeof [] === 'object')
函數(Function)(typeof function(){} === 'function')spa
引用類型
與原始類型
的區別指針
引用值的變量名存在棧裏,可是值倒是存在堆裏,棧裏的變量名只是個指針並指向了一個堆空間
,這個堆空間存的是一開始賦的值,當 arr1 = arr 時,實際上是把 arr1 指向了和 arr 指向的同一個堆空間,這樣當改變 arr 的內容時,其實就是改變了這個堆空間的內容,天然一樣指向這個堆空間的 arr1 的值也隨着改變 // num的改變對num1徹底沒有影響
var num = 123, num1 = num;
num = 234;
console.log(num) // 234
console.log(num1) // 123
// 只是改變了arr的值,可是arr1也跟着改變了
var arr = [1,2,3], arr1 = arr;
arr.push(4)
console.log(arr) // [1,2,3,4]
console.log(arr1) // [1,2,3,4]
複製代碼
再來看個函數參數是對象的狀況code
function test(person) {
person.age = 26
person = {
name: 'yyy',
age: 30
}
return person
}
const p1 = {
name: 'yck',
age: 25
}
const p2 = test(p1)
console.log(p1) // {name: "yck", age: 26}
console.log(p2) // {name: "yyy", age: 30}
複製代碼
(1)上面代碼中,首先函數傳參是傳遞對象指針的副本
(2)到函數內部修改參數的屬性這步,當前 p1 的值也被修改了
(3)可是當從新爲 person 分配了一個對象時就出現了分歧,請看下圖,因此最 後 person 擁有了一個新的地址(指針),即和 p1 沒有任何關係,致使了 最終兩個變量的值是不相同的
原始數據類型的值直接存放在棧中,對象爲引用數據類型,它們的引用變量存儲在棧中,指向於存儲在堆中的實際對象。沒法直接操縱堆中的數據,即沒法直接操縱對象,但可經過棧中對對象的引用來操做對象,就像經過遙控機操做電視機同樣,區別在於這個電視機自己並無控制按鈕
爲何引用值要放在堆中,而原始值要放在棧中?
(1)堆比棧大,棧比堆的運算速度快;對象是一個複雜的結構且能夠自由擴展,如數組能夠無限擴充、對象能夠自由添加屬性
(2)相對而言原始類型比較穩定且它只佔據很小的內存,不將原始類型放在堆是由於是爲了避免影響棧的效率,且經過引用到堆中查找實際對象是要花費時間的,而這個綜合成本遠大於直接從棧中取得實際值的成本,因此原始類型值直接存放在棧中
算術運算符(算術運算符的優先級是從左到右的)
+
:數學上的相加功能、拼接字符串(字符串和任何數據相加都會變成字符串)–
/*
//
/%
:f分別對應數學上的相減、相乘、相除、取餘功能=
:賦值運算符,優先級最低()
:和數學上同樣,加括號的部分優先級最高++
:自加 1 運算,當寫在變量前時是先自加 1 再執行運算,寫在變量後時是先運算再自加 1--
:用法和 ++ 同樣,不過是減法操做+=
:讓變量自加多少-=、/=、*-、%=
等等比較運算符
> 、< 、>= 、<= 、!= 、== 不嚴格等於、===嚴格等於
不嚴格等於
就是說這兩個數據進行了轉化後值相同則整兩個數據相等;而嚴格等於
則是兩個數據不進行數據轉化也相等注:NaN 不等於任何數據包括它自己,null 和 undefined 就等於它自己
邏輯運算符
&& 的做用
:只有是 true 時纔會繼續日後執行,一旦第一個表達式就錯了後面的第二個表達式根本不執行。若表達式的返回結果都是 true 則這裏 && 的返回結果是最後一個正確的表達式的結果|| 的做用
:只要有一個表達式是 true 則結束,後面的就不走了且返回的結果是這個正確的表達式的結果,若都是 false 則返回結果就是 false注意:這裏有一個缺點,當傳的參數是一個布爾值且傳的是 false,則 || 語句的特色就會忽略掉所傳的這個參數值而去賦成默認的初始值,因此爲了解決這個弊端,就須要利用 ES6 的一些知識
默認爲 false 的值:undefined、null、" "、0、-0、false、NaN
// String(100000000000000000000000) -> "1e+23"
parseInt(100000000000000000000000); // 1
複製代碼
isNaN(NaN) // true
isNaN('abc') // true
isNaN(123) // false
複製代碼
undefined
,null
,false
,NaN
,''
,0
,-0
,其餘全部值都轉爲 true,包括全部對象原始值 | 轉換爲數值 | 轉換爲字符串 | 轉換爲布爾值 |
---|---|---|---|
number | / | 0 -> "0",5 -> "5" | 除了 0、-0、NaN 都爲 true |
string | " " -> 0,"1" -> 1,"a" -> NaN | / | 除了空字符串都爲 true |
undefined | NaN | "undefined" | false |
null | 0 | "null" | false |
[] | 0 | " " | true |
[10,20] | NaN | "10,20" | true |
{} | NaN | "[object, Object]" | true |
{a: 1} | NaN | "[object, Object]" | true |
function() {} | NaN | "function(){}" | true |
true | 1 | "true" | true |
false | 0 | "false" | false |
Symbol | NaN | "function Symbol() { [native code] }" | true |
Symbol() | 拋錯 | "Symbol()" | true |
一些常規和很是規的轉換狀況
// 常規
"0" == null // false
"0" == undefined // false
"0" == NaN // false
"0" == 0 // true
"0" == "" // false
false == null // false
false == undefined // false
false == NaN // false
false == {} // false
"" == null // false
"" == undefined // false
"" == NaN // false
"" == {} // false
0 == null // false
0 == undefined // false
0 == NaN // false
0 == {} // false
// 很是規
"0" == false // true
false == 0 // true
false == "" // true
false == [] // true
"" == 0 // true
"" == [] // true
0 == [] // true
複製代碼
對 == 兩邊的值認真推敲,如下兩個原則能夠有效地避免出錯,這時最好用 === 來避免不經意的強制類型轉換
(1)若兩邊的值中有 true
或 false
,千萬不要使用 ==
(2)若兩邊的值中有 []
、""
或者 0
,儘可能不要使用 ==
總結
undefined == null,結果是 true 且它倆與全部其餘值比較的結果都是 false
- String == Boolean,須要兩個操做數同時轉爲 Number
- String/Boolean == Number,須要 String/Boolean 轉爲 Number
- Object == Primitive,須要 Object 轉爲 Primitive(具體經過 valueOf 和 toString 方法)
==
:等於,===
:嚴格等於,Object.is()
:增強版嚴格等於
const a = 3;
const b = "3";
a == b; // true
a === b; // false,由於*a*,*b*的類型不同
Object.is( a, b ); //false,由於*a*,*b*的類型不同
複製代碼
===
這個比較簡單,只須要利用下面的規則來判斷兩個值是否恆等
若類型不一樣,就不相等
若兩個都是數值且是同一個值,那麼相等 有一個是 NaN 就不相等 若兩個都是字符串且每一個位置的字符都同樣,那麼相等;不然不相等 若兩個值都是一樣的 Boolean 值,那麼相等 若兩個值都引用同一個對象或函數,那麼相等,即兩個對象的物理地址也必須保持一致;不然不相等。 若兩個值都是 null 或者都是 undefined,那麼相等
Object.is()
其行爲與 === 基本一致,不過有兩處不一樣:
+0 不等於 -0
NaN 等於自身
+0 === -0 //true
NaN === NaN // false
Object.is(0, -0) // false
Object.is(+0, -0) // false
Object.is(0, +0) // true
Object.is(-0, -0) // true
Object.is(NaN, 0/0) // true
Object.is(NaN, NaN) // true
Object.is('foo', 'foo'); // true
Object.is(window, window); // true
Object.is('foo', 'bar'); // false
Object.is([], []); // false
const foo = { a: 1 };
const bar = { a: 1 };
Object.is(foo, foo); // true
Object.is(foo, bar); // false
Object.is(null, null); // true
複製代碼
Object.is()
在嚴格等於的基礎上修復了一些特殊狀況下的失誤,具體來講就是 +0
和 -0
,NaN
和 NaN
。
function objectIs(x, y) {
if(x === y) {
// 運行到 1/x === 1/y 時 x 和 y 都爲 0,但 1/+0 = +Infinity,1/-0 = -Infinity 是不同的
return x !== 0 || y !== 0 || 1 / x === 1 / y;
} else {
// NaN === NaN 是false,在這裏作個攔截,x !== x 必定是 NaN, y 同理
// 兩個都是 NaN 時返回 true
return x !== x && y !== y;
}
}
複製代碼