前端笔记--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网站造成是用户自己访问的假相,从而来进行一些列的操作,常见的就是转账。
解决:在关键业务点设置验证码。

posted @ 2020-08-08 23:10  炸呼呼er  阅读(235)  评论(0编辑  收藏  举报