写点什么

2022 秋招前端面试题(十)(附答案)

  • 2022 年 8 月 10 日
    浙江
  • 本文字数:13859 字

    阅读完需:约 45 分钟

扩展运算符的作用及使用场景

(1)对象扩展运算符


对象的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。


let bar = { a: 1, b: 2 };let baz = { ...bar }; // { a: 1, b: 2 }复制代码
复制代码


上述方法实际上等价于:


let bar = { a: 1, b: 2 };let baz = Object.assign({}, bar); // { a: 1, b: 2 }复制代码
复制代码


Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。(如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性)。


同样,如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。


let bar = {a: 1, b: 2};let baz = {...bar, ...{a:2, b: 4}};  // {a: 2, b: 4}复制代码
复制代码


利用上述特性就可以很方便的修改对象的部分属性。在redux中的reducer函数规定必须是一个纯函数reducer中的state对象要求不能直接修改,可以通过扩展运算符把修改路径的对象都复制一遍,然后产生一个新的对象返回。


需要注意:扩展运算符对对象实例的拷贝属于浅拷贝


(2)数组扩展运算符


数组的扩展运算符可以将一个数组转为用逗号分隔的参数序列,且每次只能展开一层数组。


console.log(...[1, 2, 3])// 1 2 3console.log(...[1, [2, 3, 4], 5])// 1 [2, 3, 4] 5复制代码
复制代码


下面是数组的扩展运算符的应用:


  • 将数组转换为参数序列


function add(x, y) {  return x + y;}const numbers = [1, 2];add(...numbers) // 3复制代码
复制代码


  • 复制数组


const arr1 = [1, 2];const arr2 = [...arr1];复制代码
复制代码


要记住:扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中,这里参数对象是个数组,数组里面的所有对象都是基础数据类型,将所有基础数据类型重新拷贝到新的数组中。


  • 合并数组


如果想在数组内合并数组,可以这样:


const arr1 = ['two', 'three'];const arr2 = ['one', ...arr1, 'four', 'five'];// ["one", "two", "three", "four", "five"]复制代码
复制代码


  • 扩展运算符与解构赋值结合起来,用于生成数组


const [first, ...rest] = [1, 2, 3, 4, 5];first // 1rest  // [2, 3, 4, 5]复制代码
复制代码


需要注意:如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。


const [...rest, last] = [1, 2, 3, 4, 5];         // 报错const [first, ...rest, last] = [1, 2, 3, 4, 5];  // 报错复制代码
复制代码


  • 将字符串转为真正的数组


[...'hello']    // [ "h", "e", "l", "l", "o" ]复制代码
复制代码


  • 任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组


比较常见的应用是可以将某些数据结构转为数组:


// arguments对象function foo() {  const args = [...arguments];}复制代码
复制代码


用于替换es5中的Array.prototype.slice.call(arguments)写法。


  • 使用Math函数获取数组中特定的值


const numbers = [9, 4, 7, 1];Math.min(...numbers); // 1Math.max(...numbers); // 9复制代码
复制代码

陈述输入 URL 回车后的过程

1.读取缓存:         搜索自身的 DNS 缓存。(如果 DNS 缓存中找到IP 地址就跳过了接下来查找 IP 地址步骤,直接访问该 IP 地址。)2.DNS 解析:将域名解析成 IP 地址3.TCP 连接:TCP 三次握手,简易描述三次握手           客户端:服务端你在么?            服务端:客户端我在,你要连接我么?            客户端:是的服务端,我要链接。            连接打通,可以开始请求来4.发送 HTTP 请求5.服务器处理请求并返回 HTTP 报文6.浏览器解析渲染页面7.断开连接:TCP 四次挥手
关于第六步浏览器解析渲染页面又可以聊聊如果返回的是html页面根据 HTML 解析出 DOM 树根据 CSS 解析生成 CSS 规则树结合 DOM 树和 CSS 规则树,生成渲染树根据渲染树计算每一个节点的信息根据计算好的信息绘制页面复制代码
复制代码

vue-router

vue-router是vuex.js官方的路由管理器,它和vue.js的核心深度集成,让构建但页面应用变得易如反掌
<router-link> 组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to 属性指定目标地址
<router-view> 组件是一个 functional 组件,渲染路径匹配到的视图组件。
<keep-alive> 组件是一个用来缓存组件
router.beforeEach
router.afterEach
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
介绍了路由守卫及用法,在项目中路由守卫起到的作用等等复制代码
复制代码

说一下常见的 git 操作

git branch 查看本地所有分支git status 查看当前状态 git commit 提交 git branch -a 查看所有的分支git branch -r 查看远程所有分支git commit -am "nit" 提交并且加注释 git remote add origin git@192.168.1.119:ndshowgit push origin master 将文件给推到服务器上 git remote show origin 显示远程库origin里的资源 git push origin master:developgit push origin master:hb-dev 将本地库与服务器上的库进行关联 git checkout --track origin/dev 切换到远程dev分支git branch -D master develop 删除本地库developgit checkout -b dev 建立一个新的本地分支devgit merge origin/dev 将分支dev与当前分支进行合并git checkout dev 切换到本地dev分支git remote show 查看远程库git add .git rm 文件名(包括路径) 从git中删除指定文件git clone git://github.com/schacon/grit.git 从服务器上将代码给拉下来git config --list 看所有用户git ls-files 看已经被提交的git rm [file name] 删除一个文件git commit -a 提交当前repos的所有的改变git add [file name] 添加一个文件到git indexgit commit -v 当你用-v参数的时候可以看commit的差异git commit -m "This is the message describing the commit" 添加commit信息git commit -a -a是代表add,把所有的change加到git index里然后再commitgit commit -a -v 一般提交命令git log 看你commit的日志git diff 查看尚未暂存的更新git rm a.a 移除文件(从暂存区和工作区中删除)git rm --cached a.a 移除文件(只从暂存区中删除)git commit -m "remove" 移除文件(从Git中删除)git rm -f a.a 强行移除修改后文件(从暂存区和工作区中删除)git diff --cached 或 $ git diff --staged 查看尚未提交的更新git stash push 将文件给push到一个临时空间中git stash pop 将文件从临时空间pop下来
复制代码

JS 闭包,你了解多少?

应该有面试官问过你:


  1. 什么是闭包?

  2. 闭包有哪些实际运用场景?

  3. 闭包是如何产生的?

  4. 闭包产生的变量如何被回收?


这些问题其实都可以被看作是同一个问题,那就是面试官在问你:你对JS闭包了解多少?


来总结一下我听到过的答案,尽量完全复原候选人面试的时候说的原话。


答案1: 就是一个function里面return了一个子函数,子函数访问了外面那个函数的变量。


答案2: for 循环里面可以用闭包来解决问题。


for(var i = 0; i < 10; i++){    setTimeout(()=>console.log(i),0)}// 控制台输出10遍10.for(var i = 0; i < 10; i++){    (function(a){    setTimeout(()=>console.log(a),0)    })(i)} // 控制台输出0-9复制代码
复制代码


答案3: 当前作用域产产生了对父作用域的引用。


答案4: 不知道。是跟浏览器的垃圾回收机制有关吗?


开杠了。请问,小伙伴的答案和以上的内容有多少相似程度?


其实,拿着这些问题好好想想,你就会发现这些问题都只是为了最终那一个问题。


闭包的底层实现原理


1. JS执行上下文


我们都知道,我们手写的 js 代码是要经过浏览器 V8 进行预编译后才能真正的被执行。例如变量提升、函数提升。举个栗子。


// 栗子:var d = 'abc';function a(){    console.log("函数a");};console.log(a);   // ƒ a(){ console.log("函数a"); }a();              // '函数a'var a = "变量a";  console.log(a);   // '变量a'a();              // a is not a functionvar c = 123;
// 输出结果及顺序:// ƒ a(){ console.log("函数a"); }// '函数a'// '变量a'// a is not a function
// 栗子预编后相当于:function a(){ console.log("函数a");};var d;console.log(a); // ƒ a(){ console.log("函数a"); }a(); // '函数a'
a = "变量a"; // 此时变量a赋值,函数声明被覆盖
console.log(a); // "变量a"a(); // a is not a function
复制代码
复制代码


那么问题来了。 请问是谁来执行预编译操作的?那这个谁又是在哪里进行预编译的?


是的,你的疑惑没有错。js 代码运行需要一个运行环境,那这个环境就是执行上下文。 是的,js 运行前的预编译也是在这个环境中进行。


js 执行上下文分三种:


  • 全局执行上下文: 代码开始执行时首先进入的环境。

  • 函数执行上下文:函数调用时,会开始执行函数中的代码。

  • eval执行上下文:不建议使用,可忽略。


那么,执行上下文的周期,分为两个阶段:


  • 创建阶段

  • 创建词法环境

  • 生成变量对象(VO),建立作用域链作用域链作用域链(重要的事说三遍)

  • 确认this指向,并绑定this

  • 执行阶段。这个阶段进行变量赋值,函数引用及执行代码。


你现在猜猜看,预编译是发生在什么时候?


噢,我忘记说了,其实与编译还有另一个称呼:执行期上下文


预编译发生在函数执行之前。预编译四部曲为:


  1. 创建AO对象

  2. 找形参和变量声明,将变量和形参作为 AO 属性名,值为undefined

  3. 将实参和形参相统一

  4. 在函数体里找到函数声明,值赋予函数体。最后程序输出变量值的时候,就是从AO对象中拿。


所以,预编译真正的结果是:


var AO = {    a = function a(){console.log("函数a");};    d = 'abc'}复制代码
复制代码


我们重新来。

1. 什么叫变量对象?

变量对象是 js 代码在进入执行上下文时,js 引擎在内存中建立的一个对象,用来存放当前执行环境中的变量。

2. 变量对象(VO)的创建过程

变量对象的创建,是在执行上下文创建阶段,依次经过以下三个过程:


  • 创建 arguments 对象。

  • 对于函数执行环境,首先查询是否有传入的实参,如果有,则会将参数名是实参值组成的键值对放入arguments 对象中。否则,将参数名和 undefined组成的键值对放入 arguments 对象中。


//举个栗子 function bar(a, b, c) {    console.log(arguments);  // [1, 2]    console.log(arguments[2]); // undefined}bar(1,2)复制代码
复制代码


  • 当遇到同名的函数时,后面的会覆盖前面的。


console.log(a); // function a() {console.log('Is a ?') }function a() {    console.log('Is a');}function a() {  console.log('Is a ?')}
/**ps: 在执行第一行代码之前,函数声明已经创建完成.后面的对之前的声明进行了覆盖。**/复制代码
复制代码


  • 检查当前环境中的变量声明并赋值为undefined。当遇到同名的函数声明,为了避免函数被赋值为 undefined ,会忽略此声明


console.log(a); // function a() {console.log('Is a ?') }console.log(b); // undefinedfunction a() {  console.log('Is a ');}function a() {console.log('Is a ?');}var b = 'Is b';var a = 10086;
/**这段代码执行一下,你会发现 a 打印结果仍旧是一个函数,而 b 则是 undefined。**/复制代码
复制代码


根据以上三个步骤,对于变量提升也就知道是怎么回事了。

3. 变量对象变为活动对象

执行上下文的第二个阶段,称为执行阶段,在此时,会进行变量赋值,函数引用并执行其他代码,此时,变量对象变为活动对象。


我们还是举上面的例子:


console.log(a); // function a() {console.log('fjdsfs') }console.log(b); // undefinedfunction a() {   console.log('Is a');}function a() { console.log('Is a?');}var b = 'Is b';console.log(b); // 'Is b'var a = 10086; console.log(a);  // 10086var b = 'Is b?';console.log(b); // 'Is b?'复制代码
复制代码


在上面的代码中,代码真正开始执行是从第一行 console.log() 开始的,自这之前,执行上下文是这样的:


// 创建过程EC= {  VO: {}; // 创建变量对象  scopeChain: {}; // 作用域链}VO = {  argument: {...}; // 当前为全局上下文,所以这个属性值是空的  a: <a reference> // 函数 a  的引用地址  b: undefiend  // 见上文创建变量对象的第三步}
复制代码
复制代码
词法作用域(Lexical scope

这里想说明,我们在函数执行上下文中有变量,在全局执行上下文中有变量。JavaScript的一个复杂之处在于它如何查找变量,如果在函数执行上下文中找不到变量,它将在调用上下文中寻找它,如果在它的调用上下文中没有找到,就一直往上一级,直到它在全局执行上下文中查找为止。(如果最后找不到,它就是 undefined)。


再来举个栗子:


 1: let top = 0; //  2: function createWarp() { 3:   function add(a, b) { 4:     let ret = a + b 5:     return ret 6:   } 7:   return add 8: } 9: let sum = createWarp()10: let result = sum(top, 8)11: console.log('result:',result)

复制代码
复制代码


分析过程如下:


  • 在全局上下文中声明变量top 并赋值为 0.

  • 2 - 8 行。在全局执行上下文中声明了一个名为 createWarp 的变量,并为其分配了一个函数定义。其中第 3-7 行描述了其函数定义,并将函数定义存储到那个变量(createWarp)中。

  • 第 9 行。我们在全局执行上下文中声明了一个名为 sum 的新变量,暂时,值为 undefined

  • 第 9 行。遇到(),表明需要执行或调用一个函数。那么查找全局执行上下文的内存并查找名为 createWarp 的变量。 明显,已经在步骤 2 中创建完毕。接着,调用它。

  • 调用函数时,回到第 2 行。创建一个新的createWarp执行上下文。我们可以在 createWarp 的执行上下文中创建自有变量。js 引擎createWarp 的上下文添加到调用堆栈(call stack)。因为这个函数没有参数,直接跳到它的主体部分.

  • 3 - 6 行。我们有一个新的函数声明,createWarp执行上下文中创建一个变量 addadd 只存在于 createWarp 执行上下文中, 其函数定义存储在名为 add 的自有变量中。

  • 第 7 行,我们返回变量 add 的内容。js 引擎查找一个名为 add 的变量并找到它. 第 4 行和第 5 行括号之间的内容构成该函数定义。

  • createWarp 调用完毕,createWarp 执行上下文将被销毁。add 变量也跟着被销毁。add 函数定义仍然存在,因为它返回并赋值给了 sum 变量。 (ps: 这才是闭包产生的变量存于内存当中的真相

  • 接下来就是简单的执行过程,不再赘述。。

  • ……

  • 代码执行完毕,全局执行上下文被销毁。sum 和 result 也跟着被销毁。


小结一下


现在,如果再让你回答什么是闭包,你能答出多少?


其实,大家说的都对。不管是函数返回一个函数,还是产生了外部作用域的引用,都是有道理的。


所以,什么是闭包?


  • 解释一下作用域链是如何产生的。

  • 解释一下 js 执行上下文的创建、执行过程。

  • 解释一下闭包所产生的变量放在哪了。

  • 最后请把以上 3 点结合起来说给面试官听。

说一下怎么把类数组转换为数组?

//通过call调用数组的slice方法来实现转换Array.prototype.slice.call(arrayLike)
//通过call调用数组的splice方法来实现转换Array.prototype.splice.call(arrayLike,0)
//通过apply调用数组的concat方法来实现转换Array.prototype.concat.apply([],arrayLike)
//通过Array.from方法来实现转换Array.from(arrayLike)复制代码
复制代码

ES6 新特性

1.ES6引入来严格模式    变量必须声明后在使用    函数的参数不能有同名属性, 否则报错    不能使用with语句 (说实话我基本没用过)    不能对只读属性赋值, 否则报错    不能使用前缀0表示八进制数,否则报错 (说实话我基本没用过)    不能删除不可删除的数据, 否则报错    不能删除变量delete prop, 会报错, 只能删除属性delete global[prop]    eval不会在它的外层作用域引入变量    eval和arguments不能被重新赋值    arguments不会自动反映函数参数的变化    不能使用arguments.caller (说实话我基本没用过)    不能使用arguments.callee (说实话我基本没用过)    禁止this指向全局对象    不能使用fn.caller和fn.arguments获取函数调用的堆栈 (说实话我基本没用过)    增加了保留字(比如protected、static和interface)
2.关于let和const新增的变量声明
3.变量的解构赋值
4.字符串的扩展 includes():返回布尔值,表示是否找到了参数字符串。 startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。 endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。5.数值的扩展 Number.isFinite()用来检查一个数值是否为有限的(finite)。 Number.isNaN()用来检查一个值是否为NaN。6.函数的扩展 函数参数指定默认值7.数组的扩展 扩展运算符8.对象的扩展 对象的解构9.新增symbol数据类型
10.Set 和 Map 数据结构 ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。 Set 本身是一个构造函数,用来生成 Set 数据结构。
Map它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。11.Proxy Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问 都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。 Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。 Vue3.0使用了proxy12.Promise Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。 特点是: 对象的状态不受外界影响。 一旦状态改变,就不会再变,任何时候都可以得到这个结果。13.async 函数 async函数对 Generator 函数的区别: (1)内置执行器。 Generator 函数的执行必须靠执行器,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。 (2)更好的语义。 async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。 (3)正常情况下,await命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象。 (4)返回值是 Promise。 async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。14.Class class跟let、const一样:不存在变量提升、不能重复声明... ES6 的class可以看作只是一个语法糖,它的绝大部分功能 ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。15.Module ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";。 import和export命令以及export和export default的区别复制代码
复制代码

HTML5 有哪些更新

1. 语义化标签

  • header:定义文档的页眉(头部);

  • nav:定义导航链接的部分;

  • footer:定义文档或节的页脚(底部);

  • article:定义文章内容;

  • section:定义文档中的节(section、区段);

  • aside:定义其所处内容之外的内容(侧边);

2. 媒体标签

(1) audio:音频


<audio src='' controls autoplay loop='true'></audio>复制代码
复制代码


属性:


  • controls 控制面板

  • autoplay 自动播放

  • loop=‘true’ 循环播放


(2)video 视频


<video src='' poster='imgs/aa.jpg' controls></video>复制代码
复制代码


属性:


  • poster:指定视频还没有完全下载完毕,或者用户还没有点击播放前显示的封面。默认显示当前视频文件的第一针画面,当然通过 poster 也可以自己指定。

  • controls 控制面板

  • width

  • height


(3)source 标签因为浏览器对视频格式支持程度不一样,为了能够兼容不同的浏览器,可以通过 source 来指定视频源。


<video>     <source src='aa.flv' type='video/flv'></source>     <source src='aa.mp4' type='video/mp4'></source></video>复制代码
复制代码

3. 表单

表单类型:


  • email :能够验证当前输入的邮箱地址是否合法

  • url : 验证 URL

  • number : 只能输入数字,其他输入不了,而且自带上下增大减小箭头,max 属性可以设置为最大值,min 可以设置为最小值,value 为默认值。

  • search : 输入框后面会给提供一个小叉,可以删除输入的内容,更加人性化。

  • range : 可以提供给一个范围,其中可以设置 max 和 min 以及 value,其中 value 属性可以设置为默认值

  • color : 提供了一个颜色拾取器

  • time : 时分秒

  • data : 日期选择年月日

  • datatime : 时间和日期(目前只有 Safari 支持)

  • datatime-local :日期时间控件

  • week :周控件

  • month:月控件


表单属性:


  • placeholder :提示信息

  • autofocus :自动获取焦点

  • autocomplete=“on” 或者 autocomplete=“off” 使用这个属性需要有两个前提:

  • 表单必须提交过

  • 必须有 name 属性。

  • required:要求输入框不能为空,必须有值才能够提交。

  • pattern=" " 里面写入想要的正则模式,例如手机号 patte="^(+86)?\d{10}$"

  • multiple:可以选择多个文件或者多个邮箱

  • form=" form 表单的 ID"


表单事件:


  • oninput 每当 input 里的输入框内容发生变化都会触发此事件。

  • oninvalid 当验证不通过时触发此事件。

4. 进度条、度量器

  • progress 标签:用来表示任务的进度(IE、Safari 不支持),max 用来表示任务的进度,value 表示已完成多少

  • meter 属性:用来显示剩余容量或剩余库存(IE、Safari 不支持)

  • high/low:规定被视作高/低的范围

  • max/min:规定最大/小值

  • value:规定当前度量值


设置规则:min < low < high < max

5.DOM 查询操作

  • document.querySelector()

  • document.querySelectorAll()


它们选择的对象可以是标签,可以是类(需要加点),可以是 ID(需要加 #)

6. Web 存储

HTML5 提供了两种在客户端存储数据的新方法:


  • localStorage - 没有时间限制的数据存储

  • sessionStorage - 针对一个 session 的数据存储

7. 其他

  • 拖放:拖放是一种常见的特性,即抓取对象以后拖到另一个位置。设置元素可拖放:


<img draggable="true" />复制代码
复制代码


  • 画布(canvas ): canvas 元素使用 JavaScript 在网页上绘制图像。画布是一个矩形区域,可以控制其每一像素。canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。


<canvas id="myCanvas" width="200" height="100"></canvas>复制代码
复制代码


  • SVG:SVG 指可伸缩矢量图形,用于定义用于网络的基于矢量的图形,使用 XML 格式定义图形,图像在放大或改变尺寸的情况下其图形质量不会有损失,它是万维网联盟的标准

  • 地理定位:Geolocation(地理定位)用于定位用户的位置。‘


总结: (1)新增语义化标签:nav、header、footer、aside、section、article(2)音频、视频标签:audio、video(3)数据存储:localStorage、sessionStorage(4)canvas(画布)、Geolocation(地理定位)、websocket(通信协议)(5)input 标签新增属性:placeholder、autocomplete、autofocus、required(6)history API:go、forward、back、pushstate


移除的元素有:


  • 纯表现的元素:basefont,big,center,font, s,strike,tt,u;

  • 对可用性产生负面影响的元素:frame,frameset,noframes;

position 的属性有哪些,区别是什么

position 有以下属性值:



前面三者的定位方式如下:


  • relative: 元素的定位永远是相对于元素自身位置的,和其他元素没关系,也不会影响其他元素。

  • fixed: 元素的定位是相对于 window (或者 iframe)边界的,和其他元素没有关系。但是它具有破坏性,会导致其他元素位置的变化。

  • absolute: 元素的定位相对于前两者要复杂许多。如果为 absolute 设置了 top、left,浏览器会根据什么去确定它的纵向和横向的偏移量呢?答案是浏览器会递归查找该元素的所有父元素,如果找到一个设置了position:relative/absolute/fixed的元素,就以该元素为基准定位,如果没找到,就以浏览器边界定位。如下两个图所示:

HTML5 的离线储存怎么使用,它的工作原理是什么

离线存储指的是:在用户没有与因特网连接时,可以正常访问站点或应用,在用户与因特网连接时,更新用户机器上的缓存文件。


**原理:**HTML5 的离线存储是基于一个新建的 .appcache 文件的缓存机制(不是存储技术),通过这个文件上的解析清单离线存储资源,这些资源就会像 cookie 一样被存储了下来。之后当网络在处于离线状态下时,浏览器会通过被离线存储的数据进行页面展示


使用方法: (1)创建一个和 html 同名的 manifest 文件,然后在页面头部加入 manifest 属性:


<html lang="en" manifest="index.manifest">复制代码
复制代码


(2)在 cache.manifest 文件中编写需要离线存储的资源:


CACHE MANIFEST    #v0.11    CACHE:    js/app.js    css/style.css    NETWORK:    resourse/logo.png    FALLBACK:    / /offline.html复制代码
复制代码


  • CACHE: 表示需要离线存储的资源列表,由于包含 manifest 文件的页面将被自动离线存储,所以不需要把页面自身也列出来。

  • NETWORK: 表示在它下面列出来的资源只有在在线的情况下才能访问,他们不会被离线存储,所以在离线情况下无法使用这些资源。不过,如果在 CACHE 和 NETWORK 中有一个相同的资源,那么这个资源还是会被离线存储,也就是说 CACHE 的优先级更高。

  • FALLBACK: 表示如果访问第一个资源失败,那么就使用第二个资源来替换他,比如上面这个文件表示的就是如果访问根目录下任何一个资源失败了,那么就去访问 offline.html 。


(3)在离线状态时,操作 window.applicationCache 进行离线缓存的操作。


如何更新缓存:


(1)更新 manifest 文件


(2)通过 javascript 操作


(3)清除浏览器缓存


注意事项:


(1)浏览器对缓存数据的容量限制可能不太一样(某些浏览器设置的限制是每个站点 5MB)。


(2)如果 manifest 文件,或者内部列举的某一个文件不能正常下载,整个更新过程都将失败,浏览器继续全部使用老的缓存。


(3)引用 manifest 的 html 必须与 manifest 文件同源,在同一个域下。


(4)FALLBACK 中的资源必须和 manifest 文件同源。


(5)当一个资源被缓存后,该浏览器直接请求这个绝对路径也会访问缓存中的资源。


(6)站点中的其他页面即使没有设置 manifest 属性,请求的资源如果在缓存中也从缓存中访问。


(7)当 manifest 文件发生改变时,资源请求本身也会触发更新。

v-model 语法糖是怎么实现的

<!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>Document</title></head><body>    <!-- v-model 只是语法糖而已 -->    <!-- v-model 在内部为不同的输入元素使用不同的property并抛出不同的事件 -->    <!-- text和textarea 元素使用value property 和 input事件 -->    <!-- checkbox 和radio使用checked  property 和 change事件-->    <!-- select 字段将value 作为prop 并将change 作为事件 -->    <!-- 注意:对于需要使用输入法(如中文、日文、韩文等)的语言,你将会发现v-model不会再输入法    组合文字过程中得到更新 -->    <!-- 再普通标签上 -->    <input v-model="sth" />  //这一行等于下一行    <input v-bind:value="sth" v-on:input="sth = $event.target.value" />    <!-- 再组件上 -->    <currency-input v-model="price"></currentcy-input>        <!--上行代码是下行的语法糖         <currency-input :value="price" @input="price = arguments[0]"></currency-input>        -->         <!-- 子组件定义 -->        Vue.component('currency-input', {         template: `          <span>           <input            ref="input"            :value="value"            @input="$emit('input', $event.target.value)"           >          </span>         `,         props: ['value'],        })   </body></html>复制代码
复制代码

浏览器是如何对 HTML5 的离线储存资源进行管理和加载?

  • 在线的情况下,浏览器发现 html 头部有 manifest 属性,它会请求 manifest 文件,如果是第一次访问页面 ,那么浏览器就会根据 manifest 文件的内容下载相应的资源并且进行离线存储。如果已经访问过页面并且资源已经进行离线存储了,那么浏览器就会使用离线的资源加载页面,然后浏览器会对比新的 manifest 文件与旧的 manifest 文件,如果文件没有发生改变,就不做任何操作,如果文件改变了,就会重新下载文件中的资源并进行离线存储。

  • 离线的情况下,浏览器会直接使用离线存储的资源。

CSS 优化和提高性能的方法有哪些?

加载性能:


(1)css 压缩:将写好的 css 进行打包压缩,可以减小文件体积。


(2)css 单一样式:当需要下边距和左边距的时候,很多时候会选择使用 margin:top 0 bottom 0;但 margin-bottom:bottom;margin-left:left;执行效率会更高。


(3)减少使用 @import,建议使用 link,因为后者在页面加载时一起加载,前者是等待页面加载完成之后再进行加载。


选择器性能:


(1)关键选择器(key selector)。选择器的最后面的部分为关键选择器(即用来匹配目标元素的部分)。CSS 选择符是从右到左进行匹配的。当使用后代选择器的时候,浏览器会遍历所有子元素来确定是否是指定的元素等等;


(2)如果规则拥有 ID 选择器作为其关键选择器,则不要为规则增加标签。过滤掉无关的规则(这样样式系统就不会浪费时间去匹配它们了)。


(3)避免使用通配规则,如*{}计算次数惊人,只对需要用到的元素进行选择。


(4)尽量少的去对标签进行选择,而是用 class。


(5)尽量少的去使用后代选择器,降低选择器的权重值。后代选择器的开销是最高的,尽量将选择器的深度降到最低,最高不要超过三层,更多的使用类来关联每一个标签元素。


(6)了解哪些属性是可以通过继承而来的,然后避免对这些属性重复指定规则。


渲染性能:


(1)慎重使用高性能属性:浮动、定位。


(2)尽量减少页面重排、重绘。


(3)去除空规则:{}。空规则的产生原因一般来说是为了预留样式。去除这些空规则无疑能减少 css 文档体积。


(4)属性值为 0 时,不加单位。


(5)属性值为浮动小数 0.**,可以省略小数点之前的 0。


(6)标准化各种浏览器前缀:带浏览器前缀的在前。标准属性在后。


(7)不使用 @import 前缀,它会影响 css 的加载速度。


(8)选择器优化嵌套,尽量避免层级过深。


(9)css 雪碧图,同一页面相近部分的小图标,方便使用,减少页面的请求次数,但是同时图片本身会变大,使用时,优劣考虑清楚,再使用。


(10)正确使用 display 的属性,由于 display 的作用,某些样式组合会无效,徒增样式体积的同时也影响解析性能。


(11)不滥用 web 字体。对于中文网站来说 WebFonts 可能很陌生,国外却很流行。web fonts 通常体积庞大,而且一些浏览器在下载 web fonts 时会阻塞页面渲染损伤性能。


可维护性、健壮性:


(1)将具有相同属性的样式抽离出来,整合并通过 class 在页面中进行使用,提高 css 的可维护性。


(2)样式与内容分离:将 css 代码定义到外部 css 中。

说一下 vue3.0 你了解多少?

 <!-- 响应式原理的改变 Vue3.x 使用Proxy取代 Vue2.x 版本的Object.defineProperty --> <!-- 组件选项声明方式Vue3.x 使用Composition API setup 是Vue3.x新增的一个选项,他    是组件内使用Composition API 的入口 --> <!-- 模板语法变化slot具名插槽语法 自定义指令 v-model 升级 --> <!-- 其它方面的更改Suspense支持Fragment(多个根节点) 和Protal (在dom其他部分渲染组建内容)组件     针对一些特殊的场景做了处理。基于treeshaking优化,提供了更多的内置功能。 -->复制代码
复制代码

Promise 是什么,解决了什么,之前怎么实现的

    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。    解决来之前在请求中回调请求产生的回调地狱,使得现在的代码更加合理更加优雅,也更加容易定位查找问题。复制代码
复制代码

为什么需要清除浮动?清除浮动的方式

浮动的定义: 非 IE 浏览器下,容器不设高度且子元素浮动时,容器高度不能被内容撑开。 此时,内容会溢出到容器外面而影响布局。这种现象被称为浮动(溢出)。


浮动的工作原理:


  • 浮动元素脱离文档流,不占据空间(引起“高度塌陷”现象)

  • 浮动元素碰到包含它的边框或者其他浮动元素的边框停留


浮动元素可以左右移动,直到遇到另一个浮动元素或者遇到它外边缘的包含框。浮动框不属于文档流中的普通流,当元素浮动之后,不会影响块级元素的布局,只会影响内联元素布局。此时文档流中的普通流就会表现得该浮动框不存在一样的布局模式。当包含框的高度小于浮动框的时候,此时就会出现“高度塌陷”。


浮动元素引起的问题?


  • 父元素的高度无法被撑开,影响与父元素同级的元素

  • 与浮动元素同级的非浮动元素会跟随其后

  • 若浮动的元素不是第一个元素,则该元素之前的元素也要浮动,否则会影响页面的显示结构


清除浮动的方式如下:


  • 给父级 div 定义height属性

  • 最后一个浮动元素之后添加一个空的 div 标签,并添加clear:both样式

  • 包含浮动元素的父级标签添加overflow:hidden或者overflow:auto

  • 使用 :after 伪元素。由于 IE6-7 不支持 :after,使用 zoom:1 触发 hasLayout**


.clearfix:after{    content: "\200B";    display: table;     height: 0;    clear: both;  }  .clearfix{    *zoom: 1;  }复制代码
复制代码

对 HTML 语义化的理解

语义化是指根据内容的结构化(内容语义化),选择合适的标签(代码语义化)。通俗来讲就是用正确的标签做正确的事情。


语义化的优点如下:


  • 对机器友好,带有语义的文字表现力丰富,更适合搜索引擎的爬虫爬取有效信息,有利于 SEO。除此之外,语义类还支持读屏软件,根据文章可以自动生成目录;

  • 对开发者友好,使用语义类标签增强了可读性,结构更加清晰,开发者能清晰的看出网页的结构,便于团队的开发与维护。


常见的语义化标签:


<header></header>  头部
<nav></nav> 导航栏
<section></section> 区块(有语义化的div)
<main></main> 主要区域
<article></article> 主要内容
<aside></aside> 侧边栏
<footer></footer> 底部复制代码
复制代码

absolute 与 fixed 共同点与不同点

共同点:


  • 改变行内元素的呈现方式,将 display 置为 inline-block

  • 使元素脱离普通文档流,不再占据文档物理空间

  • 覆盖非定位文档元素


不同点:


  • abuselute 与 fixed 的根元素不同,abuselute 的根元素可以设置,fixed 根元素是浏览器。

  • 在有滚动条的页面中,absolute 会跟着父元素进行移动,fixed 固定在页面的具体位置。

用户头像

还未添加个人签名 2022.07.31 加入

还未添加个人简介

评论

发布
暂无评论
2022秋招前端面试题(十)(附答案)_helloworld1024fd_InfoQ写作社区