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解析等......

相关文章
相关标签/搜索