# Javascript一些基础面试题 **Published by:** [MarkTang's Blog](https://paragraph.com/@marktang-s-blog/) **Published on:** 2022-08-26 **URL:** https://paragraph.com/@marktang-s-blog/javascript ## Content JS面试一些使Javascript更加简洁的小技巧清空或截断数组 const arr = [11, 22, 33, 44, 55, 66]; // truncanting arr.length = 3; console.log(arr); // => [11, 22, 33] //clearing arr.length = 0; console.log(arr); // => [] console.log(arr[2]); // => undefined 几个面试题JS中使用 typeof 能得到的哪些类型? 考点:JS变量类型何时使用 === 何时使用 == ? 考点:强制类型转换window.onload和DOMContentLoaded的区别? 考点:浏览器渲染过程用JS创建10个标签,点击的时候弹出来对应的序号 考点:作用域简述如何实现一个模块加载器,实现类似require.js的基本功能 考点:JS模块化实现数组的随机排序 考点:JS基础算法变量类型和计算题目JS中使用 typeof 能得到的哪些类型? 考点:JS变量类型何时使用 === 何时使用 == ? 考点:强制类型转换JS中有哪些内置函数JS变量按照存储方式区分为哪些类型,并描述其特点如何理解JSON解答undefined、string、number、boolean、object、functionobj.a == null 除了判断null和undefined外,都需要使用全等符数据封装类对象- Object、Array、Boolean、Number、String、Function、Date、RegExp、Error值类型、引用类型JSON 只不过是一个 JS 对象而已, 也是一个数据格式知识点变量类型变量计算变量类型值类型 vs 引用类型typeof 运算符详解变量计算 - 强制类型转换字符串拼接== 运算符if语句逻辑运算原型和原型链题目如何准确判断一个变量是数组类型写一个原型链继承的例子描述new一个对象的过程zepto(或其他框架) 源码中如何使用原型链解答arr instanceof Array // 动物 function Animal() { this.eat = function () { console.log('animal eat') } } // 狗 function Dog() { this.bark = function () { console.log('dog bark') } } Dog.prototype = new Animal() // 哈士奇 var hashiqi = new Dog() // 接下来代码演示时,会推荐更加贴近实战的原型继承示例! 描述new 一个对象的过程创建一个新对象this 指向这个新对象执行代码,即对this 赋值返回 thiszepto (或其他框架) 源码中如何使用原型链阅读源码是高效提高技能的方式但不能 "埋头苦钻" 有技巧在其中慕课网搜索 "zepto设计和源码分析"知识点构造函数构造函数 - 扩展原型规则和示例原型链instanceof原型规则和示例所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了 "null" 以外)所有的引用(数组、对象、函数),都有一个__proto__ (隐式原型)属性,属性值是一个普通的对象所有的函数,都有一个 prototype (显式原型) 属性,属性值也是一个普通对象所有的引用类型(数组、对象、函数), __ proto __ 属性值指向它的构造函数的 "prototype" 属性值 var obj = {}; obj.a = 100; var arr = []; arr.a = 100; function fn () {} fn.a = 100; console.log(obj.__proto__); console.log(arr.__proto__); console.log(fn.__proto__); console.log(fn.prototype) console.log(obj.__proto__ === Object.prototype) 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__ proto __ (即它的构造函数的 prototype) 中寻找。 // 构造函数 function Foo(name, age) { this.name = name; } Foo.prototype.alertName = function () { alert(this.name); } // 创建示例 var f = new Foo('zhangsan') f.printName = function () { console.log(this.name); } // 测试 f.printName(); // zhangsan f.alertName(); // zhangsan var item; for (item in f) { //高级浏览器已经在 for in 中屏蔽了来自原型的属性 //但是这里建议大家还是加上这个判断,保证程序的健壮性 if (f.hasOwnProperty(item)) { console.log(item) } } 原型链 //构造函数 function Foo(name, age) { this.name = name; } Foo.prototype.alertName = function () { alert(this.name); } // 创建示例 var f = new Foo('zhangsan') f.printName = function () { console.log(this.name) } // 测试 f.printName() f.alertName() f.toString() // 要去 f.__proto__.__proto__中查找 instanceof用于判断 引用类型 属于哪个 构造函数 的方法f instanceof Foo 的判断逻辑是:f的 __ proto __ 一层一层往上,能否对应到 Foo.prototype再试着判断 f instanceof Object作用域和闭包-执行上下文题目说一下对变量提升的理解变量定义函数声明(注意和函数表达式的区别)说明 this 几种不同的使用场景作为构造函数执行作为对象属性执行作为普通函数执行call apply bind创建10个标签,点击的时候弹出来对应的序号 // 这是一个错误的写法!!! var i,a for (i = 0; i < 10; i++) { a = document.createElement('a') a.innerHTML = i + '
' a.addEventListener('click', function (e) { e.preventDefault() alert(i) }) document.body.appendChild(a) } // 这是正确的写法!!! var i for (i = 0; i < 10; i++) { (function(i){ var a = document.createElement('a') a.innerHTML = i + '
' a.addEventListener('click', function (e) { e.preventDefault() alert(i) }) document.body.appendChild(a) })(i) }如何理解作用域自由变量作用域链,即自由变量的查找闭包的两个场景实际开发中闭包的应用 // 闭包实际应用中主要用于封装变量,收敛权限 function isFirstLoad() { var _list = [] return function (id) { if (_list.indexOf(id) >= 0) { return false } else { _list.push(id) return true } } } // 使用 var firstLoad = isFirstLoad() firstLoad(10) // true firstLoad(10) // false firstLoad(20) // true 知识点执行上下文this作用域作用域链闭包执行上下文 console.log(a) // undefined var a = 100 fn('zhangsan') // 'zhangsan' 20 function fn(name) { age = 20 console.log(name, age) var age } 范围:一段全局: 变量定义、函数声明 ==一段函数: 变量定义、函数声明、this、arguments ==函数==PS:注意"函数声明"和"函数表达式"的区别thisthis 要在执行时才能确认值,定义时无法确认 var a = { name: 'A', fn: function () { console.log(this.name) } } a.fn() // this === a a.fn.call({name: 'B'}) // this === {name: 'B'} var fn1 = a.fn fn1() // this === window 作为构造函数执行作为对象属性执行作为普通函数执行call apply bind作用域没有块级作用域只有函数和全局作用域 // 无块级作用域 if (true) { var name = 'zhangsan' } console.log(name) // 函数和全局作用域 var a = 100 function fn() { var a = 200 console.log('fn', a) } console.log('global', a) fn() //作用域链 var a = 100 function fn() { var b = 200 // 当前作用域没有定义的变量,即 "自由变量" console.log(a) console.log(b) } fn() 闭包 function F1() { var a = 100 // 返回一个函数 (函数作为返回值) return function () { console.log(a) } } // f1 得到一个函数 var f1 = F1() var a = 200 f1() 函数作为返回值函数作为参数传递异步和单线程什么是异步题目同步和异步的区别是什么?分别举一个同步和异步的例子同步会阻塞代码执行,而异步不会alert是同步,setTimeout是异步一个关于setTimeout的笔试题 console.log(1) setTimeout(function () { console.log(2) }, 0) console.log(3) setTimeout(function () { console.log(4) }, 1000) console.log(5) 前端使用异步的场景有哪些定时任务:setTimeout, setInverval网络请求:ajax请求,动态加载事件绑定知识点什么是异步(对比同步)前端使用异步的场景异步和单线程 // 异步-不阻塞 console.log(100) setTimeout(function() { console.log(200) }, 1000) console.log(300) // 同步-阻塞 console.log(100) alert(200) console.log(300) 何时需要异步在可能发生等待的情况等待过程中不能像 alert 一样阻塞程序运行因此,所有的 "等待的情况" 都需要异步前端使用异步的场景定时任务:setTimeout, setInverval网络请求:ajax请求,动态加载事件绑定 // ajax请求代码示例 console.log('start') $.get('./data1.json', function(data1) { console.log(data1) }) console.log('end') // <img>加载示例 console.log('start') var img = document.createElement('img') img.onload = function(){ console.log('loaded') } img.src = '/xxx.png' console.log('end') // 事件绑定示例 console.log('start') document.getElementById('btn1').addEventListener('click', function() { alert('clicked') }) console.log('end') 单线程执行第一行,打印100执行setTimeout后,传入setTimeout的函数会被暂存起来,不会立即执行(单线程的特点,不能同时干两件事)执行最后一行,打印300待所有程序执行完,处于空闲状态时,会立马看有没有暂存起来的要执行。发现暂存起来的setTimeout中的函数无需等待时间,就立即拿过来执行其他知识题目获取 2017-06-10 格式的日期 function formatDate(dt) { if (!dt) { dt = new Date() } var year = dt.getFullYear() var month = dt.getMonth() + 1 var date = dt.getDate() if (month < 10) { // 强制类型转换 month = '0' + month } if (date < 10) { // 强制类型转换 date = '0' + date } // 强制类型转换 return year + '-' + month + '-' + date } var dt = new Date() var formatDate = formatDate(dt) console.log(formatDate) 获取随机数,要求是长度一致的字符串格式 var random = Math.random() var random = random + '0000000000' // 后面加上10个零 var random = random.slice(0, 10) console.log(random) 写一个能遍历对象和数组的通用forEach函数 function forEach(obj, fn) { var key if (obj instanceof Array) { // 准确判断是不是数组 obj.forEach(function (item, index) { fn(index, item) }) } else { // 不是数组就是对象 for (key in obj) { fn(key, obj[key]) } } } var arr = [1,2,3] // 注意,这里参数的顺序换了,为了和对象的遍历格式一致 forEach(arr, function (index, item) { console.log(index, item) }) var obj = {x: 100, y: 200} forEach(obj, function (key, value) { console.log(key, value) }) 知识点日期Math数组API对象API日期 Date.now() // 获取当前时间毫秒数 var dt = new Date() dt.getTime() // 获取毫秒数 dt.getFullYear() //年 dt.getMonth // 月 (0-11) dt.getDate() // 日 (0-31) dt.getHours() // 小时 (0-23) dt.getMinutes() // 分钟 (0-59) dt.getSeconds() // 秒 (0-59) Math获取随机数 Math.random()数组APIforEach 遍历所有元素every 判断所有元素是否都符合条件some 判断是否有至少一个元素符合条件sort 排序map 对元素重新组装,生成新数组filter 过滤符合条件的元素forEach var arr = [1,2,3] arr.forEach(function (item, index) { // 遍历数组的所有元素 console.log(index, item) }) every var arr = [1,2,3] var result = arr.every(function (item, index) { // 用来判断所有的数组元素,都满足一个条件 if (item < 4) { return true } }) console.log(result) some var arr = [1,2,3] var result = arr.some(function (item, index) { // 用来判断所有的数组元素,只要有一个满足条件即可 if (item < 2) { return true } }) console.log(result) sort var arr = [1,4,2,3,5] var arr2 = arr.sort(function(a, b) { // 从小到大排序 return a - b // 从大到小排序 // return b - a }) console.log(arr2) map var arr = [1,2,3,4] var arr2 = arr.map(function(item, index) { // 将元素重新组装,并返回 return '<b>' + item + '</b>' }) console.log(arr2) filter var arr = [1,2,3,4] var arr2 = arr.filter(function(item, index) { // 通过某一个条件过滤数组 if (item >= 2) { return true } }) console.log(arr2) 对象API var obj = { x: 100, y: 200, z: 300 } var key for (key in obj) { // 注意这里的 hasOwnProperty, 再讲原型链时候讲过了 if (obj.hasOwnProperty(key)) { console.log(key, obj[key]) } } JS-Web-APIJS基础知识: ECMA 262 标准JS-Web-API: W3C 标准W3C标准中关于JS的规定有:DOM操作BOM操作事件绑定ajax请求(包括http协议)存储DOM操作题目DOM 是哪种基本的数据结构?树DOM 操作的常用API有哪些获取DOM节点,以及节点的property和Attribute获取父节点,新增节点,删除节点DOM 节点的Attribute 和 property有何区别property 只是一个JS对象的属性的修改Attribute 是对html标签属性的修改知识点DOM本质DOM节点操作DOM结构操作DOM节点操作获取DOM节点propertyAttribute var div1 = document.getElementById('div1') // 元素 var divList = document.getElementsByTagName('div') // 集合 console.log(divList.length) console.log(divList[0]) var containerList = document.getElementsByClassName('.container') // 集合 var pList = document.querySelectorAll('p') // 集合 // property var pList = document.querySelectorAll('p') var p = pList[0] console.log(p.style.width) // 获取样式 p.style.width = '100px' console.log(p.className) p.className = 'p1' // 获取 nodeName 和 nodeType console.log(p.nodeName) console.log(p.nodeType) // Attribute var pList = document.querySelectorAll('p') var p = pList[0] p.getAttribute('data-name') p.setAttribute('data-name', 'sss') p.getAttribute('style') p.setAttribute('style', 'font-size:28px;') DOM结构操作新增节点获取父元素获取子元素删除节点 // 新增节点 var div1 = document.getElementById('div1') // 添加新节点 var p1 = document.createElement('p') p1.innerHtml = 'this is p1' div1.appendChild(p1) // 添加新创建元素 // 移动已有节点 var p2 = document.getElementById('p2') div1.appendChild(p2) // 获取父元素和子元素 var div1 = document.getElementById('div1') var parent = div1.parentElement var child = div1.childNodes div1.removeChild(child[0]) BOM操作题目如何检测浏览器的类型 var ua = navigator.userAgent var isChrome = ua.indexOf('Chrome') console.log(isChrome) 拆解url的各部分 location.href location.protocol location.pathname location.search location.hash 知识点navigatorscreenlocationhistory // navigator var ua = navigator.userAgent var isChrome = ua.indexOf('Chrome') console.log(isChrome) // screen console.log(screen.width) console.log(screen.height) // location console.log(location.href) console.log(location.protocol) // 'http:' 'https:' console.log(location.pathname) // user/api console.log(location.search) console.log(location.hash) // history history.back() history.forward() 事件题目编写一个通用的事件监听函数描述事件冒泡流程对于一个无限下拉加载图片的页面,如何给每个图片绑定事件知识点通用事件绑定事件冒泡代理通用事件绑定 var btn = document.getElementById('btn1') btn.addEventListener('click', function (event) { console.log('clicked') }) function bindEvent(elem, type, fn) { elem.addEventListener(type, fn) } var a = document.getElementById('link1') bindEvent(a, 'click', function(e) { e.preventDefault() // 阻止默认行为 alert('clicked') }) 关于IE低版本的兼容性IE低版本使用 attachEvent绑定事件,和W3C标准不一样IE低版本使用量以非常少,很多网站都早已不支持建议对IE低版本的兼容性: 了解即可,无需深究如果遇到对IE低版本要求苛刻的面试,果断放弃代理 var div1 = document.getElementById('div1') div1.addEventListener('click', function(e) { var target = e.target if (target.nodeName === 'A') { alert(target.innerHTML) } }) 完善通用绑定事件的函数 function bindEvent(elem, type, selector, fn) { if (fn == null) { fn = selector selector = null } elem.addEventListener(type, function (e) { var target if (selector) { target = e.target if (target.matches(selector)) { fn.call(target, e) } } else { fn(e) } }) } 代理的好处代码简洁减少浏览器内存占用Ajax题目手动编写一个ajax, 不依赖第三方库跨域的几种实现方式知识点XMLHttpRequest状态码说明跨域XMLHttpRequest var xhr = new XMLHttpRequest() xhr.open("GET", "/api", false) xhr.onreadystatechange = function () { // 这里的函数异步执行,可参考之前JS基础中的异步模块 if (xhr.readyState == 4) { if (xhr.status == 200) { alert(xhr.responseText) } } } xhr.send(null) IE兼容性问题IE低版本使用 ActiveXObject, 和W3C标准不一样IE低版本使用量以非常少,很多网站都早已不支持建议对IE低版本的兼容性: 了解即可,无需深究如果遇到对IE低版本要求苛刻的面试,果断放弃readyState0 - (未初始化)还没有调用send()方法1 - (载入)已调用send()方法,正在发送请求2 - (载入完成)send()方法执行完成,已经接收到全部响应内容3 - (交互) 正在解析响应内容4 - (完成) 响应内容解析完成,可以在客户端调用了status2xx - 表示成功处理请求。如2003xx - 需要重定向,浏览器直接跳转4xx - 客户端请求错误,如4045xx - 服务器端错误跨域什么是跨域JSONP服务器端设置 http header (cors)什么是跨域浏览器有同源策略,不允许ajax访问其他域接口跨域条件:协议、域名、端口、有一个不同就算跨域可以跨域的三个标签但是有三个标签允许跨域加载资源 三个标签的场景用于打点统计,统计网站可能是其他域跨域注意事项所有的跨域请求都必须经过信息提供方允许如果未经允许即可获取,那是浏览器同源策略出现漏洞JSONP实现原理加载 http://coding.m.test.com/classindex.html不一定服务器端真正有一个 classindex.html 文件服务器可以根据请求,动态生成一个文件,返回同理于存储题目请描述一下 cookie, sessionStorage 和 localStorage的区别?容量是否会携带到ajax中API易用性知识点cookielocalStorage 和 sessionStoragecookie本身用于客户端和服务器端通信但是它有本地存储的功能,于是就被 "借用"使用 document.cookie = .... 获取和修改即可cookie 用于存储的缺点存储量太小,只有4kb所有http请求都带着,会影响获取资源的效率API简单,需要封装才能用 document.cookie = ....localStorage 和 sessionStorageHTML5专门为存储而设计,最大容量5MAPI简单易用localStorage.setItem(key, value); localStorage.getItem(key);IOS safari 隐藏模式下localStorage.getItem 会报错建议统一使用 try-catch 封装关于开发环境面试官想通过开发环境了解面试者的经验开发环境,最能提现工作产出的效率模块化知识点不使用模块化的情况使用模块化AMDCommonJS运行环境页面加载 - 渲染过程题目从输入url到得到html的详细过程window.onload 和 DOMContentLoaded的区别知识点加载资源的形式加载一个资源的过程浏览器渲染页面的过程加载资源的形式输入url(或跳转页面)加载htmlhttp://coding.m.test.com加载html中的静态资源 加载一个资源的过程浏览器根据DNS服务器得到域名的IP地址向这个IP的机器发送http 请求服务器收到、处理并返回http 请求浏览器得到返回内容浏览器渲染页面的过程答案根据HTML结构生成DOM Tree根据CSS生成 CSSOM将DOM和CSSOM整合形成RenderTree根据 RenderTree 开始渲染和展示遇到 window.onload // 页面的全部资源加载完才会执行,包括图片、视频等 DOMContentLoaded // DOM渲染完即可执行,此时图片、视频还可能没有加载完性能优化原则多使用内存、缓存或者其他方法减少CPU计算、减少网络从哪里入手加载页面和静态资源页面渲染加载资源优化静态资源的压缩合并静态资源缓存使用CDN让资源加载更快使用SSR后端渲染,数据直接输出到HTML中渲染优化CSS放前面,JS放后面懒加载(图片懒加载、下拉加载更多)减少DOM查询,对DOM查询做缓存减少DOM操作,多个操作尽量合并在一起执行事件节流尽早执行操作(如DOMContentLoaded)合并DOM插入 //合并DOM插入 var listNode = document.getElementById('list') // 要插入10个 li 标签 var frag = document.createDocumentFragment() // 不触发DOM操作 var x, li; for (x = 0; x < 10; x++) { li = document.createElement("li") li.innerHTML = "list item " + x frag.appendChild(li) } listNode.appendChild(frag) //触发DOM操作 事件节流 var textarea = document.getElementById('text') var timeoutId textarea.addEventListener('keyup', function () { if (timeoutId) { clearTimeout(timeoutId) } timeoutId = setTimeout(function () { // 触发 change 事件 }, 100) }) 安全性知识点XSS跨站请求攻击XSRF 跨站请求伪造XSS在新浪博客写一篇文章,同时偷偷插入一段攻击代码中,获取cookie, 发送自己的服务器发布博客,有人查看博客内容会把查看者的cookie发送到攻击者的服务器预防前端替换关键字,例如替换 为>后端替换XSRF你已登录一个购物网站,正在浏览器商品该网站付费接口是xxx.com/pay?id=100 但是没有任何验证然后你收到一封邮件,隐藏着 src=xxx.com/pay?id=100>你查看邮件的时候,就悄悄付费。预防增加验证流程,如输入指纹、密码、短信验证码浏览器中的线程GUI渲染线程javascript引擎线程HTTP请求线程浏览器事件触发线程异步APIDOM EventsXMLHttpRequest(ajax)fetchWebSocketsService WorkerTimerPromise异步处理方案异步数据的链式处理异步代码同步化catch 获取异常then 正常结束event-loop事件轮巡异步队列jquery-deferreddtd的API可分成两类,用意不同第一类:dtd.resolve dtd.reject第二类:dtd.then dtd.done dtd.fail这两类应该分开,否则后果严重使用 返回dtd.promise() 解决随意在监听范围调用rejectasync/awaitES7提案then 只是将 callback拆分了async/await 是最直接的同步写法用法使用 await , 函数必须用async标识await 后面跟的是一个Promise实例需要babel-polyfill虚拟dom Virtual Dom问题v-dom 是什么?v-dom 如何运用?介绍Diff算法v-dom 是什么?用js模拟dom结构如果dom有变化,对比出变化的地方,只修改这个变化的地方提高重绘性能JS是前端中唯一:图灵完备语言将DOM对比操作放在JS层,提高效率遇到的问题DOM操作是 "昂贵"的,js运行效率高尽量减少DOM操作,而不是 "推倒重来"项目越复杂,影响越严重vdom即可解决这个问题snabbdom实现vdom的开源库vue2.0借用了snabbdomh函数、patch函数h函数返回v-nodepatch函数将vnode patch到一个真实的dom容器中Diff算法问题什么是diff算法?vdom为何用diff算法?diff算法的实现过程vdom为何使用diff算法答案:DOM操作是 "昂贵"的,因此尽量减少DOM操作找出本次DOM必须更新的节点来更新,其他的不更新这个 "找出" 的过程,就需要diff算法diff算法的实现过程patch(container, vnode)patch(vnode, newVnode)什么是diff算法?是linux的基础命令 diff git diff { tag: 'ul', attrs: { id: 'list' }, children: [ { tag: 'li', attrs: { className: 'item' }, children: ['Item 1'] } ] } MVVM问题如何理解MVVM?如何实现MVVM?是否解读过vue的源码?题目说一下使用jQuery和使用框架的区别?说一下对MVVM的理解?vue中如何实现响应式?vue中如何解析模板?vue的整个实现流程?从jQuery到框架数据和视图的分离,解耦(开放封闭原则)以数据驱动视图,只关心数据变化,DOM操作被封装如何理解 MVVMMVVM - Model View ViewModel三者之间的联系,以及如何对应到各段代码ViewModel的理解,联系View和Modelvue三要素响应式:vue如何监听到data的每个属性变化?模板引擎:vue的模板如何被解析,指令如何处理?渲染:vue的模板如何被渲染成html?以及渲染过程vue中如何实现响应式什么是响应式Object.defineProperty模拟什么是响应式修改data属性之后,vue立刻监听到data属性被代理到vm上问题解答关键是理解 Object.defineProperty将data的属性代理到vm上vue中如何解析模板模板是什么render函数render函数与vdom模板是什么?本质:字符串有逻辑,如v-if v-for等与html格式很像,但有很大区别最终还要转换为html来显示模板最终必须转换成JS代码,因为:有逻辑(v-if v-for),必须用JS才能实现(图灵完备)转换为html渲染页面,必须用JS才能实现因此,模板最终要转换成一个JS函数(render函数)render 函数 - with的用法 var obj = { name: 'zhangsan', age: 20, getAddress: function () { alert('beijing') } } function fn() { with(obj){ console.log(name) console.log(age) getAddress() } } 从哪里可以看到render函数?复杂一点的例子,render函数是什么样子的?v-if v-for v-on 都是怎么处理的?vm._c其实就是snabbvdom的h解答模板:字符串,有逻辑,嵌入JS变量.....模板必须转换为JS代码(有逻辑、渲染html、JS变量)render函数是什么样子的render函数执行是返回vnodeupdateComponent模板中的所有信息都被render函数包含vue的整个实现流程第一步:解析模板成render函数第二步:响应式开始监听第三步:首次渲染,显示页面,且绑定依赖初次渲染,执行updateComponent,执行vm._render()执行render函数,会访问到vm.list和vm.title会被响应式的get方法监听到执行updateComponent,会走到vdom的patch方法patch将vnode渲染成DOM,初次渲染完成为何要监听get,直接监听set不行吗?data中有很多属性,有些被用到,有些可能不被用到被用到的会走到get,没用到就不会走get第四步:data属性变化,触发re-render组件化题目说一下对组件化的理解JSX本质是什么?JSX和vdom的关系说一下setState的过程阐述自己对React和Vue的认识说一下对组件化的理解组件的封装组件的复用组件的封装视图数据变化逻辑(数据驱动视图变化)组件的复用props传递解答组件的封装: 封装视图、数据、变化逻辑组件的复用: props传递、复用JSX本质是什么?JSX语法JSX解析成JS独立的标准JSX语法html形式引入JS变量和表达式if...else循环style和className事件JSX语法根本无法被浏览器所解析那么它如何在浏览器运行?JSX 解析JSX其实是语法糖开发环境会将JSX编译成JS代码JSX的写法大大降低了学习成本和编码工作量同时,JSX也会增加debug成本JSX独立的标准JSX是React引入的,但不是React独有的React已经将它作为一个独立标准开放,其他项目也可用React.createElement 是可以自定义修改的说明:本身功能已经完备;和其他标准兼容和扩展性没问题另:有机会录制>,就用JSX标准问题解答JSX语法(标签、JS表达式、判断、循环、事件绑定)JSX是语法糖,需被解析成JS才能运行JSX是独立的标准,可被其他项目使用JSX和vdom的关系分析:为何需要vdomReact.createElement和h何时patch?自定义组件的解析为何需要vdomvdom 是 React初次推广开来的,结合JSXJSX就是模板,最终要渲染成html初次渲染 + 修改 state 后的 re-render正好符号vdom的应用场景React.createElement和h初次渲染 - ReactDOM.render(,container)会触发patch(container,vnode)re-render - setState会触发patch(vnode,newVnode)自定义组件的解析'div' - 直接渲染 即可,vdom可以做到Input和List,是自定义组件(class),vdom默认不认识因此Input和List定义的时候必须声明render函数根据props初始化实例,然后执行实例的render函数render函数返回的还是vnode对象问题解答为何需要vdom: JSX需要渲染成html,数据驱动视图React.createElement和h,都生成vnode何时patch: ReactDOM.render(...) 和 setState自定义组件的解析:初始化实例,然后执行render说一下 React setState 的过程setState的异步vue修改属性也是异步setState的过程setState的异步 addTitle(title) { const currentList = this.state.list console.log(this.state.list) // ['a', 'b'] this.setState({ list:currentList.concat(title) // 'c' }) console.log(this.state.list) // ['a', 'b'] } setState 为何需要异步?可能会一次执行多次 setState你无法规定、限制用户如何使用setState没必要每次setState 都重新渲染,考虑性能即便是每次重新渲染,用户也看不到中间的效果vue修改属性也是异步效果、原因和setState一样setState的过程答案:每个组件实例,都有renderComponent方法执行renderComponent会重新执行实例的renderrender函数返回newVnode,然后拿到preVnode执行patch(preVnode,newVnode)问题解答setState的异步:效果、原因vue修改属性也是异步:效果、原因setState的过程:最终走到patch(preVnode,newVnode)React vs Vue两者的本质区别看模板和组件化的区别两者共同点总结问题答案前言文无第一武无第二,技术选型没有绝对的对与错技术选型要考虑的因素非常多作为面试者,要有自己的主见和面试官的观点不一致没关系,只要能说出理由两者的本质区别vue - 本质是MVVM框架,由MVC发展而来React - 本质是前端组件化框架,由后端组件化发展而来但这并不妨碍他们两者都能实现相同的功能模板的区别vue - 使用模板 (最初由 angular 提出)React - 使用JSX模板语法上,我更加倾向于JSX模板分离上,我更加倾向于vue组件化的区别React本身就是组件化,没有组件化就不是Reactvue也支持组件化,不过是在MVVM上的扩展查阅vue组件化的文档,洋洋洒洒很多(侧面反映)对于组件化,我更加倾向于React,更加清晰两者共同点都支持组件化都是数据驱动视图问题解答国内使用,首推vue。文档更易读、易学、社区够大如果团队水平较高,推荐使用React。组件化和JSXhybrid移动端占大部分流量,已经远远超过PC一线互联网公司都有自己的App这些App中有很大比例的前端代码(不要惊讶)拿微信举例,你每天浏览微信的内容,多少是前端?题目hybrid是什么,为何用hybrid?介绍一下hybrid更新和上线的流程?hybrid和h5的主要区别前端JS和客户端如何通讯?hybrid是什么,为何会用hybrid?hybrid 文字解释存在价值,为何会用hybridwebviewfile:// 协议hybrid实现流程hybrid 文字解释hybrid 即 "混合",即前端和客户端的混合开发需前端开发人员和客户端开发人员配合完成某些环节也可能涉及到server端PS:不要以为自己是前端就可以不理会客户端的知识hybrid存在价值可以快速迭代更新「关键」(无需app审核,思考为何?)体验流畅(和NA的体验基本类似)减少开发和沟通成本,双端公用一套代码webview是app中的一个组件(app 可以有webview,也可以没有)用于加载H5页面,即一个小型的浏览器内核file:// 协议其实在一开始接触html开发,就已经使用了file协议只不过你当时没有 "协议" "标准"等这些概念再次强调 "协议" "标准"的重要性!!!file协议: 本地文件,快http(s)协议: 网络加载,慢hrbrid 具体实现不是所有场景都适合使用hybrid:使用NA: 体验要求极致,变化不频繁 (如头条的首页)使用hybrid: 体验要求高,变化频繁 (如头条的新闻详情页)使用h5: 体验无要求, 不常用 (如举报、反馈等页面)实现前端做好静态页面 (html js css), 将文件交给客户端客户端拿到前端静态页面,以文件形式存储在app中客户端在一个webview中,通过file协议加载前端文件(html js css)具体实现 - 遗留问题app发布之后,静态文件如何实时更新?静态页面如何获取内容?问题解答hybrid 是客户端和前端的混合开发hybrid 存在的核心意义在于快速迭代,无需审核hybrid实现流程(图),以及webview和file协议hybrid 更新上线流程思考 (目的,可行途径)要替换每个客户端的静态文件只能客户端来做 (客户端是我们开发的)客户端去server下载最新的静态文件我们维护server的静态文件完整流程分版本,有版本号, 如201803211015将静态文件压缩成zip包,上传到服务端客户端每次启动,都去服务端检查版本号如果服务端版本号大于客户端版本号,就去下载最新的zip包下载完之后解压包,然后将现有文件覆盖问题解答掌握流程图要点1:服务端的版本和zip包维护要点2:更新zip包之前,先对比版本号要点3:zip下载解压和覆盖hybrid和h5的比较优点体验更好,跟NA体验基本一致可快速迭代,无需审核缺点开发成本高。联调、测试、查bug都比较麻烦运维成本高。参考此前讲过的更新上线的流程适用的场景hybrid: 产品的稳定功能,体验要求高,迭代频繁h5: 单次的运营活动(如 xx 红包) 或不常用功能问题解答优点:体验好,可快速迭代缺点:开发成本高,运维成本高适用的场景:hybrid适合产品型,h5适合运营型JS和客户端通讯回顾之前遗留的问题JS和客户端通讯的基本形式schema协议简介和使用schema使用的封装内置上线之前遗留的问题新闻详情页适用hybrid,前端如何获取新闻内容?不能用ajax获取。第一 跨域,第二 速度慢客户端获取新闻内容,然后JS通讯拿到内容,再渲染JS和客户端通讯的基本形式JS访问客户端能力,传递参数和回调函数客户端通过回调函数返回内容schema 协议简介和使用之前介绍了http(s) 和 file协议schema协议 - 前端和客户端通讯的约定 /* 网上搜的微信的部分 schema 协议 */ weixin://dl/scan 扫一扫 weixin://dl/feedback 反馈 weixin://dl/moments 朋友圈 weixin://dl/settings 设置 内置上线将以上封装的代码打包,叫做invoke.js 内置到客户端客户端每次启动webview,都默认执行invoke.js本地加载,免去网络加载的时间,更快。本地加载,没有网络请求,黑客看不到schema协议,更安全问题解答通讯的基本形式:调用能力,传递参数,监听回调对schema协议的理解和使用调用schema代码的封装内置上线的好处:更快、更安全如何热爱编程?热爱!怎么证明?如何证明你热爱编程?看书写博客做开源看书 - 手下不离书构建知识体系的最好方式自己买书,不要借书看书有技巧博客 - 合格程序员的必备开源 - github 的 star 是硬通货 ## Publication Information - [MarkTang's Blog](https://paragraph.com/@marktang-s-blog/): Publication homepage - [All Posts](https://paragraph.com/@marktang-s-blog/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@marktang-s-blog): Subscribe to updates - [Twitter](https://twitter.com/MarkTan43749744): Follow on Twitter