基石-ES5基礎(一) 數據類型&類型轉換/判斷

數據類型&&類型轉換/判斷

你們好,本系列文章是總結我學習ES5的一些筆記以及一些認爲比較重要的知識點,其中在ES部分有以下整理:html

  • 基石:ES5基礎(一) 數據類型&類型轉換面試

  • 基石:ES5基礎(二) 對象&對象特徵屬性算法

  • 基石:ES5基礎(三) 原型&原型鏈&繼承數組

  • 基石:ES5基礎(四) 函數&做用域&閉包bash

我知道,有不少大佬已經hold這些知識點,而且很是高贊。但人家的終歸是人家的.. 沒法親力親爲,何談印象深入閉包

首先感謝各位閱讀本文章,因爲弟弟剛學不久,雖然對語言的高度認識還不夠,對寫文檔的能力弱一逼,以及總結概括技巧很是拙劣。可是我知道你們都是敞亮人都是哥哥,會鞭策好我讓新萌獲得最快成長,謝謝你們的海涵!!函數

首先先扔幾道高頻面試題來開始咱們本次ES5之旅。post

高頻面試題

請描述一下原始值引用值的區別學習

請簡單解釋一下 0.1 + 0.2 != 0.3ui

簡述 1.abc=123 經歷了哪些過程

4 + [1,2,3]'a' + + 'b' 以及[] == ![]分別返回什麼?

判斷類型的方法以及不一樣,瞭解過$.type嗎?

原始值和引用值

ES規範中規定了數據類型分爲兩大陣營,一方是原始值,另一方是引用值。我剛接觸ES的時候就一直有一個疑惑,爲何計算機要設計原始值以及引用值?直接放一個鍋裏不行嗎?後來當我學過堆和棧的概念以後,彷彿理解了一些。

首先回顧一下原始值和引用值分別是什麼。

回顧原始值和引用值

初見原始值

6.1ECMAScript Language Types

An ECMAScript language type corresponds to values that are directly manipulated by an ECMAScript programmer using the ECMAScript language. The ECMAScript language types are Undefined, Null, Boolean, String, Symbol, Number, and Object. An ECMAScript language value is a value that is characterized by an ECMAScript language type.

manipulate:操縱 characterized by:特色

在ECMA標準中,規定了ES有以下幾種數據類型供給咱們操做

Undefined Null Boolean String Symbol(ES6新增) Number 以及 Object.

經過ECMA標準以及在紅寶書上咱們學習到對於數據類型的講解和分類,分爲原始值和引用值。首先看一下在ECMA標準中是如何定義着7種數據類型。

原始數據類型

Undefined

6.1.1The Undefined Type

The Undefined type has exactly one value, called undefined. Any variable that has not been assigned a value has the value undefined.

assign: 指派 exactly: 確切

確切的說Undefined類型只有一個值,是undefined。表示任何的變量若是沒有被分配值那麼默認是undefined.注意和null的區別

Null

6.1.2The Null Type

The Null type has exactly one value, called null.

Null類型只有一個值,是null.官方文檔沒有給出額外的信息,可是在紅寶書中說過

從邏輯的角度講null是用來表示一個空指針,而且typeof返回object。一般是用來表示一個對象已經聲明,最好使用null來初始化而不是其餘值。

null和undefined的關係

根據ECMA-262規定對他們的相等性判斷是返回true,其根本緣由是undefined是派生自null。

可是他們的語義和用途是不一樣的.任何狀況下咱們都不必對變量顯示賦值爲undefined。對於對象變量尚未保存對象時,咱們應該明確的讓此對象等於null.

Boolean

6.1.3The Boolean Type

The Boolean type represents a logical entity having two values, called true and false.

represents: 表明 logical: 邏輯學

String

6.1.4The String Type

The String type is generally used to represent textual data in a running ECMAScript program, in which case each element in the String is treated as a UTF-16 code unit value.

treat: 對待

String類型一般用於表示正在運行的程序中文本數據,每一個元素都被視爲UTF-16代碼單元值。

  • Symbol ES6章節介紹
Number

6.1.6The Number Type

The Number type has exactly 18437736874454810627 (that is, 264-253+3) values, representing the double-precision 64-bit format IEEE 754-2008 values as specified in the IEEE Standard for Binary Floating-Point Arithmetic, except that the 9007199254740990 (that is, 253-2) distinct 「Not-a-Number」 values of the IEEE Standard are represented in ECMAScript as a single special NaN value.

specifiey: 指定 distinct: 明顯的

Number類型中能表示那麼多的值,由於採用的是IEEE二進制雙精度浮點數運算標準。這爲0.1 + 0.2 != 0.3埋下了伏筆。在ECMA中,使用一個特殊值NaN表示不在標準範圍內的值。

all NaN values are indistinguishable from each other.

indistinguishable: 難分辨

全部的NaN彼此之間是沒法區分的,也就是說 NaN == NaN爲 false,可是請注意在ES6中NaN使用Object.is來判斷是相等的。

Note that there is both a positive zero and a negative zero. For brevity, these values are also referred to for expository purposes by the symbols +0 and -0, respectively.

negate: 取消/負的

注意,在ECMA中,對於0是分+0和-0的。咱們知道在ES5中對於-0 ===+0爲true.可是在ES6使用Object.is判斷爲不相等。

引用值

Object

6.1.7The Object Type

An Object is logically a collection of properties. Each property is either a data property, or an accessor property:

對象在邏輯上是屬性的集合,每一個屬性或者是數據屬性,或者是訪問器屬性。本身分一類絲絕不過度,除了經常使用的ObjectArrayFunction等都屬於特殊的對象;數據屬性和訪問器屬性會在對象部分詳解。

簡單回顧後,引出核心問題之一,簡述一下原始值和引用值的區別?

原始值區別

不變性與可變性

原始類型的不變性

JavaScript中的原始類型的值被直接存儲在棧中,在變量定義時,棧就爲其分配好了內存空間,因爲棧中的內存空間的大小是固定的,那麼註定了存儲在棧中的變量就是不可變的。 棧內存特色

  1. 存儲的值大小固定
  2. 空間較小
  3. 能夠直接操做變量,運行效率高
  4. 系統自動分配

多數狀況下,基本類型直接表明了最底層的語言實現。

全部基本類型的值都是不可改變的。但須要注意的是,基本類型自己和一個賦值爲基本類型的變量的區別。變量會被賦予一個新值,而原值不能像數組、對象以及函數那樣被改變。

var str = "SU";
str.slice(1);
console.log(str) // SU;
str += "1";
console.log(str) // SU1;
複製代碼

當咱們調用方法後,是在原字符串的基礎上進行操做產生一個新的字符串,而並不是直接修改str,着印證了字符串的不變性。 但你會發現第二次str被修改了,看似是str被改變了實際上是發生了下面的代碼

var str = str + 1; 
//等同於str += 1;
複製代碼

是由於JavaScript中的原始值存放在中,在變量定義的同時就分配好了內存空間。因此 str += 1其實是在內存中開闢了一個新空間,並不是是改變字符串的值,並不違原始類型的不可變性。

引用類型的可變性

引用類型的值實際存儲在堆內存中,它在棧中只存儲了一個固定長度的地址,這個地址指向堆內存中的值.

var spy = {
age : 23,
}
console.log(spy) //age : 23,
spy[daughter] = "qianqian";
console.log(spy) //age : 23,daughter : qianqian
複製代碼

印證了引用類型的可變性。

堆內存特色:

  1. 存儲的值大小不定,可動態調整
  2. 空間較大,運行效率低
  3. 沒法直接操做其內部存儲,使用引用地址讀取
  4. 經過代碼進行分配空間 引用值類型能夠輕易的改變他們的數據,好比數組的pop();

複製

原始值複製後的變量指向的內存空間徹底不一樣,變量參與任何操做都互不影響。

var foo = 123;
var bar = foo; //bar : 123
bar ++; // 124
console.log(foo) // 123
複製代碼

引用值複製的是棧中存儲的地址,會指向堆內存中同一個對象。改變其中一個任意一個變量的數據就會致使相同地址的變量發生改變。

var spy = {
age : 23,
}
spy[daughter] = "qianqian";
var lcy = spy;
lcy[sex] = "female" ;
console.log(lcy); //age:23,sex:female,daughter:qianqian
console.log(spy); //age:23,sex:female,daughter:qianqian
複製代碼

將spy這個對象複製給lcy對象後,操做lcy對象進行設置屬性和賦值,會發現spy對象也被一樣操做。

所以一般使用淺拷貝和深拷貝來實現對引用值進行復制。

  • 精進EMCA-手寫遞進的深層拷貝
  • 精進EMCA-深刻垃圾回收機制

比較

對於原始類型,比較時會直接比較它們的值,若是值相等,即返回true。

var a == 1;
var b == 1;
console.log(a == b) // true;
b = 3;
console.log(a == b) // false;
複製代碼

對於引用類型,比較時會比較它們的引用地址,雖然兩個變量在堆中存儲的對象具備的屬性值可能相等,可是它們被存儲在了不一樣的存儲空間,所以比較值爲false。

var arr1 = [1];
var arr2 = [1];
console.log(arr1 == arr2) //false;
複製代碼

值傳遞和引用傳遞

函數參數傳遞的並非變量的引用,而是變量拷貝的副本,當變量是原始類型時,這個副本就是值自己;當變量是引用類型時,這個副本是指向堆內存的地址。因此,請默唸三遍:ECMAScript中全部函數的參數都是按值傳遞的

function add(num){
num++;
return num;
}
var a = 1;
console.log(add(1),a); // 2  1;

複製代碼

當原始值1做爲函數參數傳遞給函數時,可以證實是傳遞的值自己。就和原始值賦值是同樣的性質(本質是arugments作緩衝層)

function setName(obj){
obj[name] = "spy";
return obj;
}
function createNew(obj){
obj[name] = "spy";
obj = new Object(); //notice
return obj;
}
var obj = new Object();
setName(obj);
console.log(obj); // spy; 
var newObj = createNew(obj); 
console.log(obj);// spy;
console.log(newObj); // {}

複製代碼

解釋一下notice標記的代碼,此時的obj是隸屬於createNew函數的內部變量,在函數執行結束後會當即銷燬。在這裏只是更新內部同名屬性obj指針,可是不會影響外部的obj。所以hold參數是按值傳遞的。

請默唸三遍:ECMAScript中全部函數的參數都是按值傳遞

基本包裝類

特殊引用值:Boolean Number String

在ECMA標準中,還規定了3種特殊引用類型:Boolean Number String統稱爲基本類型包裝類,他和引用類型同樣,也具備與各自的基本類型相應的特殊行爲.

var name = "Su";
var length = name.length();

複製代碼

咱們知道,基本類型值不是對象,於是從邏輯上 講它們不該該有方法(儘管如咱們所願,它們確實有方法.其實,爲了讓咱們實現這種直觀的操做,後臺已經自動完成了一系列的處理。

  1. 建立String類型的實例
  2. 在實例name上調用length方法
  3. 銷燬這個實例

所以能夠理解成以下

var name = new String("Su");
var length = name.length();
name = null;

複製代碼

這樣就能夠將基本的字符串和對象同樣看待。同時分別適用於Boolean和Number。

注意undefined和null沒有基本包裝類,也就意味着他們沒有方法,設置不了屬性。

undefined.name = 'Su'; //Uncaught TypeError: Cannot set property 'name' of undefined
undefined.length(); //Uncaught TypeError: Cannot read property 'length' of undefined

複製代碼

由於undefined是派生自null,所以null也一樣沒有方法和沒法設置屬性。所以能夠經過包裝類的角度解釋爲何undefined和null的調用方法和設置屬性會報錯

生命週期

引用類型與基本包裝類型的主要區別就是對象的生存期

自動建立的基本包裝類型的對象,則只存在於一 行代碼的執行瞬間,而後當即被銷燬。這意味着咱們不能在運行時爲基本類型值添加屬性和方法。

能夠通俗的理解爲給基本類型設置屬性後,再次訪問這個屬性會返回undefined(非嚴格模式)。

var spy = "Su";
spy.sex = "male";
console.log(spy.sex); // undefined;

複製代碼

在這裏只是簡單的介紹了一下基本包裝類,在後面的學習過程當中,會詳細的剖析。

  • 精進EMCA-深刻裝箱拆箱

類型轉換

由於ECMASciprt是弱語言,因此常常會發生類型轉換,例如剛纔闡述的基本包裝類也屬於一種隱式類型轉換。

類型轉換分爲兩種,隱式轉換即程序自動進行的類型轉換,強制轉換即咱們手動進行的類型轉換。

在這裏咱們並不討論API層面的強制轉換,Focus在隱式類型轉換。

邏輯語句

if語句或者邏輯語句時,若是隻有單個變量,會先將變量轉換爲Boolean值,只有下面幾種狀況會轉換成false,其他被轉換成true

null undefined ' ' NaN 0 false

首先咱們要知道,在 JS 中類型轉換隻有三種狀況,分別是:

  • 轉換爲布爾值
  • 轉換爲數字
  • 轉換爲字符串

轉Boolean

條件判斷時,除了 undefinednullfalseNaN''0-0,其餘全部值都轉爲 true,包括全部對象。

對象轉原始類型

對象發生類型轉換時,會調用內部的[[ToPrimitive]]規則,這個算法邏輯一般能夠總結爲:

  • 若是已是原始類型了,不須要轉換
  • 調用 x.valueOf(),若是轉換爲原始類型,就返回轉換的值
  • 調用 x.toString(),若是轉換爲原始類型,就返回轉換的值
  • 若是都沒有返回原始類型,就會報錯
'[oject Array]' == [ ] // true

'1,2,3' == [1, 2, 3] // true

複製代碼

同時也能夠利用 Symbol.toPrimitive ,重寫[[ToPrimitive]]規則。該方法在轉原始類型時調用優先級最高。

var test = {
	valueof () {
	return 1;
	},
	toString() {
	return 2;
	},
	[Symbol.toPrimitive]() {
	return 3;
	}
}
var a = 1;
a + test; // 4

複製代碼

同時要注意類型轉換和優先級的組合

[] == ![] 左側根據[[ToPrimitive]]轉換成0,的優先級大於== 因此會將[]轉化成false後進行比較。

四則運算

+運算符不一樣於其餘運算符,總結其特色爲:

  • 其中一方爲字符串,會把另外一方也轉換爲字符串
  • 其中一方不是字符串或者數字,那麼會將它轉換爲數字或者字符串
1 + "abc" // "1abc" notice 1
true + true // 2 notice 2
4 + [1,2,3] // "41,2,3" notice2 和對象轉原始值 


複製代碼

在作+運算時,要注意優先級,例如

"a" + + "b" 結果爲aNaN能夠理解爲a +(+"b")一般狀況下+ "1"是用來強制轉換。

*、-、/運算符就相對簡單一些,要其中一方是數字,那麼另外一方就會被轉爲數字

4 * '3' // 12
4 * [] // 0 
4 * [1, 2] // NaN 

複製代碼

== 和 ===

==操做符對比雙方類型不同的話就會發生類型轉換。

判斷流程爲:

  1. 二者類型是否相同,相同的話比較大小
  2. 類型不一樣,進行類型轉換
  3. 判斷是不是null和undined在進行比較
  4. 判斷二者類型是否爲string和number,string會轉成number
  5. 其中一方是否爲 boolean,是的話就會把 boolean 轉爲 number 再進行判斷
  6. 判斷其中一方是否爲 object 且另外一方爲stringnumber 或者 symbol,是的話就會把 object 轉爲原始類型再進行判斷。

那麼 a == 1 && a == 2 && a == 3如何分析呢?

這是在ConardLi大佬博客中看到的一道題,關於變量和類型大佬講的更細緻,我會在最後將文章連接放出來。

類型判斷

typeOf

可以判斷原始類型,除了null(二進制000問題)

typeof 對於原始類型來講,除了 null 均可以顯示正確的類型

typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'

複製代碼

沒法判斷引用值,除了Function類型

typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'

複製代碼

instanceof

內部機制是經過原型鏈進行判斷,能夠判斷引用值類型,一般狀況下是沒法對原始值進行判斷。

const Person = function() {}
const p1 = new Person()
p1 instanceof Person // true

var str = 'hello world'
str instanceof String // false

var str1 = new String('hello world')
str1 instanceof String // true

複製代碼

toString

若是此方法在自定義對象中未被覆蓋,toString纔會達到預想的效果進行類型判斷。事實上,大部分引用類型好比Array、Date、RegExp等都重寫了toString方法,所以咱們一般使Object原型未被覆蓋的toString()方法。

$.type()

  • 精進EMCA-一步步仿寫$.type()

最後,我相信不少哥哥對開篇提出來的問題早就一目瞭然,只不過是我幫您加深了一下印象。

若是本篇文章您以爲有什麼地方有問題,必定要給我指正。若是您以爲還不錯,而且期待後續文章,點贊關注走一波~

梳理一下欠下待整理的文章:

  • 精進EMCA-手寫Object.assign()以及深 淺拷貝;
  • 精進EMCA-深刻垃圾回收機制
  • 精進EMCA-深刻裝箱拆箱
  • 精進EMCA-手寫$.type()

參考文章

ConardLi大佬:【JS 進階】你真的掌握變量和類型了嗎

ECMAScript® 2018 Language Specification

MDN:Object.prototype.valueOf()

相關文章
相關標籤/搜索