Javascript面试题

137 阅读19分钟

js 中常见的数据类型:

  • string
  • number
  • boolean
  • underfined
  • Null
  • NaN
  • object
  • symbol

js中基本数据类型有

  • number
  • string
  • boolean
  • null
  • undefined

js中的引用数据类型有

  • object
  • array
  • function

检测数据类型的方法

  1. typeof

可以判断出'string','number','boolean','underfined','symbol'
当判断typeof(null)时值为'object',判断数组和对象的值均为'object'

  1. instanceof

原理是构造函数的 prototype 属性是否出现在对象的原型链中的任何位置

字符串比较问题

"2">"11" //结果:true

  1. 在同是字符串的时候,首先左边第一位的是 '2' 转换成 0000 0010(二进制) 与右边第一位 '1' 转换成 0000 0001 (二进制) 做比较大小
  2. 在字符串和number进行比较时,把字符串隐式转化成number类型在进行比较。

cookie,sessionStorage和localStorage

  1. cookie用来保存登录信息,大小限制为4KB左右
  2. localStorage是Html5新增的,用于本地数据存储,保存的数据没有过期时间,一般浏览器大小限制在5MB
  3. sessionStorage接口方法和localStorage类似,但保存的数据的只会在当前会话中保存下来,页面关闭后会被清空。image

跨域

什么是跨域? ** 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。
什么是同源策略?
同源策略/ SOP(Same origin policy)是一种约定,由Netscape公司发布。它是浏览器最核心,也最基本的安全功能,如果有了同源策略,所谓相似是指
协议+域名+端口**三者相同,如果有一个不同,即为非同源。 跨域解决方案: 1. cors 跨域资源共享 设置响应头

response.setHeader("Access-Control-Allow-Origin", "*")

缺点:是会引起CSRF攻击,如何解决这个问题呢? 解决方法:1.主要使用token 2. 尽量使用Post 2. jsonp jsonp缺点:只能实现get一种请求。

CORS与JSONP 区别

  1. JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
  2. 使用CORS,可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
  3. JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS。[低版本IE7以下不支持,要支持IE7还是要用jsonp方式]

cookie 跨域问题

  1. nginx反向代理
  2. jsonp
  3. nodejs superagent 其实本质也是jsonp的方式

localStorage跨域问题

可以使用postMessage和iframe

思路如下:

假设有a页面和b页面,两个页面。

通过a页面去修改b页面的本地数据:

> ① 在a页面创建一个iframe,嵌入b页面。
> ② a页面通过postMessage传递指定格式的消息给b页面。
> ③ b页面解析a页面传递过来的消息内容,调用localStorage API 操作本地数据。
> ④ b页面包装localStorage的操作结果,并通过postMessage传递给a页面。
> ⑤ a页面解析b页面传递回来的消息内容,得到 localStorage 的操作结果。

var 、let 和const区别

  1. 在作用域方面

var 声明的变量不存在块及作用
let 声明的变量存在块及作用域、

  1. 在变量提升方面

var 声明的变量存在变量提升
let const声明的变量不存在变量提升 唯一不同的是 const 声明的变量一旦赋值就不能再改变了

  1. 在重复声明方面

var 可以多次声明,let不存在多次声明
const和let基本相同 唯一不同的是const声明的变量一旦赋值就不能在改变。

面向对象

特点:封装,继承,多态

  1. 封装
    把一些数据和对数据的操作集中在一起,向外暴露需要的接口(属性和方法)

  2. 继承
    一个类型的实例能够访问另外一个类型的属性和方法。 类和类之间的关系 子类继承父类,你只不过是子类中的实例。 构造函数继承 原型链继承 组合继承 寄生组合式继承 class 继承

  3. 多态

     所谓多态,就是同一个方法的多种调用方式
    

promise

promise是一个类,是异步编程的解决方案和回调函数的使用有关,多个回调函数使用行成回调地狱。promise有三个状态分别是等待,成功,失败,每个promise都有一个.then的方法,resolve代表成功,reject代表失败,不管先调用resolve方法,还是先调用reject方法,按照先调用先生效的原则。

ES5新增的有哪些?

像 数组:

forEach();map();every()filter();isArray()

string:

trim()

Data:

Date.now()

Json:

 JSON.stringify()//将js对象转化成字符串
 JSON.parse()//将字符串转换为js值

this指向

ES6新增的有哪些?

  1. let const可以再说说var,const,let的区别
  2. 模板字符串
  3. 解构赋值对数组和对象的优化
  4. 箭头函数,可以多说说,this指向问题
  5. 扩展运算符 ...
  6. set方法,常用于数组去重
  7. promise解决异步问题
  8. set和map方法
  9. for...of...

ES7新增

  1. 求幂运算符
    Math.pow(3, 2) === 3 ** 2 
    
  2. Array.prototype.includes()
      indexOf(3) > -1 //true
      区别:1. 简便性;includes返回值是布尔值,indexOf返回值是索引 2.精确性 对于NaN,在严格模式下,includes返回值是布尔值,indexOf返回值是-1
    

ES8新增

  1. 使用async await异步解决
  2. Object.entries()
      Object.entries({ one: 1, two: 2 })    
      //[['one', 1], ['two', 2]]
      Object.extries([1, 3])    //[['0', 1], ['1', 3]]
    
  3. 字符串填充padStart()、padEnd() **promise **
Promise.all():当所有的异步都执行完毕以后才会执行.then中的操作。  
    Promise.race():只要有一个promise执行完毕后就会执行.then操作。

基础promise封装

===========

functionajax(url) {

    return newPromise(function (resolve, reject) {

        if (window.XMLHttpRequest) {

             var xhr=newXMLHttpRequest();

        } else {

            var xhr=newActiveXObject("Microsoft.XMLHTTP");

        }

        xhr.open("GET", url, true);

        xhr.send();

        xhr.onreadystatechange = function () {

        if (xhr.readyState==4) {

            if (xhr.status==200) {

                var data = xhr.responseText;

                resolve(data);

                }

            }

        }

    })

}

set和map区别

 set:   
 set是ES6提供的一种新的数据结构,类似于数组,但是成员的值是唯一的没有重复的,接受的参数是一个数组方法有:        add():添加   
    delete():删除   
    size:长度   
    has():查找   
    clear:清除所有 
map:   
map类似于对象,也是键值对的集合, 但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键方法有:   
    set():设置   
    get():获取   
    delete():删除   
    has():查找   
    clear():清除所有

for of 和for in区别:

for in 循环特别适合遍历对象 for…in是遍历数组、对象的key for…of是遍历数组的value

闭包的概念及优缺点

========= 闭包的概念:

就是在一个函数外部能够访问该函数内部局部变量的函数。

优点:
避免全局变量的污染。
希望一个变量长期储存在内存中。

缺点:
内内存泄漏
增加内存的使用量

this 指向问题

宗旨就是:谁调用它,它就指向谁

  1. 箭头函数中的this指向它的环境。
  2. 构造函数调用,this 指向实例对象
  3. 对象方法调用, 此时 this 指向 该方法所属的对象
  4. 通过事件绑定, 此时 this 指向 绑定事件的对象

箭头函数和普通函数的区别?

  1. 外型上的区别:箭头函数是有箭头的,普通函数没有。
  2. 函数名上的区别:箭头函数都是匿名函数,普通函数可以是匿名函数也可以不是匿名函数。
  3. new运算符上的区别:箭头函数不能使用new运算符,普通函数可以是new运算符。
  4. this指向的区别:箭头函数的this指向它的环境,普通函数的this指向,谁调用它,它就指向谁。

如何改变this指向以及它们的区别

call apply bind

  1. call和apply区别是参数个数不同。
  2. call的参数可以无限个;而apply的参数最多只能有两个。
  3. bind是创建一个新函数,与被调函数有相同的函数体。

http和https的区别?

  1. https协议需要申请证书,一般不免费
  2. http是超文本传输协议,信息是明文传输的,https具有安全性,https采用的是加密传输协议。
  3. http和https采用的不同的连接方式,http端口号是80,https端口号是443.

节流和防抖

防抖
防抖是指在一段时间内,函数被触发多次,但是只执行一次,当达到了一定的时间间隔就会执行一次 使用场景:搜索,window触发resize

function debounce(fn, delay) {
  let timer = null;
  return function () {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, arguments);
    }, delay);
  }
}

节流
节流是会有规律的每隔时间n就执行一次函数。 使用场景:鼠标不断点击触发;监听滚动事件

function throttle(fn, cycle) {
  let start = Date.now();
  let now;
  let timer;
  return function () {
    now = Date.now();
    clearTimeout(timer);
    if (now - start >= cycle) {
      fn.apply(this, arguments);
      start = now;
    } else {
      timer = setTimeout(() => {
        fn.apply(this, arguments);
      }, cycle);
    }
  }
}

bom 和 dom 有什么区别,常见的 bom 对象有哪些?

BOM 是 Browser Object Model 缩写,即浏览器对象模型 Dom 是 Document Object Model 缩写,即文档对象模型

常见的 bom 对象有哪些

  1. window 对象——BOM 核心
  2. document 对象
  3. location 对象 获得当前页面的 URL,并重定向到新的页面
    • location.herf
    • location.port
  4. navigator 对象 获得与浏览器有关的信息 userAgent 是最常用的属性,用来完成浏览器判断
  5. screen 对象 主要用来获取用户的屏幕信息。
  6. history 对象
  • back() 返回上一页。

  • forward() 返回下一页

go(“参数”) -1 表示上一页,1 表示下一页。

原生 js 事件传播流程是什么?

  • 事件捕获
  • 处于目标阶段
  • 事件冒泡阶段

js单线程

JavaScript 的单线程,与它的用途有关。JavaScript浏览器脚本语言,JavaScript 的主要用途是实现用户互动,及操作 DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。
为了利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM。所以,这个新标准并没有改变 JavaScript 单线程的本质。

面向对象(封装继承多态)

  1. 封装 把相关的信息(无论数据或方法)存储在对象中的能力
  2. 继承 从另一个类(或多个类)得来类的属性和方法的能力
  3. 多态 编写能以多种方法运行的函数或方法的能力

回流和重绘

区别:回流必将引起重绘;而重绘不一定会引起回流。

回流: 当 render tree 中的一部分(或全部)元素的属性改变而需要重新构建页面。这就称为回流(reflow)。 重绘:

当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,称为重绘。


比方说:只有颜色改变的时候就只会发生重绘而不会引起回流,
当页面布局和几何属性改变时就需要回流.

get 和 post 区别

  1. get 传输不安全,因为数据放在请求的 url 中;post 所有操作用户是看不到的
  2. get 传输数据量小,因为受到 url 长度的限制;post 传输的数据量较大,一般不受限制
  3. get 执行效率比 post 更好。get 是 form 提交的默认方式。

为什么要使用深拷贝?

在改变新的数组(对象)的时候,不改变原数组(对象)

深拷贝和浅拷贝

  1. 浅拷贝: 将原对象或原数组的引用(地址)直接赋给新对象,新数组,新对象/数组只是原对象的一个引用。
  • 实现浅拷贝的方法有:
  1. for...in...

  2. object.assign()

  3. 直接用 = 号

  4. 扩展运算符 ...

  5. 深拷贝: 将原对象的各项属性的“值”(数组的所有元素)拷贝给新的对象和数组,是“值”而不是“引用”。

  • 实现深拷贝的方法:
  1. Json.parse()和Json.stringfiy()
  2. 使用Jquery中的$.extend()

js变量提升的原理

我们习惯将 var a = 2;看做是一个声明,而实际上 javascript 引擎并不这么认为。它将 var a 和 a = 2 看做是两个单独的声明,第一个是编译阶段的任务,而第二个则是执行阶段的任务。

HTTP 状态码及含义

    200 OK ,表明请求已经成功. 默认情况下状态码为200的响应可以被缓存。

    302 Found,临时重定向。重定向状态码表明请求的资源被暂时的移动到了由 Location 头部指定的 URL 上。浏览器会重定向到这个URL,但是搜索引擎不会对该资源的链接进行更新。

    400 Bad Request,表示由于语法无效,服务器无法理解该请求。客户端不应该在未经修改的情况下重复此请求。

    403 Forbidden,指的是服务器端有能力处理该请求,但是拒绝授权访问。进入该状态后,不能再继续进行验证。该访问是永久禁止的,并且与应用逻辑密切相关(例如不正确的密码)

    404 Not Found,说明服务器端无法找到所请求的资源。404 不能说明请求的资源是临时还是永久丢失。如果服务器知道该资源是永久丢失,那么应该返回 410 (Gone) 而不是 404 。

    500 Internal Server Error,表示所请求的服务器遇到意外的情况并阻止其执行请求。

    502 Bad Gateway,表示作为网关或代理角色的服务器,从上游服务器(如tomcat、php-fpm)中接收到的响应是无效的。服务器挂掉了的意思。

    503 Serveice Unavailable,表示服务器尚未处于可以接受请求的状态。

web 性能优化

  1. 减少 http 请求
  2. 使用 css scripts
  3. 样式放在头部,js 放在底部
  4. 压缩组件
  5. 使用 CDN

图片

map 和 forEach 有什么区别?

  1. forEach()返回值是 undefined,不可以链式调用。

2、 map()返回一个新数组,原数组不会改变。

3、 没有办法终止或者跳出 forEach()循环,除非抛出异常,所以想执行一个数组是否满足什么条件,返回布尔值,可以用一般的 for 循环实现,或者用 Array.every()或者 Array.some();

4、 $.each()方法规定为每个匹配元素规定运行的函数,可以返回 false 可用于及早停止循环。

异步与同步

  • 同步概念

    同步是阻塞模式。当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法。

  • 异步概念

    异步是非阻塞模式。当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调用者不用等待该方法执行完毕,我们称这个方法为异步方法

    举个生活中的例子:

    同步就像你冬天早上起来上班,需要先把水烧好后再去洗脸刷牙,这样一来你就会觉得时间不够用,那么此时异步就能节约很多的时间,你可以在烧热水的时间内,完成洗漱等事情,不需要等待热水烧好。

DOM 事件流传播

  1. 事件捕获阶段(当你使用事件捕获时,父级元素先触发,子级元素后触发,即 div 先触发,p 后触发。)
  2. 处于目标阶段
  3. 事件冒泡阶段(当你使用事件冒泡时,子级元素先触发,父级元素后触发,即 p 先触发,div 后触发) 图片

阻止事件流方式:

  • q1:阻止冒泡方式有哪些?
    1. IE:事件对象.cancelBubble=true;
    2. 火狐:事件对象.stopPropagation();
  • q2:阻止浏览器的默认行为有哪些?
    1. event.preventDefault(); //现代浏览器
    2. event.returnValue = false; //IE 低版本及部分现代浏览器。
    3. return false;//兼容性比较好 最好用在代码的最后面。

DOM2 级事件和普通(传统)事件区别:

1.捕获由外向内传播;冒泡由内向外。
2.传统事件不支持捕获;DOM2级事件支持捕获。
3.传统事件多个相同的事件名,会覆盖 ;DOM2级事件会依次执行。
4.DOM2级事件有兼容性的。

事件委托?

概念:利用冒泡原理,把某个子元素事件委托给父元素执行。 优点:

  1. 提高性能
  2. 动态监听

谈谈js事件循环机制

程序开始执行之后,主程序则开始执行同步任务,碰到异步任务就把它放到任务队列中,等到同步任务全部执行完毕之后,js引擎便去查看任务队列有没有可以执行的异步任务,将异步任务转为同步任务,开始执行,执行完同步任务之后继续查看任务队列,这个过程是一直循环的,因此这个过程就是所谓的事件循环,其中任务队列也被称为事件队列。通过一个任务队列,单线程的js实现了异步任务的执行,给人感觉js好像是多线程的。

async 和 await

原理:async 和 await 用了同步的方式去做异步,async 定义的函数的返回值都是 promise,await 后面的函数会先执行一遍,然后就会跳出整个 async 函数来执行后面js的代码

Ajax

ajax是“Asynchronous JavaScript and XML”的缩写,是一种创建交互式网页应用的网页开发技术, ajax具有以下优点:

  1. 使用异步方式与服务器通信,具有更加迅速的响应能力。
  2. 优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占用。
  3. Ajax最大特点可以实现动态不刷新(局部刷新) 同时ajax也是有缺点的: 1、ajax不支持浏览器back按钮。
    2、安全问题 AJAX暴露了与服务器交互的细节。
    3、对搜索引擎的支持比较弱。
    4、破坏了程序的异常机制。
    5、不容易调试。

如何获取用户的浏览器是什么?

navigator.userAgent

原型和原型链

每一个构造函数都有一个原型对象protype,每一个原型对象都有一个指向构造函数的内部指针construct,每一个实例都有一个指向原型对象的内部指针-proto-,原型对象上的属性和方法都能被实例所访问到。

排序算法

  • 选择排序
function selectionSort(arr) {
    var len = arr.length;
    var minIndex, temp;
    for (var i = 0; i < len - 1; i++) {
        minIndex = i;
        for (var j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {     // 寻找最小的数
                minIndex = j;                 // 将最小数的索引保存
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}
  • 冒泡排序
var examplearr=[8,94,15,88,55,76,21,39];
function sortarr(arr){
    for(i=0;i<arr.length-1;i++){
        for(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;
            }
        }
    }
    return arr;
}
sortarr(examplearr);
console.log(examplearr);
  • 快速排序
 function quickSort(arr){
        if(arr.length<1){
            return arr;
        }
        var pivotIndex=Math.floor(arr.length/2);//找到那个基准数
        var pivot=arr.splice(pivotIndex,1)[0]; //取出基准数,并去除,splice返回值为数组。
        var left=[]; 
        var right=[];
        for(var i=0;i<arr.length;i++){
            if(arr[i]<pivot){
                left.push(arr[i]);
            }else{
                right.push(arr[i]);
            }
        }
        return quickSort(left).concat([pivot],quickSort(right)); //加入基准数
    }
    arr=[2,1,5,8,3,7,4,6,9];
    console.log(quickSort(arr)); //[1, 2, 3, 4, 5, 6, 7, 8, 9]

浏览器的渲染原理(浏览器是怎样工作的)

用户输入url,浏览器向服务器发送请求,服务器返回html文件,浏览器载入html代码,当浏览器解析到head中有使用link标签引入的css样式的时候,浏览器再次向服务器发出请求,请求css文件,然后服务器返回css文件,当浏览器解析到html中body部分,此时的css文件已经到手了,浏览器开始渲染页面,当浏览器解析到img标签的时候,再次向服务器发送请求,此时浏览器是不会先加载图片的,而是先加载后面的代码,当服务器返回图片时,浏览器重新渲染这部分代码,当浏览器发现script标签的时候,很快去运行它,当浏览器解析到,此时浏览器叹了口气,终于完成任务了,我太难了。

数组的方法

slice()方法

            格式:数组.slice(start,end)
	功能:可以基于当前数组获取指定区域的元素包含start,不包含end,即[start,end)。
	返回值:生成新的数组,原数组不发生变化。

splice()方法

        格式:数组.splice(start,length,数据1,数据2...)
	功能:增加,删除,修改
	参数:start指开始截取的位置
		  lenght截取的元素长度
		  从第三个参数开始:在start位置,插入的元素
		
	返回值:截取元素组成的数组
	

join()方法

            格式:数组.join(字符串)
	功能:将数组中的元素,用传入的拼接符,拼接成一个字符串。
	返回值:拼接好的字符串。

reverse()方法

            格式:数组.reverse()
	功能:逆序

sort()方法

            格式:数组.sort() 默认从小到大排序,按照字符串进行排序的。
	参数:一个函数,该函数表示怎么进行排序的。
	

ECMAscript 5新增的数组方法:

indexOf()

            格式:数组.indexOf(item,start)
	参数:item是数组任意元素
		  start是指数组下标
	功能:在数组中查找第一次出现item元素下标,从start开始查找。
	返回值:-1表示没有查找到的元素下标
			>=0表示查找到的元素下标
	

数组遍历:

            for
	for...in 
	forEach

forEach

            数组.forEach(function(item,index,array){
	  item是数组任意元素
	  index是指数组下标
	  array数组本身
	})

map

            数组.map(function(item,index,array){
	//遍历要做的事情
    // 返回值:新数组,原数组不发生改变
	})

filter

            数组.filter(function(item,index,array){  //过滤
	//过滤的条件
	})

some

            数组.some(function(item,index,array){   //查找数组的是否有符合条件的元素,如果有返回true,没有返回false//只找到符合条件的元素,后面的循环都停止了。
	})	

every

            数组.every(function(){//查找数组的元素是否都符合条件,是返回true,否则返回false.
	//
	})

reduce

            数组.reduce(function(prev,next,index,array){
     prev 第一次是下标为0的元素。
	 第二次是上一次return的值。
     next 从下标1开始,当前遍历到的元素。
     array数组本身。

})

字符串的方法

charAt()

    字符串.charAt(下标) 下标是从零开始

charCodeAt()

    charCodeAt(下标) 返回对应下标对应的ASCII值

fromCharCode(码值)

    功能:将ascii值转为对应的字符

substring() 截取

    格式:字符串.substring(start,end)
功能:将字符串中[start,end)提取这一部分字符,生成新字符。
返回值:新字符串。

substr() 截取

    格式:substr(start,length)
返回值:新生成的字符串。

slice() 提取

    格式:字符串.slice(start,end)
功能:将字符串中[start,end)提取这一部分字符,生成新字符。
返回值:新生成的字符串。

replace() 替换

    格式:字符串.replace(oldStr,newStr)
功能:用newStr替换oldStr
返回值:新生成的字符串。

split()

    格式:字符串.split(分割符,length)
参数:分隔符对原字符串进行分割,length返回数组的长度,一般不用。
功能:用分割符对原字符串,进行字符串分割。将分割完毕后的字符串放在数组中返回。
返回值:数组。	

join()

    格式:数组.join()
功能:将数值转换成字符串。
    

export(导出)和import(导入) 之间的区别:

ES6主要有两个功能:export(导出)和import(导入)
export用于对外输出本模块(一个文件可以理解为一个模块)变量的接口
import用于在一个模块中加载另一个含有export接口的模块。
也就是说使用export命令定义了模块的对外接口以后,其他JS文件就可以通过import命令加载这个模块(文件)。

export与export default之间的区别:

1、export与export default均可用于导出常量、函数、文件、模块等
2、你可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用
3、在一个文件或模块中,export、import可以有多个,export default仅有一个
4、通过export方式导出,在导入时要加{ },export default则不需要

这样来说其实很多时候export与export default可以实现同样的目的。
注意第四条,通过export方式导出,在导入时要加{ },export default则不需要。使用export default命令,为模块指定默认输出,这样就不需要知道所要加载模块的变量名。

数组去重问题

1. 双层 for 循环嵌套

for (var i = 0; i < arr.length; i++) {
  for (var j = i + 1; j < arr.length; j++) {
    if (arr[i] == arr[j]) {
      //如果第一个等于第二个,splice方法删除第二个
      arr.splice(j, 1);
      j--;
    }
  }
}
  1. es5 方法,Ie8 以下不支持
var arrs = [];
//遍历当前数组
for (var i = 0; i < array.length; i++) {
  //如果临时数组里没有当前数组的当前值,则把当前值push到新数组里面
  if (arrs.indexOf(array[i]) == -1) {
    arrs.push(array[i]);
  }
}
return arrs;
  1. es6 利用 set 去重
function newArr(arr) {
  return Array.from(new Set(arr));
}
  1. sort 去重
    function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return;
    }
    arr = arr.sort()
    var arrry= [arr[0]];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            arrry.push(arr[i]);
        }
    }
    return arrry;
}
利用sort()排序方法,然后根据排序后的结果进行遍历及相邻元素比对

网页性能优化

1、资源压缩合并,减少http请求

2、非核心代码异步加载 --> 异步加载的方式 --> 异步加载的区别

3、利用浏览器缓存 --> 缓存的分类 --> 缓存的原理

4、使用CDN

5、DNS预解析

浏览器打开网页都经历了什么?

浏览器页面准备:如unload前一个页面、初始化资源等。
重定向:如果服务端返回header中定义了重定向才会有这个过程,如果没有重定向,不会产生这个过程。
app cache:会先检查这个域名是否有缓存,如果有缓存就不需要DNS解析域名。这里的app是值应用程序application,不指手机app。
DNS解析:把域名解析成IP,如果直接用ip地址访问,不产生这个过程。
TCP连接:http协议是经过TCP来传输的,所以产生一个http请求就会有TCP connect,但是依赖于长连接,不会产生这个过程。
发送请求:请求服务端资源。
接受请求:接受服务端返回数据。
解析HTML结构
加载外部脚本和样式表文件:正常来说JS、css都是外部加载的。
解析并执行脚本代码
构建与解析HTML DOM树
加载外部图片
页面加载完成,页面正常显示。

图片懒加载和预加载

  • 预加载:提前加载图片,当用户需要的时候直接从本地缓存中渲染。
  • 懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数量或延迟请求数量。
  • 两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。
  • 懒加载对服务器前端有一定的缓解压力,预加载 则会增加服务器前端压力。 参考:图片的懒加载和预加载