面试之js与浏览器与适配

浏览器相关

修改数组下标 不会影响视图

  • 数据是修改的,但是视图没有变化的
    • 原因:vue2是使用ObjectDefineProperty来实现的,只监听data数据存在的变量,对于循环添加新值的时候,监听不到,因此造成数据发生变化,视图没有发生变化
    • vue3 则不会有这样的问题,因为proxy会一直监听整个数据
  • 解决方式:使用 this.$set的方法去解决
<!-- -->
<template>
  <div>
    MenuBar
    {{ msg }}
    <div v-for="item in arr" :key="item">
      {{ item }}
    </div>
    <button @click="clickBtn">修改最后一项</button>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
@Component({
  components: {}
})
export default class MenuBar extends Vue {
  msg = '我是msg'
  arr = [1, 2, 3]
  clickBtn = () => {
    this.arr[2] = 333 // 直接修改下标 数据修改了 但是视图不修改
    console.log('this.arr', this.arr) // 修改数组下标的值 不会影响视图 [1, 2, 333]
    // this.$set(this.arr, 0, 111) // 注释掉上方 验证下方 输出 [1,2,333] 视图也发生变化
    // console.log('this.arr', this.arr) // 修改数组下标的值 不会影响视图 [1, 2, 333]
  }
}
</script>
<style scoped></style>

  • 效果
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

浏览器的同源策略

  • 概念:浏览器的同源策略是指 js脚本在未经允许的情况下,不能够访问其他域下的内容。
  • 什么是同源
    • 协议,域名,端口都相同的则是同源,其中一个不同则都不属于同源。
  • 同源策略的限制三个方面
    • 1、一个域下的js脚本不能访问另一个域下面的cookie,localStorage和indexDB
    • 2、一个域下的js脚本不能够操作另一个域下的DOM.
    • 3、ajax不能做跨域请求。
  • 为什么script标签,img标签,以及link标签可以链接其他域中的资源?
    • 因为这些标签不能通过相应的结果来进行安全问题的操作。

解决跨域与何为协议、域名、端口

  • http://www.test.com/index.html
  • 协议
    • 常见的协议:http、https
  • 域名
    • 常见域名:.com、.top、.net、.org ( 每一个域名对应一个相应的IP)
    • 主域名、子域名之分( 主域名:test,子域名:www )
  • 端口
    • :80、:8080等
      在这里插入图片描述
  • 解决跨域
    • proxy代理
    • cors

输入一个url 的一次完整的http服务过程

  • 把题目可以解析为:在web浏览器输入:www.baidu.com 回车后会发生什么?

1:首先www.baidu.com 这个网址进行DNS域名解析,得到相应的IP地址
2:根据这个IP找到相应的服务器,发起TCP三次握手
3:建立TCP链接之后,发起HTTP请求
4:服务器相应HTTP请求,服务器把html代码发送给浏览器
5:浏览器解析html代码,并请求html之中的资源(比如:css,img等) 这是先得到html,再请求其他的
6:浏览器对页面进行渲染,呈现给用户
7:服务器关闭TCP连接(http连接)

垃圾回收机制

  • 常用的变量标记法
    • 当变量进入执行环境,就会被标记为进入环境,离开环境后会被标记为离开环境,在环境中的变量不会被释放,因为随时可能被用到。
    • 垃圾收集器在运行时会给内存中所有的变量加上标记,但是会去掉那些环境中的变量,以及被环境中的变量引用的变量,如果后续在被加上标记就会被视为准备删除的变量。
    • 最后垃圾收集器销毁这些带标记的变量,回收他们所占的内存。
      function aaa() {
        var b = 20 // 当前变量b进入环境 标记为变量进入环境
        console.log('b', b)
      }
      aaa() // aaa方法调用 使用到b变量 函数执行完毕 销毁变量 标记b变量被消耗
      // console.log('bb', b) // 会查找不到当前变量,因为被消耗了!

http和https区别 https的s是什么?

  • http:超文本传输协议
  • https:超文本传输安全协议
  • https的s是什么?
    • s指的是:利用 SSL 来加密数据包
  • http 和 https的区别
    • HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
    • 使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。
    • HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
    • http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
      HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源

http状态码

  • 成功200
  • 重定向方面的问题 300
    • 301:永久重定向
    • 302: 临时重定向
    • 304:缓存造成的问题
  • 请求或者权限相关错误 400
    • 400:报文存在语法错误
    • 401:发送请求需要 通过 http 发起请求
    • 403:请求资源被拒绝,没有权限访问
    • 404:没有找到该资源
  • 服务器相关500
    • 500:服务器发生报错

http2

  • http2就是对http1进行了升级,优化了http1的一些问题
  • 1:头部压缩
    • http1对Body进行压缩,但是没有对头部进行压缩
    • http2 对头部进行了压缩
  • 2:队头阻塞
    • http1采用tcp协议,为节省资源,采用长链接,会造成对头阻塞
    • http2 引入流和帧,解决http1的对头阻塞

前端做网络接口请求,网络情况不优,前端要怎么处理

  • 请求的时候 加loading 并且配置请求响应时间,超时不等待

浏览器线程和进程

浏览器进程

  • 浏览器的进程就是浏览器开辟的一个独立执行环境,对静态代码的执行环境,就是浏览器的一个tab页就是一个进程。

浏览器线程

  • 浏览器的线程:浏览器线程是建立再浏览器的进程之中,一个进程里面可以包含多个线程,线程之间是可以通信的,只不过代价相对较大
  • 线程的分类
    • GUI线程:
      • 就是针对html,css,dom节点渲染等相关的加载渲染线程
  • js引擎线程
    • 就是浏览器针对js代码执行,实现对应的交互效果(谷歌浏览器的js引擎为V8引擎)
    • 注意点:js引擎是单线程的,因此存在消息队列
  • 事件线程
    • 就是控制事件循环的相关线程是存在事件线程的,而不是js线程,再线程之中符合条件触发之后,把之放在消息队列之中,然后js引擎去执行对应的消息队列
  • 定时器线程
    • 就是setTimeout、setInterval这两种定时器,是存在定时器线程的,就是等待时间达到满足条件后,把要处理的事情放置再消息队列之中,等待js引擎去执行
  • http线程
    • 就是针对htpp相关请求的线程

前端缓存

  • http缓存
    • http缓存是http请求传输时用到的缓存,主要是在服务器代码上设置的(若是请求同一个接口时,返回的数据一致,是直接拿缓存的结果)
  • 浏览器缓存
    • 浏览器缓存有 localStorage sessionStorage cookie 等

前端如何做信息攻防

  • xss攻击
    • 原因:就是服务器对客户端的信任
    • 解决:需要 对用户输入进行过滤(对应script标签过滤掉)
  • csrf -> 请求伪造
    • 原因:就是服务器对客户端的信任
    • 解决:需要 用户请求时,携带token值

前端跨越处理

  • 直接使用 proxy的形式处理即可 (或者后端处理 cors)

js

typeof 与 instanceof的区别

  • typeof用于判断简单数据类型,对于对象而已,除了function 其他的都是Object
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'

typeof null // object
typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'
  • instanceof 用于判断复杂类型 原理是:根据对象的原型链来找出这个变量属于什么类型
const Person = function() {}
const p1 = new Person()
p1 instanceof Person // true

var str = 'hello world'
str instanceof String // false
//这是因为 str只是存储字符串的一个变量 不是一个对象,因此没有原型 直接为false

var str1 = new String('hello world')
str1 instanceof String // true

  let arr = [1, 2, 3]
  let obj = {
    name: "pink"
  }

  console.log("arr", arr instanceof Array);
  console.log("arr", arr instanceof Object);
  console.log("obj", obj instanceof Object);
  // 都为true 因此在判断数组的时候 需要先判断!

== 与 ===

  • == 就是相互比较的两者,会转化为同一种类型去比较,若是值是一致的,为true,否者为flase

  • === 就是直接比较两者,若是类型不一致,值不一致,直接返回flase

原型链

  • 概念:原型链就是所有原型所构成的一条链条,这链条称之为原型链
    • 对象查找机制的时候,通过 proto 查找对象原型上的属性,若是没有当前属性,则又查找对象原型下的 __proto__去查找,直到Object对象最顶层,若是没有找到,则返回null
  • 原型
    • 没有函数都有一个 prototype属性 称之为原型( 这个prototype属性指向函数的原型对象 )
    • 由于这个属性是一个对象,也称之为原型对象
    • 原型作用
      • 存储属性和方法 ( 拿数据或者调用方法 obj.xxx 、obj.fun() )
      • 也可以用于js的继承

在这里插入图片描述

判断数组、对象的几种方式

判断数据的三种方式

  • Array.isArray(arr)
  • arr instanceof Array
  • Object.prototype.toString.call(arr).slice(8, -1)
let arr = [11, 22]
let obj = { age: '20' }
// console.log('res', Array.isArray(arr), 'res2', Array.isArray(obj)) // res true res2 false
// console.log('res', arr instanceof Array) // res true
console.log('res', Object.prototype.toString.call(arr).slice(8, -1)) // res Array

判断对象的

  • typeof obj
  • obj instanceof obj
  • Object.prototype.toString.call(obj).slice(8, -1))
let obj = { age: '20' }
// console.log('res', typeof obj) // res object
// console.log('res', obj instanceof Object) // res true
// console.log('res', Object.prototype.toString.call(obj).slice(8, -1)) // res object

继承

  • es6方式
    • 使用 extend的 方式 直接去继承
  • es5方式
    • call + 原型对象的形式 去继承

数组的区别

apply和call的区别 bind

  • 都是改变this的指向
  • 区别点 (调用时候,传递的参数)
    • apply - 传递参数为数组
      • apply( this, [] )
      • 可以用于求数组最大值什么的
    • call - 传递参数为 多个元素
      • call( this, X , X )
      • 用于继承
    • bind - 改变this的指向,但是返回一个新的函数(原始函数不被直接调用)
      • bind( this, XX )

01 + 02 != 0.3

  • 因为计算机时用 0 1 去存储数据的,因此对于浮点数,有着止不禁的数值,于是加起来不等于 0.3
  • 解决
    • 方式1:
      • 就是 0.1 与 0.2 皆 * 10 相加后,除于10, 然后0.3 * 10 ,之后除于10,再进行比较
        let res = (0.1 * 10 + 0.2 * 10) / 10 === (0.3 * 10) / 10
        console.log('res', res) // res true
        
    • 方式2:直接使用 loadsh 工具库,进行处理

cookies,sessionStorage和localStorage的区别

  • 相同点
  • 他们都是以键值对的形式存储
  • 区别
    • cookies
      • 会话级别,浏览器关闭,数据清空
      • 存储大概 3k
      • 可以使用 express 去设置存储时间
    • sessionStorage
      • 会话级别,浏览器关闭,数据清空
      • 存储5M
      • 同一个页面数据可共享
    • localStorage
      • 永久存在,浏览器关闭,数据还在,除非手动删除
      • 存储可 5M
      • 同一个浏览器数据可共享

async 与 await

  • 一个函数若是前面添加了 async 则当前函数返回的则为一个 Promise
  • async 和 await是 对于异步操作的解决方案,它是Generator(生成器)函数的语法糖
    • async|await是编写异步的新方法,之前ES6中用的是promise。
    • async|await是建立在promise基础之上的新写法。
    • async|await也是非阻塞的
  • async 与 await 的优点
    • async|await解决了回调地狱的问题
    • async|await支持并发执行
    • async|await对异步处理更加简洁
    • async|await可以在try…catch中捕获错误
  • async 与 await 的缺点
    • await将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了await会导致性能上的降低

箭头函数

  • 可以改变this的指向
  • 箭头函数不具有arguments
  • 箭头函数没有原型属性
  • 就是当参数为一个的时候,可以省略()
  • 当事件处理函数为一条语句的时候,可以省略retrun 和 {}

数组的方法

数组es6

  • find 查找数组之中是否符合条件的值,若是符合则返回当前符合条件的数据
    • 可以return ( 也就是当前所筛选的条件 )
  • findIndex 找出满足条件的第一个元素,返回当前符合条件的下标
    • 可return ( 也就是当前所筛选的条件- 下标 )
  • Array.from 把可遍历的数组,转换为真实数组
  • Array.of 把值转换为数组
  • includes 判断数组里面是否包含有符合条件的元素,返回Boolean
  • flat 数组的平铺,把二维数组等平铺为一维数组
  • replaceAll 字符串的全局匹配与替换
  • trim trimStart trimEnd 去掉空格

数组es5

  • forEach遍历循环

    • 不能return 值
    • 不会影响原始数组,只是单纯的遍历
  • map 遍历数组,对数据进行加工后,返回一个新的数组

    • 可以return 值 (所return是所加工后的数据)
    • 不会影响原始数组,只是在原始数据提供的数据进行加工,返回新数组
  • filter 过滤掉符合条件的数据,返回一个新的数组

    • 可以return 值 ( 所return就是所过滤的符号数据 )
  • some 满足一个条件则返回一个true,否则全不满足,则为flase

  • eveny 满足所有条件则返回一个true,否则有一个不满足,则为flase

  • reduce

    reduce((sum,item)=>{},0)要有两个参数,第一个参数一定要初始化
    let arr = [{name:‘张三’,index:0},{name:‘李四’,index:1}];
    let result = arr.((array,item)=>{
    array.push(item.name)
    return array;;
    },[ ])
    结果 result 为[‘张三’,‘李四’]
    
  • concat( ):数组合并

    • concat() 方法不会更改现有数组。它总是返回一个新数组
  • join 数组转换为字符串

  • split 字符串转换为数组

  • push 向数组新增一个元素

  • pop 删除数组最后一个元素

  • unshift 向数组前面新增一个元素

  • shift 删除数组第一个元素

  • reserve 数组的翻转

  • sort 数组的排序

  • slice 数组元素的截取,返回一个新数组,新数组是截取的元素,可以为负值

  • splice 删除元素,并向数组添加新元素

  • substring 裁减数组元素

  • toString( ):数组转字符串;

  • 注意点:那些会影响原始数组

    • pop,push,unshift,shift,reverse,sort,splice

深拷贝 与 浅拷贝

深拷贝

  • 深拷贝不仅仅拷贝外层的数据(栈存放的地址),还有拷贝的是(栈存放的地址 指向堆里面的数据)
  • 思路
    • 先遍历拷贝的对象,是否为数组,若为数组则递归遍历
    • 若是为对象,也递归遍历
    • 最后为简单数据的时候,直接把值写入
function deepCopy(obj) {
  // 判断传递进来的是 数组还是对象
  let newObj = Array.isArray(obj) ? [] : {};
  for (let key in obj) {
    if (obj[key] && typeof obj[key] === "object") {
      newObj[key] = deepCopy(obj[key]);
    } else {
      // 没有子元素 简单数据类型
      newObj[key] = obj[key];
    }
  }
  // console.log("newObj", newObj);
  return newObj;
}
let obj = {
  id: 1,
  name: "lala",
  msg: {
    age: 20,
  },
  color: ["red", "pink", "yellow"],
};
let resObj = deepCopy(obj);
resObj.id = 2
resObj.msg.age = 30
resObj.color[1] = 'pink222'
console.log('obj',obj);
console.log("resObj", resObj);
  • 结果
    在这里插入图片描述

浅拷贝

  • 浅拷贝就是拷贝的是外面的一层数据,就是拷贝栈里面存放的地址
    let obj = {
      id: 1,
      name: "pink",
      msg: {
        age: 18
      }
    }
    let o = {}
    for (let k in obj) {
      // k为属性名 obj[k]为属性名  直接把属性值拷贝给另一个对象
      o[k] = obj[k];
    }
    console.log(o); //{id: 1, name: "pink", msg: {…}} age=18
    obj.msg.age = 30;
    console.log(o);
    console.log(obj);

事件冒泡和事件捕获

  • 事件的阶段:捕获阶段 - 当前目标阶段 - 冒泡阶段
  • 事件冒泡
    • 事件触发点,逐层向上触发同一类型的事件,直到document才停止
  • 事件捕获
    • 从document逐层向下捕获事件,直到当前事件触发点( 当前目标阶段 )
  • 阻止事件冒泡
    • e.stopPropagation()
    • cancelBubble = true
    • return false
  • 阻住默认事件
    • event. preventDefault()
  • 冒泡事件之事件委托
<ul id="ul">
	<li>1</li>
  <li>2</li>
	<li>3</li>
</ul>
	let ul = document.querySelector('#ul')
	ul.addEventListener('click', (event) => {
		console.log(event.target);
	})

闭包

  • 原理
    • 在函数外部,调用操作函数,函数内部的数据
    • 在函数内部,return了一个函数,内层的函数,使用了外层的变量,使之这个变量由局部变量(局部作用域)变为自由变量(具有全局性)
    • 也就是说,延申了变量的作用域
  • 优点
    • 延申了变量的作用域,使之变量原本处于函数内的作用域变为 一个自由变量( 任意时间内被调用,不会被销毁 )
  • 缺点
    • 过度的使用闭包可能会导致内存占用过多

数组es5、es6去重

es6去重

  • Array.from配合 new Set 去重简单数据的数组
      const arr = [1, 2, 3, 3, 4, 4];
      const newArr = Array.from(new Set(arr));
      console.log("newArr", newArr);

es5去重

  • 简单数据类型
      const arr = [1, 2, 3, 3, 4, 4];
      const newArr = []
      arr.forEach((item,idx)=>{
        if ( newArr.indexOf(arr[idx]) === -1) {
          newArr.push(arr[idx])
        }
      })
      console.log('newArr',newArr); // [1, 2, 3, 4]

数组里面包含对象

      let person = [
        { id: 0, name: "小明" },
        { id: 1, name: "小张" },
        { id: 2, name: "小李" },
        { id: 0, name: "小明" },
      ];
      let obj = {};
      let newPerson = [];
      newPerson = person.reduce((cur, next) => {
        obj[next.id] ? "" : (obj[next.id] = true && cur.push(next));
        return cur;
      }, []);
      console.log("newPerson", newPerson); 
      // newPerson [ { id: 0, name: "小明" },{ id: 1, name: "小张" },{ id: 2, name: "小李" }, ]

冒泡排序

  • 原始数组,在开辟一个新的数组,与初始数组相互比较,于是需要双for循环。
  • 双重for循环,在内层for循环之后,arr[j] 与 arr[j+1] 进行比较,若是前面一项的值大于后面一项的值,则采用临时变量交换法,把两个值交换过来即可。
<script>
    var arr = [5, 76, -8, 23, -15, 88, 47];
    for (var i = 0; i < arr.length; i++) {
        for (var j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                var temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    console.log(arr);
</script>

适配 rem布局等

rem布局 ( r也就是根标签 - html)

  • 原理

    • html之中设置font-size为16px,则
    • rem的大小是随着根元素html中的font-size的大小变化而变化
  • echarts 怎么适配

    • 使用rem的设置盒子的宽高、然后echarts监听resize 方法,去实现echarts的响应式
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值