# Interview **Repository Path**: gitofcjf/Interview ## Basic Information - **Project Name**: Interview - **Description**: 面试题 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-08-30 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [TOC] ## 面试题 *** ### vue双向绑定的原理 vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式实现的 #### 数据劫持 对象中的数据改变时可以通过`Object.defineProperty()`设置的setter和getter来劫持,原理其实和Ed6中的proxy原理很相似 #### 思路分析 对于mvvm模式 view的data改变 --> 可以通过监听事件来实现 而对于 data更新 --> view的data 我们可以使用`Object.defineProperty()`的setter和geeter来劫持数据 然后执行更新view的方法 ![思路分析](img/1.png) #### 实现过程 我们需要一个Observer来监听数据,然后去通知Watcher,由于Watcher很多,所以我们还得设置一个dep消息订阅器将所有Watcher进行管理,也作为Observer和Watcher之间的一个桥梁。 1. 实现一个监听器Observer,用来劫持并监听数据变化,若有变动就通知订阅者 2. 订阅者Watcher,可以收到属性的变化,并执行相应的函数,从而更新视图 3. 解析器Compiler,可以扫描和解析每个节点的相关指令,将有指令的子节点都解析成watcher *** ### react和vue有什么不同 1. 都是用了虚拟dom,缩小页面更新成本 2. 组件化的ui,开发效率高 3. vue是双向数据绑定:开发效率高。react是单向数据绑定:父组件可以通过props传递属性给子组件,而子组件只能通过父组件传递进来的方法对父组件的属性进行修改。对于大型项目,对数据流动很多而且复杂的项目,单向数据绑定的好处就体现了。 *** ### let和const的区别 1. let声明的变量可以改变 而const的不能改变 2. let一般局部变量 *** ### es6的特性 let const 箭头函数 set map weakmap proxy class 模块化 promise 结构赋值 函数的参数扩展 数组:json数组 字符串转数组(Array.of()比eval()高效) find() fill() for..of循环(三种方式) entries()能一个一个遍历 forEach()跳过空元素 filter()跳过空元素 some()寻找某一个 map()复制出一个新的数组 join()插入字符形成字符串 toString()相当于join(',') *** ### es6数组的操作 1. json数组转化为数组 -> Array.from(json) 2. 将变量转化为数组 -> Array.of(1,2,3,4) 3. 找出数组中符合匿名函数的元素 -> Array.find(function(){}) 4. 指定位置填充元素 -> Array.fill(元素,开始位置,结束位置) 5. 遍历数组 -> for of 6. 遍历数组的索引 -> for(let key of arr.keys()) 7. 遍历数组的元素 -> for(let key of arr.values()) *** ### 浏览器原生开始支持module module分为静态的和动态的 *** ### npm ##### dependencies 用来控制包的版本范围 ##### devDependencies 这类开发中使用的依赖,别人若是npm install就不会被下载 #### npm install module - 会把module包安装到node_modules中 - 不会修改package.json - 之后运行npm install,不会自动安装module包 #### npm install module --save - 安装到node_modules - 修改package.json - 运行npm i买命令时,会自动安装module包 #### npm install module --save-dev `npm install module --save` 把包的信息存入package.json的devDependencies中 npm install 不会被安装 *** ### webpack和gulp的区别 gulp:自动化网页刷新,css预处理,代码检测,图片压缩。 webpack:模块打包工具,分析模块之间的依赖关系,合并成一个静态资源 *** ### webpack打包慢 原因和解决方法 模块太多 webpack可以配置 externals 来讲依赖的库指向全局变量,从而不再打包这个库 webpack-parallel-uglify-plugin 并行压缩 CommonsChunkPlugin 提取公共模块 happyPack 多进程构建 *** ### http响应中的content-type包含哪些内容 1. 消息主体用的是什么编码方式 `application/x-www-form-urlencoded` 2. 服务器告诉浏览器消息是以json格式传递的:`application/json` *** ### 浏览器缓存有哪些 通常缓存有那几种方式 优点 1. 减少请求 2. 减少服务器压力 3. 加快加载速度 浏览器从第一次请求中获取header信息 强缓存:强缓存若命中,浏览器直接从缓存中读取资源,不发送请求 协商缓存:若强缓存没命中,那么浏览器会将header信息发送给服务器,服务器它决定这个资源是否需要即是否命中协商缓存,如果命中,服务器返回304,让浏览器从缓存中获取,不返回资源。若没命中,那么将资源返回客户端,并更新浏览器本地缓存 #### 强缓存 强缓存是通过http返回头红的Expires或者Cache-Control两个字段来控制的,用来表示资源的缓存时间 ##### Expires 该字段是http1.0的规范,他的值为一个代表资源的失效时间,当服务器时间和浏览器时间偏差大会出现问题 ##### Cache-Contrl `Cache-Control:max-age=3600` 代表着资源有效时间为3600秒 - no-chache:不使用本地存储,使用协商存储 - no-store:禁止浏览器缓存数据 - public:可以倍所有用户缓存,包括终端用户和CDN中间服务器 - private:智能被终端用户浏览器缓存,不允许CDN服务器缓存 ##### Chache-Control与Expires可以在服务器端配置的同时启用,而Cache-Control级别更高 #### 协商缓存 协商缓存就是服务器确定缓存资源是否可用 主要通过以下两对标识来决定 ##### Last-Modify/If-Modify-Since 浏览器第一个请求一个资源的时候,服务器返回的header会加上Last-Modify,Last-Modify是一个时间,标志该资源最后的修改日期, 当浏览器再次请求该资源的时候,request的请求头会包含If-Modified-Since 值为Last-Modify,服务器根据这个值决定是否可以继续用这个资源 还是返回新的资源 若命中返回304 否则返回新资源 ##### ETAg/If-NONe-Match 与Last-Modified/If-Modified-Since不同的是,ETag返回一个校验码,保证每个资源是唯一的,资源的变化都会导致ETAg变化 服务器In-None-Match是否为true来决定是否返回新资源 ##### 在Last-Modify/If-Modify-Since的基础上ETag主要解决这类问题 - 一些文件周期性改变,但是内容不改变,服务器会认为要修改,但是我们不想他改变 - 某些文件修改频繁,Last-Modify/If-Modify-Since是以秒为级别的,但修改频率过高,Last-Modify/If-Modify-Since不变化,我们不期望这样 ###### 一般优先验证ETag *** ### 平时怎么学习新技术的 掘金 简书 慕课 infoq *** ### node和koa使用如何 koa是比express还要轻量级,健壮的web框架 不需要回调 而是采用中间件之间不断的调用来实现回调`next()` *** ### 模块化加载时 加载顺序是怎么样的 common.js 同步顺序执行 AMD 提前加载,直接提前加载,有可能浪费资源 require.js 速度快 有可能浪费资源 CMD 提前加载,在真正使用该模块式才加载 seajs 按需加载 性能比AMD差 *** ### 介绍一下闭包和闭包常用场景 指有权访问别的作用域中变量的函数。一般用于函数内创建另一个函数 不适用场景:不适合闭包函数是个非常大的函数 闭包会常驻内存,消耗比较大 *** ### 为什么会出现闭包这东西,解决了什么问题 由于作用域链的存在,子级变量无法访问父级变量的,所以有了闭包的概念,现在可以用箭头函数解决 *** ### 作用域链是什么 作用域链的尽头是什么 比如函数嵌套函数,每个函数内部有一个作用域,函数会先从自己的作用域里面查找变量,若是找不到就会沿着作用域链往上查找。 *** ### 一个Ajax建立的过程是怎么样的,主要用到哪些状态码 ajax:异步请求 1. 穿件XMLHttpRequest对象, 2. 创建HTTP请求,指定请求方式,url 3. 设置响应HTTP请求状态变化的函数 4. 发送HTTP请求 5. 获取异步调用的数据 6. 更新DOM ```javascript var xmlHttp = new XMLHttpRequest(); xmlHttp.open('GET','demo.php','true'); xmlHttp.send() xmlHttp.onreadystatechange = function(){ if(xmlHttp.readyState === 4 & xmlHttp.status === 200){ } } ``` 当前状态readystate 0 初始化 --> new 1 正在加载 --> open 2 加载完成 --> send 3 交互中 --> 等待服务器响应 4 响应发送完毕 --> 接受到服务器数据 常见状态码 1. 404 没有找到页面 2. 403 禁止访问 3. 500 内部服务器出错 4. 200 ok 5. 304 协商缓存 即使用本地缓存 *** ### 状态码的存在解决的什么问题 302/307 临时重定向 301 永久定向 借助状态码,浏览器知道响应是什么状态,以及服务器的状态 *** ### 语义化是什么 html5语义化相当多, 布局类:header footer section nav article time --> 时间类 keygen --> 加密秘钥 process --> 进度条 语义化能重新看代码的时候 结构很清晰。便于和他人沟通,计算机能更高效处理标签内容 *** ### content-box 和 border-box的区别 使用场景 以border为判断,若增加了padding 而content随之变大 同时border被撑大 那么就是content-box 符合正常思维 若增加了padding 而content却没变大 border也不变 那么就是border-box 较为好用 content-box:W3c标准盒子模型 元素宽度 = 内容宽度 + padding + border border-box:ie盒子模型 元素宽度 = 内容宽度 有时候在添加border或者padding时会破坏布局 那么就需要判断使用border-box 还是 content-box *** ### 使用babel将es6语法降低为es5 1. 全局安装babel-cli 2. 项目依赖安装babel-preset-es2015 3. 创建.babelrc,并写入 ```javascript [ "presets":[ "es2015" ], "plugins":[ ] ] ``` 4. 执行babel 源文件路径 -o 目标文件路径 5. 可以使用npm脚本简化命令 *** ### HTML5的新特性 1. 3D属性的支持 2. 多媒体标签 3. 语义化标签(布局等) 4. 本地存储 5. canvas 6. 新的事件 *** ### 在一个ul上有10个li,实现点击li,打印对应的index 方法:我们利用事件冒泡的机制,将点击事件加载ul上,匹配li即可 好处: 1. 减少代码编写、减少内存占用 2. 这样便不需要理会li的个数的操作 *** ### 三个div占整行并且均分 单纯的设置行内块并且width:33.3%是不行的,原因是每块之间有间距(此间距非margin非padding非border) 1. float:left 2. flex布局 *** ### js的垃圾清除机制 1. 标志清除 2. 引用计数 *** ### 关于堆栈 stack是自动分配的内存空间,系统自动释放空间,堆是动态分配的空间,需主动释放空间。 系统会先从栈中获取对象的地址指针,然后堆中获取. *** ### 函数防抖与函数节流 函数防抖:在一个频繁触发的场景内,如何让函数执行的次数变少 方法:而场景内就1.清除定时器 2.重启定时器任务。这样我们就能通过设定时间来减低函数执行的频率 场景:频繁的场景,比如输入验证等。 函数节流:一个函数一定时间内只能执行一次 方法:通过定时器或者一个标志位来实现 场景:非常频繁的场景,比如动画、手势等 *** ### N个数里面找到第N大的数 快排之后找即可 *** ### 两栏布局 1. 浮动 2. flex *** ### cookie session 都是用来记录用户操作或者信息的 cookie存储在客户端、session存储在服务器 session比cookie安全、cookie存储的信息不多4k *** ### 关于this this指向的是当前运行环境变量 每个函数的执行都有其对应的作用域、在这作用域内有其局部的属性和方法,this所指向的就是这个作用域。 *** ### 编写一个快速排序 递归方式 ```javascript var quickSort = function(arr){ // 递归终止条件 if(arr.length<=1){ return arr } let left = [] let right = [] let mid = arr.splice(Math.floor(arr.length/2),1) for(let i=0;imid?right.push(arr[i]):left.push(arr[i]) } return quickSort(left).concat(mid,quickSort(right)) } ``` *** ### 如果让一个div从左上角到右下角 考点:有哪几种方式实现动画 1. 定位,改变left和top 2. jquery的animate 3. c3的transition *** ### promise 将嵌套回调以一种更加清晰的方式编写 有三种状态pending、resolved、rejected 通过then和catch来实现同步编程 *** ### 关于原型链 类似于父子关系的继承 js中所有变量都是对象,继承自object,所以原型链的尽头是null 而对象分两个:函数对象和普通对象。函数对象通过``new function(){}``来创建 每个对象都有自己对应的原型 函数对象的原型为prototype属性,其__proto__属性指向的是其原型链上的父级。 普通对象没有prototype属性,但是有原型,可以通过孩子的__proto__来设置。 ```javascript // 函数对象 var parent = function(name){ this.name = name; } parent.prototype.getName = function(){ return this.name } var son = new parent("huahua"); console.log(son.__proto__ === parent.prototype) // son自身的原型上找不到getName的方法,便会去原型链上的父级去寻找 console.log(son.getName()) // 普通对象 var temp = {} temp.__proto__.name = '123' console.log(temp.name) ``` ![avatar](./img/_proto_.jpg) 请区分构造函数和原型 ```javascript // 这个是构造函数 var temp = function(name){ this.name = name } // 给temp原型设置属性 temp.prototype.age = 10 ``` *** ### 6种继承方式 原型继承 ```javascript var Person = function(name){ this.name = name } Person.prototype.age = 10 // 原型链继承 var Per = function(){ this.name = 'Per' } Per.prototype = new Person() var Per1 = new Per() console.log(Per1.name) console.log(Per1.age) // instanceof 查看per1是否是Person的实例 console.log(Per1 instanceof Person) ``` 优点:新实例会继承``实例的构造函数属性``、``父级构造函数的属性``、``父级的原型`` 缺点:1.没办法想父级构造器传递参数 2.属性依赖于唯一原型 3.只能继承一个 借助构造函数继承 ```javascript var Person = function(name){ this.name = name } Person.prototype.age = 10 // 构造函数继承 var Per = function(){ // 所以本质是复制父级构造函数 伪继承 Person.call(this,'ker') this.age = 12 } var Per1 = new Per() console.log(Per1.name) // ker console.log(Per1.age) // 12 // instanceof 查看per1是否是Person的实例 console.log(Per1 instanceof Person) // false ``` 优点:1.解决了原型链继承的三个缺点 2.可以传参数 3.可以继承多个父类 缺点:1.只继承父级的构造函数没有原型 2.无法实现父类构造函数的复用 组合继承(原型链继承以及构造函数继承组合) 常用 ```javascript var Person = function(name){ this.name = name } Person.prototype.age = 10 // 组合继承 var Per = function(name){ // 所以本质是复制父级构造函数 伪继承 Person.call(this,name) } Per.prototype = new Person() var Per1 = new Per('ker') console.log(Per1.name) // ker console.log(Per1.age) // 10 // instanceof 查看per1是否是Person的实例 console.log(Per1 instanceof Person) // true ``` 优点:1.比较正统的继承方式 2.每个新实例引入的构造函数的属性都是私有的 缺点:1.调用的两次父级的构造函数,消耗内存。 2.子类的构造函数会覆盖父级的构造函数 原型式继承 ```javascript var Person = function(name){ this.name = name } Person.prototype.age = 10 // 原型式继承 var content = function(obj){ // 所以本质是复制父级构造函数 伪继承 function F(){} F.prototype = obj return new F() } var temp = new Person('ker') var temp1 = content(temp) temp1.height = 18 console.log(temp1.name) // ker console.log(temp1.age) // 10 console.log(temp1.height) // 18 // instanceof 查看per1是否是Person的实例 console.log(temp1 instanceof Person) // true ``` 优点:制作一个对象来作为原型 缺点:所有实例都会继承这个对象的属性 缺点:无法实现复用(属性都是后来加上去的) 寄生类继承 ```javascript var Person = function(name){ this.name = name } Person.prototype.age = 10 // 寄生式继承 var content = function(obj){ // 所以本质是复制父级构造函数 伪继承 function F(){} F.prototype = obj return new F() } var temp = new Person() var subContent = function(obj){ let temp = content(obj) temp.name = 'ker' return temp } var temp1 = subContent(temp) console.log(temp1.name) // ker console.log(temp1.age) // 10 // instanceof 查看per1是否是Person的实例 console.log(temp1 instanceof Person) // true ``` 就是在原型式继承上套一层函数 寄生组合式继承 寄生:在函数内部返回对象再使用 组合:1. 函数的原型等于另一个实例 2. 在构造函数内调用父级构造函数 ```javascript var Person = function(name){ this.name = name } Person.prototype.age = 10 // 寄生组合继承 // 寄生 通过函数包装 返回一个结果的原型是输入的函数对象 var content = function(obj){ // 所以本质是复制父级构造函数 伪继承 function F(){} F.prototype = obj return new F() } var temp = content(Person.prototype) // 组合 在子类中调用父类的构造函数 var Per = function(){ Person.call(this) } Per.prototype = temp var temp1 = new Per() console.log(temp1.age) // 10 // instanceof 查看per1是否是Person的实例 console.log(temp1 instanceof Person) // true ``` 优点:在组合继承的基础上,修复了其问题 *** ### 深拷贝和浅拷贝 浅拷贝:拷贝地址,指向的内存地址的一样的 深拷贝:将属性完全拷贝形成另一个内存地址不一样的变量 深拷贝方法: 1. 递归 ```javascript function clone(arr){ let arrClone = Array.isArray(arr)?[]:{} for(key in arr){ // key是属性名字 是string类型 // 属性是对象 arr[key]&&typeof arr[key] === 'object'?arrClone[key] = clone(arr[key]):arrClone[key] = arr[key] } return arrClone } let arr = { name:1, age:18, temparr:[1,2,3] } console.log(clone(arr)) ``` 2. 借助Json的parse和stringfy 3. Jquery的extend( [deep ], target, object1 [, objectN ] ) *** ### 判断数组的方法 1. ``a instanceof Array`` 2. ``a.contructor === Object `` 3. ``Object.prototype.toString.call(a) === '[Object Array]'`` 4. Es6``Array.isArray()`` *** ### 关于ToString方法和Object.prototype.toString.call(a) Object对象自带toString方法,返回的是参数的类型,比如`[Object Array]` 而继承自Object的实例都会将toString方法进行改写,所以对实例使用 toString是没办法知道该实例的类型。 所以我们得用Object的原型的toString方法来判断实例的什么类型 *** ### ES6基础数据类型 - number - boolean - Object (null 也是 Object类型) - function - symbol - string - undefinded 而typeof就是用来判断一个数据是属于什么类型的 因为null的存在,所以typeof没办法判断一个数据是否是Object *** ### 跨域通信解决方案 由于同源策略的存在,XmlHttpRequest只允许请求当前源,但是script标签没有这个限制 1. Jsonp:在script标签的url中添加预置好的函数名作为参数去请求,服务器会返回一段js代码(其中要交互的数据会放在传输的参数即函数名内),在客户端,执行该js代码便会和预置好的函数相契合,达到跨域通信的功能。 好处:兼容性好 坏处:只能get,而且冗余 2. 通过服务器设置Access-Control-Allow-Origin来允许跨域 3. websocket *** ### 多页面通信有哪些方案,各有什么不同 1. websocket 2. localStorage 当`非当前页面`对localstorage进行修改时,当前页面的storage事件便会触发。 ``window.addEventListener('storage',(e)=>{consolelog(e)})`` 3. sharedWorker webWork:是H5浏览器新出的,它允许js线程在浏览器上在产生一个线程去干别的事情(计算等),而不影响js线程。 sharedWebWorker:是webWorker其中一种。 - 它能被多个window使用 - worker.js放在服务器,webwork无法再本地使用 - worker.js作为中间件,负责消息的接受和发送 服务器端 ```javascript // sharedWorker所用到的j文件,放在服务器即可 let data = '' // 客户端连接了 onconnect = function(e){ let port = e.ports[0] // 接收到消息 port.onmessage = function(e){ // 如果消息是get 就将data发送回去,否则将传递过来的数据放入data e.data === 'get'?port.postMessage(data):data=e.data } } ``` 客户端 ```javascript let worker = new SharedWorker('work.js') worker.port.addEventListener('message',(e)=>{ console.log('来自worker的数据:'+e.data) },false) worker.port.start() // 将worker注册在window对象上 window.worker = worker // 获取和发送消息都是调用postMessage方法 window.worker.port.postMessage('get') window.worker.port.postMessage('发送消息给worker') ``` 特点:websocket的编写过于复杂,sharedWorker无法在本地执行,localstorage就比较方便了。 *** ### 文件上传 使用 koa-body ```javascript const upload = async(ctx,next)=>{ // 获取文件 const files = ctx.request.files.file; for(let file of files){ // 创建可读流 // file.path 文件在服务器本地的地址 const reader = fs.createReadStream(file.path) let filePath = path.join(__dirname,'public/upload/') + `${file.name}` // 创建可写流 const upStream = fs.createWriteStream(filePath) // 可读流通过管道写入可写流 reader.pipe(upStream) } return ctx.response.body = '上传成功' } ``` *** ### xss和csrf xss:Cross-site scripting(跨站脚本) 1. 反射型:他们不知情的情况下,通过点击链接访问黑客设定好的网站(或端口),即是反射型xss 2. 储存型:通过在留言板的地方写入javascript脚本保存在服务器,然后他人访问改留言便会执行该script脚本对cookie等信息获取。 方法:对输入来源进行检测,比如模式替换等。 csrf:Cross-site request forgery(跨站伪造请求),script脚本或者链接去触发请求端口。 方法:验证码token来源 两者的区别是目的不一样 *** ### margin负值的原理和应用 [margin负值原理和应用](https://www.jianshu.com/p/487d131537b4) *** ### 圣杯布局和双飞翼布局 特点:两侧宽度固定,中间宽度任意 本质上都是marin负值在浮动上的应用 - 圣杯布局 1. 将center、left、right放入container中,center为首位,container设置padding-left和padding-right 2. center、left、right设置左浮动, 3. left设置margin-left:-100%,然后相对定位,right:padding-left的值 4. right设置margin-right:-padding-right的值 5. 布局最小宽度,因为用了定位,宽度 = padding-left + padding-right + padding-left 6. float元素具有包裹性,即如果不设置宽度,那么元素宽度就是内容的宽度 - 双飞翼布局 1. 将center放入container中,left和right与container同级。 2. center设置margin-left和margin-right,container、left、right都设置左浮动 3. left设置margin-left:-100% 4. right设置:margin-left:margin-right *** ### offsetHeight、scrollHeight、clientHeight的区别 offsetHeight:clientHeight + borderWidth*2 scrollHeight:内容可见部分和不可见部分高度 clientHeight:内容可见部分高度 + paddingVertical ![区分](./img/区分.png) *** ### 垂直居中 单行行内元素垂直居中:line-height或者padding 多行行内元素垂直居中:1.table-cell布局 + vertival-align:middle 2. flex布局 块级元素: 1. 知道宽高:定位 + 负边距 2. 不知道宽高:tranform:translateX(-50%) 3. flex:justify-contents:center;align-items:center; *** ### transition的属性值和应用 属性值:时间 属性值 延迟 变换函数 应用:C3动画 *** ### px rem和em的区别 px:是每个设备上一个像素单位 em:针对字体大小的单位,当前元素的em单位的换算继承自父元素。浏览器一般是1em = 16px,当我们在body上设置font-size:62.5%之后,元素的字体大小都是1em = 10px,这样我们就能将px/10然后单位换成em了。 rem:C3新增的相对单位,继承自根节点,通过这个单位我们直接在根节点设置就可以全局控制。 *** ### 严格模式的特点 1. 变量得提前声明 2. 函数定义得在顶部,而不是随便在某个地方定义在运行 3. 'use strict'字段得放在开头(解析的开始),所以一般用自执行函数来包裹较好。 4. this不在指向全局的对象(window),而是undefined 本质上就是越来越规范,这点ts做的很好。 目的: 1. 消除javascript语法的不合理,不严谨的地方 2. 消除代码运行的不安全处 3. 提高执行效率 4. 为未来的javascript版本做好准备(比如新变量的声明) *** ### js原型链 通过prototype和__proto__来不断往原型链的尽头走 *** ### 关于图片预加载和懒加载 预加载:预先进行图片的加载、提升体验 ```javascript function preLoad(url){ // 如果图片已经在浏览器,执行的操作 var img = new Image() img.src = url // 图片是否在缓存中 // 如果在缓存这种就不load if (img.complete) { console.log('图片在缓存中') return } // 图片加载完之后执行的操作 img.onload = function(){ console.log(img.width) return } } preLoad("http://129.28.168.85/img/cosers/2.png") ``` 懒加载:加快加载速度、节省流量 ```javascript var flag = true var documentClientHeight = document.documentElement.clientHeight // 图片懒记载 function imgLazyLoad(documentClientHeight){ let imgs = document.getElementsByClassName('lazyImg') for (let i = 0; i < imgs.length; i++) { // 如果图片进入到窗口+100的边界 并且 图片的src属性为'' 则加载 if (imgs[i].getBoundingClientRect().top 构建渲染数 -> 计算盒子模型大小和位置 -> 绘制渲染数 6. 下载静态资源 *** ### UMD规范、CommonJs、Amd、Cmd、ES6 CommonJs:是偏向于服务器的规范,通过require加载模块、export输出模块 Amd(预加载、异步)、Cmd(懒加载):()都是加载用的规范,Amd必须在文件开头引入依赖、Cmd可以在任何地方引入依赖 Umd规范:Amd和CommonJs的混合,在写一个文件时,我们得判断该环境支持的是export还是define Es6:通过export、import来引入加载模块 #### es6和commonJs的区别; es6:import Module from '' commonJs:var Module = require '' AMD:define(['./MYMode.js],function(Module){ }) *** ### http请求头 请求类型有:get、post、put、delete、options options:返回服务器针对特定资源所支持的HTTP请求方法 - Accept:浏览器可以接受的MiMe类型 - Accept-Encoding:浏览器接受的解码方式,比如gzip - Accept-charset:浏览器支持的字符集 - Connection:是否保持长连接、这个对于需要加载多图片的页面有用,短时间内不需要多次建立http链接。 - Cookie:发送的cookie - If-Modified-Since:在文档有效期内,服务器的页面更新了才会返回``304 Not Modified``表示没有更新文档 - Host:主机地址和端口 - User-Agent:浏览器类型 *** ### http响应头 - Connection:服务器是否应答了长链接 - Content-Encoding:返回内容的编码方式 - Content-type:返回内容的类型 - Date:这个可以用于矫正浏览器的标准时间 - Expires:服务器希望这个报文什么时候失效 - Set-Cookie:设置cookie - Last-Modified:文档最近改变的日期 *** ### nginx 1. 反向代理 2. 分布集群 3. 均衡负载 *** ### 框架问题 MVVC和MVC的区别:MVC是model-view-control结合体,model(服务器获取的数据)的数据对应view中的数据,但是view想获取/修改model中的数据,得通过controller来实现。而MVVC是model-view-viewModel的结合体,view的数据对应于viewModel的数据,view可直接修改/获取viewModel中的数据,而model用于修改viewModel。 子组件向父组件传递信息:vue是父组件先自义定一个函数,然后子组件通过$emit来触发全局的对应名字的函数;而react是父组件将方法传递给子组件,子组件直接调用即可。 兄弟之间如何通信:vue是通过vuex;react可以通过redux或者子->父->子 生命周期函数:vue的生命中周期函数 beforeCreated->created->beforeMount->mounted->updated->destroyed; react的生命周期: - 初始化 + getDefaultProps + getInitialState + componentWillMount + render 组件开始渲染渲染 + componentDidMount 组件渲染完成 - 运行中 + shouldComponentUpdate + ComponentWillUpdate + render + componentDisUpdate - 销毁 + componentWillUnmount + componentUnmount *** ### Redux 1. Store:全局的store放在最高层组件 2. Action:对store中的数据进行的操作 3. reducer:根据Action,将state更新,并返回一个新的state 4. state更新完后,执行subscribe()进行页面刷新 5. 子组件听过dispatch来发出Action *** ### 清除浮动 浮动元素:父元素的宽高计算是不会将浮动元素计算在内的,也就说说子浮动元素可能会超出父元素方位,并影响其他区域的布局。此时我们便希望浮动元素控制在父元素宽高内。 clear:通过在父元素的最下面块级元素/添加一个高度为0的块级元素/或者伪类after 设置clear:both(不允许该元素的左右存在浮动元素) 让父元素形成BFC:BFC区域的元素会将浮动元素的宽高计算在内。有两种方式实现:1. overflow:auto 2. display:inline-block ```html
float left
...
...
``` ```less .topDiv { width: 500px; padding: 4px; border: 2px solid black; // 让父元素 形成BFC区域(块级格式上下文)的两种常见方式 // overflow: auto; // 下面的方式可能有副作用 不推荐 // display: inline-block; } .floatDiv { width: 100px; height: 100px; border: 2px dotted red; color: red; margin: 4px; float: left; } .bottomDiv { width: 500px; height: 100px; margin: 5px 0; border: 2px dotted black; clear: both; } .textDiv { color: blue; border: 2px solid blue; // 让该元素的指定方向不存在浮动元素 // clear: left } // 通过伪类来形成底层块级元素 // .clearFix:after { // content: ''; // // 必须是块级元素 // display: block; // clear: left // } ``` *** ### 前端性能优化 1. 减少http请求:sprite图片、合并js和css文件 2. 使用缓存:外部js、css文件不易失效,使用expires、cache-control 3. 压缩资源:gzip、js、css 4. 代码层:样式表在头、脚本低(先让用户看到页面) *** ### 事件模型和事件代理 事件的三个阶段:事件捕获、目标、事件冒泡 w3c绑定事件:target.addEventListener(event,handle,false) 解绑:target.removeEventListener(event,handle,false) 事件代理的优点: 1. 节省内存 减少事件注册 比如在一个table上代理所有的td的事件 2. 允许动态添加子元素 *** ### 正则表达式 #### 书写规则 ```js // 字面量写法 书写简单、但不支持变量 let re = /abc/ // 构造函数形式 支持变量、但是要注意字符转义规则 let re2 = new RegExp('abc') ``` #### 元字符 \d:匹配数字 \w:匹配数字、字母、下划线 \s:查询空白(包括空格、制表符等)字符 \D:查找非数字 \W:匹配除了数字、字母、下划线 \S:查询非空白(包括空格、制表符等)字符 \b:匹配单词边界 \B:匹配非单词边界 #### 量词 -> 匹配多少个 1. +代表多个 ``/\d+/`` 多个数字结合 2. {n,m} -> {最少,最多} ``/\d{2,5}/`` 2到5个的数字连接体 3. {n} -> {多少个} ``/\d{3}/`` 三个数字的连接体 4. {n,} -> {至少n个} ``/\d{2,}/`` 至少2个数字的连接体 #### 范围匹配 - [asdf] 其中一个 - [0-9] 0-9的数字 - [a-z] a-z的字母 - [A-Z] A-Z的字母 - [A-z] 所有字母 - [\u4e00-\u9fa5] 匹配中文 #### 分组 通过``()``来对正则表达式分组,每个()里都是一个正则 通过正则可以改变量词的影响范围 ``/[a-z]\d+/``和``/([a-z]\d)+/``的量词的意义是不一样的 #### 位置限制 ^:匹配字符串的开始位置 $:匹配字符串的结束位置 ```js let testStr = '123dsadsadsadsadasdsad321' let re = /^\d+/g let re2 = /\d+$/g testStr.match(re) //123 testStr.match(re2) //321 ``` #### 修饰符 g:匹配所有符合规则的字符串 i:忽略大小写 m:匹配多行 #### 范围排除 [^abc]:表示除a、b、c以外的字符 [^0-9]:匹配除0-9以外的字符 [^a-z]:匹配除a-z以外的字符 [a^bc]:这里的^只能表示普通字符^ #### 多选匹配 使用|隔开 分别返回 | 两边各自匹配的结果 ```js let testStr = 'sdad23ASDAkjlOU20isdEWYmz082' let re = /[a-z]+|[A-Z]+/ testStr.match(re) // ['sdad','ASDA','jl','OU','isd','EwY','mz'] ``` #### 正则方法 test():用于验证字符串是否符合正则表达式的规则 exec():与String.match()相似 #### 支持正则表达式的字符串方法 search():查询字符串中符合正则表达式的子字符串的开始位置,没有则返回-1 match(): 匹配正则表达式的所有子字符串 split(): 将字符串以某种方式分割成多个子字符串 replace():将字符串中符合正则表达式的子字符串替代成指定的字符串 ```js let testStr = 'asd2131sad' let re = /\d+/g testStr.search(re) //3 testStr = '我是中华人民共和国公民,我爱我的祖国!' re = /中华人民共和国/g testStr.replace(re,'*******') // 我是*******公民,我爱我的祖国! testStr = 'asd-123sad-12ad-lgk-sasd' re = /[a-z]\-/g testStr.split(re) // ["as", "123sa", "12a", "lg", "sasd"] ``` *** ### 将url的查询参数解析成对象 ```js function getQueryObject(url) { url = url == null ? window.location.href : url; var search = url.substring(url.lastIndexOf("?") + 1); var obj = {}; search.split('&').forEach(i => { let temp = i.split('=') obj[temp[0]] = temp[1] }) return obj } console.log(getQueryObject("http://www.cnblogs.com/leee/p/4456840.html?name=1&dd=ddd**")) ``` *** ### position的值的含义 1. relative:相对定位,相对于自身在正常文档流中的位置进行定位 2. position:绝对定位,相对于最近一级定位部位static的父元素进行定位 3. fixed:绝对定位,相对于window或者iframe进行定位 4. static:默认值,没有定位 *** ### CSS选择器有哪些、那些属性可以继承、优先级如何计算、C3西能伪类有哪些 选择符: 1. id选择器 2. 类选择器 3. 标签选择器 h1,p,span 4. 子代选择器 ul>li -> ul后第一代的所有li 5. 后代选择器 ul span -> ul后所有代的span 6. 兄弟选择器 h1 + p -> 紧跟在h1后面的有着相同父元素的标签p 7. 属性选择器 button[disable='false'] -> 所有属性disable为false的buton标签 8. 通配符选择器 * -> 所有元素 9. 伪类选择器 a:hover/focus li:nth-child -> 表示一种状态 10. 伪元素选择器 a:before , after、p::first-letter 优先级: !important > 行内样式(1000) > id(0100) > 类(0010) > 标签/伪类(0001) 伪类和伪元素的区别: 正常选择器通过dom树来获取远古,但是对于不存在与dom树上的信息,以及不能以正常方式选择的元素,得通过伪类和伪元素来选择 伪类:dom树上不存在的信息,比如a:hover、a:focus等;无法正常选择的元素,比如li:nth-chlid 伪元素:不存在的元素:div:after;无法正常获得的元素:p::first-letter 文本内容的第一个字母 *** ### Array.sort() 对数组进行排序,默认的排序方式是将每个元素toString(),然后按照字符串比较的方式比较 如果我们想要指定比较方式,就得传入比较函数Array.sort(compare) ```js // 比较函数接受两个参数 function compare(a,b){ // 若a>b 返回正数 则a与b互换 return a - b // 若a>b 返回附属 则a与b不互换 // return b - a } ``` *** ### 两个数组合并新的数组并且排序 - 如果两个数组是没排序过的,就先把两个数组合并,然后对其进行快排 - 如果两个数组是排序过的,则开头一个一个的比较,然后在把剩下的push进去即可 ```js function mergeAndSort(arr1,arr2) { function compare(a,b){ return a - b } let len1 = arr1.length, len2 = arr2.length, i = 0, j = 0, k = 0, res = new Array() arr1.sort(compare) arr2.sort(compare) console.log(arr1 , arr2) while(i < len1 && j < len2){ res[k++] = arr1[i] > arr2[j] ? arr2[j++] : arr1[i++] } while(i 来给浏览器的history添加一个节点,通过浏览器的url栏也会是这个url,``但是浏览器却不会去访问这个url,类似与没有实际作用的url``。在ajax的时候,我们放一个url进浏览器的history,这样便能保存ajax的状态。浏览器也能通过`前进`和`后退`键来返回ajax的前一个状态 也可以通过history.replaceState(js对象, 任何字符串, 旧的url, 新的url)来修改某个state *** ### 实现一个once函数 本质是考作用域,将 是否执行过的判断条件放到 和 需要执行的函数 同级 ,然后返回需要执行的函数 ```js let test = function(){console.log('我只执行一次') } function Once(fn){ let isFirst = true return function(){ if (isFirst) { isFirst = !isFirst fn() } } } let func = Once(test) func() // 我只执行一次 func() // 没执行 ``` *** ### 分域名请求资源(图片等)的好处 问题:浏览器的并发请求是针对同一个域名的,超过数量限制的并发请求会被阻塞 方法:通过分域名并发请求资源便不会有限制。 *** ### 页面加载顺序 1. 浏览器拿到html页面,先执行head中的代码:遇到引用的外部文件则下载,遇到script标签,浏览器引擎就将控制权交给js引擎。js脚本执行完就将控制权交回。(此时一般都在异步下载东西) 2. head中的代码解析完,就会解析body中的代码(此时很可能还在异步下载东西)。遇到script标签就把控制权交给js引擎。 3. 当浏览器解析完html元素和css规则(构建dom树,css样式树),就将控制权交给渲染引擎,开始绘制页面。 所以我们一般吧script标签放在body底层,这样不仅不影响浏览器解析html,还可以在加载完dom树之后获取元素(否则可能元素还没加载到dom上就获取)。 ``window.onload()`` -> 页面中所有内容加载完之后执行的事件 ``$(function(){})`` -> 和上面差不多 *** ### 常见的排序方法、数组乱序方法 - 排序:这些Array.sort()帮我们自动执行了 + 冒泡 + 插入 + 快排 - 数组乱序 + 洗牌 + sort(比较函数) ```js function shuffler(arr) { let len = arr.length for (let i = 0; i < len-1; i++) { // 随机选择一个index 而且可选范围也要变化 let index = parseInt(Math.random()*(len - i)) temp = arr[index] // 将选中的元素 随意与另一个互换(len-i-1) arr[index] = arr[len - i -1] arr[len - i - 1] = temp } return arr } var arr = [-2,1,3,4,5,6,7,8,9]; console.log(shuffler(arr)) // 会变化 ``` *** ### 计算机网络的分层概述 tcp/ip:链路层、网络层、传输层、应用层 OSI模型:物理层、链路层、网络层、传输层、会话层、表示层、应用层 *** ### jscss缓存问题 问题:在原js/css文件的有效期没到的时候,script或者link的url没变化,那么浏览器就不回去在此获取这些文件,这样服务器上修改的js和css文件就不会被访问。 方法:我们通过在url里面加上参数,比如版本号或者时间戳,这样浏览器便会访问新的资源 *** ### setTimeout、setInterval的区别 setInterval:在执行运行时间较长的函数时,前一次的函数没执行完,时间都到,但是第二次函数并不会立即开始,而是放入队列中,等待第一次函数执行完才执行第二次。 setTimeout:狠准时,时间到就开始新一次方法。 *** ### Webpack常用功能 本质:对静态文件进行处理(图片、字体图标等),对js模块之间的相互加载进行优化 通过webpack.config.js文件来配置webpack 1. 入口文件 entry + 文件路径 + 可以放多个入口文件 2. 琐碎 resolve + extensions:添加省略的后缀 + alias:给常用文件添加别名 这样require的时候减少搜索时间 3. 出口文件 output + 包括文件路径和生成文件的名字 + 对应于多个出口文件,filename:'[name].js' 4. loader文件 module:对静态文件(图片、css、html等非js模块)进行处理 + test:对什么样的格式的文件进行处理 + loader:使用什么loader处理 + include:要处理的目录 (不写include就全选) + exclude:不处理的目录 比如node_modules + 与loaders同级,preLoaders是在loaders执行之前执行,比如对所有文件进行eslint的判断 5. 插件 plugins:比起loader能调用更多的webpack API + webpack.optimize.UglifyJsPlugin:压缩打包文件 + webpack.NoErrorsPlugin:出错不中断打包程序 + TransferWebpackPlugin:将制定文件夹复制到指定位置 6. js规范 eslint + configFile:eslint的配置文件的路径 7. 执行命令:webpack --config webpack.config.js --colors 8. 热编译 webpack-dev-server + 指定目录里面的文件发生变化就会触发热编译,而且编译的结果是放在内存中的这样能编译的更快 + 在entry节点中添加'webpack/hot/dev-server', + 在devServer节点中添加hot: true + 在plugins节点中添加new webpack.HotModuleReplacementPlugin(), // 热更新插件 + webpack-dev-server --config webpack-dev-config.js --inline --colors ```js var webpack = require( 'webpack' ) var path = require( 'path' ) var buildPath = path.resolve( __dirname, "js/webpackTest/build") var nodeModulesPath = path.resolve( __dirname, "js/webpackTest/node_modules") var config = { // 入口文件配置 可以多个入口 entry: [ 'webpack/hot/dev-server',, path.resolve( __dirname, 'js/webpackTest/entry.js') ], // 多个入口文件 // entry: { // m1: path.resolve(__dirname,'js/webpackTest/entry.js'), // m2: path.resolve(__dirname,'js/webpackTest/entry2.js') // } resolve: { // 当require文件找不到的时候 就添加指定后缀,其实就是在require时能省略后缀 extensions: [ "js",'jsx'], // alias配置项 可以为常用模块设置别名、节省编译时搜索需要的时间 alias: { 'react':path.join(nodeModulesPath,'react/react.js') } }, // 出口文件配置 output: { path: buildPath, filename: 'app.js' // 对应于多个入口文件 // filename: '[name].js' }, plugins:[ // 打包压缩 new webpack.optimize.UglifyJsPlugin({ compress:{ warnings:false } }), // 允许错误不打断程序 new webpack.NoErrorsPlugin(), // 热更新插件 new webpack.HotModuleReplacementPlugin(), // 将指定目录放入指定位置 new TransferWebpackPlugins([ {from:'www'} ], path.resolve( __dirname, 'src')) ], module:{ preLoaders:[ { test: /\.(js|jsx)$/, loader: 'eslint', include: [ path.resolve( __dirname, "src/app") ], exclude: [ nodeModulesPath ] } ], loaders:[ { // 将es6语法转es5语法 test: /\.js$/, loader: 'babel-loader', exclude: [ nodeModulesPath ] }, { // 将css/less/saas 文件的内容提取出来 将其放入html的style标签内 test: /\.css$/, loader: 'style!css',// 如果是less配置:'style!css!less' exclude: nodeModulesPath }, { // 图片或字体转base64 test:/\.(png|woff|svg|ttf|eot)$/, loader:'url-loader?limit=10000',//小于10k的图片行base64的转换 } ] }, // eslint配置文件路径 eslint: { configFile: './eslintrc' }, // 热编译 devServer:{ contentBase: '', // 静态文件路径 是相对路径 默认config文件所在的目录 devtool: 'eval', hot: true, // 自动刷新 inline: true, port: 3005 }, devtools: 'eval', } module.exports = config; ``` *** ### saas 1. 嵌套 2. 允许for循环、if判断 *** ### WebSocket 和 ajax轮询 WebSocket是Html5提出的新协议:客户端和服务器建立一次连接之后,便不会断开,便能不断从服务器那里获取消息,可以实现消息推送 ajax轮询:客户端每隔0.5秒向服务器询问一次,这样会建立多次http连接,浪费资源 *** ### transition和margin的百分比是根据什么计算的 transition是相对于自身的 margin是相对于参照物的:比如相对其父级(得是块级)的宽度来设定的 (当然也可以设置为高度为参照物) *** ### 冒泡排序、快速排序、去重、查找字符串中的最多值 冒泡排序 ```js // 冒泡排序 var bubbleSort = function(arr){ // 从第一个开始 每一个与其后面的所有进行比较,将小放前面 for (let i = 0; i < arr.length-1; i++) { for (let j = i+1; j < arr.length; j++) { if (arr[i] > arr[j]) { var temp = arr[i] arr[i] = arr[j] arr[j] = temp } } } return arr } console.log(bubbleSort([5,3,4,8,6,7,2])) ``` 快速排序 ```js ```