面试题整理

1. W3C标准盒模型、IE盒模型

W3C盒模型:包括margin、border、padding、content,并且content部分不包含其他部分。
IE盒模型:包括margin、border、padding、content,和w3c盒子模型不同的是,IE盒子模型的content部分包含了padding和border.

2. 弹性盒flex布局的各种属性

容器的属性:

  • flex-direction属性:决定主轴的方向(即项目的排列方向)。

    1.row(默认值):主轴为水平方向,起点在左端。
    2.row-reverse:主轴为水平方向,起点在右端。
    3.column:主轴为垂直方向,起点在上沿。
    4.column-reverse:主轴为垂直方向,起点在下沿。
    
  • flex-wrap属性:默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。

    1.nowrap(默认):不换行。
    2.wrap:换行,第一行在上方。
    3.wrap-reverse:换行,第一行在下方。
    
  • flex-flow属性:是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。
  • justify-content属性定义了项目在主轴上的对齐方式。

    1.flex-start(默认值):左对齐
    2.flex-end:右对齐
    3.center: 居中
    4.space-between:两端对齐,项目之间的间隔都相等。
    5.space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
    
  • align-items属性:定义项目在交叉轴上如何对齐。

    1.flex-start:交叉轴的起点对齐。
    2.flex-end:交叉轴的终点对齐。
    3.center:交叉轴的中点对齐。
    4.baseline: 项目的第一行文字的基线对齐。
    5.stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。
    
  • align-content属性:定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

    1.flex-start:与交叉轴的起点对齐。
    2.flex-end:与交叉轴的终点对齐。
    3.center:与交叉轴的中点对齐。
    4.space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
    5.space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
    6.stretch(默认值):轴线占满整个交叉轴。
    

项目的属性:

  • order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。
  • flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
  • flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
  • flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。
  • flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。
  • align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。

3. CSS优化技巧

性能优化:

  • 内联首屏关键CSS(Critical CSS)

    性能优化中有一个重要的指标——首次有效绘制(First Meaningful Paint,简称FMP)
    即指页面的首要内容(primary content)出现在屏幕上的时间。
    这一指标影响用户看到页面前所需等待的时间,
    而内联首屏关键CSS(即Critical CSS,可以称之为首屏关键CSS)能减少这一时间。
    内联CSS能够使浏览器开始页面渲染的时间提前
    只将渲染首屏内容所需的关键CSS内联到HTML中
    
  • 异步加载CSS
  • 文件压缩
  • 去除无用CSS
  • 有选择地使用选择器

    1.保持简单,不要使用嵌套过多过于复杂的选择器。
    2.通配符和属性选择器效率最低,需要匹配的元素最多,尽量避免使用。
    3.不要使用类选择器和ID选择器修饰元素标签,多此一举,还会降低效率。
    4.不要为了追求速度而放弃可读性与可维护性。
  • 减少使用昂贵的属性
    如box-shadow/border-radius/filter/透明度/:nth-child等。
  • 优化重排与重绘
    1.减少重排

    重排会导致浏览器重新计算整个文档,重新构建渲染树,这一过程会降低浏览器的渲染速度。
    1.改变font-size和font-family
    2.改变元素的内外边距
    3.通过JS改变CSS类
    4.通过JS获取DOM元素的位置相关属性(如width/height/left等)
    5.CSS伪类激活
    6.滚动滚动条或者改变窗口大小
    使用Flex时,比使用inline-block和float时重排更快,所以在布局时可以优先考虑Flex。

    2.避免不必要的重绘

    当元素的外观(如color,background,visibility等属性)发生改变时,会触发重绘。
    在网站的使用过程中,重绘是无法避免的。
    不过,浏览器对此做了优化,它会将多次的重排、重绘操作合并为一次执行。
    不过我们仍需要避免不必要的重绘,如页面滚动时触发的hover事件,
    可以在滚动的时候禁用hover事件,这样页面在滚动时会更加流畅。
    
  • 不要使用@import

    首先,使用@import引入CSS会影响浏览器的并行下载。
    使用@import引用的CSS文件只有在引用它的那个css文件被下载、解析之后,
    浏览器才会知道还有另外一个css需要下载,这时才去下载,
    然后下载后开始解析、构建render tree等一系列操作。
    这就导致浏览器无法并行下载所需的样式文件。
    
    其次,多个@import会导致下载顺序紊乱。
    在IE中,@import会引发资源文件的下载顺序被打乱,
    即排列在@import后面的js文件先于@import下载,
    并且打乱甚至破坏@import自身的并行下载。
    
    所以不要使用这一方法,使用link标签就行了。
    

代码优化:

  • 合并多个相同属性;
  • 把具有相同属性的标签写在一块;
  • 简化颜色;
  • 在父级元素中用Class;
  • 不要使用令人眼花缭乱的注释;
  • 不要在行内元素中加入CSS;
  • 移除多余的空格和空行,减小style文件大小.

4. HTTP和HTTPS的区别

1、https协议需要到CA (Certificate Authority,证书颁发机构)申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

5. 常用GIT指令

  • 新建代码库

    # 在当前目录新建一个Git代码库
    $ git init
    
    # 新建一个目录,将其初始化为Git代码库
    $ git init [project-name]
    
    # 下载一个项目和它的整个代码历史
    $ git clone [url]
    
  • 配置

    # 显示当前的Git配置
    $ git config --list
    
    # 编辑Git配置文件
    $ git config -e [--global]
    
    # 设置提交代码时的用户信息
    $ git config [--global] user.name "[name]"
    $ git config [--global] user.email "[email address]"
    
  • 增加/删除文件

    # 添加指定文件到暂存区
    $ git add [file1] [file2] ...
    
    # 添加指定目录到暂存区,包括子目录
    $ git add [dir]
    
    # 添加当前目录的所有文件到暂存区
    $ git add .
    
    # 添加每个变化前,都会要求确认
    # 对于同一个文件的多处变化,可以实现分次提交
    $ git add -p
    
    # 删除工作区文件,并且将这次删除放入暂存区
    $ git rm [file1] [file2] ...
    
    # 停止追踪指定文件,但该文件会保留在工作区
    $ git rm --cached [file]
    
    # 改名文件,并且将这个改名放入暂存区
    $ git mv [file-original] [file-renamed]
    
  • 代码提交

    # 提交暂存区到仓库区
    $ git commit -m [message]
    
    # 提交暂存区的指定文件到仓库区
    $ git commit [file1] [file2] ... -m [message]
    
    # 提交工作区自上次commit之后的变化,直接到仓库区
    $ git commit -a
    
    # 提交时显示所有diff信息
    $ git commit -v
    
    # 使用一次新的commit,替代上一次提交
    # 如果代码没有任何新变化,则用来改写上一次commit的提交信息
    $ git commit --amend -m [message]
    
    # 重做上一次commit,并包括指定文件的新变化
    $ git commit --amend [file1] [file2] ...
    
  • 分支

    # 列出所有本地分支
    $ git branch
    
    # 列出所有远程分支
    $ git branch -r
    
    # 列出所有本地分支和远程分支
    $ git branch -a
    
    # 新建一个分支,但依然停留在当前分支
    $ git branch [branch-name]
    
    # 新建一个分支,并切换到该分支
    $ git checkout -b [branch]
    
    # 新建一个分支,指向指定commit
    $ git branch [branch] [commit]
    
    # 新建一个分支,与指定的远程分支建立追踪关系
    $ git branch --track [branch] [remote-branch]
    
    # 切换到指定分支,并更新工作区
    $ git checkout [branch-name]
    
    # 切换到上一个分支
    $ git checkout -
    
    # 建立追踪关系,在现有分支与指定的远程分支之间
    $ git branch --set-upstream [branch] [remote-branch]
    
    # 合并指定分支到当前分支
    $ git merge [branch]
    
    # 选择一个commit,合并进当前分支
    $ git cherry-pick [commit]
    
    # 删除分支
    $ git branch -d [branch-name]
    
    # 删除远程分支
    $ git push origin --delete [branch-name]
    $ git branch -dr [remote/branch]
    
  • 标签

    # 列出所有tag
    $ git tag
    
    # 新建一个tag在当前commit
    $ git tag [tag]
    
    # 新建一个tag在指定commit
    $ git tag [tag] [commit]
    
    # 删除本地tag
    $ git tag -d [tag]
    
    # 删除远程tag
    $ git push origin :refs/tags/[tagName]
    
    # 查看tag信息
    $ git show [tag]
    
    # 提交指定tag
    $ git push [remote] [tag]
    
    # 提交所有tag
    $ git push [remote] --tags
    
    # 新建一个分支,指向某个tag
    $ git checkout -b [branch] [tag]
    
  • 查看信息

    # 显示有变更的文件
    $ git status
    
    # 显示当前分支的版本历史
    $ git log
    
    # 显示commit历史,以及每次commit发生变更的文件
    $ git log --stat
    
    # 搜索提交历史,根据关键词
    $ git log -S [keyword]
    
    # 显示某个commit之后的所有变动,每个commit占据一行
    $ git log [tag] HEAD --pretty=format:%s
    
    # 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件
    $ git log [tag] HEAD --grep feature
    
    # 显示某个文件的版本历史,包括文件改名
    $ git log --follow [file]
    $ git whatchanged [file]
    
    # 显示指定文件相关的每一次diff
    $ git log -p [file]
    
    # 显示过去5次提交
    $ git log -5 --pretty --oneline
    
    # 显示所有提交过的用户,按提交次数排序
    $ git shortlog -sn
    
    # 显示指定文件是什么人在什么时间修改过
    $ git blame [file]
    
    # 显示暂存区和工作区的差异
    $ git diff
    
    # 显示暂存区和上一个commit的差异
    $ git diff --cached [file]
    
    # 显示工作区与当前分支最新commit之间的差异
    $ git diff HEAD
    
    # 显示两次提交之间的差异
    $ git diff [first-branch]...[second-branch]
    
    # 显示今天你写了多少行代码
    $ git diff --shortstat "@{0 day ago}"
    
    # 显示某次提交的元数据和内容变化
    $ git show [commit]
    
    # 显示某次提交发生变化的文件
    $ git show --name-only [commit]
    
    # 显示某次提交时,某个文件的内容
    $ git show [commit]:[filename]
    
    # 显示当前分支的最近几次提交
    $ git reflog
    
  • 远程同步

    # 下载远程仓库的所有变动
    $ git fetch [remote]
    
    # 显示所有远程仓库
    $ git remote -v
    
    # 显示某个远程仓库的信息
    $ git remote show [remote]
    
    # 增加一个新的远程仓库,并命名
    $ git remote add [shortname] [url]
    
    # 取回远程仓库的变化,并与本地分支合并
    $ git pull [remote] [branch]
    
    # 上传本地指定分支到远程仓库
    $ git push [remote] [branch]
    
    # 强行推送当前分支到远程仓库,即使有冲突
    $ git push [remote] --force
    
    # 推送所有分支到远程仓库
    $ git push [remote] --all
    
  • 撤销

    # 恢复暂存区的指定文件到工作区
    $ git checkout [file]
    
    # 恢复某个commit的指定文件到暂存区和工作区
    $ git checkout [commit] [file]
    
    # 恢复暂存区的所有文件到工作区
    $ git checkout .
    
    # 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
    $ git reset [file]
    
    # 重置暂存区与工作区,与上一次commit保持一致
    $ git reset --hard
    
    # 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变
    $ git reset [commit]
    
    # 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致
    $ git reset --hard [commit]
    
    # 重置当前HEAD为指定commit,但保持暂存区和工作区不变
    $ git reset --keep [commit]
    
    # 新建一个commit,用来撤销指定commit
    # 后者的所有变化都将被前者抵消,并且应用到当前分支
    $ git revert [commit]
    
    # 暂时将未提交的变化移除,稍后再移入
    $ git stash
    $ git stash pop
    
  • 其他

    # 生成一个可供发布的压缩包
    $ git archive
    

6. 如何解决跨域

  跨域是指一个域下的文档或脚本试图去请求另一个域下的资源

同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
同源策略限制以下几种行为:

  1. Cookie、LocalStorage 和 IndexDB 无法读取
  2. DOM 和 Js对象无法获得
  3. AJAX 请求不能发送

跨域解决方案:
1、 通过jsonp跨域

  通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,
  在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,
  基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。
  缺点:只能实现get一种请求。

2、 跨域资源共享(CORS)

  普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,
  若要带cookie请求:前后端都需要设置。
  由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。
  目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS)),CORS也已经成为主流的跨域解决方案。

3、 nginx代理跨域
nginx配置解决iconfont跨域

  浏览器跨域访问js、css、img等常规静态资源被同源策略许可,
  但iconfont字体文件(eot|otf|ttf|woff|svg)例外,
  此时可在nginx的静态资源服务器中加入以下配置:
  location / {
    add_header Access-Control-Allow-Origin *;
  }

nginx反向代理接口跨域

  跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。
  服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
  实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,
  反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

nginx具体配置:

  #proxy服务器
  server {
      listen       81;
      server_name  www.domain1.com;

      location / {
          proxy_pass   http://www.domain2.com:8080;  #反向代理
          proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
          index  index.html index.htm;

          # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
          add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
          add_header Access-Control-Allow-Credentials true;
      }
  }

4、 nodejs中间件代理跨域

  node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,
  也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,
  实现当前域的cookie写入,方便接口登录认证。

5、 WebSocket协议跨域

  WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。

7. JS有几种数据类型?值类型和引用数据类型区别?

值类型

  number
  string
  boolean
  null
  undefined
  symbol (ES6)
  bigint (ES10)

引用数据类型

  object

值类型和引用数据类型区别:

  值类型:是按值访问的,可以直接操作保存在变量中的实际值

  引用数据类型:是保存在堆内存中的对象。
  不可以直接访问堆内存空间中的位置和操作堆内存空间。
  只能操作对象在栈内存中的引用地址。
  引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。
  通过这个引用地址可以快速查找到保存中堆内存中的对象。

8. 什么是事件流?事件捕获?事件冒泡?

当一个HTML元素产生一个事件时,该事件会在元素节点与根节点之间的路径传播,路径所经过的节点都会收到该事件,这个传播的过程叫做DOM事件流
元素触发事件时,事件的传播过程称为事件流,过程分为捕获和冒泡两种
冒泡事件:事件由子元素传递到父元素的过程
捕获事件:事件由父元素到子元素传递的过程

9. 如何添加一个DOM对象到body中?innerHTML和innerText区别?

  //新建一个div元素节点
  var div=document.createElement("div");
  div.innerText = "helloworld";
  //把div元素节点添加到body元素节点中成为其子节点,但是放在body的现有子节点的最后
  document.body.appendChild(div);
  //插入到最前面
  document.body.insertBefore(div, document.body.firstElementChild);

innerHTML和innerText区别:
innerHTML设置或获取标签所包含的HTML+文本
信息(从标签起始位置到终止位置全部内容,包括HTML标签,但不包括自身)
innerText设置或获取标签所包含的文本信息(从标签起始位置到终止位置的内容,去除HTML标签,但不包括自身)
面试题整理_第1张图片

10. 什么是闭包?堆栈溢出有什么区别?内存泄漏?哪些操作会造成内存泄漏?怎样防止内存泄漏?

闭包就是能够读取其他函数内部变量的函数。

  由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,
  因此可以把闭包简单理解成"定义在一个函数内部的函数"。
  所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

栈溢出是指不断的调用方法,不断的压栈,最终超出了栈允许的栈深度,就会发生栈溢出,比如递归操作没有终止,死循环。
堆栈溢出仅是js语法环境的“栈”溢出,而内存泄漏是会涉及到电脑硬件(变量过多未回收或其他原因导致浏览器占用cpu过高,浏览器卡死)
造成内存泄露的操作:

  1.意外的全局变量
  2.闭包
  3.没有清理的DOM元素引用
  4.被遗忘的定时器或者回调
  5.子元素存在引起的内存泄露
  6.IE7/8引用计数使用循环引用产生的问题

防止内存泄露:

  1)减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;
  2)注意程序逻辑,避免“死循环”之类的 ;
  3)避免创建过多的对象  原则:不用了的东西要及时归还。

11. 简述ajax执行流程(代码)

第一步创建ajax对象:

    new XMLHttpRequest();

第二步填写请求信息:

    xhr.open('method',url,Asynchronous)
method(请求方式):
    1、get通常用来获取数据,偶尔也会携带一些数据
        (1)发送的内容显示在浏览器地址栏上。
        (2)因为浏览器地址栏长度有限制,而get是通过url向服务器发送数据的,所以发送的数据大小会被限制。
    2、post通常用来向服务器发送数据,偶尔也会携带一些数据到客户端。
        (1)显示在请求头信息。
        (2)post传输数据理论来说是没有大小限制的,post的大小限制来自于服务器。
url:请求文件的地址
Asynchronous(是否异步):
    异步:不会等待数据请求结束继续执行下边的代码,当数据请求成功的时候,在回去进行处理。
    同步:必须等待前边的数据请求结束才可以执行下一行代码,可能会造成页面假死。

第三步监控请求结果

  ajax对象下边有一个onload在数据拿到之后就会触发
  xhr.responseText表示请求到的文本内容。
  xhr.responseXML表示请求到的XML。

第四步发送请求

  xhr.send(data)
  data发送给服务器的数据

例:

  document.onclick=function(){
      var xhr=new XMLHttpRequest();
      xhr.open('get','1.txt',true);
      xhr.onload=function(){
          alert(xhr.responseText);
      }
      xhr.send();
  }

12. 什么是构造函数?与普通函数有什么区别?

构造函数:

  用new关键字来调用的函数,首字母一般大写
  用this来构造它的属性以及方法

两者区别在于:

1、调用方式不一样

  //构造函数也是一个普通函数,创建方式和普通函数一样。
  function Foo(){}
  Foo();//普通函数调用方式
  var f = new Foo();//构造函数调用方式
  • 普通函数调用方式:直接调用person();
  • 构造函数调用方式:需要使用new关键字来调用 new person();

2、作用也不一样(构造函数用来新建实例对象)
3、首字母大小写习惯

  • 一般构造函数的函数名称会用大写
  • 普通函数用小写

4、函数中this的指向不同

  • 普通函数中的this,在严格模式下指向undefined,非严格模式下指向window对象。

    function foo(){
        console.log(this===window);
    }
    foo();
    //代码运行结果:true
    
  • 构造函数的this则是指向它创建的对象实例。

    function Foo(){
        this.name = "博客园";
    }
    var f = new Foo();
    console.log(f.name);
    //代码运行结果:博客园
    //补充:构造函数的函数名和类名相同:Foo()这个构造函数,Foo是函数名,也是这个对象的类名。
    

5、写法的不同

  • 构造函数:

    function Person(name){
        this.name = name;
    }
    var p = new Person('John');//使用new关键字,不使用return
    
  • 普通函数:

    function person(name){
        var obj = new Object();
        obj.name = name;
        return obj;//使用return
    }
    var p = person('john');
    

你可能感兴趣的