web前端常见二十道面试题

01 作用域

var num1 = 55;
var num2 = 66;

function f1(num, num1){
    // var num = 55;
    // var num1 = 66;
    num = 100;
    num1 = 100;
    num2 = 100;
    console.log(num) // 100
    console.log(num1) // 100
    console.log(num2) // 100
}

f1(num1, num2)
console.log(num1) // 100
console.log(num2) // 66
console.log(num) // 报错 Uncaught ReferenceError: num is not defined

02 值类型和引用类型的传递

function Person(name,age,salary){
    this.name = name;
    this.age = age;
    this.salary = salary;
}

function f1(person){
    // var person = p
    person.name = "ls"
    person = new Person("aa", 23, 10)
}

var p = new Person("zs", 18, 1000)
console.log(p.name) // zs

f1(p)
console.log(p.name) // ls

03 封装函数进行字符串驼峰命名的转换

已知有字符串foo = "get-lement-by-id",写一个function将其转化成驼峰命名表示法"getElementById"

// 已知有字符串foo = "get-lement-by-id",写一个function将其转化成驼峰命名表示法"getElementById"
// var foo = "get-element-by-id";
function toUppercaseMessage(foo){
    // 根据某个字符串进行切割
    var arr = foo.split('-');
    // 获取元素中的第一个字符并转换成大写
    for(var i = 0 ; i < arr.length ; i++){
        // arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substr(1, arr[i].length-1)
        // arr[i] = arr[i][0].toUpperCase() + arr[i].substring(1, arr[i].length)
        //arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substr(1)
        arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substring(1)
    }
    // 根据某个字符将数组转换成字符串
    return arr.join('')
}

console.log(toUppercaseMessage("get-element-by-id"));

04 冒泡排序

var arr = [5,43,32,20,122];
// 轮数
for(var i = 0 ; i < arr.length ; i++){
    // 次数
    for(var j = 0 ; j < arr.length - 1 - i ; j++){
        // 判断前一个大于后一个数时进行交换
        if(arr[j] > arr[j+1]){
            // 借助第三方变量交换两个变量的值
            var temp = arr[j]
            arr[j] = arr[j+1]
            arr[j+1] = temp
        }
    }
}
console.log(arr)

05 翻转数组

var arr =[0,1,2,3,4,5,6,7,8,9];
for(var i = 0 ; i < arr.length / 2 ; i++){
    // arr[0]	arr[arr.length-1-0]
    // arr[1] 	arr[arr.length-1-1]
    // arr[2]	arr[arr.length-1-2]
    // arr[3] 	arr[arr.length-1-3]

    // 借助第三方变量交换两个变量的值 
    var temp =  arr[i];
    arr[i] = arr[arr.length - 1 - i];
    arr[arr.length - 1 - i] = temp;
}
console.log(arr)

06 去掉数组中重复的数据

/**
 * 1. 创建一个新数组,把原数组中的第一个元素插入到新数组中
 * 2. 遍历原数组中的每一个元素分别和新数组中的每一个元素进行比较
 */
// 原数组
var arr =[0,1,2,3,3,5,6,9,8,9];
// 新数组
var temp = [];
temp[0] = arr[0]
// arr中的每个元素
for(var i = 0 ; i < arr.length ; i++){
    // t中的每个元素
    for(var j = 0 ; j < temp.length ; j++){
        // 当原数组中的值和新数组中的值相同的时候,就没必要在进行比较了,跳出内循环。
        if(temp[j] == arr[i]){
            break;
        }
        // 拿原数组中的某个元素比较到新数组中的最后一个元素还没有重复
        if(j == temp.length-1){
            // 将数据插入新数组
            temp.push(arr[i]);
        }
    }
}
console.log(temp)

07 1物理像素的实现

物理像素 和 css像素的关系:像素比 = 物理像素 / css像素

  • 方法一
<style>
* {
    margin: 0;
    padding: 0;
}
#box {
    width: 0.5rem;
    height: 0.5rem;
    border-bottom: 1px solid #000;
}
</style>
<meta name="viewport" content="width=1, user-scalable=no, initial-scale=1.0">
<div id="box"></div>
window.onload = function(){
    // 像素比
    var dpr = window.devicePixelRatio;
    // 缩放比例
    var scale = 1 / dpr;

    var width = document.documentElement.clientWidth;

    // 获取meta标签
    var metaNode = document.querySelector('meta[name="viewport"]');
    metaNode.setAttribute('content','width=1, user-scalable=no, initial-scale='+ scale)

    // 页面中元素宽度、高度、比例反向乘回来
    var htmlNode = document.querySelector('html');
    htmlNode.style.fontSize = width * dpr + 'px';
}
  • 方法二
* {
    margin: 0;
    padding: 0;
}
#box {
    width: 200px;
    height: 200px;
    padding: relative;
}
#box::before {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 1px;
    background: #000;
}
@media screen and (-webkit-min-device-pixel-ratio: 2){
    #box::before {
        transform: scaleY(0.5)
    }
}
@media screen and (-webkit-min-device-pixel-ratio: 3){
    #box::before {
        transform: scaleY(0.333333)
    }
}
<meta name="viewport" content="width=1, user-scalable=no, initial-scale=1.0">
<div id="box"></div>
* {
    margin: 0;
    padding: 0;
}
#box {
    width: 200px;
    height: 200px;
    padding: relative;
}
#box::before {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 1px;
    background: #000;
}
@media screen and (-webkit-min-device-pixel-ratio: 2){
    #box::before {
        transform: scaleY(0.5)
    }
}
@media screen and (-webkit-min-device-pixel-ratio: 3){
    #box::before {
        transform: scaleY(0.333333)
    }
}

08 元素的水平垂直居中

* {
    margin: 0;
    padding: 0;
}
#wrap {
    width: 500px;
    height: 500px;
    background: grey;
    position: relative;
}
<div id="wrap">
    <div class="box"></div>
</div>
  • 方法一

#wrap .box {
    width: 200px;
    height: 200px;
    background: pink;
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    margin: auto;
}
  • 方法二
#wrap .box {
    width: 200px;
    height: 200px;
    background: pink;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%);
}
  • 方法三
#wrap .box {
    width: 200px;
    height: 200px;
    background: pink;
    position: absolute;
    left: 50%;
    top: 50%;
    margin-left: -100px;
    margin-top: -100px;
}
  • 方法四
#wrap {
    width: 500px;
    height: 500px;
    background: grey;
    display: flex;
    justify-content: center;
    align-items: center;
}
#wrap .box {
    width: 200px;
    height: 200px;
    background: pink;
}

09 用纯css创建一个三角形

#box {
    width: 0px;
    height: 0px;
    border: 100px solid;
    border-top-color: red;
    border-right-color: transparent;
    border-bottom-color: transparent;
    border-left-color: transparent;
}
<div id="box"></div>

10 rem的适配

适配解决的问题是同一个元素在不同屏幕上呈现同样的效果。

html根元素的字体大小设置为屏幕区域的宽

* {
     margin: 0;
     padding: 0;
}
#box {
    width: 0.5rem;
    height: 0.5rem;
    background: red;
}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<div id="box"></div>
 window.onload = function(){
     // 获取屏幕区域的宽度
     var width = document.documentElement.clientWidth;
     // 获取html
     var htmlNode = document.querySelector('html');
     // 设置字体大小
     htmlNode.style.fontSize = width + 'px';
}

11 求背景图片左边到#box盒子左边框外侧的距离

* {
    margin: 0;
    padding: 0;
}
#box {
    width: 400px;
    height: 200px;
    background: pink;
    padding: 100px;					# 100px
    border: 100px solid blue;		 # 100px
    background-image: url("bg.jpg");  # 默认是从content开始的,即背景图覆盖padding
    background-repeat: no-repeat;
    background-origin: content-box;	  # 设置让从padding开始
    background-position: -50px 0;	  # 向左便宜50
}
# 计算最终的值为 100 + 100 - 50 = 150px
<div id="box"></div>

12 js综合面试题

/* function Foo(){
            getName = function(){
                alert(1);
                return this;
            }
        }
        Foo.getName = function(){
            alert(2)
        }
        Foo.prototype.getName = function(){
            alert(3)
        }
        var getName = function(){
            alert(4)
        }
        function getName(){
            alert(5)
        }*/


        // 变量提升后
        function Foo(){
            getName = function(){
                alert(1);
                return this;
            }
        }
        // var getName;
        function getName(){
            alert(5)
        }
        /***************************************/
        Foo.getName = function(){
            alert(2)
        }
        Foo.prototype.getName = function(){
            alert(3)
        }
        getName = function(){
            alert(4)
        }

        // 请写出下列的输出结果     .运算符优先级最高
        Foo.getName(); // 2
        getName();  // 4
        // Foo().getName(); // (Foo()).getName() => window.getName()  1
        getName();  // 1
        new Foo.getName();  // new (Foo.getName)() ——> new (function(){alert(2)})()  2
        new Foo().getName(); // (new Foo()).getName() ——> foo.getName()  3
        new new Foo().getName();  // new ((new Foo()).getName)() ——> ——> new (function(){alert(3)})() 3

13 什么是函数节流和函数防抖

  • 函数节流:一个函数执行一次后,只有大于设定的执行周期才会执行第二次

    • 有个需要频繁触发函数,处于优化性能角度,在规定时间内,只让函数触发的第一次生效,后面不生效

    • # 节流函数
      @param fn 要被节流的函数
      @param delay 规定的时间
      function throttle(fn, delay){
          // 记录上一次函数触发的时间
          var lastTime = 0;
          return function (){
              // 记录当前函数触发的时间
              var nowTime = Date.now() ;
      
              if(nowTime - lastTime > delay){
                  // 修改this的指向问题
                  fn.call(this);
                  fn()
                  // 同步时间
                  lastTime = nowTime;
              }
          }
      }
      document.onscroll = throttle(function(){
          console.log("scroll事件被触发了"+Date.now())
      }, 200)
      
  • 防抖函数:一个需要频繁触发的函数,在规定的时间内,只让最后一次生效,前面的不生效

    • function debounce(fn, delay){
          // 记录上一次的延时器
          var timer = null;
          return function(){
              // 清除上一次延时器
              clearTimeouit(timer);
              // 重新设置新的延时器
              timer = setTimeout(function(){
                  fn().apply(this);
              }, delay);
          }
      }
      
      document.getElementById('btn').onclick = debounce(function(){
          console.log("防抖函数被触发了" + Date.now())
      },1000)
      

14 什么是跨域 ?解决跨域的办法有哪些?

  1. 同源策略
    • 是浏览器安全策略
    • 协议、域名、端口号必须完全一致
  2. 跨域
    • 违背同源策略就会产生跨域
  3. 解决跨域
    • jsonp cors 服务器代理
// 创建script标签
var script = document.createElement('script')
// 设置回调函数
function getData(data){
    // 数据请求回来会被触发的函数
}
// 设置script的src属性,设置请求地址
script.src = 'http://localhost:3000?callback=getData';
// 让script生效
document.body.appendChild(script)

15 从一个url地址到最终页面渲染完成,发生了什么?

  1. DNS解析: 将域名地址解析为ip地址
    • 浏览器DNS缓存
    • 系统DNS缓存
    • 路由器DNS缓存
    • 网络运营商DNS缓存
    • 递归搜索:blog.baidu.com
      • .com域名下查找DNS解析
      • .baidu
      • blog
      • 出错
  2. TCP连接: TCP三次握手
    • 第一次握手,由浏览器发起,告诉服务器我要发送请求了
    • 第二次握手,由服务器发起,告诉浏览器我准备接收了,你赶紧发送吧
    • 第三次握手,由浏览器发送,告诉服务器,我马上就发了准备接受吧
  3. 发送请求
    • 请求报文:HTTP协议的通信内容
  4. 接受响应
    • 响应报文
  5. 渲染页面
    • 遇见HTML标记,浏览器调用HTML解析器解析成Token并构建成dom树
    • 遇见style/link标记,浏览器调用css解析器,处理css标记构建cssom树
    • 遇到script标记,调用javascript解析器,处理script代码(绑定事件、修改dom树、cssom树)
    • 将dom树和cssom树 合并成一个渲染树
    • 根据渲染树来计算布局,计算每个节点的几何信息(布局)
    • 将各个节点颜色绘制到屏幕上(渲染)
    • ps: 这五个步骤不一定安装顺序执行,如果dom树或cssom树被修改了,可能会执行多次布局和渲染,往往实际页面中,这些步骤都会执行多次的。
  6. 断开连接: TCP的四次挥手
    • 第一次挥手:由浏览器发起的,发送给服务器,我东西发送完了(请求报文),你准备关闭吧
    • 第二次挥手:由服务器发起的,告诉浏览器,我东西接收完了(请求报文),我准备关闭了,你也准备把
    • 第三次挥手:由服务器发起,告诉浏览器,我东西发送完了(响应报文),你准备关闭吧
    • 第四次挥手:由浏览器发起的,告诉服务器,我东西接收完了,我准备关闭了(响应报文),你也准备吧

16 宏任务和微任务

  1. 宏任务
    • 分类
      • setTimeout
      • setInterval
      • requrestAnimationFrame
    • 宏任务所处的队列就是宏任务队列
    • 第一个宏任务队列中只有一个任务:执行主线程的js代码
    • 宏任务队列可以有多个
  2. 微任务
    • 分类
      • new Promise().then(回调)
      • process.nextTich
    • 微任务所处的队列就是微任务队列
    • 只有一个微任务队列
    • 在上一个宏任务队列执行完毕后如果有微任务队列就会执行微任务队列中的所有任务

17 node.js事件轮询机制

setImmediate(function(){
    console.log('setImmediate()')
})
setTimeout(function(){
    console.log('setTimeout()')
},0)
process.nextTick(function(){
    console.log('processs.nextTick()')
})
# 执行结果 
processs.nextTick()
setTimeout()
setImmediate()

借助libuv库实现的概括事件轮询机制,分为6个阶段

  1. timers定时器阶段
    • 计时和执行到点的定时器回调函数
  2. pending callbacks
    • 某些系统操作(例如TCP错误类型)的回调函数
  3. idle,prepare
    • 准备工作
  4. poll轮询阶段(轮询队列)
    • 如果轮询队列不为空,依次同步取出轮询队列中的第一个回调函数执行,知道轮询队列为空或者达到系统最大的限制。
    • 如果轮询队列为空
      • 如果之前 设置过setImmediate函数
        • 直接进入下一个check阶段
      • 如果之前没有设置过setImmediate函数
        • 在当前pll阶段等待
          • 知直到轮询队列添加到回调函数,就去第一个情况执行
          • 如果定时器到点了,也会去下一个阶段
  5. check查阶段
    • 执行setImmediate设置的回调函数
  6. close callbacks 关闭 阶段
    • 执行 close事件回调函数

process.nextTick()能在任意阶段优先执行

18 Vue的MVVM的实现原理

  1. 理解
    • Vue作为MVVM模式的实现库的2中技术
      • 模板解析
      • 数据绑定
    • 模板解析:实现初始化显示
      • 解析大括号表达式
      • 解析指令
    • 数据绑定:实现更新显示
      • 通过数据劫持实现

19 Vuex管理状态的机制

  1. 对Vuex基本理解
    • 是什么:Vuex是一个专为vue.js应用程序开发的状态管理的vue插件
    • 作用:集中式管理vue多个组件共享的状态和后台获取的数据
  2. Vuex的工作原理

20 说说Vue组件间的通信方式

通信的种类
  1. 父组件向子组件通信
  2. 子组件向父组件通信
  3. 隔代组件间通信
  4. 兄弟组件间通信
实现通信方式
  1. props
  2. vue自定义事件
  3. 消息订阅与发布
  4. vuex
  5. slot
方式1: props
  1. 通过一般属性实现父向字通信
  2. 通过函数属性实现子向父通信
  3. 缺点:隔代组件和兄弟组件间通信比较麻烦
方式2: vue自定义事件
  1. vue内置实现,可以代替函数类型的props
    • 绑定监听:<MyComp @eventName=“callback”>
    • 触发(分发)事件:this.$emit(“eventName”,data)
  2. 缺点:只适合于子向父通信
方式3:消息订阅与发布
  1. 需要引入消息订阅与发布的实现库,如:pubsub-js
    • 订阅消息:PubSub.subscribe(‘msg’,(msg,data)=>{})
    • 发布消息:PubSub.publish(‘msg’,data)
  2. 优点:此方式可用于任意关系组件间通信
方式4: vuex
  1. 是什么:vuex是vue官方提供的集中式管理vue多组件共享状态数据的vue插件
  2. 优点:对组件间关系没有限制,且相比于pubsub库管理更集中,更方便
方式5:slot
  1. 是什么:专门用来实现父向子传递带数据的标签
    • 子组件
    • 父组件

注意:通信的标签模板是在父组件中解析好后再传递给子组件的。

21 React与Vue

相同点:

  1. 都有组件化开发和Virtual DOM
  2. 都支持props进行父子组件间数据通信
  3. 都支持数据驱动视图,不直接操作真实DOM,更新状态数据界面就自动跟新
  4. 都支持服务器端渲染
  5. 都支持native的方案,React Native, Vue的Weex

不同点:

  1. 数据绑定: vue实现了数据的双向绑定,react数据流动是单向的
  2. 组件写法不一样,React推荐的做法是JSX,也就是把HTML和CSS全都写进Javascript了,即’all in js’; Vue推荐的做法是webpack+vue-loader的单文件组件格式,即html,css,js写在同一个文件
  3. state对象在react应用中不可变的,需要使用setState方法更新状态;在vue中,在state对象不是必须的,数据由data属性在vue对象中管理。
  4. virtual DOM不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树,而对于React而言,每当应用的状态被改变时,全部组建都会重新渲染,所以react汇总会需要shouldComponentUpdate这个生命周期函数方法来进行控制
  5. React严格上只针对MVC的view层,Vue是MVVM模式

22 小程序和mpVue的对比

小程序开发有哪些痛点?

  • 频繁调用 setData及 setData过程中页面跳闪

  • 组件化支持能力太弱(几乎没有)

  • 不能使用 less、scss 等预编译器

  • request 并发次数限制

mpvue开发小程序的优点:

1,彻底的组件化开发能力:提高代码复用性

2,完整的 Vue.js 开发体验

3,方便的 Vuex 数据管理方案:方便构建复杂应用

4,快捷的 webpack 构建机制:自定义构建策略、开发阶段 hotReload

5,支持使用 npm 外部依赖

6,使用 Vue.js 命令行工具 vue-cli 快速初始化项目

23 变量提升/预处理

js引擎在代码正在执行之前会做一个预处理的工作:

  • 收集变量

  • 收集函数

    依据:

    ​ var 将 var后面的变量定义但是不赋值 var username = undefined;

    ​ function(){} 提前定义该函数

console.log(username); // undefined
var username = 'kobe';
console.log(suername);

fun();	// 正常执行函数
function fun(){
    console.log('fun()');
}
  • 执行上下文

    • 理解:代码执行的环境

    • 时机:代码正式执行之前会进入到执行环境

    • 工作:

      • 创建变量对象:

        • 变量
        • 函数及函数的参数
        • 全局:window
        • 局部:抽象的但是确实存在
      • 确认this的指向

        • 全局:this -> window
        • 局部:this -> 调用其的对象
      • 创建作用域链

        • 父级作用域链+当前的变量对象
      • 扩展

        • ECobj = {

          ​ 变量对象:{变量,函数,函数的形参}

          ​ scopeChain: 父级作用域链 + 当前的变量对象

          ​ this: {window||调用其的对象}

          }

  • 执行上下文对象

猜你喜欢

转载自blog.csdn.net/yw00yw/article/details/83623334