面试题

584 阅读8分钟

js三种存储方式区别

  • 传递方式不同 cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。 sessionStorage和loaclStorage不会自动把数据发给服务器,仅在本地保存。
  • 数据大小不同 cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。 存储大小限制也不同,cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。 sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或者更大。 -数据有效期不同 sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持; localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据; cookie只在设置cookie过期时间之前一直有效,即使窗口或浏览器关闭。
  • 作用域不同 sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面; localStorage在所有同源窗口中都是共享的; cookie也是在所有同源窗口中都是共享的。 Web Storage支持事件通知机制,可以将数据更新的通知发送给监听者。 Web Storage的api接口使用更方便。

indexedDB

indexedDB就是一个非关系型数据库,它不需要你去写一些特定的sql语句来对数据库进行操作,因为它是nosql的,数据形式使用的是json

区别

首先说说Cookies,英文直接翻译过来就是小甜点,听起来很好吃,实际上并不是,每次HTTP接受和发送都会传递Cookies数据,它会占用额外的流量。例如,如果你有一个10KB的Cookies数据,发送10次请求,那么,总计就会有100KB的数据在网络上传输。Cookies只能是字符串。浏览器里存储Cookies的空间有限,很多用户禁止浏览器使用Cookies。所以,Cookies只能用来存储小量的非关键的数据。

其次说说LocalStorage,LocalStorage是用key-value键值模式存储数据,但跟IndexedDB不一样的是,它的数据并不是按对象形式存储。它存储的数据都是字符串形式。如果你想让LocalStorage存储对象,你需要借助JSON.stringify()能将对象变成字符串形式,再用JSON.parse()将字符串还原成对象。但如果要存储大量的复杂的数据,这并不是一种很好的方案。毕竟,localstorage就是专门为小数量数据设计的,所以它的api设计为同步的。而IndexedDB很适合存储大量数据,它的API是异步调用的。IndexedDB使用索引存储数据,各种数据库操作放在事务中执行。IndexedDB甚至还支持简单的数据类型。IndexedDB比localstorage强大得多,但它的API也相对复杂。对于简单的数据,你应该继续使用localstorage,但当你希望存储大量数据时,IndexedDB会明显的更适合,IndexedDB能提供你更为复杂的查询数据的方式。

从输入网址按下回车之后发生了什么

  1. 用户输入URL地址
  2. 浏览器解析URL解析出主机名
  3. 浏览器将主机名转换成服务器ip地址(浏览器先查找本地DNS缓存列表 没有的话 再向浏览器默认的DNS服务器发送查询请求 同时缓存)
  4. 浏览器将端口号从URL中解析出来
  5. 浏览器建立一条与目标Web服务器的TCP连接(三次握手)
  6. 浏览器向服务器发送一条HTTP请求报文
  7. 服务器向浏览器返回一条HTTP响应报文
  8. 关闭连接 浏览器解析文档
  9. 如果文档中有资源 重复6 7 8 动作 直至资源全部加载完毕

渲染的基本流程

  1. HTML解析出DOM Tree
  2. CSS解析出Style Rules
  3. 将二者关联生成Render Tree
  4. Layout 根据Render Tree计算每个节点的信息
  5. Painting 根据计算好的信息绘制整个页面

new 关键字做了什么

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。 当代码 new Foo(...) 执行时,会发生以下事情: 一个继承自 承自 Foo.prototytotype 的新对象被创建。 使用指定的参数调用构造函数 Foo ,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)

    var cat = new Animal("cat");
    new Animal("cat") = {
        var obj = {};
        obj.__proto__ =  = Animal.prototytotype;
        var result =  = Animal.call(obj(obj,"cat");
        return typeof result === 'object'? result : obj;
    }
  • 1.创建一个空对象obj;
  • 2.把obj的__proto__ 指向Animal的原型对象prototype,此时便建立了obj对象的原型链:obj->j->Animal.prototytotype->e->Object.prototytotype->null
  • 3.在obj对象的执行环境调用Animal函数并传递参数“cat”。 相当于var result = = obj.Animal("ca("cat")。        当这句执行完之后,obj便产生了属性name并赋值为"cat"。【关于JS中call的用法请阅读:JS的call和apply】
  • 4.考察第3步返回的返回值,如果无返回值或者返回一个非对象值,则将obj返回作为新对象;否则会将返回值作为新对象返回。

this 的绑定机制

  1. this跟函数在哪里定义没有关系,函数在哪里调用才决定了this到底引用的是啥
  2. this机制的四种规则
  • 默认绑定全局变量,这条规则是最常见的,也是默认的。当函数被单独定义和调用的时候,应用的规则就是绑定全局变量。
  • 隐式绑定 隐式调用的意思是,函数调用时拥有一个上下文对象,就好像这个函数是属于该对象的一样
  • 显示绑定 学过bind()\apply()\call()函数的都应该知道,它接收的第一个参数即是上下文对象并将其赋给this。
  • new新对象绑定 如果是一个构造函数,那么用new来调用,那么绑定的将是新创建的对象

JavaScript 垃圾回收机制

V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制。因此,V8 将内存(堆)分为新生代和老生代两部分。 新生代算法 新生代中的对象一般存活时间较短,使用 Scavenge GC 算法。 在新生代空间中,内存空间分为两部分,分别为 From 空间和 To 空间。在这两个空间中,必定有一个空间是使用的,另一个空间是空闲的。新分配的对象会被放入 From 空间中,当 From 空间被占满时,新生代 GC 就会启动了。算法会检查 From 空间中存活的对象并复制到 To 空间中,如果有失活的对象就会销毁。当复制完成后将 From 空间和 To 空间互换,这样 GC 就结束了。

老生代算法 老生代中的对象一般存活时间较长且数量也多,使用了两个算法,分别是标记清除算法和标记压缩算法。 在讲算法前,先来说下什么情况下对象会出现在老生代空间中: 新生代中的对象是否已经经历过一次 Scavenge 算法,如果经历过的话,会将对象从新生代空间移到老生代空间中。 To 空间的对象占比大小超过 25 %。在这种情况下,为了不影响到内存分配,会将对象从新生代空间移到老生代空间中。

在老生代中,以下情况会先启动标记清除算法: 某一个空间没有分块的时候 空间中被对象超过一定限制 空间不能保证新生代中的对象移动到老生代中 在这个阶段中,会遍历堆中所有的对象,然后标记活的对象,在标记完成后,销毁所有没有被标记的对象。在标记大型对内存时,可能需要几百毫秒才能完成一次标记。这就会导致一些性能上的问题。为了解决这个问题,2011 年,V8 从 stop-the-world 标记切换到增量标志。在增量标记期间,GC 将标记工作分解为更小的模块,可以让 JS 应用逻辑在模块间隙执行一会,从而不至于让应用出现停顿情况。但在 2018 年,GC 技术又有了一个重大突破,这项技术名为并发标记。该技术可以让 GC 扫描和标记对象时,同时允许 JS 运行。 清除对象后会造成堆内存出现碎片的情况,当碎片超过一定限制后会启动压缩算法。在压缩过程中,将活的对象像一端移动,直到所有对象都移动完成然后清理掉不需要的内存。