zoukankan      html  css  js  c++  java
  • 前端笔记--JS

    JS数据类型

    • 基本类型:Number String Boolean Null Undefined Symbol(ES6新增)
    • 引用类型:Object (包括Array Function)

    基本类型和引用类型的区别

    1.基本类型的值是不可变的,不能给基本类型添加属性和方法;而引用类型添可以为其加属性和方法,也可以删除其属性和方法。
    2.基本类型的变量是存储在栈区的,栈区中存放标量的标识符和变量的值。而引用类型存储在栈区中的是变量标识符和指针,该指针指向存储在堆区中的对象。
    3.基本类型的比较是值的比较,只有在他们值相等的时候才相等;而引用类型的比较是引用的比较,即对象存储地址的比较。
    4.基本类型,一个变量向另一个变量赋值时,是把该变量的值拷贝给新变量,赋值后,两个变量的操作是互不影响的;而引用类型,一个变量向另一个变量赋值时,是让新变量的指针指向堆区中的同一个对象,赋值后,两个变量存储的对象地址是一样的,即指向同一个对象,他们的操作是互相影响的。

    使用typeof判断数据类型

    typeof 123 //number
    typeof '123' //string
    typeof true // boolean
    typeof false //boolean
    typeof undefined // undefined
    typeof Math.abs // function
    typeof function () {} // function
    typeof null // object
    typeof [] // object
    typeof {} // object
    

    == 和 === 的比较

    1.使用 == 会有强制类型转换,=== 不会有类型强制转换
    2.发生强制类型转换的情况

    • 字符串拼接
      100 + '10' // '10010'
    • == 运算符
    100 == '100' //true 100转换为'100'
    '' == 0 //true '' 0 转换为false
    null == undefined //true null undefined转换为false
    
    • if语句
    var a = 100
    if(a){}
    var b = ''
    if(b){}
    
    • 逻辑运算
    10 && 0 //0
    '' || 'abc' //'abc'
    !10 // false
    

    3.什么时候使用 == 什么时候使用 ===

    • jquery源码中推荐的写法
      if(obj.a == null){
      //这个条件相当于 obj.a === null || obj.a === undefined的简写
      }
    • 其他情况全部用 ===

    JS中的内置函数

    • Number
    • String
    • Boolean
    • Array
    • Function
    • Date
    • RegExp
    • Error

    如何理解JSON

    1.一种数据格式
    2.JS内置对象

    • JSON.parse()
    • JSON.stringify()

    原型和原型链

    原型规则

    1.所有的引用类型(数组、对象、函数)都具有对象的特性,都能自由扩展属性。
    2.所有的引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通对象。
    3.所有的函数都有一个prototype(显示原型)属性,属性值也是一个普通对象。
    4.所有引用类型的__proto__属性的值指向其构造函数的prototype属性的值。
    5.当试图得到一个引用类型的某个属性时,如果对象本身没有这个属性,则去它的__proto__中找,即去其构造函数的prototype中找。

    instanceof

    用于判断引用类型属于哪个构造函数的方法
    foo instanceof Foo
    判断逻辑:
    foo的__proto__一层一层往上,能否对应到Foo.prototype

    new一个对象的过程

    • 创建一个空对象
    • 将所创建对象的__proto__属性值设为构造函数的prototype属性值
    • 执行构造函数中的代码,构造函数中的this指向该对象
    • 返回对象

    constructor

    写一个原型链继承的例子(封装DOM查询)

    function Elem(id){
        this.elem = document.getElementById(id);
    }
    
    Elem.prototype.html = function(val){
        var elem = this.elem;
        if(val){
            elem.innerHTML = val;
            return this;
        }else{
            return elem.innerHTML;
        }
    }
    Elem.prototype.on = function(type,fn){
        var elem = this.elem;
        elem.addEventListener(type,fn);
        return this;
    }
    var elem = new Elem('div1');
    elem.html('<p>test</p>').on('click',function(){alert('test成功!')});
    

    this

    • this要在执行时才能确定值,定义时无法确认

    this使用的几种场景

    1.全局环境
    在全局执行环境中,this都指向全局对象window
    2.作为构造函数
    this指向正在构造的新对象
    3.作为对象的方法
    当函数作为对象里的方法被调用时,this指向调用该函数的对象
    4.作为普通函数
    this默认指向全局对象,在严格模式下,如果 this 没有被执行环境定义,那它将保持为 undefined
    5.call apply bind方法
    call和apply方法能将this值绑定到调用中的特定对象

    function add(c, d) {
      return this.a + this.b + c + d;
    }
    
    var o = {a: 1, b: 3};
    
    // 第一个参数是作为‘this’使用的对象
    // 后续参数作为参数传递给函数调用
    add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
    
    // 第一个参数也是作为‘this’使用的对象
    // 第二个参数是一个数组,数组里的元素用作函数调用中的参数
    add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
    

    bind方法返回的是一个新的函数,无论这个函数是如何被调用的,this将永久地被绑定到了bind的第一个参数,bind方法只生效一次。

    作用域和闭包

    变量提升

    • 变量和函数的声明都会提升到作用域的最顶端
    • 函数声明的优先级高于变量声明的优先级,并且函数的声明和定义部分一起被提升

    var let const的区别

    1.var只有全局作用域和函数作用域的概念,而let只有块级作用域的概念
    2.var存在变量提升,而 let,const声明的变量却不存在变量提升,使用let命令声明变量前,该变量都是不可用的,这在语法上称为“暂时性死亡”
    3.var可以多次声明变量,而let不允许在同一作用域内,重复声明一个变量
    4.使用var function声明的变量作为全局对象的属性,而使用let const声明的变量不属于全局对象的属性
    5.const变量一旦被赋值就不能再改变了,const声明的变量必须经过初始化

    作用域和作用域链

    作用域

    1.全局作用域

    • 最外层函数和 在最外层函数外面声明的变量拥有全局作用域
    • 所有未定义直接赋值的变量自动声明为拥有全局作用域
    • 所有window对象的属性拥有全局作用域
      2.函数作用域
      声明在函数内部的变量
      3.ES6的块级作用域
      通过let和const声明,声明的变量只有在指定块的作用域内被访问

    作用域链

    • 自由变量
      在当前作用域内没有定义的变量称为自由变量
    • 作用域链
      要得到自由变量的值,就需要向父级作用域寻找,如果父级也没有,再一层一层往上寻找,直到找到全局作用域还是没找到就放弃。这种一层一层的关系就叫做作用域链。
      注意:在函数中,取自由变量的值时,要到创建函数的那个作用域中取,无论函数在哪里调用。

    闭包

    闭包就是能访问其他函数内部变量的函数,也可以理解成定义在其他函数内部的函数。

    创建十个a标签,点击时依次弹出对应的序号

        for(let i = 0;i <= 10;i++) {
            const a = document.createElement("a");
            a.innerHTML = i + '<br>';
            a.addEventListener('click', function () {
                alert(i);
            })
            document.body.appendChild(a);
        }
    

    使用闭包方式

        for(var i = 0;i <= 10;i++) {
            (function (i) {
                var a = document.createElement("a");
                a.innerHTML = i + '<br>';
                a.addEventListener('click', function (e) {
                    e.preventDefault(); //阻止默认行为
                    alert(i);
                })
                document.body.appendChild(a);
            })(i)
        }
    

    异步

    单线程

    单线程就是在同一个时间只能做一件事,如果在同一时间有多个任务的话,这些任务就需要排队,前一个任务执行完,才会执行下一个任务。

    同步和异步

    • 同步任务就是在主线程上排列的任务一个接一个的执行,只有前一个任务执行完成,才能继续执行下一个任务,会阻塞代码执行(alert)
    • 异步任务是不进入主线程,而进入任务队列的任务,只有等主线程任务执行完毕,任务队列开始通知主线程,请求执行任务,该任务才会进入主线程执行,不会阻塞代码执行(setTimeout)

    前端使用异步的场景有哪些

    1.定时任务 setTimeout setInterval
    2.网络请求 ajax请求 动态img加载
    3.事件绑定
    4.回调函数
    5.Promise
    6.async/await

    回调函数

    函数A作为参数传递给函数B,函数A就叫做回调函数,函数A在函数B中被调用。

    Date

    Math

    Array

    ES6新增
    forEach()、filter()、map()、every()、some()、reduce()、indexOf()、lastIndexOf()、find()、findIndex()、includes()

    • slice()和splice()的区别
      1.arrayObject.slice(start,end),返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素,该方法并不会修改数组。
      2.arrayObject.splice(index,howmany,item1,.....,itemX),splice() 方法可删除从 index 处开始的零个或多个元素,并且用参数列表中声明的一个或多个值来替换那些被删除的元素,该方法会修改数组。

    获取随机数,要求是长度一致的字符串格式

        const num = Math.random() + '0000000000';
        const num2 = num.slice(0,10);
        console.log(num2)
    

    写一个能遍历数组和函数的通用forEach函数

        function forEach(obj,fn) {
            if (obj instanceof Array){
                obj.forEach((item,index) => fn(index,item))
            }else {
                for (let key in obj){
                    if(obj.hasOwnProperty(key)){
                      fn(key,obj[key])
                    }
                }
            }
        }
        const arr = [1,2,3,4,5]
        const obj = {name:"jack",age:19,type:"girl-girl"}
        forEach(arr,(index,item) => console.log(index +":"+item))
        forEach(obj,(key,value) => console.log(key +":"+value))
    

    封装日期处理函数

        function formatDate(dt){
            function add(val) {
                return val < 10?'0'+val:val
            }
            dt = dt || new Date();
            const year = dt.getFullYear();
            const month = add(dt.getMonth() + 1);
            const date = add(dt.getDate());
            const hour = add(dt.getHours());
            const minute = add(dt.getMinutes());
            const second = add(dt.getSeconds());
            return year+'-'+month+'-'+date+' '+hour+':'+minute+':'+second
        }
        console.log(formatDate())
    

    JS Web API

    DOM

    Attribute 和 Property的区别

    1.Atribute是DOM节点自带属性,如在HTML中常用的id,class,src,title,alt等,该属性的三个相关方法,setAttribute、getAttribute、removeAttribute。
    2.Property则是这个DOM元素作为对象,其附加的属性或内容,如childNodes、firstChild等。
    3.一些常用的Attribute属性如id,class,src,title,alt等,也作为Property附加在DOM对象上,也可以取值赋值,但是自定义的Attribute属性就不能进行取值赋值了。

    DOM结构操作

    1.新增节点

    const div1 = document.getElementById('div1');
    //新增节点
    const p1 = document.createElement('p');
    p1.innerHTML = 'this is p1';
    div1.appendChild(p1);
    //移动已有节点
    const p2 = document.getElementById('p2');
    div1.appendChild(p2);
    

    2.删除节点
    div.removeChild(div1.children[0])
    3.获取父元素
    p1.parentNode
    4.获取子元素
    div1.children[0]
    5.parentNode parentElement childNodes children的区别
    childNodes指的是返回当前元素子节点的所有类型节点,其中连空格和换行符都会默认文本节点,childern指的是返回当前元素的所有元素节点
    parentNode和parentElement在通常情况下都是一样的,但是找到根部document时,parentElement显示null,parentNode可以显示出来

    BOM

    Browser对象

    • Window
    • Navigator
    • Screen
    • History
    • Location

    如和检测浏览器的类型

    const isFF = navigator.userAgent.indexOf('Firefox') > -1
    if(isFF){
        console.log('Firefox!!!')
    }
    

    如何拆解url各部分

    Location相关属性

    location.href
    //"https://www.w3school.com.cn/tiy/t.asp?f=hdom_loc_hash#part2"
    location.protocol
    //"https:"
    location.hostname
    //"www.w3school.com.cn"
    location.pathname
    //"/tiy/t.asp"
    location.search
    //"?f=hdom_loc_hash"
    location.hash
    //"#part2"
    

    事件

    事件冒泡

    事件冒泡:当一个元素接收到事件的时候,会把他接收到的事件一层层传递给父级,一直到window。发生在文档元素上的大多数事件都会冒泡,但focus、blur、scroll事件不会冒泡。
    阻止事件冒泡:
    标准的W3C方式:e.stopPropagation() ie8不支持
    非标准的IE方式:e.cancelBubble = true 所有浏览器支持
    兼容写法:

    function stopBubble(e) {
        //如果提供了事件对象,则这是一个非IE浏览器
       if ( e && e.stopPropagation )
          //因此它支持W3C的stopPropagation()方法
          e.stopPropagation();
      else
      //否则,我们需要使用IE的方式来取消事件冒泡
        window.event.cancelBubble = true;
    }
    

    事件委托

    事件委托就是利用事件冒泡,把原本需要绑定在子元素上的响应事件委托为父元素,让父元素担当事件监听的职务。
    事件委托的优点:
    1.减少事件注册,减少内存占用。
    2.新增子元素时无需再次对其绑定。

    封装通用事件监听函数

        function bindEvent(elem,type,selector,fn){
            //处理是一般绑定
            if(!fn){
                fn = selector;
                selector = null;
            }
            elem.addEventListener(type,function(e){
                if (selector){
                    //处理委托
                    const target = e.target;
                    if (target.matches(selector)){
                        fn.call(target,e)
                    }
                } else{
                    //一般绑定
                    fn(e)
                }
            })
        }
    

    Ajax

    XMLHttpRequest对象

    XMLHttpRequest 对象用于在后台与服务器交换数据。
    属性:

    • XMLHttpRequest.readyState 属性返回一个 XMLHttpRequest 代理当前所处的状态。
      |值| 状态| 描述|
      | -- | -- | -- |
      |0| UNSENT| 代理被创建,但尚未调用 open() 方法。|
      |1| OPENED| open() 方法已经被调用。|
      |2| HEADERS_RECEIVED| send() 方法已经被调用,并且头部和状态已经可获得。|
      |3| LOADING| 下载中; responseText 属性已经包含部分数据。|
      |4| DONE| 下载操作已完成。|

    • XMLHttpRequest.status 返回了XMLHttpRequest 响应中的数字状态码。

    • XMLHttpRequest.responseType 属性返回响应数据的类型,默认的"text"类型。

    • XMLHttpRequest.responseText 在一个请求被发送后,从服务器端返回文本。如果请求未成功或尚未发送,则返回 null。

    • XMLHttpRequest response 属性返回响应的正文。返回的类型取决于 responseType 属性。

    方法:

    • XMLHttpRequest.open() 方法初始化一个请求。
    • XMLHttpRequest.send() 方法用于发送 HTTP 请求。默认为异步请求。
    • XMLHttpRequest.setRequestHeader() 是设置HTTP请求头部的方法。
    • XMLHttpRequest.getResponseHeader() 方法返回包含指定头文本的字符串。

    手写一个ajax

        //封装ajax
        function ajax(obj){
            const method = obj.method || 'GET';
            const async = obj.async || true;
            const data = obj.data || {};
            const xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function () {
                if (xhr.readyState ===4){
                    if (xhr.status === 200){
                        obj.success(xhr.responseText);
                    }
                }
            };
            if (method === 'get' || 'GET'){
                let url = obj.url + '?';
                for (let key in data){
                    url = url + key + '=' + data[key] + '&';
                }
                url = url.slice(0,-1);
                xhr.open(method,url,async);
                xhr.send(null);
            }else if (method === 'post' || 'POST'){
                xhr.open(method,obj.url,async);
                xhr.setRequestHeader('Content-Type','application/json');
                xhr.send(JSON.stringify(data))
            }
        }
    
        ajax({
            method:'POST',
            url:'https://api-hmugo-web.itheima.net/api/public/v1/my/orders/req_unifiedorder',
            async:true,
            success:function (data) {
                console.log(data);
            }
        })
    

    跨域

    浏览器的同源策略,浏览器会阻止一个域的javascript脚本与另一个域的内容进行交互。
    同源策略限制以下几种行为
    1.cookie localStorage无法读取
    2.DOM和js对象无法获得
    3.ajax请求不能发送
    跨域:协议、域名、端口有一个不同就叫跨域。

    允许跨域请求资源的三个标签

    • img
    • link
    • script

    解决跨域的方法

    1.jsonp
    通过动态创建script,再请求一个带参网址实现跨域通信,允许用户传递一个callback参数给服务端,当服务端返回时,执行该回调函数
    jsonp缺点:只能实现get一种请求

    • jsonp实现
      手写jsonp实现
        function jsonp(obj){
            const script = document.createElement('script');
            //处理url链接
            let url = obj.url + '?';
            if (obj.data){
                for (let key in obj.data){
                    url = url + key + '=' + obj.data[key] + '&';
                }
            }
            //处理callback函数名
            const callbackName = 'my_jsonp' + Math.random().toString().replace('.','');
            url += 'callback=' + callbackName;
            script.src = url;
            script.type = 'text/javascript';
            document.body.appendChild(script);
            //将callback函数名赋给window
            window[callbackName] = function (data) {
                obj.success(data);
                //成功后删除script标签
                document.body.removeChild(script);
            }
        }
        jsonp({url:'https://api-hmugo-web.itheima.net/api/public/v1/goods/detail',
            data:{goods_id:1},
            success:function(data){
                console.log(data)
            }
        });
    

    2.document.domain+iframe跨域
    仅限于主域相同,子域不同的跨域场景
    实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域
    1)父窗口(http://www.demo.com/a.html)

    <iframe src="http://child.demo.com/b.html"></iframe>
    <script>
      document.domain = "demo.com"
      const user = "admin"
    </script>
    

    2)子窗口(http://child.demo.com/b.html)

    <script>
      document.domain = "demo.com"
      //获取父窗口中的变量
      alert("get js data from parent--->" + window.parent.user)
    </script>
    

    3.location.hash + iframe跨域
    实现原理:a如果要跟b跨域通信,通过中间代理页面c来实现,不同域之间利用iframe的location.hash传值,相同域之间直接通过js访问来通信
    缺点:数据直接暴露在url中,数据容量有限
    实例
    4.window.name + iframe跨域
    window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
    实例

    常见的HTTP状态码

    • 2xx 请求成功 200
    • 3xx 重定向 301-永久重定向 302-临时重定向
    • 4xx 客户端请求错误 400-请求报文中存在语法错误 401-未经许可,需要通过HTTP认证 403-服务器拒绝该次访问 404-无法找到请求的资源
    • 5xx 服务器端错误 500-服务器在执行请求时发生了错误 503-服务器暂时处于超负载或正在进行停机维修,无法处理请求

    本地存储

    • cookie本身用于客户端与服务端通信,
    • 因为它有本地存储的功能,于是被借用
    • 存储量只有4KB左右
    • 所有http请求都带着,会影响获取资源的效率
    • API简单,需要封装才能用 document.cookie

    localStorage sessionStorage

    • HTML5专门为存储设计,存储容量为5MB
    • API简单易用
      localStorage.setItem("name","demi")
      localStorage.getItem("name")
    • 存储容量
      cookie:4KB
      localStorage sessionStorage:5MB
    • 数据有效期
      cookie:可以设置失效时间,如果没有设置的话,默认是关闭浏览器后失效
      localStorage:永久有效,除非被手动清除
      sessionStorage:仅在当前网页会话下有效,关闭页面或者浏览器就会被清除
    • 通信
      cookie:每次都会携带在HTTP请求头中,如果存储过多数据会带来性能问题
      localStorage sessionStorage:仅在客户端中保存,不参与和服务器的通信
    • 易用性
      cookie:需要自己进行封装
      localStorage sessionStorage:源生接口可以接受,也可以再次封装
    • 应用场景
      cookie:
      localStorage:
      sessionStorage:

    模块化

    什么是模块化

    模块化就是把一个复杂的程序封装成不同的模块,每个模块实现某个特定的功能,向外暴露接口供外部其他模块使用,彼此之间互不影响。

    模块化规范

    CommonJS

    node应用由模块组成,采用CommonJS模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量函数都是私有的,对其他文件不可见。是同步加载的。

    • 基本语法
      1.暴露模块
    module.exports = {
       module1:module1,
       module2:module2
    }
    exports.module1 = module1
    exports.module2 = module2
    

    2.引入模块
    const $ = require('jquery')

    • 实现步骤
      1.下载安装node.js
      2.创建项目结构npm init
      3.下载第三方模块
      4.编写模块代码
      5.利用webpack打包工具处理js
      6.在页面中引入

    AMD

    异步加载模块

    • 基本语法
      1.define() 定义暴露模块
    //定义没有依赖的模块
    define(function(){
      return 模块
    })
    //定义有依赖的模块
    define(['module1,module2'],function(m1,m2){
      return 模块
    })
    

    2.require() 引入使用模块

    require(['module1,module2'],function(m1,m2){
      使用m1/m2
    })
    
    • 实现步骤
      1.下载require.js并导入
      2.编写require.js的模块代码
      3.在页面中引入require.js文件,设置data-main入口文件

    ES6模块

    export命令用于导出模块,import用于引入模块

    //test.js
    export let myName="laowang";
    //index.js
    import {myName} from "./test.js";
    console.log(myName);//laowang
    

    如果要输出多个变量可以将这些变量包装成对象进行模块化输出:

    //test.js
    let myName="laowang";
    let myAge=90;
    let myfn=function(){
        return "我是"+myName+"!今年"+myAge+"岁了"
    }
    export {
        myName,
        myAge,
        myfn
    }
    //index.js
    import {myfn,myAge,myName} from "./test.js";
    console.log(myfn());//我是laowang!今年90岁了
    console.log(myAge);//90
    console.log(myName);//laowang
    

    如果你不想暴露模块当中的变量名字,可以通过as来进行操作:

    //test.js
    let myName="laowang";
    let myAge=90;
    let myfn=function(){
        return "我是"+myName+"!今年"+myAge+"岁了"
    }
    export {
        myName as name,
        myAge as age,
        myfn as fn
    }
    //index.js
    import {fn,age,name} from "./test.js";
    console.log(fn());//我是laowang!今年90岁了
    console.log(age);//90
    console.log(name);//laowang
    

    也可以直接导入整个模块,将上面的接收代码修改为:

    //index.js
    import * as info from "./test.js";//通过*来批量接收,as 来指定接收的名字
    console.log(info.fn());//我是laowang!今年90岁了
    console.log(info.age);//90
    console.log(info.name);//laowang
    

    默认导出(default export)
    一个模块只能有一个默认导出,对于默认导出,导入的名称可以和导出的名称不一致。

    //test.js
    export default function(){
        return "默认导出一个方法"
    }
    //index.js
    import myFn from "./test.js";//注意这里默认导出不需要用{}。
    console.log(myFn());//默认导出一个方法
    
    //可以将所有需要导出的变量放入一个对象中,然后通过default export进行导出
    //test.js
    export default {
        myFn(){
            return "默认导出一个方法"
        },
        myName:"laowang"
    }
    //index.js
    import myObj from "./test.js";
    console.log(myObj.myFn(),myObj.myName);//默认导出一个方法 laowang
    

    如果导入的多个文件中,变量名字相同,即会产生命名冲突的问题,为了解决该问题,ES6为提供了重命名的方法,当你在导入名称时可以这样做:

    /******************************test1.js**********************/
    export let myName="我来自test1.js";
    /******************************test2.js**********************/
    export let myName="我来自test2.js";
    /******************************index.js**********************/
    import {myName as name1} from "./test1.js";
    import {myName as name2} from "./test2.js";
    console.log(name1);//我来自test1.js
    console.log(name2);//我来自test1.js
    

    ES6模块化和CommonJS的区别

    1.CommonJS模块输出的是值的拷贝,ES6模块输出的是值的引用
    2.CommonJS模块是运行时加载,ES6模块是编译时输出接口

    构建工具

    webpack

    上线回滚

    • 上线流程
      1.将测试完成的代码提交到git版本库的master分支
      2.将当前服务器的代码全部打包并记录版本号,备份
      3.将master分支上的代码提交覆盖到线上服务器,生成新的版本号
    • 回滚流程
      1.将当前服务器的代码全部打包并记录版本号,备份
      2.将备份的上一个版本号解压,覆盖到线上服务器,并生成新的版本号

    运行环境

    页面加载过程

    • 加载资源的形式
      1.通过url加载html
      2.加载html中的静态资源
    • 加载一个资源的过程
      1.浏览器根据DNS服务器得到域名对应的IP地址
      2.向这个IP地址发送http请求
      3.服务器收到、处理并返回http请求
      4.浏览器得到返回内容
    • 浏览器渲染页面的过程
      1.浏览器将获取的HTML文档解析成DOM树(DOM Tree)
      2.处理CSS标记,构成层叠样式表模型(CSSOM)
      3.将DOM和CSSOM整合形成渲染树(Render Tree)
      4.根据Render Tree开始渲染和展示
      5.遇到script标签时会阻塞渲染
    • window.onload和DOMContentLoaded的区别
      1.window.onload要等页面的全部资源加载完才会执行,包括图片视频等
      2.DOMContentLoaded等DOM渲染完即可执行,此时图片视频还没有加载完

    性能优化

    1.加载资源优化

    • 静态资源的合并压缩(尽可能将外部的脚本、样式进行合并,多个文件合并为一个,使用雪碧图)
    • 静态资源缓存(合理地设置http缓存,尽可能的让资源在缓存中呆的时间更久)
    • 使用CDN让资源加载更快(用户访问网站的时候,将将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求。)
    • 使用SSR后端渲染,数据直接输出到HTML中
      2.渲染优化
    • css放到head中,script放到body最下面
    • 懒加载(图片懒加载,下拉加载更多)
      在图片没有进入可视区域时,先不给的src赋值,这样浏览器就不会发送请求了,等到图片进入可视区域再给src赋值。
    <img id="img1" src="img/loading.gif" data-src="img/pic1.png" />
    <script>
    let img1 = document.getElementById('img1')
    img1.src = img1.getAttribute('data-src')
    </script>
    
    • 减少DOM查询,对DOM查询做缓存
    • 减少DOM操作,多个DOM操作尽量合并在一起执行
      合并DOM插入
      插入10个li标签
      createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。
    let listNode = document.getElementById('list');
    let frag = document.createDocumentFragment();
    for(let i = 0 ;i<10;i++){
      let li = document.createElement('li');
      li.innerHTML = 'li' + i;
      frag.appendChild(li);
    }
    listNode.appendChild(frag);
    
    • 事件节流(防抖)
      设置一个时间间隔,时间间隔内只允许执行一次

    • 尽早执行操作(如DOMContentLoaded)

    安全性

    XSS(Cross Site Script)跨站脚本攻击

    恶意攻击者往Web页面里插入恶意代码,当用户浏览该页之时,嵌入其中的代码会被执行,从而达到恶意用户的特殊目的。
    解决:不信赖用户输入,对特殊字符如”<”,”>”进行转义。

    CSRF(Cross Site Request Forgery)跨站请求伪造

    CSRF攻击过程是用户登录网站A,输入个人信息,在本地保存服务器生成的cookie。然后在A网站点击由攻击者构建的一条恶意链接跳转到B网站,然后B网站携带着的用户cookie信息去访问A网站。让A网站造成是用户自己访问的假相,从而来进行一些列的操作,常见的就是转账。
    解决:在关键业务点设置验证码。

  • 相关阅读:
    2015上阅读计划
    《梦断代码》读书笔记 第2篇
    四则运算3
    求数组中最大子数组的和(一维)
    四则运算2—单元测试
    四则运算2
    《梦断代码》读书笔记 第1篇
    四组运算2(思路)
    四则运算1
    读书笔记
  • 原文地址:https://www.cnblogs.com/zhahuhu/p/13417223.html
Copyright © 2011-2022 走看看