JS的一些面試點(diǎn)

JS

數(shù)據(jù)類(lèi)型

JavaScript中什么是基本數(shù)據(jù)類(lèi)型什么是引用數(shù)據(jù)類(lèi)型?以及各個(gè)數(shù)據(jù)類(lèi)型是如何存儲(chǔ)的?

基本數(shù)據(jù)類(lèi)型有

  • Number
  • String
  • Boolean
  • Null
  • Undefined
  • Symbol(ES6新增數(shù)據(jù)類(lèi)型)
  • bigInt
    引用數(shù)據(jù)類(lèi)型統(tǒng)稱(chēng)為Object類(lèi)型,細(xì)分的話(huà)有
  • Object
  • Array
  • Date
  • Function
  • RegExp
    基本數(shù)據(jù)類(lèi)型的數(shù)據(jù)直接存儲(chǔ)在棧中;而引用數(shù)據(jù)類(lèi)型的數(shù)據(jù)存儲(chǔ)在堆中,每個(gè)對(duì)象在堆中有一個(gè)引用地址。引用類(lèi)型在棧中會(huì)保存他的引用地址,以便快速查找到堆內(nèi)存中的對(duì)象。

提示:棧內(nèi)存是自動(dòng)分配內(nèi)存的。而堆內(nèi)存是動(dòng)態(tài)分配內(nèi)存的,不會(huì)自動(dòng)釋放。所以每次使用完對(duì)象的時(shí)候都要把它設(shè)置為null,從而減少無(wú)用內(nèi)存的消耗

類(lèi)型轉(zhuǎn)換

在JS中為什么0.2+0.1>0.3?

因?yàn)樵贘S中,浮點(diǎn)數(shù)是使用64位固定長(zhǎng)度來(lái)表示的,其中的1位表示符號(hào)位,11位用來(lái)表示指數(shù)位,剩下的52位尾數(shù)位,由于只有52位表示尾數(shù)位。

0.1轉(zhuǎn)為二進(jìn)制是一個(gè)無(wú)限循環(huán)數(shù)0.0001100110011001100......(1100循環(huán))
由于只能存儲(chǔ)52位尾數(shù)位,所以會(huì)出現(xiàn)精度缺失,把它存到內(nèi)存中再取出來(lái)轉(zhuǎn)換成十進(jìn)制就不是原來(lái)的0.1了,就變成了0.100000000000000005551115123126,而為什么02+0.1是因?yàn)?/p>

// 0.1 和 0.2 都轉(zhuǎn)化成二進(jìn)制后再進(jìn)行運(yùn)算
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111

 // 轉(zhuǎn)成十進(jìn)制正好是 0.30000000000000004

那為什么0.2+0.3=0.5呢?

// 0.2 和 0.3 都轉(zhuǎn)化為二進(jìn)制后再進(jìn)行計(jì)算
0.001100110011001100110011001100110011001100110011001101 +
0.0100110011001100110011001100110011001100110011001101 = 
0.10000000000000000000000000000000000000000000000000001 //尾數(shù)為大于52位
 
// 而實(shí)際取值只取52位尾數(shù)位,就變成了
0.1000000000000000000000000000000000000000000000000000   //0.5

答:0.2 和0.3分別轉(zhuǎn)換為二進(jìn)制進(jìn)行計(jì)算:在內(nèi)存中,它們的尾數(shù)位都是等于52位的,而他們相加必定大于52位,而他們相加又恰巧前52位尾數(shù)都是0,截取后恰好是0.1000000000000000000000000000000000000000000000000000也就是0.5

判斷數(shù)據(jù)類(lèi)型有幾種方法

typeof

缺點(diǎn):typeof null的值為Object,無(wú)法分辨是null還是Object
instanceof

缺點(diǎn):只能判斷對(duì)象是否存在于目標(biāo)對(duì)象的原型鏈上
constructor

Object.prototype.toString.call()

一種最好的基本類(lèi)型檢測(cè)方式 Object.prototype.toString.call() ;它可以區(qū)分 null 、 string 、

boolean 、 number 、 undefined 、 array 、 function 、 object 、 date 、 math 數(shù)據(jù)類(lèi)型。

缺點(diǎn):不能細(xì)分為誰(shuí)誰(shuí)的實(shí)例

// -----------------------------------------typeof
    typeof undefined // 'undefined' 
    typeof '10' // 'String' 
    typeof 10 // 'Number' 
    typeof false // 'Boolean' 
    typeof Symbol() // 'Symbol' 
    typeof Function // ‘function' 
    typeof null // ‘Object’ 
    typeof [] // 'Object' 
    typeof {} // 'Object'
 
 
    // -----------------------------------------instanceof
    function Foo() { }
    var f1 = new Foo();
    var d = new Number(1)
 
 
    console.log(f1 instanceof Foo);// true
    console.log(d instanceof Number); //true
    console.log(123 instanceof Number); //false   -->不能判斷字面量的基本數(shù)據(jù)類(lèi)型
 
 
    // -----------------------------------------constructor
    var d = new Number(1)
    var e = 1
    function fn() {
      console.log("ming");
    }
    var date = new Date();
    var arr = [1, 2, 3];
    var reg = /[hbc]at/gi;
 
    console.log(e.constructor);//? Number() { [native code] }
    console.log(e.constructor.name);//Number
    console.log(fn.constructor.name) // Function 
    console.log(date.constructor.name)// Date 
    console.log(arr.constructor.name) // Array 
    console.log(reg.constructor.name) // RegExp
 
 
 
 
    //-----------------------------------------Object.prototype.toString.call()
    console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]" 
    console.log(Object.prototype.toString.call(null)); // "[object Null]" 
    console.log(Object.prototype.toString.call(123)); // "[object Number]" 
    console.log(Object.prototype.toString.call("abc")); // "[object String]" 
    console.log(Object.prototype.toString.call(true)); // "[object Boolean]" 
 
 
    function fn() {
      console.log("ming");
    }
    var date = new Date();
    var arr = [1, 2, 3];
    var reg = /[hbc]at/gi;
    console.log(Object.prototype.toString.call(fn));// "[object Function]" 
    console.log(Object.prototype.toString.call(date));// "[object Date]" 
    console.log(Object.prototype.toString.call(arr)); // "[object Array]"
    console.log(Object.prototype.toString.call(reg));// "[object RegExp]"

instanceof原理

instanceof原理實(shí)際上就是查找目標(biāo)對(duì)象的原型鏈

function myInstance(L, R) {//L代表instanceof左邊,R代表右邊
     var RP = R.prototype
     var LP = L.__proto__
     while (true) {
       if(LP == null) {
         return false
       }
       if(LP == RP) {
         return true
       }
       LP = LP.__proto__
     }
   }
   console.log(myInstance({},Object)); 
   

為什么typeof null是Object

因?yàn)樵贘avaScript中,不同的對(duì)象都是使用二進(jìn)制存儲(chǔ)的,如果二進(jìn)制前三位都是0的話(huà),系統(tǒng)會(huì)判斷為是Object類(lèi)型,而null的二進(jìn)制全是0,自然也就判斷為Object

==和===有什么區(qū)別

答:

===是嚴(yán)格意義上的相等,會(huì)比較兩邊的數(shù)據(jù)類(lèi)型和值大小

數(shù)據(jù)類(lèi)型不同返回false
數(shù)據(jù)類(lèi)型相同,但值大小不同,返回false
==是非嚴(yán)格意義上的相等,

兩邊類(lèi)型相同,比較大小

兩邊類(lèi)型不同,根據(jù)下方表格,再進(jìn)一步進(jìn)行比較。

Null == Undefined ->true
String == Number ->先將String轉(zhuǎn)為Number,在比較大小
Boolean == Number ->現(xiàn)將Boolean轉(zhuǎn)為Number,在進(jìn)行比較
Object == String,Number,Symbol -> Object 轉(zhuǎn)化為原始類(lèi)型

手寫(xiě)call、apply、bind

答:
call和apply實(shí)現(xiàn)思路主要是:
判斷是否是函數(shù)調(diào)用,若非函數(shù)調(diào)用拋異常
通過(guò)新對(duì)象(context)來(lái)調(diào)用函數(shù)
給context創(chuàng)建一個(gè)fn設(shè)置為需要調(diào)用的函數(shù)
結(jié)束調(diào)用完之后刪除fn
bind實(shí)現(xiàn)思路
判斷是否是函數(shù)調(diào)用,若非函數(shù)調(diào)用拋異常
返回函數(shù)
判斷函數(shù)的調(diào)用方式,是否是被new出來(lái)的
new出來(lái)的話(huà)返回空對(duì)象,但是實(shí)例的proto指向_this的prototype
完成函數(shù)柯里化
Array.prototype.slice.call()
call:

Function.prototype.myCall = function (context) {
      // 先判斷調(diào)用myCall是不是一個(gè)函數(shù)
      // 這里的this就是調(diào)用myCall的
      if (typeof this !== 'function') {
        throw new TypeError("Not a Function")
      }
 
      // 不傳參數(shù)默認(rèn)為window
      context = context || window
 
      // 保存this
      context.fn = this
 
      // 保存參數(shù)
      let args = Array.from(arguments).slice(1)   //Array.from 把偽數(shù)組對(duì)象轉(zhuǎn)為數(shù)組
 
      // 調(diào)用函數(shù)
      let result = context.fn(...args)
 
      delete context.fn
 
      return result
 
    }

apply

Function.prototype.myApply = function (context) {
      // 判斷this是不是函數(shù)
      if (typeof this !== "function") {
        throw new TypeError("Not a Function")
      }
 
      let result
 
      // 默認(rèn)是window
      context = context || window
 
      // 保存this
      context.fn = this
 
      // 是否傳參
      if (arguments[1]) {
        result = context.fn(...arguments[1])
      } else {
        result = context.fn()
      }
      delete context.fn
 
      return result
    }

bind

Function.prototype.myBind = function(context){
      // 判斷是否是一個(gè)函數(shù)
      if(typeof this !== "function") {
        throw new TypeError("Not a Function")
      }
      // 保存調(diào)用bind的函數(shù)
      const _this = this 
      // 保存參數(shù)
      const args = Array.prototype.slice.call(arguments,1)
      // 返回一個(gè)函數(shù)
      return function F () {
        // 判斷是不是new出來(lái)的
        if(this instanceof F) {
          // 如果是new出來(lái)的
          // 返回一個(gè)空對(duì)象,且使創(chuàng)建出來(lái)的實(shí)例的__proto__指向_this的prototype,且完成函數(shù)柯里化
          return new _this(...args,...arguments)
        }else{
          // 如果不是new出來(lái)的改變this指向,且完成函數(shù)柯里化
          return _this.apply(context,args.concat(...arguments))
        }
      } 
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容