JavaScript重點原理理解

JavaScript有一些重要的概念需要更加透徹的理解,大概講解以下幾個:

1.call,apply ,bind的使用,什麼是argument ?bind的原生js實現

call和apply都是爲函數綁定執行的上下文,指定一個對象來替換函數執行時候的this,它倆的區別在於call需要傳入完整的參數列表,而apply可以傳入一個參數數組。bing函數則是爲函數綁定執行的上下文,需要傳入一個this作爲參數,不同於call和apply,bind不是立即執行的。下面看一下bind的實現:

//bind函數就是返回一個函數,但是函數的this綁定到指定的this上
Function.prototype.myBind = function(newThis){
  if(typeof this !== 'function') {
    throw new TypeError('need bing to a function')
  }
  let self = this;
  let arg = [].slice.call(arguments,1)
  return function() {
    self.apply(newThis,arg.concat([].slice.call(arguments)))
  }
}

argument是函數內部的參數,每個都有,可以用argument.length來訪問參數的長度,但是argument不是真的數組,需要用bind裏面的數組的slice方法轉爲真正的數組,我們可以衝argument的length實現類似函數重載的效果

2.this的顯式綁定和隱式綁定還有new綁定

this在函數執行的時候,會隱式綁定到包含這個函數的對象上,this指向這個對象。顯示綁定就是上面說的call apply bind,再就是new出來的函數的實例,this自動指向這個實例對象。

那什麼是隱式綁定丟失呢?

function foo() {
    console.log( this.a );
}
 
var obj = {
    a: 2,
    foo: foo
};
 
var bar = obj.foo; // function reference/alias!
 
var a = "oops, global"; // `a` also property on global object
 
bar(); // "oops, global"

這種情況下,bar在調用的時候沒有在對象上,就默認綁定到window,嚴格模式下和node環境中都是undefined。再看一個例子

var obj = {
  id: 'vexekefo',
  cool() {
    console.log(this.id);
  }
};
var id = 'someone';
obj.cool();  // vexekefo

setTimeout(obj.cool, 100); // someone

解決辦法,我們可以使用箭頭函數,箭頭函數沒有自己的this,它的this是定義它的上下文決定的

3.JavaScript的事件循環機制,宏任務和微任務,JavaScript的單線程如何理解

Js的事件循環機制:js引擎遇到一個異步事件後並不會一直等待其返回結果,而是會將這個事件掛起,繼續執行執行棧中的其他任務。當一個異步事件返回結果後,js會將這個事件加入與當前執行棧不同的另一個隊列,我們稱之爲事件隊列。被放入事件隊列不會立刻執行其回調,而是等待當前執行棧中的所有任務都執行完畢, 主線程處於閒置狀態時,主線程會去查找事件隊列是否有任務。如果有,那麼主線程會從中取出排在第一位的事件,並把這個事件對應的回調放入執行棧中,然後執行其中的同步代碼...,如此反覆,這樣就形成了一個無限的循環。這就是這個過程被稱爲“事件循環(Event Loop)”的原因。

分爲宏任務隊列,和微任務隊列,宏任務是setTimeOut和setInterval,微任務是promise,node環境下還有process.nextTick,

每次從宏任務取一個,再清空微任務隊列,然後進入下一輪循環

4.JavaScript的原型鏈,作用域分別是什麼,可以用來做什麼

js中的一切皆對象,每個對象都有一個prototype屬性,它指向對象的原型對象,而原型對象也有prototype屬性,繼續指向原型對象的原型對象,最終都是Object。Object的prototype是null,我們可以通過_proto_在瀏覽器訪問

我們可以用它做繼承,在原型鏈上定義的函數和屬性,順着原型鏈往下的對象都可以訪問到

作用域鏈:在作用域最前端的是活動對象,而最後端是全局執行環境window(瀏覽器宿主中);變量訪問原則是,根據作用域前端往上進行搜索,如果提前搜索到變量,則停止搜索,例如上面這個例子中,name變量的值是"sub"因爲其在最前端的變量對象中已經定義了,就不會往上繼續檢索;

5.JavaScript的繼承方式主要有什麼,如何實現

組合繼承,原型繼承,寄生組合繼承,在原型鏈上定義可複用的函數,在構造函數內定義自己的變量。

6.JavaScript在瀏覽器中的事件,捕獲和冒泡

瀏覽器事件分爲三個階段,先捕獲,找到發生事件的DOM,然後處於事件發生階段,然後冒泡,我們可以利用冒泡實現事件的委託,最常用的就是渲染了很多li標籤,不必給每個li綁定onclick事件,而是在他們的父元素ul上綁定一個,如果不想讓子組件的某個事件冒泡觸發父組件的同名事件,可以使用stopPropagation方法

7.JavaScript的基本數據類型有哪些,基本包裝類型有哪些,它們在堆棧中的存儲方式是什麼

基本數據類型有string number null undefined bool symbol,還有個複雜數據類型object,基本包裝類型有Number String Date Object Regexp等,基本數據類型存儲在棧中,定義後不可更改,而對象類型都是在棧中存儲數據的引用地址,真實的數據存在堆中

8.實現對象的深拷貝和淺拷貝,它們各自應用的場景

淺拷貝是隻拷貝了對象的引用地址,在堆中沒有新開一塊複製原有的數據,深拷貝則是會在堆中新開一塊空間,完全複製一份,

深拷貝可以用轉化成JSON的方式,但是隻適用於Number Sring Boolean Array,就是這種可以被JSON表示的對象,其他複雜的類型則需要單獨編寫函數來處理

9 Javascript的閉包是什麼,什麼時候需要使用閉包

js的閉包:函數體內定義的函數可以拿到函數的局部變量,可以用這個封裝私有屬性,可以用來實現模塊化,單例模式,

閉包的作用是可以用閉包訪問局部變量,可以讓局部變量駐留在內存中

10JavaScript模塊化,不同的標準有哪些,模塊化是如何實現的(聯繫閉包)

我們開發React的時候用的exports import是ES6的語法,開發node的時候的module.export和require是commonjs的標準。

const Helmet = require('react-helmet').default
const ReactDomServer = require('react-dom/server')
const ejs = require('ejs')
const serialize = require('serialize-javascript')
const SheetsRegistry = require('react-jss').SheetsRegistry
const colors = require('material-ui/colors')
const createMuiTheme = require('material-ui/styles').createMuiTheme
const create = require('jss').create
const preset = require('jss-preset-default').default
const asyncBootstrapper = require('react-async-bootstrapper').default

可以看到兩者的不同,react-helmet用的ES6語法編寫的模塊,如果要在node使用,我們需要用default去拿到真正的模塊實例

11 new 操作符具體做了什麼

new操作符新建一個空對象,然後把構造函數的原型對象賦給新對象,然後把構造函數賦值給原型對象的constructor屬性,然後

執行構造函數,把新對象傳進去,參數也傳進去

let New = function (P) {
        let o = {};
        let arg = Array.prototype.slice.call(arguments,1);
        
        o.__proto__ = P.prototype;
        P.prototype.constructor = P;
       
        P.apply(o,arg);
        
        return o;

12async是什麼,如何使用

是ES7的異步,配合await一起使用

13箭頭函數的指向

指向定義箭頭函數時候的詞語作用域

14事件委託機制

就是冒泡的時候子元素的事件委託給父元素執行

15.0.1+0.2 !==  0.3原因和解決辦法

js中浮點數表達不精確,可以用tofix(2)+parseInt

16數組的去重,扁平化

去重:可以用new Set,直接構造

扁平化:使用reduce函數+Array.isArray+遞歸

17函數柯里化與惰性求值

函數柯里化我們在中間件學習的時候看過store =>next=>action => {}

這種方式可以累積參數,在需要調用的時候再調用函數,不是立即執行

18定時器爲何存在不準的情況,手寫一個倒計時

js定時器不準,前端重要邏輯要用後端傳來的時間

19 let const var的區別

let const沒有變量提升 var有變量提升 let const可以綁定到塊級作用域

20 class的實現機制

class是基於ES5的function實現的,是原型繼承的語法糖

21 防抖和節流函數怎麼寫

都是避免避免無限觸發綁定在dom上的事件,一些比如scroll,輸入框輸入等觸發頻率較高的,我們不能讓綁定的回調一直觸發,因此有了防抖和節流兩種辦法。

防抖適用於輸入搜索,我們不要每次input的lvalue變化我們都去搜索一次,我們在輸入延遲個500ms左右再執行一次。就是一次輸入後,如果500ms沒有再輸入,我們就執行搜索匹配

節流適用於scroll拉取網絡數據,我們每過一段時間拉取一下,也不能說最後一次拉取,那中途就沒有用來渲染的數據了

 

<html>
  <head></head>
  <body>
    <div style='height:400px;width:800px;line-height:400px;background-color: gray;text-align: center;font-size: 30px;'>


    </div>
    <script>  
      let num = 1;
       ele = document.querySelector('div');
      function count() {
        ele.innerHTML = num;
        num++;
      }
      ele.onmousemove = throttle(count,1000)
      // 添加防抖,在最後一次觸發後再過1000ms執行,最後一次之前都不執行
      function debounce(fn,wait) {
       let timeout;
       return function() {
        if(timeout !== null) clearTimeout(timeout);
        timeout = setTimeout(fn,wait);
       }
      }
      //添加節流,減少執行次數,比如下滑拉取數據,不適合最後一次拉取,適合中途過一段時間拉取一次
      function throttle(fn,wait) {
        let preTime = Date.now();
        return function() {
          let now = Date.now();
          if(now - preTime >= wait) {
            fn();
            preTime = Date.now()
          }
        }
      }


    </script>
  </body>
</html>

 

 

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