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
- cookie本身用于客户端与服务端通信,
- 因为它有本地存储的功能,于是被借用
- 存储量只有4KB左右
- 所有http请求都带着,会影响获取资源的效率
- API简单,需要封装才能用 document.cookie
localStorage sessionStorage
- HTML5专门为存储设计,存储容量为5MB
- API简单易用
localStorage.setItem("name","demi")
localStorage.getItem("name")
cookie localStorage sessionStorage的区别
- 存储容量
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网站造成是用户自己访问的假相,从而来进行一些列的操作,常见的就是转账。
解决:在关键业务点设置验证码。