JavaScript單線程、加載與模塊化

JavaScript單線程與瀏覽器多線程

  1. Javascript是單線程的:由於JS運行在瀏覽器中,是單線程的,每一個window一個JS線程。做爲瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操做DOM。若以多線程的方式操做這些DOM,則可能出現操做的衝突。假設有兩個線程同時操做一個DOM元素,線程1要求瀏覽器刪除DOM,而線程2卻要求修改DOM樣式,這時瀏覽器就沒法決定採用哪一個線程的操做。
  2. 瀏覽器不是單線程的,引擎可能存在以下線程:javascript

    • javascript引擎線程
    • 界面渲染線程
    • 瀏覽器事件觸發線程
    • Http請求線程等

Event Loop

事件循環機制:
主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)。主線程運行的時候,產生堆(heap)和棧(stack),棧中的代碼調用各類外部API,它們在"任務隊列"中加入各類事件(click,load,done)。只要棧中的代碼執行完畢,主線程就會去讀取"任務隊列",依次執行那些事件所對應的回調函數。php

  • 執行棧:全部同步任務都在主線程上執行,造成一個執行棧;
  • 任務隊列:主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件,當棧爲空時,就會從任務隊列中取出一個任務並執行。

定時器:
定時器包括setTimeout與setInterval兩個方法。它們的第二個參數是指定其回調函數推遲每隔多少毫秒數後執行。
零延遲 setTimeout(func, 0):零延遲並非意味着回調函數馬上執行。它取決於主線程當前是否空閒與「任務隊列」裏其前面正在等待的任務。當計時器時間規定的時候,回調函數會被添加到「任務隊列」中,等到執行棧的任務執行完畢,就會來執行這裏的任務。因此,有的時候即便計時器已經到0了,也會不當即執行計時器中的回調任務。html

Ajax異步請求:
在發起ajax請求的時候,瀏覽器會開一個線程去執行這一步驟,發請求這一步是屬於執行棧中的同步任務,在請求發完以後就會執行棧中的下一個任務,而等到請求有告終果,就會把結果放入「任務隊列」中,等待執行;java

JavaScript異步編程

JavaScript是單線程的,所謂「單線程」,就是同一時間只能執行一個任務。若是有多個任務,就必須排隊,直到前一個任務完成。這種模式的好處就是實現比較簡單,尤爲在瀏覽器環境中,能夠避免不少沒必要要的麻煩。壞處就是若是一個任務耗時很長,那麼該任務後面的任務就必須一直等待,致使整個頁面都卡住沒法繼續往下執行。git

爲了解決這個問題,JavaScript將任務的執行模式分爲兩類,一個是同步模式,另外一個是異步模式。github

"同步模式"就是上一段的模式,後一個任務等待前一個任務結束,而後再執行,程序的執行順序與任務的排列順序是一致的、同步的;"異步模式"則徹底不一樣,每個任務有一個或多個回調函數(callback),前一個任務結束後,不是執行後一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行,因此程序的執行順序與任務的排列順序是不一致的、異步的。ajax

JS異步編程的4中方法包括:sql

  • 回調函數:chrome

    f1();
    f2();
    
    function f1(callback){
        setTimeout(function(){
            callback();
        })
    }
    
    f1(f2);
  • 事件監聽:採用事件驅動模式。任務的執行不取決於代碼的順序,而取決於某個事件是否發生。數據庫

    //jQuery
    f1.on('done', f2);
    
    function f1(){
       setTimeout(function () {
           //do something
         f1.trigger('done');//執行完成後,當即觸發done事件,從而開始執行f2。
       }, 1000);
    }
  • 發佈/訂閱:又稱觀察者模式

    jQuery.subscribe("done", f2);//訂閱'done'這個信號
    
    function f1(){
       setTimeout(function () {
         //do something
         jQuery.publish("done");//f1()執行完以後,發出'done'這個信號,f2開始執行
       }, 1000);
    }
    
    jQuery.unsubscribe("done", f2);//取消訂閱
  • Promises:ES6原生提供了Promise對象。所謂Promise對象,就是表明了將來某個將要發生的事件(一般是一個異步操做)。它的好處在於,有了Promise對象,就能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象還提供了一整套完整的接口,使得能夠更加容易地控制異步操做。

    var promise = new Promise(function(resolve, reject) {
      if (/* 異步操做成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    
    promise.then(function(value) {
      // success
    }, function(value) {
      // failure
    });

ES6模塊管理

ECMAScript 6基於export和import,定義了模塊的導出和導入規範,在語言標準層面實現了模塊機制。該標準的目標是建立一種可以兼容CommoneJS和AMD兩標準的規範,便可以像CommoneJS同樣語法簡潔、使用單一的接口且支持循環依賴,又能夠像AMD支持異步加載和可配置的模塊加載。

大體來講,當 JS 引擎運行一個模塊的時候,它的行爲大體可概括爲如下四步:

  1. 解析:引擎實現會閱讀模塊的源碼,而且檢查是否有語法錯誤。
  2. 加載:引擎實現會(遞歸地)加載全部被引入的模塊。
  3. 連接:引擎實現會爲每一個新加載的模塊建立一個做用域,而且將模塊中的聲明綁定填入其中,包括從其餘模塊中引入的。

JS加載

動態加載經常使用的4種方法:

  1. document.write:document.write("<script src='package.js'><\/script>");
  2. 動態改變已有script的src屬性:js.src = "package.js";
  3. 動態建立script元素:

    var script = document.createElement("script");
    script.src = "XXX";
    script.type = "XXX";
    document.head.appendChild(script);
  4. 用XMLHTTP取得要腳本的內容,再建立 Script 對象。

項目中加載JS的方法:
在頁面底部寫一個自調用函數,加載入口JS,而後入口中的方法執行,加載須要的其餘JS。

<script>
    (function(G,D){var el=D.createElement('script'),d="XXX';
        el.src=((G._moduleConfig&&G._moduleConfig.domain)?G._moduleConfig.domain:d)+"XXX?t=XXX";
        D.getElementsByTagName('head')[0].appendChild(el);
    })(this,document);
</script>

在VM平臺上,能夠選擇將幾個模塊分組打包到一個入口文件中,而後在這個入口文件中,將每一個組動態生成一個script標籤,插入head中。

異步加載:
瀏覽器在渲染一個頁面的時候,遇到一個<script>標籤就會去下載對應的js文件,若是採用同步加載JS的方式,會形成頁面阻塞,直到JS加載完成再繼續渲染,因此有了異步加載JS的形式。

常見的異步加載JS方式,將js代碼包裹在匿名函數中並當即執行,動態生成<script>標籤去拉取js,能夠實現多個js文件並行下載,只有在解析執行的時候會阻塞渲染。另外,可使用script標籤的asyncdefer屬性。

延遲加載:
延遲加載就是一開始並不加載這些暫時不用的js,而是在須要的時候或稍後再經過js 的控制來異步加載。
也就是將 js 切分紅許多模塊,頁面初始化時只加載須要當即執行的 js ,而後其它 js 的加載延遲到第一次須要用到的時候再加載。
特別是頁面有大量不一樣的模塊組成,不少可能暫時不用或根本就沒用到。

script的兩階段加載與延遲執行
JS的加載實際上是由兩階段組成:下載內容(download bytes)和執行(parse and execute)。
瀏覽器在下載完 js 的內容後就會當即對其解析和執行,無論是同步加載仍是異步加載。
前面說的異步加載,解決的只是下載階段的問題,但代碼在下載後會當即執行。
而瀏覽器在解析執行 JS 階段是阻塞任何操做的,這時的瀏覽器處於無響應狀態。
咱們都知道經過網絡下載 script 須要明顯的時間,但容易忽略了第二階段,解析和執行也是須要時間的。script的解析和執行所花的時間比咱們想象的要多,尤爲是script 不少很大的時候。有些是須要馬上執行,而有些則不須要(好比只是在展現某個界面或執行某個操做時才須要)。
這些script 能夠延遲執行,先異步下載緩存起來,但不當即執行,而是在第一次須要的時候執行一次。
利用特殊的技巧能夠作到下載與執行的分離。好比將 JS 的內容做爲object對象加載緩存起來,因此就不會當即執行了,而後在第一次須要的時候再執行。

SQL相關

  1. 多表查詢:

    • 定義:根據特定的鏈接條件從不一樣的表中獲取所需的數據;
    • 語法:SELECT table1.column, table2.column FROM table1, table2 WHERE table1.column1 = table2.column2;
    • 注意:where不要省了,省略where即爲笛卡爾集,並且where條件要有效,兩張表間有一個相同的字段,纔好進行有效的多表查詢;
    • 簡化:爲了簡化SQL書寫,可爲表名定義別名,格式:from 表名別名,如:from emp e,dept d;
    • 優化:建議使用表的別名及表前綴,使用表別名能夠簡化查詢,而使用表前綴則能夠提升查詢性能;
  2. 鏈接類型:從數據顯示方式來說有:內鏈接和外鏈接。

    • 內鏈接:只返回知足鏈接條件的數據。

      • 例:select empno,ename,sal,dname,loc from emp,dept where emp.deptno=dept.deptno;
      • 等值鏈接:在鏈接條件中使用等於號(=)運算符比較被鏈接列的列值,其查詢結果中列出被鏈接表中的全部列,包括其中的重複列。
      • 不等值鏈接:在鏈接條件使用除等於運算符之外的其它比較運算符比較被鏈接的列的列值。這些運算符包括>、>=、<=、<、!>、!<和<>。
      • 天然鏈接:在鏈接條件中使用等於(=)運算符比較被鏈接列的列值,但它使用選擇列表指出查詢結果集合中所包括的列,並刪除鏈接表中的重複列。
    • 外鏈接:除了返回知足鏈接條的行之外,還返回左(右)表中,不知足條件的行,稱爲左(右)鏈接;

      • 左外鏈接:是以左表爲基準,將左表沒有的對應項顯示,右表的列爲NULL

        • select * from book as a left join stu as b on a.sutid = b.stuid
      • 右外鏈接:是以右表爲基準,將a.stuid = b.stuid的數據進行鏈接,然以將右表沒有的對應項顯示,左表的列爲NULL

        • select * from book as a right join stu as b on a.sutid = b.stuid
      • 全鏈接:完整外部聯接返回左表和右表中的全部行。當某行在另外一個表中沒有匹配行時,則另外一個表的選擇列表列包含空值。若是表之間有匹配行,則整個結果集行包含基表的數據值。

        • select * from book as a full outer join stu as b on a.sutid = b.stuid
  3. CI框架經常使用的數據庫操做:

    • 常規查詢:$this->db->query('YOUR QUERY HERE');
    • 查詢綁定:查詢綁定能夠簡化你的查詢語法,它經過系統自動的爲你將各個查詢組裝在一塊兒。 參考下面的例子:
    $sql = "SELECT * FROM some_table WHERE id = ? AND status = ? AND author = ?";
    $this->db->query($sql, array(3, 'live', 'Rick'));
    • $this->db->count_all():該方法用於獲取數據表的總行數,第一個參數爲表名;
    • 查詢構造器類:odeIgniter 提供了查詢構造器類,查詢構造器容許你使用較少的代碼來在數據庫中 獲取、新增或更新數據。有時只須要一兩行代碼就能完成數據庫操做。CodeIgniter 並不須要爲每一個數據表提供一個類,而是使用了一種更簡單的接口。

      • 查詢:
      $query = $this->db->get('mytable');  // Produces: SELECT * FROM mytable
      
      $query = $this->db->get('mytable', 10, 20);
      
      // Executes: SELECT * FROM mytable LIMIT 20, 10
      // (in MySQL. Other databases have slightly different syntax)
      • $this->db->select():該方法用於編寫查詢語句中的 SELECT 子句:
      $this->db->select('title, content, date');
      $query = $this->db->get('mytable');
      
      // Executes: SELECT title, content, date FROM mytable
      • $this->db->from():該方法用於編寫查詢語句中的 FROM 子句:
      $this->db->select('title, content, date');
      $this->db->from('mytable');
      $query = $this->db->get();  // Produces: SELECT title, content, date FROM mytable
      • $this->db->join():該方法用於編寫查詢語句中的 JOIN 子句:
      $this->db->select('*');
      $this->db->from('blogs');
      $this->db->join('comments', 'comments.id = blogs.id');
      $query = $this->db->get();
      
      // Produces:
      // SELECT * FROM blogs JOIN comments ON comments.id = blogs.id
      
      //你能夠傳入第三個參數指定鏈接的類型,有這樣幾種選擇:left,right,outer,inner,left outer 和 right outer 。
      
      $this->db->join('comments', 'comments.id = blogs.id', 'left');
      // Produces: LEFT JOIN comments ON comments.id = blogs.id
      • $this->db->where():該方法提供了4中方式讓你編寫查詢語句中的 WHERE 子句(全部的數據將會自動轉義,生成安全的查詢語句。):
      1.簡單的 key/value 方式:
      
      $this->db->where('name', $name); // Produces: WHERE name = 'Joe'
      注意自動爲你加上了等號。
      
      若是你屢次調用該方法,那麼多個 WHERE 條件將會使用 AND 鏈接起來:
      
      $this->db->where('name', $name);
      $this->db->where('title', $title);
      $this->db->where('status', $status);
      // WHERE name = 'Joe' AND title = 'boss' AND status = 'active'
      
      2.自定義 key/value 方式:
      
      爲了控制比較,你能夠在第一個參數中包含一個比較運算符:
      
      $this->db->where('name !=', $name);
      $this->db->where('id <', $id); // Produces: WHERE name != 'Joe' AND id < 45
      
      3.關聯數組方式:
      
      $array = array('name' => $name, 'title' => $title, 'status' => $status);
      $this->db->where($array);
      // Produces: WHERE name = 'Joe' AND title = 'boss' AND status = 'active'
      你也能夠在這個方法裏包含你本身的比較運算符:
      
      $array = array('name !=' => $name, 'id <' => $id, 'date >' => $date);
      $this->db->where($array);
      
      4.自定義字符串:
      
      你能夠徹底手工編寫 WHERE 子句:
      
      $where = "name='Joe' AND status='boss' OR status='active'";
      $this->db->where($where);
      $this->db->where() 方法有一個可選的第三個參數,若是設置爲 FALSE,CodeIgniter 將不保護你的表名和字段名。
      
      $this->db->where('MATCH (field) AGAINST ("value")', NULL, FALSE);
      • $this->db->like():該方法用於生成 LIKE 子句,在進行搜索時很是有用。
      簡單 key/value 方式:
      
      $this->db->like('title', 'match');
      // Produces: WHERE `title` LIKE '%match%' ESCAPE '!'
      若是你屢次調用該方法,那麼多個 WHERE 條件將會使用 AND 鏈接起來:
      
      $this->db->like('title', 'match');
      $this->db->like('body', 'match');
      // WHERE `title` LIKE '%match%' ESCAPE '!' AND  `body` LIKE '%match% ESCAPE '!'
      能夠傳入第三個可選的參數來控制 LIKE 通配符(%)的位置,可用選項有:'before','after' 和 'both' (默認爲 'both')。
      
      $this->db->like('title', 'match', 'before');    // Produces: WHERE `title` LIKE '%match' ESCAPE '!'
      $this->db->like('title', 'match', 'after'); // Produces: WHERE `title` LIKE 'match%' ESCAPE '!'
      $this->db->like('title', 'match', 'both');  // Produces: WHERE `title` LIKE '%match%' ESCAPE '!'
      關聯數組方式:
      
      $array = array('title' => $match, 'page1' => $match, 'page2' => $match);
      $this->db->like($array);
      // WHERE `title` LIKE '%match%' ESCAPE '!' AND  `page1` LIKE '%match%' ESCAPE '!' AND  `page2` LIKE '%match%' ESCAPE '!'
      • $this->db->group_by():$this->db->group_by("title"); // Produces: GROUP BY title;
      • $this->db->order_by():$this->db->order_by('title', 'DESC');
      • $this->db->limit():$this->db->limit(10); // Produces: LIMIT 10
      • $this->db->count_all_results():該方法用於獲取特定查詢返回結果的數量,也可使用查詢構造器的這些方法: where(),or_where(),like(),or_like() 等等。
      • $this->db->delete():$this->db->delete('mytable', array('id' => $id)); // Produces: // DELETE FROM mytable // WHERE id = $id

爲何chrome瀏覽器的速度很快

  1. Chrome 把瀏覽器上作的每件事都拆成獨立的進程,每一個tab都是一個獨立的進程,並利用進程間通信來完成它們之間的同步,windows系統下能夠經過任務管理器看到許多chrome 標籤頁的進程。
  2. 在還沒點擊 URL 以前,Chrome 已經在幫用戶加載了

參考文章:
JavaScript的加載和執行性能優化
Javascript 異步加載詳解
ES6 的模塊系統
seaJs學習筆記之seaJs的異步加載和加載多個js文件
Promise對象
Javascript異步編程的4種方法
JavaScript 運行機制詳解:再談Event Loop
JavaScript:完全理解同步、異步和事件循環(Event Loop)
JavaScript單線程和瀏覽器事件循環簡述
Javascript是單線程的深刻分析
查詢輔助函數
SQL的幾種鏈接:內鏈接、左聯接、右鏈接、全鏈接、交叉鏈接
SQL基礎-->多表查詢
CHROME進程間通訊
瀏覽器是如何工做的
瀏覽器是怎樣工做的:渲染引擎,HTML解析等......

相關文章
相關標籤/搜索