8、手写原生AJAX
步骤
- 创建 XMLHttpRequest 实例
- 发出 HTTP 请求
- 服务器返回 XML 格式的字符串
- JS 解析 XML,并更新局部页面
不过随着历史进程的推进,XML 已经被淘汰,取而代之的是 JSON。
了解了属性和方法之后,根据 AJAX 的步骤,手写最简单的 GET 请求。
myButton.addEventListener("click", function () { ajax(); }); function ajax() { let xhr = new XMLHttpRequest(); //实例化,以调用方法 xhr.open("get", "https://www.baidu.com"); //参数2,url。参数三:异步 xhr.onreadystatechange = () => { //每当 readyState 属性改变时,就会调用该函数。 if (xhr.readyState === 4) { //XMLHttpRequest 代理当前所处状态。 if (xhr.status >= 200 && xhr.status < 300) { //200-300请求成功 let string = request.responseText; //JSON.parse() 方法用来解析JSON字符串,构造由字符串描述的JavaScript值或对象 let object = JSON.parse(string); } } }; request.send(); //用于实际发出 HTTP 请求。不带参数为GET请求 }
基于promise实现:
function ajax(url) { const p = new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open("get", url); xhr.onreadystatechange = () => { if (xhr.readyState == 4) { if (xhr.status >= 200 && xhr.status <= 300) { resolve(JSON.parse(xhr.responseText)); } else { reject("请求出错"); } } }; xhr.send(); //发送hppt请求 }); return p; } let url = "/data.json"; ajax(url) .then((res) => console.log(res)) .catch((reason) => console.log(reason));
9、柯里化函数的实现
柯里化函数的定义:将多参数的函数转换成单参数的形式。
柯里化函数实现的原理:利用闭包原理在执行可以形成一个不销毁的作用域,然后把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个最少参数函数。
问法有很多,比较全面的可看该文:JS函数柯里化
10、实现一个双向数据绑定
let obj = {} let input = document.getElementById('input') let span = document.getElementById('span') // 数据劫持 Object.defineProperty(obj, 'text', { configurable: true, enumerable: true, get() { console.log('获取数据了') }, set(newVal) { console.log('数据更新了') input.value = newVal span.innerHTML = newVal } }) // 输入监听 input.addEventListener('keyup', function(e) { obj.text = e.target.value })
11、rem 基本设置
// 提前执行,初始化 resize 事件不会执行 setRem() // 原始配置 function setRem () { let doc = document.documentElement let width = doc.getBoundingClientRect().width let rem = width / 75 doc.style.fontSize = rem + 'px' } // 监听窗口变化 addEventListener("resize", setRem)
12、手写发布订阅
发布订阅模式的发布和订阅都由一个调度中心来处理
发布订阅模式是完全解耦的,因为调度中心中存的直接就是逻辑处理函数
要点:都要实现添加/删除/派发更新三个事件。
class Event { // 首先定义一个事件容器,用来装事件数组(因为订阅者可以是多个) #handlers = {} // 事件添加方法,参数有事件名和事件方法 addEventListener(type, handler) { // 首先判断handlers内有没有type事件容器,没有则创建一个新数组容器 if (!(type in this.#handlers)) { this.#handlers[type] = [] } // 将事件存入 this.#handlers[type].push(handler) } // 触发事件两个参数(事件名,参数) dispatchEvent(type, ...params) { // 若没有注册该事件则抛出错误 if (!(type in this.#handlers)) { return new Error('未注册该事件') } // 便利触发 this.#handlers[type].forEach(handler => { handler(...params) }) } // 事件移除参数(事件名,删除的事件,若无第二个参数则删除该事件的订阅和发布) removeEventListener(type, handler) { // 无效事件抛出 if (!(type in this.#handlers)) { return new Error('无效事件') } if (!handler) { // 直接移除事件 delete this.#handlers[type] } else { const idx = this.#handlers[type].findIndex(ele => ele === handler) // 抛出异常事件 if (idx === -1) { return new Error('无该绑定事件') } // 移除事件 this.#handlers[type].splice(idx, 1) if (this.#handlers[type].length === 0) { delete this.#handlers[type] } } } }
13、数组去重的实现
该题比较灵活,答题时可根据题目选取最便捷的写法。
14、实现数组拍平
参考:
15、实现斐波那契数列
16、实现图片懒加载
与普通的图片懒加载不同,如下这个多做了 2 个精心处理:
- 图片全部加载完成后移除事件监听;
- 加载完的图片,从 imgList 移除;
let imgList = [...document.querySelectorAll('img')] let length = imgList.length const imgLazyLoad = function() { let count = 0 return (function() { let deleteIndexList = [] imgList.forEach((img, index) => { let rect = img.getBoundingClientRect() if (rect.top < window.innerHeight) { img.src = img.dataset.src deleteIndexList.push(index) count++ if (count === length) { document.removeEventListener('scroll', imgLazyLoad) } } }) imgList = imgList.filter((img, index) => !deleteIndexList.includes(index)) })() } // 这里最好加上防抖处理 document.addEventListener('scroll', imgLazyLoad)
17、实现一个JSONP
JSONP 核心原理:script 标签不受同源策略约束,所以可以用来进行跨域请求,优点是兼容性好,但是只能用于 GET 请求。
const jsonp = ({ url, params, callbackName }) => { const generateUrl = () => { let dataSrc = '' for (let key in params) { if (params.hasOwnProperty(key)) { dataSrc += `${key}=${params[key]}&` } } dataSrc += `callback=${callbackName}` return `${url}?${dataSrc}` } return new Promise((resolve, reject) => { const scriptEle = document.createElement('script') scriptEle.src = generateUrl() document.body.appendChild(scriptEle) window[callbackName] = data => { resolve(data) document.removeChild(scriptEle) } }) }