前端面试 -- JS-Web-API
JS-Web-API
在前端面试 – JS基础知识部分,我们了解到了JS的一些内置对象和API,那么JS基础与本文要了解到的JS-Web-API之间有什么联系呢?诸君莫急,答案即将为您揭晓!
从JS基础知识到JS-Web-API
回顾JS基础知识
- 变量类型和计算
- 原型和原型链
- 作用域和闭包
- 异步和单线程
- 其他(如日期、Math、数组、对象常用API)
- 特点:表面看起来并不能用于工作中开发代码
- 内置函数:Object Array Boolean String …
- 内置对象:Math JSON …
- 我们连在网页弹出一句hello world都不能实现
JS-Web-API
- JS基础知识:ECMA262标准
- JS-Web-API:W3C标准
- 获取元素document.getElementById(id),浏览器需要:
- 定义一个document全局变量,对象类型
- 给它定义一个getElementById的属性,属性值是一个函数
- 但是W3C标准没有规定任何JS基础相关的东西
- 不管什么变量类型、原型、作用域和异步
- 只管定义用于浏览器中JS操作页面的API和全局变量
- 全面考虑,JS内置的全局函数和对象有哪些?
- 之前讲过的Object Array Boolean String Math JSON等
- window document
- navigator.userAgent screen location history
总结
- 常说的JS(浏览器执行的JS)包含两部分:
- JS基础知识(ECMA262标准)
- JS-Web-API(W3C标准)
1-DOM操作
- DOM:是Document Object Model (文档对象模型)的简称
1-1 题目
- DOM是哪种基本的数据结构?
- DOM操作的常用API有哪些?
- DOM节点的attr和property有何区别?
1-2 知识点
- DOM本质
- xml文档类型–树型结构
<!-- 使用xml写出的一封邮件 --> <?xml version="1.0" encoding="UTF-8"?> <note> <to>Tom</to> <from>Jane</from> <heading>Reminder</heading> <body>Dont't forgot me this weekend!</body> <other> <a></a> <b></b> </other> </note>
- html文档类型(一种特殊的xml文档类型)–树型结构
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>5-1-DOM的本质</title> </head> <body> <div> <p>this is p</p> </div> </body> </html>
- DOM节点操作
- 获取DOM节点
- prototype
- attribute
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>5-2-DOM节点操作</title> </head> <body> <div id="div1" class="class-div1"> <p id="p1" data-name="p1-data-name" class="class-p1" style="width: 200px;">this is p1</p> <p id="p2">this is p2</p> </div> <div id="div2"> <p id="p3">this is p3</p> <p id="p4">this is p4</p> </div> <script> // 获取DOM节点 let div1 = document.getElementById('div1'); // 元素 let divList = document.getElementsByTagName('div'); // 集合 let pList = document.querySelectorAll('p'); // 集合 let p1 = document.getElementById('p1'); // 元素 let p = pList[0]; // 元素 console.log(div1); console.log(divList); // HTMLCollection(2) [div#div1.class-div1, div#div2, div1: div#div1.class-div1, div2: div#div2] console.log(pList); // NodeList(4) [p#p1, p#p2, p#p3, p#p4] // property console.log(div1.className); // class-div1 div1.className = 'abc'; console.log(div1.className); // abc console.log(divList.length); // 2 console.log(divList[0]); console.log(p.style.width); // 获取样式 200px p.style.width = 100 + "px"; // 修改样式 console.log(p.style.width); // 获取样式 100px console.log(p.className); // 获取class class-p1 p.className = 'cla-p1'; // 修改class // 获取nodeName 和 nodeType console.log(p.nodeName); // p console.log(p.nodeType); // 1 //attribute console.log(p1.getAttribute('data-name')); // p1-data-name p1.setAttribute('data-name', 'xyz'); console.log(p1.getAttribute('style')); // width: 100px; p1.setAttribute('style', 'font-size: 30px;'); </script> </body> </html>
- DOM结构操作
- 新增节点
- 获取父节点
- 获取子节点
- 删除节点
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>5-3-DOM结构操作</title> </head> <body> <div id="div1"> <p id="p1">this is p1</p> <p id="p2">this is p2</p> </div> <div id="div2"> <p id="p3">this is p3</p> <p id="p4">this is p4</p> </div> <script> // 新增节点 let div1 = document.getElementById('div1'); let p = document.createElement('p'); // 添加新节点 p.innerHTML = 'this is p'; div1.appendChild(p); // 添加新创建的元素 // 移动已有节点 let p4 = document.getElementById('p4'); div1.appendChild(p4); // 获取父节点 console.log(p4.parentElement); // div1 console.log(div1.parentElement); // body // 获取子节点 console.log(div1.childNodes); // NodeList(6) [text, p#p1, text, p#p2, text, p#p4] console.log(div1.childNodes[0].nodeType); // text 3 console.log(div1.childNodes[1].nodeType); // p 1 console.log(div1.childNodes[0].nodeName); // text #text console.log(div1.childNodes[1].nodeName); // p p // 删除节点 let chiildNodes = div1.childNodes; div1.removeChild(chiildNodes[1]); </script> </body> </html>
1-3 题目解答
- DOM是哪种基本的数据结构?
- 树型结构
- DOM操作的常用API有哪些?
- 获取DOM节点,以及节点的property和attribute
- 获取父节点,获取子节点
- 新增节点,删除节点
- DOM节点的attr和property有何区别?
- property只是一个JS对象的属性的修改
- attribute是对html标签属性的修改
2-BOM操作
- BOM:是Browser Object Model (浏览器对象模型)的简称
2-1 题目
- 如何检测浏览器的类型?
- 拆解url的各部分
2-2 知识点
- navigator
- screen
- location
- history
// 示例网页:https://www.qingnian8.com/?cid=99&a=b#mid=100 // navigator let ua = navigator.userAgent; console.log(ua); // 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1' let isChrome = ua.indexOf('Chrome') >= 0; console.log(isChrome); // true // screen console.log(screen.width); // 1920 console.log(screen.height); // 1080 // location console.log(location.href); // 'https://www.qingnian8.com/?cid=99&a=b#mid=100' console.log(location.protocol); // 'https:' console.log(location.host); // 'www.qingnian8.com' console.log(location.pathname); // '/' console.log(location.search); // '?cid=99&a=b' console.log(location.hash); // '#mid=100' // history // history.back(); console.log(history.back()); history.forward();
2-3 题目解答
- 如何检测浏览器的类型?
// 问题:如何检测浏览器的类型? let ua = navigator.userAgent; console.log(ua); // 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1' let isChrome = ua.indexOf('Chrome') >= 0; console.log(isChrome); // true
- 拆解url的各部分
// 问题:拆解url的各部分 // location console.log(location.href); // 'https://www.qingnian8.com/?cid=99&a=b#mid=100' console.log(location.protocol); // 'https:' console.log(location.host); // 'www.qingnian8.com' console.log(location.pathname); // '/' console.log(location.search); // '?cid=99&a=b' console.log(location.hash); // '#mid=100'
3-事件
3-1 题目
- 编写一个通用的事件监听函数
- 描述事件冒泡流程
- 对于一个无限下拉加载图片的页面,如何给每个图片绑定事件
3-2 知识点
- 通用事件绑定
注意:关于IE低版本的兼容性// JS原生事件绑定 let btn = document.getElementById('btn1'); btn.addEventListener('click', function() { alert('clicked'); } // 通用事件绑定函数 function bindEvent(elem, type, fn) { elem.addEventListener(type, fn); } let link1 = document.getElementById('link1'); bindEvent(link1, 'click', (e) => { e.preventDefault(); // 阻止默认行为 console.log(123); });
- IE低版本使用attachEvent绑定事件,和W3C标准不一样
- IE低版本使用量非常少,很多网站都早已不支持
- 建议对IE低版本的兼容性:了解即可,无需深究
- 如果遇到对IE低版本要求苛刻的面试,果断放弃
- 事件冒泡
<body> <div id="div1"> <p id="p1">激活</p> <p id="p2">取消</p> </div> <div id="div2"> <p id="p3">取消</p> <p id="p4">取消</p> </div> </body>
// 事件冒泡 let p1 = document.getElementById('p1'); bindEvent(p1, 'click', (e) => { e.stopPropagation(); alert('激活'); }); let body = document.body; bindEvent(body, 'click', (e) => { alert('取消'); });
- 代理
<div id="div1"> <a href="http://imooc.com" id="link1">imooc.com1</a> <a href="http://imooc.com" id="link2">imooc.com2</a> <a href="http://imooc.com" id="link3">imooc.com3</a> <a href="http://imooc.com" id="link4">imooc.com4</a> <!-- 会随时新增更多a标签 --> </div>
完善通用事件绑定的函数// 事件代理 let div1 = document.getElementById('div1'); bindEvent(div1, 'click', (e) => { e.preventDefault(); let target = e.target; if(target.nodeName === 'A') { console.log(target.innerHTML); } });
// 完善通用事件绑定的函数 function bindEvent(elem, type, selector, fn) { if(fn == null) { fn = selector; selector = null; } elem.addEventListener(type, (e) => { if(selector) { // 代理 let target = e.target; if(target.matches(selector)) { fn.call(target, e); } } else { // 不是代理 fn(e); } }); } // 使用代理 let div1 = document.getElementById('div1'); bindEvent(div1, 'click', 'a', function(e) { e.preventDefault(); console.log(this.innerHTML); }); // 不使用代理 let p1 = document.getElementById('p1'); bindEvent(p1, 'click', function(e) { console.log(p1.innerHTML); });
3-3 题目解答
- 编写一个通用的事件监听函数
// 问题:编写一个通用的事件监听函数 function bindEvent(elem, type, selector, fn) { if(fn == null) { fn = selector; selector = null; } elem.addEventListener(type, (e) => { if(selector) { // 代理 let target = e.target; if(target.matches(selector)) { fn.call(target, e); } } else { // 不是代理 fn(e); } }); }
- 描述事件冒泡流程
- DOM树型结构
- 事件冒泡
- 阻止冒泡 e.stopPropagation();
- 冒泡的应用–代理
- 对于一个无限下拉加载图片的页面,如何给每个图片绑定事件
- 使用代理
- 知道代理的两个优点
- 代码简洁
- 减少浏览器内存占用
4-Ajax
4-1 题目
- 手动编写一个ajax,不依赖第三方库
- 跨域的几种实现方式
4-2 知识点
- XMLHttpRequest
注意:关于IE低版本的兼容性// XMLHttpRequest let xhr = new XMLHttpRequest(); xhr.open('GET', 'http://ku.qingnian8.com/wxList.php', false); xhr.onreadystatechange = function() { // 这里的函数异步执行,可参考JS基础知识中的异步模块 if(xhr.readyState === 4) { if(xhr.status === 200) { console.log(xhr.responseText); } } } xhr.send(null);
- IE低版本使用ActiveXObject发送网络请求,和W3C标准不一样
- IE低版本使用量非常少,很多网站都早已不支持
- 建议对IE低版本的兼容性:了解即可,无需深究
- 如果遇到对IE低版本要求苛刻的面试,果断放弃
- 状态码说明
- readyState:指的是数据请求发送状态
- 0 - (未初始化)还没有调用send()方法
- 1 - (载入)已调用send()方法,正在发送请求
- 2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
- 3 - (交互)正在解析响应内容
- 4 - (完成)响应内容解析完成,可以在客户端调用了
- status:指的是数据请求响应状态
- 2xx - 表示成功处理请求。如200
- 3xx - 需要重定向,浏览器直接跳转
- 4xx - 客户端请求错误,如404
- 5xx - 服务器端错误
- readyState:指的是数据请求发送状态
- 跨域
- 什么是跨域
- 浏览器有同源策略,不允许 ajax 访问其他域接口
- http://www.yourname.com:80/page1.html
- http://m.imooc.com/course/ajaxcourserecom?cid=459
- 跨域条件:协议、域名、端口,有一个不同就算跨域
以下三个标签允许跨域加载资源 - <img src=xxx>
- <link href=xxx>
- <script src=xxx></script>
三个标签的使用场景 - <img>用于打点统计,统计网站可能是其他域
- <link> <script>可以使用CDN,CDN也是其他域
- <script>可以用于JSONP
跨域注意事项 - 所有的跨域请求都必须经过信息提供方允许
- 如果未经允许即可获取,那是浏览器同源策略出现漏铜
- JSONP实现原理
- 加载http://coding.m.imooc.com/classindex.html
- 不一定服务器端真的有一个classindex.html文件
- 服务器可以根据请求,动态生成一个文件,返回
- 同理于 <script src=“http://coding.m.imooc.com/api.js”></script>
- 例如你的网站要跨域访问慕课网的一个接口
- 慕课给你一个地址 http://coding.m.imooc.com/api.js
- 返回内容格式如callback({x: 100, y: 200}) (可动态生成)
<script> // JSONP实现原理 window.callback = function(data) { // 这是我们跨域得到的信息 console.log(data); } </script> <script src="http://coding.m.imooc.com/api.js"></script> <!-- 以上将返回 callback({x: 100, y: 200}) -->
- 服务器端设置 http header
- 另外一个解决跨域的简洁方法,需要服务器端来做
- 但是作为交互方,我们必须知道这个方法
- 是将来解决跨域问题的一个趋势
- 什么是跨域
4-3 题目解答
- 手动编写一个ajax,不依赖第三方库
// 问题:手动编写一个ajax,不依赖第三方库 let xhr = new XMLHttpRequest(); xhr.open('GET', 'http://ku.qingnian8.com/wxList.php', false); xhr.onreadystatechange = function() { // 这里的函数异步执行,可参考JS基础知识中的异步模块 if(xhr.readyState === 4) { if(xhr.status === 200) { console.log(xhr.responseText); } } } xhr.send(null);
- 跨域的几种实现方式
- JSONP
- 服务器端设置 http header
5-存储
5-1 题目
- 请描述一下 cookie,sessionStorage 和 localStorage 的区别?
5-2 知识点
- cookie
- 本身用于客户端和服务器端通信
- 但是它有本地存储的功能,于是就被“借用”
- 使用 document.cookie=… 获取和修改即可
cookie用于存储的缺点 - 存储量太小,只有4KB
- 所有http请求都带着,会影响获取资源的效率
- API简单,需要封装才能用 document.cookie=…
- sessionStorage 和 localStorage
- HTML5专门为存储而设计,最大容量 5M
- API简单易用:localStorage.setItem(key, value); localStorage.getItem(key);
注意: - iOS safari 隐藏模式下,localStorage.getItem 会报错,建议统一使用 try-catch 封装
5-3 题目解答
- 请描述一下 cookie,sessionStorage 和 localStorage 的区别?
- 容量
- 是否会携带到 ajax 中
- API易用性