整理之前的面試題,發現問js數據類型的頻率挺高的,回憶當初本身的答案,就是簡簡單單的把幾個類型名稱羅列了出來,便沒有了任何下文。其實這一個知識點下能夠牽涉發散出不少的知識點,若是一個面試者只是羅列的那些名詞出來,可能面試官都不肯意繼續問下去了,這該算是js基礎的基礎了。若是這個問題沒有很好的回答,其餘問題仍舊沒有突出的亮點,極可能就過不了。javascript
在網上看了一個體系,可做爲大體的學習檢閱本身的途徑,按照清單上的知識檢測本身還有哪些不足和提高,最後造成本身的知識體系。在工做、學習甚至面試時,能夠快速定位到知識點。html
1. JavaScript規定了幾種語言類型2. JavaScript對象的底層數據結構是什麼3. Symbol類型在實際開發中的應用、可手動實現一個簡單的 Symbol4. JavaScript中的變量在內存中的具體存儲形式5. 基本類型對應的內置對象,以及他們之間的裝箱拆箱操做6. 理解值類型和引用類型7. null和 undefined的區別8. 至少能夠說出三種判斷 JavaScript數據類型的方式,以及他們的優缺點,如何準確的判斷數組類型9. 可能發生隱式類型轉換的場景以及轉換原則,應如何避免或巧妙應用10. 出現小數精度丟失的緣由, JavaScript能夠存儲的最大數字、最大安全數字, JavaScript處理大數字的方法、避免精度丟失的方法
答:js的數據類型分爲簡單數據類型和複雜數據類型;java
簡單數據類型有六種,分別是String(字符串)、Number(數字)、Null(空)、undefined(未定義)、boolean(布爾值)、symbol(符號),表示不能再繼續分下去的類型,在內存中以固定的大小存儲在棧中,按值訪問;jquery
複雜數據類型是指對象,這裏有常見的array、function、object等,本質上是一組無序的鍵值對組成。它的值大小不固定,因此保存在堆中,但在棧中會存儲有指向其堆內存的地址,按引用來訪問。js不容許直接訪問內存中的位置,也就是說不能直接操做對象的內存空間。也就是說,當咱們想要訪問應用類型的值的時候,須要先從棧中得到對象的地址指針,而後經過地址指針找到其在堆中的數據。c++
須要注意的是,git
一、簡單數據類型中的boolean、number、string不是由內置函數new出來的,儘管他們有對應的引用類型;程序員
二、symbol是ES6引入的一種新的原始數據,表示獨一無二且不可改變的值。經過 Symbol 函數調用生成,因爲生成的 symbol 值爲原始類型,因此 Symbol 函數不能使用 new 調用;github
三、將一個變量賦值給另外一個變量時,基礎類型複製的是值,賦值完成兩個變量在沒有任何關係;而對象類型的複製的是地址,修改一個變量另外一個變量也會跟着一塊兒變化。(如何解決這個問題?關於深拷貝and淺拷貝)面試
這個問題目前對我來講,不可以理解究竟是想問什麼,還有問題,看到一篇這個文章,轉載《從chrome源碼看js object的實現》:https://www.rrfed.com/2017/04/04/chrome-object/chrome
(暫未學習總結)
js的數據類型分爲簡單數據類型和複雜數據類型;在內存中,簡單數據類型以固定的大小存儲在棧中;複雜數據類型存儲在堆中,且大小不固定,同時在棧中會存儲其指向堆地址的指針。
由於這裏問的是內存中的存儲形式,因此我一直注意的是內存中堆棧,後來突然看到一篇文章寫了數據結構中的堆和棧就有一點懵,先簡單記錄一下相關知識點。
是一種物理結構,用於存放不一樣數據的內存空間,分爲棧區和堆區。
棧(stack)是向低地址擴展的數據結構,是一塊連續的內存區域;通常來講其大小是系統預先規定好的,存儲大小已知的變量(函數的參數值、局部變量的值等)。由操做系統自動申請分配並釋放(回收)空間,無需程序員控制,這樣的好處是內存能夠及時獲得回收。但棧的大小有限,若是申請的空間超過棧的剩餘空間,就會提示棧溢出(通常無窮次的遞歸調用或大量的內存分配會引發棧溢出)。
在分配內存的時候相似於數據結構中的棧,先進後出的原則,即從棧低向棧頂,依次存儲。棧是向下增加,即從高地址到低地址分配內存,且內存區域連續、每一個單元的大小相同。以下圖:
是一種抽象的數據存儲結構,
棧:一種連續存儲的數據結構,特色是存儲的數據先進後出,只能在棧頂一端對數據項進行插入和刪除。
堆:是一棵徹底二叉樹結構(知識點未掌握)
var s1 = "stringtext";var s2 = s1.substring(2);
(1)建立String類型的一個實例 => var s1 = new String("stringtext");(2)在實例上調用指定的方法 => var s2 = s1.substring(2);(3)摧毀這個實例 => s1 = null;
var s1 = "stringtext";s1.color = "red"; //在這一句話執行完的瞬間,第二行建立的String就已經被銷燬了。console.log(s1.color);//執行這一行代碼時又建立了本身的String對象,而該對象沒有color屬性。//undefine
var obj = new Object("stringtext");console.log(obj instanceof String);//true
inputTpye
|
result
|
Null
|
不轉換,直接返回
|
Undefined
|
不轉換,直接返回
|
Number
|
不轉換,直接返回
|
Boolean
|
不轉換,直接返回
|
String
|
不轉換,直接返回
|
Symbol
|
不轉換,直接返回
|
Object
|
按照下列步驟進行轉換
|
js包含兩種數據類型,基本數據類型和複雜數據類型,而其對應的值基本類型的值指的是簡單的數據段,引用類型指的是那些可能有多個值構成的對象。能夠從三個方面來理解:動態的屬性、複製變量的值、傳遞參數
1)、動態的屬性
定義基本類型值和引用類型值的方式相似,即建立一個變量併爲該變量賦值。二者的區別在於,對於引用類型的值,咱們能夠爲其添加屬性和方法,也能夠改變和刪除其屬性和方法;對於基本類型的值,咱們不能爲其動態地添加屬性。
var person = new Object(); //建立一個對象並將其保存在變量person中person.name = "Song"; //爲該對象添加一個名爲name的屬性,並賦值爲Songconsole.log(person.name); //訪問name這個屬性//Song
2)、複製變量的值
在從一個變量向另外一個變量複製基本類型值和引用類型值時,兩則也是不一樣的,這主要是因爲基本類型和引用類型在內存中存儲不一樣致使的。
var a = 1;var b = a;b = 2;console.log(a);//1console.log(b);//2
內存變化大體以下:
Ⅱ複製引用類型的值
var obj1 = {name:"Song"};var obj2 = obj1;obj2.name = "D"; //改變obj2的name屬性的值,則將obj1的也改變了。console.log(obj1.name);// D
注:關於深拷貝和淺拷貝
3)、傳遞參數
ECMAScript中全部函數的參數都是按值傳遞的,不管在向參數傳遞的是基本類型仍是引用類型。(個人理解:正由於是按值傳遞的,因此咱們才能夠利用此來完成深拷貝)
有一道關於證實引用類型是按值傳遞仍是按引用傳遞的題目以下:
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); console.log(p2);
首先當咱們從一個變量向另外一個變量複製引用類型的值時,這個值是存儲在棧中的指針地址,複製操做結束後,兩個變量引用的是同一個對象,改變其中一個變量,就會影響另外一個變量。
而在向參數傳遞引用類型的值時,一樣是把內存中的地址複製給一個局部變量,因此在上述代碼中,將p1的內存地址指針複製給了局部變量person,二者引用的是同一個對象,這個時候在函數中改變變量,就會影響到外部。
接下來至關於重新開闢了一個內存空間,而後將此內存空間的地址賦給person,能夠理解爲將剛纔指向p1的指針地址給覆蓋了,因此改變了person的指向,當該函數結束後便釋放此內存。
(此圖做爲本身的理解,不表明實際,頗有可能實際並非這樣操做的。)
因此在person.age = 26;這句話執行後把p1內存裏的值改變了,打印出來p1是{name: "yck", age: 26} p2是{name: "yyy", age: 30}
而我理解的若是按引用傳遞,則至關於person的指向是和p1也同樣,因此後續只要是對person進行了操做,都會直接影響p1。
所以在這種狀況下,打印出來p1和p2都是{name: "yyy", age: 30}
1)、null類型
var car = null;console.log(typeof car);//object
var s;console.log(s == undefined);//true
問:判斷js數據類型有哪幾種方式,分別有什麼優缺點?怎麼樣判斷一個值是數組類型仍是對象?(或者typeof能不能正確判斷類型)
答:通常來講有5種經常使用的方法,分別是typeof、instanceof、Object.prototype.toString()、constructor、jquery的type();
1)對於typeof來講,在檢測基本數據類型時十分得力,對於基本類型,除了null均可以返回正確類型,對於對象來講,除了function都返回object。
基本類型
typeof "somestring" // 'string'
typeof true // 'boolean'
typeof 10 // 'number'
typeof Symbol() // 'symbol'
typeof null // 'object' 沒法斷定是否爲 null
typeof undefined // 'undefined'複雜類型
typeof {} // 'object'
typeof [] // 'object' 若是須要判斷數組類型,則不能使用這樣方式
typeof(() => {}) // 'function'
注:怎麼使用複合條件來檢測null值的類型?
var a = null;
(!a && typeof a === "object"); // true
2)對於instanceof來講,能夠來判斷已知對象的類型,若是使用instanceof來判斷基本類型,則始終返回false。
其原理是測試構造函數的prototype是否出如今被檢測對象的原型鏈上;全部的複雜類型的值都是object的實例,在檢測一個引用類型值和Object構造函數時,instanceof操做符始終返回true。
[] instanceof Array //true -》 沒法優雅的判斷一個值到底屬於數組仍是普通對象({}) instanceof Object //true(()=>{}) instanceof Function //true
並且在《高程》上還看到說一個問題,若是不是單一的全局執行環境,好比網頁中包含多個框架,那麼實際上存在兩個以上不一樣的全局執行環境,從而存在兩個以上不一樣版本的Array構造函數,若是從一個框架向另一個框架傳入數組,那麼傳入的數據與在第二個框架中原生建立的數組分別具備各自不一樣的構造函數。eg:例如index頁面傳入一個arr變量給iframe去處理,則即便arr instanceof Array仍是返回false,由於兩個引用的Array類型不是同一個。而且constructor能夠重寫因此不能確保萬無一失。
對於數組來講,至關於new Array()出的一個實例,因此arr.proto === Array.prototype;又由於Array是Object的子對象,因此Array.prototype.proto === Object.prototype。所以Object構造函數在arr的原型鏈上,便沒法判斷一個值到底屬於數組仍是普通對象。
注:判斷變量是否爲數組的方法
3)通用但比較繁瑣的方法Object.prototype.toString()
該方法本質是利用Object.prototype.toString()方法獲得對象內部屬性[[Class]],傳入基本類型也可以判斷出結果是由於對其值作了包裝。
Object.prototype.toString.call({}) === '[object Object]' -------> true;
Object.prototype.toString.call([]) === '[object Array]' -------> true;
Object.prototype.toString.call(() => {}) === '[object Function]' -------> true;
Object.prototype.toString.call('somestring') === '[object String]' -------> true;
Object.prototype.toString.call(1) === '[object Number]' -------> true;
Object.prototype.toString.call(true) === '[object Boolean]' -------> true;
Object.prototype.toString.call(Symbol()) === '[object Symbol]' -------> true;
Object.prototype.toString.call(null) === '[object Null]' -------> true;
Object.prototype.toString.call(undefined) === '[object Undefined]' -------> true;
Object.prototype.toString.call(new Date()) === '[object Date]' -------> true;
Object.prototype.toString.call(Math) === '[object Math]' -------> true;
Object.prototype.toString.call(new Set()) === '[object Set]' -------> true;
Object.prototype.toString.call(new WeakSet()) === '[object WeakSet]' -------> true;
Object.prototype.toString.call(new Map()) === '[object Map]' -------> true;
Object.prototype.toString.call(new WeakMap()) === '[object WeakMap]' -------> true;
4)根據對象的constructor判斷
[].constructor === Array --------> truevar d = new Date();d.constructor === Date ---------> true(()=>{}).constructor === Function -------> true注意: constructor 在類繼承時會出錯eg:function A(){};function B(){};A.prototype = new B(); //A繼承自Bvar aobj = new A();aobj.constructor === B --------> true;aobj.constructor === A --------> false;而instanceof方法不會出現該問題,對象直接繼承和間接繼承的都會報true:
5)jquery的type()
若是對象是undefined或null,則返回相應的「undefined」或「null」,jQuery.type( undefined ) === "undefined"jQuery.type() === "undefined"jQuery.type( null ) === "null"若是對象有一個內部的[[Class]]和一個瀏覽器的內置對象的 [[Class]] 相同,咱們返回相應的 [[Class]] 名字。jQuery.type( true ) === "boolean"jQuery.type( 3 ) === "number"jQuery.type( "test" ) === "string"jQuery.type( function(){} ) === "function"jQuery.type( [] ) === "array"jQuery.type( new Date() ) === "date"jQuery.type( new Error() ) === "error" // as of jQuery 1.9jQuery.type( /test/ ) === "regexp"
6)如何判斷一個數組?
var a = [];
a.instanceof Array; --------> true
a.constructor === Array --------> true
Object.prototype.toString.call(a) === '[object Array]' --------> true
Array.isArray([]); --------> true
(暫未整理)
答:在ECMAScript數據類型中的Number類型是使用IEEE754格式來表示的整數和浮點數值,所謂浮點數值就是該數值必須包含一個小數點,而且小數點後面必須至少有一位數字。而在使用基於IEEE754數值的浮點運算時出現參數舍入的偏差問題,即出現小數精度丟失,沒法測試特定的浮點數值。
①在進行0.1+0.2的時候首先要將其轉換成二進制。
0.1 => 0.0001 1001 1001 1001…(無限循環)0.2 => 0.0011 0011 0011 0011…(無限循環)②因爲 JavaScript 採用 IEEE 754 標準,數值存儲爲64位雙精度格式,數值精度最多能夠達到 53 個二進制位(1 個隱藏位與 52 個有效位)。若是數值的精度超過這個限度,第54位及後面的位就會被丟棄,因此在相加的時候會由於小數位的限制而將二進制數字截斷。0.0001 1001 1001 1001…+0.0011 0011 0011 0011… = 0.0100110011001100110011001100110011001100110011001100③再轉換成十進制就成了0.30000000000000004,而非咱們指望的0.3
在《js權威指南》中有指出:
Javascript採用了IEEE-745浮點數表示法(幾乎全部的編程語言都採用),這是一種二進制表示法,能夠精確地表示分數,好比1/2,1/8,1/1024。遺憾的是,咱們經常使用的分數(特別是在金融的計算方面)都是十進制分數1/10,1/100等。二進制浮點數表示法並不能精確的表示相似0.1這樣 的簡單的數字,上訴代碼的中的x和y的值很是接近最終的正確值,這種計算結果能夠勝任大多數的計算任務:這個問題也只有在比較兩個值是否相等時纔會出現。這個問題並非只在javascript中才會出現,在任何使用二進制浮點數的編程語言中都會出現這個問題。 因此說,精度丟失並非語言的問題,而是浮點數存儲自己固有的缺陷。只不過在 C++/C#/Java 這些語言中已經封裝好了方法來避免精度的問題,而 JavaScript 是一門弱類型的語言,從設計思想上就沒有對浮點數有個嚴格的數據類型,因此精度偏差的問題就顯得格外突出。javascript的將來版本或許會支持十進制數字類型以免這些舍入問題,在這以前,你更願意使用大整數進行重要的金融計算,例如,要使用整數‘分’而不是使用小數‘元’進行貨比單位的運算。
Number.EPSILON === Math.pow(2, -52)// true 說明這個值Number.EPSILON是等於 2 的 -52 次方
Number.EPSILON * Math.pow(2, 2)
),即若是兩個浮點數的差小於這個值,咱們就認爲這兩個浮點數相等。
function withinErrorMargin (left, right) { return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2); } withinErrorMargin(0.1 + 0.2, 0.3) //true
②math.js是一個普遍應用於JavaScript 和 Node.js的數學庫,它的特色是靈活表達式解析器,支持符號計算,內置大量函數與常量,並提供集成解決方案來處理不一樣的數據類型,如數字,大數字,複數,分數,單位和矩陣。
2.65.toFixed(1) //2.6 結果正確2.45.toFixed(1) //2.5 但願獲得的結果是2.42.35.toFixed(1) //2.4 結果正確
function RoundNum(n, m){ //n表示須要四捨五入的數,m表示須要保留的小數位數 var newNum = Math.round(n * Math.pow(10, m)) / Math.pow(10, m) ; //首先將要保留的小數位數的小數部分轉成整數部分,利用冪函數將n乘以10的m次方 //而後利用Math.round()方法進行四捨五入處理 //最後再除以10的m次方還原小數部分 //注:此時還未能將全部數字正確轉換。例如將1.0001保留3位小數咱們想要的結果是1.000,而此時newNum裏面的值是1 //因此還須要處理此種特殊狀況,即保留的小數位上全0 var newSNum = newNum.toString(); //這一步將剛纔進行處理過的數轉換成字符串 var rs = newSNum.indexOf('.'); //利用indexOf查找字符串中是否有.,它返回某個指定的字符串值在字符串中首次出現的位置,不存在則返回-1 if (rs < 0) { rs = newSNum.length; newSNum += '.'; } while (newSNum.length <= rs + m) { //在末尾加0 newSNum += '0'; } return newSNum; } console.log(RoundNum(1.0005, 3)); //獲得1.001
④封裝一個計算類(加、減、乘、除)
(暫未實際寫過)
答:最大數字是Number.MAX_VALUE、最大安全數字是Number.MAX_SAFE_INTEGER。Number.MAX_VALUE大於Number.MAX_SAFE_INTEGER,個人理解是js能夠精確表示最大安全數字之內的數,超過了最大安全數字但沒超過最大數字能夠表示,但不精確,若是超過了最大數字,則這個數值會自動轉換成特殊的Infinity值。
因爲內存的限制,ECMAScript並不能保存世界上全部的數值,ECMAScript可以表示的最小數值是Number.MIN_VALUE,可以表示的最大數值是Number.MAX_VALUE。超過數值是正值,則被轉成Infinity(正無窮),若是是負值則被轉成-Infinity(負無窮)。若是在某次返回了正或負的Infinity值,那麼該值將沒法繼續參與下一次的計算,因此咱們須要肯定一個數值是否是有窮的,便是不是位於最小和最大的數值之間,可使用isFinite()函數,若是該函數參數在最小和最大數值之間時會返回true。注意,若是參數類型不是數值,Number.isFinite
一概返回false
。
JavaScript 可以準確表示的整數範圍在-2^53
到2^53
之間(不含兩個端點),超過這個範圍,沒法精確表示這個值。ES6 引入了Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
這兩個常量,用來表示這個範圍的上下限。Number.isSafeInteger()
則是用來判斷一個整數是否落在這個範圍以內。