JS相關知識總結(一)

總結下這段時間吸收的許多小知識,以備忘記後翻閱。

關於面向對象

面向對象特徵:

  • 具有唯一標識性
  • 具有狀態
  • 具有行爲

JS的面向對象和JAVA的實現思路不一樣,JS是基於原型並非基於類。但是JS爲了看起來更像JAVA,爲此添加了一些特性。

拋開爲了看起來像JAVA引入的特性,JS面向對象總結起來很簡單:每個對象都有原型,對象上找不到屬性就去自身原型上找,直到Object.prototype。

在ES3時代爲了操作原型我們沒有辦法只能使用 new但是ES5之後引入了專門操作原型的方法:

  • Object.create()
  • Object.setPrototypeOf()
  • Object.getPrototypeOf()

這樣我們就可以拋開類的概念直接面對原型來實現面向對象。

JS關於模擬JAVA的面向對象部分

在我的理解中有以下部分屬於模擬JAVA的行爲:

  • function 可以被new調用
  • 引入this機制
  • instanceof 操作符

通過 new 調用函數主要做了三件事:

  • 使用被new 調用的函數的prototype屬性構造一個新對象
  • 將新對象作爲函數的this,調用該函數
  • 如沒有引用類型返回則返回這個新建的對象

上面就是在模擬JAVA中的類,在ES5中拋開new我們可以做到同樣的事情,並且不會有讓人以爲這是一個類。

this更是複雜,一大批文章去解釋JS中的this,由此也可以看出有些複雜。

JS中的數據類型

JS一共有7中數據類型:

  • Null
  • Undefined
  • Number
  • String
  • Boolean
  • Symbol (ES6新加)
  • Object (引用類型)

7種數據類型又被分爲兩類:基本類型和引用類型。

怎麼判斷數據對應的是哪種數據類型呢?

總結有三種:typeofinstanceofObject.prototype.toString.call(),這三種都有自己的特點。

typeof

基本上可以直接返回數據對應的類型,有兩個例外,一個是 typeof null返回 "object",另一個是 typeof function() {}返回 "function"

instanceof

左邊是一個對象右邊是一個構造函數。

instanceof會遍歷對象的原型,查找是否有函數的prototype屬性。

我認爲,檢查數據是否是數組就很方便了 [] instanceof Array,當然這個是有問題的,所以後面又提供了Array.isArray()方法判斷是否是數組。

Object.prototype.toString.call()

該方法提供的返回值十分詳細,不僅會返回上面其中類型,還包括很多JS內置對象,例如:ArrayDateRegExp等。

但是這個對象有個問題,會強制裝箱。也就是說判斷不出是基本類型還是引用類型,如果在意的話需要配合上typeof


Object.prototype.toString.call(1); // [object Number]

Object.prototype.toString.call(new Number(1)); // [object Number]

返回值都一樣。

關於裝箱和拆箱

說道裝箱和拆箱就要提及一下它的表象 – 隱式類型轉換。

JS中隱式類型轉換應用廣泛,最臭名昭著的就是 ==。還有當我們直接調用 str.indexOf方法的時候,str明明是基本值還不報錯就是裝箱在起作用。

裝箱

每個基本值都有一個對應的對象類,裝箱就是將基本值轉換爲對應的對象類型。但是Symbol是不能通過new 調用的,所以我們要另想辦法。

上面提到的Object.prototype.toString.call會強制裝箱:

function a() {
  return this;
}
a.call(Symbol())  // 查看打印

拆箱(toPrimitive)

拆箱會先調用對象的valueOf方法,如果返回值是非基本值再調用toString方法,返回值非基本值則報錯。

還記得偏門的面試題麼?

	if (a == 2 && a == 3) {
      console.log(true);
	}

雖然是語言不好的一面,但也可以瞭解下。這裏就用到了拆箱知識。

如果 a 是對象和基本類型做 ==比較首先會對 a 拆箱,拆箱步驟如上所述。

let b = 1;
let a = {
  valueOf: () => ++b
}

if (a == 2 && a == 3) {
  console.log(true);
}

這樣就會打印出 true

自定義拆箱

關於拆箱最後想說的是現在可以自定義這個行爲了。

let o = {
  [Symbol.toPrimitive]: () => "hello world"
};

console.log('' + o);

Number

關於Number一直有一個疑問就是NaN到底是不是數字。typeof NaN其實返回的是 number 。所以Not a Number 是一個 Number。

我們都知道JS中數字有精度問題:0.1 + 0.2 === 0.3返回的是false。這個時候們就要藉助 Number.EPSILON來幫我們判斷,Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON

JS中是區分 +0 和 -0 的,但是 +0 === -0返回true。那麼怎麼區分+0 和 -0 呢,這也是polyfill Object.is() 需要考慮的問題。

我們可以通過用 +0 和 -0 做分母來區分:

1 / 0 === Infinity // true
1 / -0 === -Infinity // true
1 / 0 === 1 / -0 // false

函數

通過實際開發中我們可以知道,有的函數可以直接調用和使用new調用,有的函數指能通過new調用有的函數只能直接調用。

這背後是有機制在控制的。JS中的數據類型大體分爲兩種,一個是基本類型,一個是引用類型。那麼按理說function不是基本類型那就是引用類型,引用類型就是隻有對象,函數也是一個對象,爲什麼函數這個對象可以調用而其他對象不行呢?

是因爲函數這個對象具有兩個私有屬性決定了上面的特性,我們並沒有手段去定義和查看這兩個私有屬性。一個是 [[constructor]],另一個是 [[call]]。分別決定了是否可以通過new調用和直接調用。

:不能通過new調用函數例如 () => {}箭頭函數,不能直接調用的函數例如Image這樣的環境提供的函數。

總結於即刻時間重學前端欄目,winter說知識是免費的,教育是收費的,這邊總結出來的應該算知識,教育還留在了欄目裏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章