JS基础总结(8)—— 其他

1. 历史状态管理

能够触发浏览器历史状态栈的操作,也是vue-router的实现原理

1.1 Hash

通过#(锚点)来实现页面内定位,不会触发页面刷新
每次改变Hash值会在浏览器状态栈中添加一条记录并触发hashchange事件
Hash是浏览器行为,对服务器无效,也不会被包含进HTTP请求

1.2 History

能够在不加载新页面的情况下改变浏览器URL
API

history.back()
hsitory.forward()
hsitory.go()

pushState:在历史状态栈中添加一条记录
replaceState:重写当前记录

// 示例
window.onload = function () {
    
     // 阻止页面后退
    pushState ()
    window.addEventListener('popstate', pushState)
};

function pushState () {
    
    
    const state = {
    
    
        title: 'title',
        url: '#'
    }
    window.history.pushState(state, 'title', '#')
}

2. 高级函数

2.1 惰性载入函数

解决调用时多次if判断的性能流失问题
使用方法:首次调用函数后,使用判断结果覆盖原函数

function createText() {
    
    
    // 伪代码
    if (system === 'windows') {
    
    
        if (broswer === 'chrome') {
    
    
            createText = function () {
    
    
                return 'windows chrome'
            }
        }
    } else if (system === 'android') {
    
    
        if (device === 'Samsung') {
    
    
            createText = function () {
    
    
                return 'android Samsung'
            }
        }
    }
    return createText()
}

2.2 Yielding Processes

解决循环造成的脚本长时间运行
当脚本的运行不是必须同步执行且数据不是必须顺序完成时,可以使用数组分块技术进行处理

// 可以根据需求修改chunk函数的入参,实现更复杂的处理
function chunk(dataList, processFn, target) {
    
    
    setTimeout(() => {
    
    
        const data = dataList.shift()
        processFn.call(target, data)
        
        if (dataList.length) {
    
    
            chunk(dataList, processFn, target)
        } 
    }, 100)
}

2.3 防抖

函数连续触发时,只在停止触发后执行一次

function debounce(fn, target = this, interval = 100) {
    
    
    clearTimeout(fn.tid)
    fn.tid = setTimeout(() => {
    
    
        fn.call(target)
    }, interval)
}

2.4 节流

函数连续触发时,只在规定间隔后触发

function throttle(fn, target = this, interval = 100) {
    
    
    if (!fn.isLock) {
    
    
        fn.call(target)
        fn.isLock = true
        setTimeout(() => {
    
    
            fn.isLock = false
        }, interval)
    }
}

3. 离线存储

3.1 cookie

3.1.1 cookie构成
name value domain path Expires/Max-age secure
名,不区分大小写 指定域下有效 指定路径下向服务器发送Cookie 失效时间,格式是GMT,需要使用new Date().toUTCString()设置才有效 是否只在HTTPS下发送Cookie

除了namevalue,其他参数都是用于指示浏览器在何时将cookie发送至服务器,而这些参数并不会作为cookie信息的一部分

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; expires=Mon, 13 Apr 2020 03:26:15 GMT; domain=.baidu.com; path=/; secure
Other-header; other-header-value

限制:因浏览器而异,通常是每个域名50个,单个大小4KB

3.1.2 cookie操作

使用domain.cookie来读写cookie

扫描二维码关注公众号,回复: 12539911 查看本文章

获取值时,domain.cookie会返回所有的cookie信息并使用;分割
设置值时,domain.cookie会设置一个新的cookie信息并添加到cookie集合中,如果设置的cookiename属性已存在,则覆盖相同namecookie

由于读写的不便利,常常会使用封装函数来简化操作

const CookieUtil = {
    
    
	get: function (name) {
    
    
	    const cookieName = encodeURIComponent(name) + '='
	    const cookieStart = document.cookie.indexOf(cookieName)
	    let cookieValue = null
	    if (cookieStart > -1) {
    
    
	        let cookieEnd = document.cookie.indexOf(';', cookieStart)
	        if (cookieEnd === -1) {
    
    
	            cookieEnd = document.cookie.length
	        }
	        cookieValue = decodeURIComponent(
	        	document.cookie.substring(cookieStart + cookieName.length, cookieEnd)
	        )
	    }
	    return cookieValue
	},
	set: function (name, value, expires, path, domain, secure) {
    
    
	    let cookieText = encodeURIComponent(name) + '=' + encodeURIComponent(value)
	    if (expires instanceof Date) {
    
    
	        cookieText += '; expires=' + expires.toUTCString()
	    }
	    if (path) {
    
    
	        cookieText += '; path=' + path
	    }
	    if (domain) {
    
    
	        cookieText += '; domain=' + domain
	    }
	    if (secure) {
    
    
	        cookieText += '; secure'
	    }
	    document.cookie = cookieText
	},
	delete: function (name, path, domain, secure) {
    
    
	    this.set(name, '', new Date(0), path, domain, secure)
	}
}
3.1.3 子cookie

cookie中存放name1=value1; name2=value2的值,用于规避浏览器最大cookie数量的限制。
可定义一系列方法来简化操作,不常用因此不深入讨论

3.1.4 cookie设置的坑

作用域: 当前路径及子路径可以访问,同级父级无法访问

设置: domainpath只能设置当前路径中已存在的,其余值设置不上

读写: 跨域请求服务器设置cookie时,该cookie存在于服务器所在域名下,chrome可以看见无法读写,edge无法看见cookie,切换至同一域名后都可以看见并读写

请求头: 跨域请求服务器通过请求头Set-Cookie设置cookie时,chrome会隐藏这个字段

有效期:max-age以秒为单位,不设置默认当前会话有效(同sessionStorage),设置0删除当前cookie

溢出:当单个cookie的大小设置后超过4K时,此次操作无效,且不会发生任何事情(无报错)

匹配规则: 匹配cookie和请求的域名以及路径,尝试携带所有符合的cookie,当请求头超过大小后会报错

3.2 storage

3.2.1 sessionStorage和localStorage

来源相同的情况下(不跨域,页面来自同一域名,协议,端口),所有页面共享数据

sessionStorage在关闭浏览器后消失(关闭会话框再恢复不会消失,不同会话框即使同一页面也不共享)
localStorage不清除始终存在

限制: 因浏览器而异,通常是每个来源2.5MB - 10MB之间

3.2.2 IndexedDB

详细教程以及API这里

主要操作四个对象:

  1. IDBRequest 对象:操作请求对象,用于新增,打开,删除数据库
  2. IDBDatabase 对象:数据库对象,用于对数据库进行操作,如增删表,创建事务等
  3. IDBTransaction对象:事务对象,用于操作数据库事务,读写必经之路
  4. IDBObjectStore 对象:仓库对象,用于具体实现对数据库表的各种操作

次要操作对象:

  • IDBIndex 对象: 数据库的索引对象,用于通过除主键以外其他键,建立索引获取值
  • IDBCursor 对象:指针对象,用于遍历 IDBObjectStoreIDBIndex 中的数据
  • IDBKeyRange 对象:主键范围对象,用于设置一个主键的搜索范围,用于定位查找数据
let myDB
// 新建数据库,返回 IDBRequest 对象
const requestBD = window.indexedDB.open('myDB', 1)

requestBD.onupgradeneeded = function (event) {
    
    
	// 返回 IDBDatabase 对象
   	myDB = event.target.result
    if (!myDB.objectStoreNames.contains('person')) {
    
    
    	// 使用 IDBDatabase 对象添加person表,返回 IDBObjectStore 对象
        const objectStore = myDB.createObjectStore('person', 
        					{
    
     keyPath: 'id' })
 		// 使用 IDBObjectStore 对象添加 person 表的索引
        objectStore.createIndex('name', 'name', {
    
     unique: false })
        objectStore.createIndex('email', 'email', {
    
     unique: true })
    }
    read()
    readByIndex()
}

function read() {
    
    
	// 使用 IDBKeyRange 对象设置主键范围
	const range = IDBKeyRange.bound(1, 5, true, true);
	// 使用 IDBDatabase 对象获取 IDBTransaction 对象
    const request = myDB.transaction(['person'], 'readonly') 
    // 使用 IDBTransaction 对象获取 IDBObjectStore对象
    				.objectStore('person')
	// 使用 IDBObjectStore 对象获取主键为 1 < key < 5 的数据
    				.get(range)

    request.onerror = function () {
    
    
        console.log('事务失败')
    }

    request.onsuccess = function (event) {
    
    
        if (request.result) {
    
    
            console.log('Name:' + request.result.name)
            console.log('age:' + request.result.age)
            console.log('email:' + request.result.email)
        }
    }
}

function readByIndex() {
    
    
    const store = myDB.transaction(['person'],'readonly')
    .objectStore('person')
    // 使用 IDBObjectStore 对象获取 IDBIndex 对象
    const request = store.index('name')
    // 使用 IDBIndex 对象获取索引 name='李四' 的数据
    				.get('李四')

    request.onsuccess = function (event) {
    
    
        const result = event.target.result
        if (result) {
    
    
            console.log(result)
        }
    }
}

function readAll() {
    
    
    const objectStore = myDB.transaction('person')
    					.objectStore('person');
	// 使用 IDBObjectStore 对象获取 IDBCursor 对象
    objectStore.openCursor().onsuccess = function (event) {
    
    
        const cursor = event.target.result;

        if (cursor) {
    
    
            console.log('Id: ' + cursor.key);
            console.log('Name: ' + cursor.value.name);
            console.log('Age: ' + cursor.value.age);
            console.log('Email: ' + cursor.value.email);
            cursor.continue();
        } else {
    
    
            console.log('没有更多数据了!');
        }
    };
}

4. 维护

4.1 可维护性

4.1.1 代码约定
  1. 可读性: 代码缩进以及注释
  2. 变量和函数命名: 使用具有含义的命名
  3. 变量类型透明: 初始化变量或注释
4.1.2 松散耦合
  1. 解耦HTML/JS: 避免直接在JS中嵌入插入HTML内容的语句
  2. 解耦CSS/JS: 避免使用JS直接修改CSS,改为修改元素的类
  3. 解耦应用逻辑/事件处理: 避免在事件处理程序中直接嵌套应用逻辑,改为抽离应用逻辑,将(事件监听,逻辑触发)与逻辑本身分离
4.1.3 编程实践
  1. 尊重对象所有权:避免修改不是你创建的对象,不为实例或原型添加属性或方法,且不重写已有方法
  2. 避免全局变量: 最多创建一个全局变量,其余变量或库挂载在全局中,使用命名空间进行维护
  3. 避免与null进行比较:使用数据本来的类型或逻辑进行判断,使用instanceoftypeof 代替
  4. 使用常量: 抽离可重复使用的值、用户界面字符串、URL、任何可能会更改的值

4.2 性能

4.2.1 变量访问
  • 作用域查找 : 由于作用域链,局部变量访问速度始终快于全局。当有重复全局变量访问需求时,将全局变量提取成局部变量来提高访问效率

  • 属性查找:变量和数组的访问是O(1),对象属性的访问是O(n)。当有重复对象属性访问需求时,将对象属性提取成局部变量来提高访问效率

4.2.2 循环优化

基本优化步骤:

  1. 减值迭代: 将i设置为最大值,使用i--来迭代会更加高效
  2. 简化终止条件: 终止条件计算时,避免O(n)的访问操作
    如: i < arr.length
  3. 简化循环体:优化循环内部逻辑
  4. 使用后测试循环:do-whilefor或者while少一次终止条件判断

展开循环:
直接对每次循环调用循环体处理函数process()会更加高效,这样会消除建立循环和处理终止条件的额外开销。当迭代次数无法确定时,可以使用一个叫做Deff装置的技术来进行优化。

注:非大数据处理情况下用会得不偿失

let iterations = Math.floor(values.length / 8)
let leftover = values.length % 8
let index = 0

if (leftover > 0) {
    
    
    do {
    
    
        process(values[index++])
    } while (--leftover > 0)
}
do {
    
    
    process(values[index++])
    process(values[index++])
    process(values[index++])
    process(values[index++])
    process(values[index++])
    process(values[index++])
    process(values[index++])
    process(values[index++])
} while (--iterations > 0) 
4.2.2 语句优化

单条语句完成多个操作要比多条语句完成相同操作要快

  1. 变量声明
// 慢
let arr = [1,2]
let index = 0
let length = 2
//快
let arr = [1,2],
	index = 0,
	length = 2
  1. 迭代值使用
// 慢
arr[index] = 0
index ++
// 快
arr[index++] = 0
  1. 声明字面量
// 慢
let arr = new Array(3)
arr[0] = 0
arr[1] = 1
arr[2] = 2
let obj= new Object()
obj.name = 'soraka'
obj.age = 18
obj.introduce = function () {
    
    
	console.log(this.name, this.age)
}
// 快
let arr = [0,1,2]
let obj = {
    
    
	name: 'soraka',
	age: 18,
	introduce: function () {
    
    
		console.log(this.name, this.age)
	}
}
4.2.3 Dom优化
  1. 最小化更新: 尽量减少在DOM操作中引起的浏览器页面更新次数
// 慢
let list = document.getElementById('myList'),
    item
for (let i = 0; i < 10; i++) {
    
    
    item = document.createElement('li')
    list.appendChild(item)
    item.appendChild(document.createTextNode('Item' + i))
} 
// 快
let list = document.getElementById('myList'),
    fragment = document.createDocumentFragment(),
    item
for (let i = 0; i < 10; i++) {
    
    
    item = document.createElement('li')
    fragment.appendChild(item)
    item.appendChild(document.createTextNode('Item' + i))
}
list.appendChild(fragment)
  1. 使用innerHTML:对于大的DOM更改,innerHTMLcreateElement()之类的DOM方法要快的多
// 更快
let list = document.getElementById('myList'),
    html = ''
for (let i = 0; i < 10; i++) {
    
    
    html += '<li>Item${i}</li>'
}
list.appendChild(html)
  1. 使用事件委托
  2. 注意HTMLCollection:任何访问HTMLCollection的操作都是在DOM文档上进行一个昂贵的查询,最小化HTMLCollection访问次数能极大的优化性能

猜你喜欢

转载自blog.csdn.net/weixin_44844528/article/details/105427954