一、JSON

json是一种传输数据的格式(以对象为样板,本质上就是对象,但用途有区别,对象就是本地用的,json是用来传输的)

JSON.stringify();    // json => string   通过这个方法,可以将json格式的数据转换成字符串格式。【传给后端使用】
JSON.parse();        // string => json   通过这个方法,可以将字符串格式的数据转换成json(对象)格式。【传给前端使用】

二、异步加载

js加载的缺点:

1、加载工具方法没必要阻塞文档,过多js加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载,而不进行后续渲染等工作。

2、有些工具需要按需加载,用到在加载,不用不加载。

js异步加载的3种方案

// 1、defer异步加载
//   defer异步加载,但要等到dom文档全部解析完才会被执行。只有IE能用,也可以将代码写到内部。加载时不会阻塞文档解析
// 2、async异步加载
//   加载完就执行,async只能加载外部脚本,不能把js写在script标签里。加载时也不阻塞文档解析。
// 3、创建script,插入到DOM中,加载完毕后callBack 【常用】

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <!-- 加上defer或者async属性后,该js变成异步加载的方式进行加载,不会阻塞文档继续解析 -->
        <!-- <script src="js/Tools.js" type="text/javascript" charset="utf-8" defer ></script> -->
        <!-- <script src="js/Tools.js" type="text/javascript" charset="utf-8" async ></script> -->
    </head>
    <body>
        <script type="text/javascript">
            var script = document.createElement("script");
            script.type = "text/javascript";
            script.src = "js/Tools.js";        // 执行到这一步,会异步下载脚本,不会执行

            document.head.appendChild(script); // 当追加到DOM中才开始执行(解析脚本)
        </script>
    </body>
</html>

 为了验证tools.js脚本异步下载,我们在tools.js中添加一个test函数。

// Tools.js
function test(){
    console.log("hello world");
}

紧接着在将Tools.js添加至DOM中后执行

<script type="text/javascript">
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "js/Tools.js";        // 执行到这一步,会异步下载脚本,不会执行

    document.head.appendChild(script); // 当追加到DOM中才开始执行(解析脚本)
    test();   // 报错 Uncaught ReferenceError: test is not defined
</script>

为什么会出现报错?倘若延迟1秒test()执行会发现并不会报错。

<script type="text/javascript">
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "js/Tools.js";        // 执行到这一步,会异步下载脚本,不会执行

    document.head.appendChild(script); // 当追加到DOM中才开始执行(解析脚本)
    setTimeout(function(){  // 1秒之后执行
        test();   // hello world
    },1000); 
</script>

综上代码可以得知,下载需要发送请求,响应后返回资源。在这个过程中,又因为是异步下载,不会阻塞页面解析,test()执行时,js还未下载完毕。

var script = document.createElement("script");
script.type = "text/javascript";
script.src = "js/Tools.js"; // 执行到这一步,会异步下载脚本,不会执行
// 【解决方法】添加onload事件,等待脚本完全下载后执行
// IE
if(script.readyState){
  script.onreadystatechange = function() {
  if (script.readyState == "complete" || script.readyState == "loaded") {
      test();
   }
  }
}else {
    script.onload = function() { // safari、chrome、firefox、opera
        test();
    }
}

document.head.appendChild(script); // 当追加到DOM中才开始执行(解析脚本)

接下来简化代码,把第三种异步加载方式封装成一个函数:loadScript();

function loadScript(url, callBack) {
    var script = document.createElement("script");
    script.type = "text/javascript";
    if (script.readyState) {
        script.onreadystatechange = function() { // Ie
            if (script.readyState == "complete" || script.readyState == "loaded") {
                callBack();
          }
        }
    } else {
        script.onload = function() {
            callBack();
        }
     }
    script.src = url; // 执行到这一步,会异步下载脚本,不会执行
    document.head.appendChild(script); // 当追加到DOM中才开始执行(解析脚本)
}

// loadScript("js/Tools.js", test);    // 报错 解析:函数里边异步加载的代码等到函数执行完时,开始调用,而此时解析到test时,函数loadScript()还没有开始执行,发生在文件加载之前。因此报错
loadScript("js/Tools.js", function() { // 这里启用一个匿名回调函数引用,在函数内部来执行该功能函数
    test();
});

倘如把外部工具库写成一个对象。例如:

var Tools = {
    addEvent: function(elem, eventType, handler) {
         // 函数体
    },
    removeEvent: function(element, eventType, handler) {
         // 函数体
    },
    stopBubble: function(event) {
         // 函数体
    },
    loadScript(url, callBack){
         // 函数体
    },
    test(){
         //  函数体
    }
    //  ... 
}

当使用异步加载时:

function loadScript(url, callBack) {
    var script = document.createElement("script");
    script.type = "text/javascript";
    if (script.readyState) {
        script.onreadystatechange = function() { // Ie
            if (script.readyState == "complete" || script.readyState == "loaded") {
                    Tools[callBack]();
          }
      }
    } else {
        script.onload = function() {
            Tools[callBack]();
        }
    }
    script.src = url; // 执行到这一步,会异步下载脚本,不会执行
    document.head.appendChild(script); // 当追加到DOM中才开始执行(解析脚本)
}

loadScript("js/Tools.js", "test"); //  解析: 这里可以以对象属性(字符串)的方式进行传递

 三、浏览器加载时间线(浏览器页面的加载过程)

1、创建Document对象,开始解析web页面。解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中。这个阶段document.readyState = "loading"
2、遇到link外部css,创建线程加载,并继续解析文档
3、遇到script外部js,并且没有设置async、defer,浏览器加载并阻塞,等待js加载完成并执行该脚本,然后继续解析文档
4、遇到script外部js,并且设置有async、defer,浏览器创建线程加载,并继续解析文档。对于async属性的脚本,脚本加载完成后立即执行。(异步禁止使用document.write())
5、遇到img等,先正常解析dom结构,然后浏览器异步加载src,并继续解析文档。
6、当文档解析完成,document.readyState = "interactive"
7、文档解析完成后,所有设置有defer的脚本会按照顺序执行。(注意:async的不同,但同样禁止使用document.write())
8、document对象触发DOMContentLoaded事件,这也标志着程序执行从同步脚本执行阶段,转换为事件驱动阶段
9、当所有async的脚本加载完成并执行后、img等加载完成后,document.readyState = "complete",window对象触发load事件
10、从此,以异步响应方式处理用户输入、网络事件等。

浏览器在加载页面的时候回用到GUI渲染线程和javascript引擎线程。其中GUI渲染线程负责渲染浏览器界面的HTML元素,Javascript引擎线程主要负责处理javascript脚本程序。由于javascript在执行过程中还可能改动界面结构和样式,因此它们之间被设计为互斥关系。也就是说,当javascript引擎执行时,GUI线程会被挂起。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script type="text/javascript">
            // 当文档解析完成 DOMContentLoaded 事件就触发,比onload事件等到文档加载并执行完成后触发效率更高。注意:DOMContentLoaded 事件只在addEventListener绑定事件身上生效
            document.addEventListener("DOMContentLoaded", function() {
                var div = document.getElementsByTagName("div")[0];
                console.log(div);
            })
            // 当页面所有脚本图片加载完成并执行后才触发,效率低 (不建议用)
            // window.onload = function(){
            //     var div = document.getElementsByTagName("div")[0];
            //     console.log(div);
            // }
        </script>
    </head>
    <body>
        <div></div>
        <script type="text/javascript">
            console.log(document.readyState); 
            document.onreadystatechange = function() {
                console.log(document.readyState);
            }
        </script>
    </body>
</html>

控制台打印的顺序结果:

ios json异步请求 json异步加载_javascript

四、总结

对于性能要求较高,需要快速将内容呈现给用户的网页,常常会将 javascript 脚本放在<body>的最后面。这样可以避免资源阻塞,页面得以迅速展示。我们还可以使用defer、async、preload等属性来标记<script>标签,来控制 javascript 的加载顺序。