web前端基础知识汇总- 面试2

192 阅读26分钟

1, 对话框  (window对象函数,使用时可以不写window.)

alert() :  警告框    一个参数(通常是警告的文字) ,    只有一个确定按钮;

confirm():  确认框   一个参数(提示文字), 函数返回根据用户点击返回 布尔值;

prompt():  提示框   第一个参数 提示文字,第二个参数表示输入框中的默认值,函数返回用户输入的值; 有确认和取消按钮;

实际上,上面3种对话框一般只用来在开发阶段用作调试而已,因为(1) 样式不能改变 (2) 不能给对话框中添加按钮添加自定义事件(3)当点击对话框时,会产生任务阻塞,只有当关闭对话框时,代码才会继续运行。(4)对话框只能显示纯文本,不能显示HTML元素

2,  Location 对象

 Location 对象包含有关当前 URL 的信息。(URL -> 统一资源定位器)

--------------------------上面为BOM操作---- 下面讲DOM操作-------------------------------

1, 创建元素: document.createElement("div");  

2,    插入元素:appendChild()   和  insertBefore()

3,    查找元素:getElementById()、getElementByName()、getElementByTagName()、getElementByClassName() 、 querySelector() 、querySelectorAll()

4, 删除元素: removeChild()

5,    替换元素: replaceChild()

6,    复制元素: cloneNode()

面试题: write() 和 innerHTML() 有哪些区别?

首先,document.write() 和 innerHTML 都能将HTML字符串解析为DOM树,再将DOM树插到指定位置,二者的区别在于  (1)  write()  是方法,存在于document对象中,innerHTML是属性,存在于Element对象中 (2)   document.write() 会自动将 对此调用的字符串参数自动连接起来,innerHTML 要拼接的话得用运算符“+=” (3)只有当前文档还在解析时,才能使用document.write()  而 innerHTML 


1. 谈谈你对HTML5 的理解?

绝大多数人心中的HTML5仅仅是HTML的第5个版本,认为H5只不过是增加了一些新标签,如、、或者增加了动画、渐变之类的炫酷效果。实际上这种理解是极其肤浅的,完全是外行人看热闹!H5从广义上说是前端开发中各种最新技术的总称,包含了HTML5、CSS3、JavaScript、ES6和各种开源框架等最新前端开发技术的总和。H5广泛而深入地吸收了移动互联网时代的技术精髓,再加上其自身跨平台、免安装、更新快的技术优势,自2014年底发布以来,已经逐渐成为现代互联网和移动互联网开发的核心技术,逐渐发展成为各行各业进入互联网+的首选开发技术。

1,typeof运算符和instanceof运算符以及isPrototypeOf()方法的区别

typeof是一个运算符,用于检测数据的类型,比如基本数据类型null、undefined、string、number、boolean,以及引用数据类型object、function,但是对于正则表达式、日期、数组这些引用数据类型,它会全部识别为object; instanceof同样也是一个运算符,它就能很好识别数据具体是哪一种引用类型。它与isPrototypeOf的区别就是它是用来检测构造函数的原型是否存在于指定对象的原型链当中;而isPrototypeOf是用来检测调用此方法的对象是否存在于指定对象的原型链中,所以本质上就是检测目标不同。

2, call()   apply()  bind() 相关问题?

  • 怎么利用call、apply来求一个数组中最大或者最小值
  • 如何利用call、apply来做继承
  • apply、call、bind的区别和主要应用场景

首先,要明白这三个函数的存在意义是什么?答案是改变函数执行时的上下文,再具体一点就是改变函数运行时的this指向

求数组中最大值最小值:

// 妙用:
var arr = [1,2,45,77,99,101];
var max = Math.max.apply(Math,arr);
var min = Math.min.apply(Math,arr);
console.log(max,min);

将子类(子构造函数)中的this指向 父类构造函数的中的this就可以实现继承,但存在缺陷,实际上实现继承一般是使用原型和原型链

// 父构造函数
function Father(name,sex){
    this.name = name;
    this.sex = sex;
}
Father.prototype.money = function(){
    console.log("赚钱!");
}
// 子构造函数
function Son(name,sex){
    Father.call(this,name,sex);
}

/* 怎么让 son继承父亲的 money方法呢? */
Son.prototype = new Father(); // 利用父构造方法就能使用父原型,就能使用父方法
Son.prototype.constructor = Son;
Son.prototype.exam = function(){
    console.log("考试");
}

function Animal(name){      
    this.name = name;      
    this.showName = function(){      
        console.log(this.name);      
    }      
}      

function Cat(name){    
    Animal.call(this, name);    
}      

// Animal.call(this) 的意思就是使用this对象代替Animal对象,那么
// Cat中不就有Animal的所有属性和方法了吗,Cat对象就能够直接调用Animal的方法以及属性了
var cat = new Cat("TONY");     
cat.showName();   //TONY

call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数。call和apply的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展现,apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数。

let arr1 = [1, 2, 19, 6];
//例子:求数组中的最值
console.log(Math.max.call(null, 1,2,19,6)); // 19
console.log(Math.max.call(null, arr1)); // NaN
console.log(Math.max.apply(null, arr1)); //  19 直接可以用arr1传递进去
数组拼接,添加
let arr1 = [1,2,3];
let arr2 = [4,5,6];

//数组的concat方法:返回一个新的数组
let arr3 = arr1.concat(arr2); 
console.log(arr3); // [1, 2, 3, 4, 5, 6]

console.log(arr1); // [1, 2, 3] 不变
console.log(arr2); // [4, 5, 6] 不变
// 用 apply方法
[].push.apply(arr1,arr2);  // 给arr1添加arr2
console.log(arr1); // [1, 2, 3, 4, 5, 6]
console.log(arr2); // 不变

 注意: 不能使用  arr1.push(arr2);  这样 arr2 会被当做元素加入到 arr1中

**3, eval() 函数的用法? **

eval() 函数执行表示为字符串形式的JavaScript代码。

eval()只有一个参数,如果传入的参数不是字符串,它直接返回这个参数。如果参数是字符串,它会把字符串当成javascript代码进行编译。

开发时,不建议使用eval()函数,不安全,消耗性能;

4.描述以下变量的区别:null,undefined或undeclared?

**null**

表示"没有对象",即该处不应该有值,转为数值时为0。典型用法是:

(1) 作为函数的参数,表示该函数的参数不是对象。

(2) 作为对象原型链的终点。

**undefined**

表示"缺少值",就是此处应该有一个值,但是还没有定义,转为数值时为NaN。典型用法是:

(1)变量被声明了,但没有赋值时,就等于undefined。

(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。

(3)对象没有赋值的属性,该属性的值为undefined。

(4)函数没有返回值时,默认返回undefined。

**undeclared**

js语法错误,没有申明直接使用,js无法找到对应的上下文。

5.==和===有什么区别?

首先,== equality 等同,=== identity 恒等。==,两边值类型不同的时候,要先进行类型转换,再比较。===,不做类型转换,类型不同的一定不等。

6.同步异步?

1、进程同步:就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事 

2、异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。


1. 什么是事件代理/事件委托?

事件代理/事件委托是利用事件冒泡的特性,将本应该绑定在多个元素上的事件绑定在他们的祖先元素上,尤其在动态添加子元素的时候,可以非常方便的提高程序性能,减小内存空间。

2.什么是事件冒泡?什么是事件捕获?

首先要理解什么是事件JavaScriptHTML之间的交互是通过事件实现的。事件,就是文档或浏览器窗口发生的一些特定的交互瞬间。可以使用监听器(或事件处理程序)来预定事件,以便事件发生时执行相应的代码。通俗的说,这种模型其实就是一个观察者模式。(事件是对象主题,而这一个个的监听器就是一个个观察者; 事件流:事件流描述的就是从页面中接收事件的顺序。而IENetscape提出了完全相反的事件流概念。IE事件流是事件冒泡,而Netscape的事件流就是事件捕获。

通俗来讲:事件冒泡  相当于你先按了同心圆中最小的圆,再事件冒泡机制,相当于你也按了大圆, 事件捕获:你先是定位到地球,然后才会是中国,再是江西;由于老版本浏览器不支持,所以很少有人使用事件捕获。

3.如何阻止冒泡?

有时候你不希望父盒子冒泡的形式接受子盒子的事件时,需要阻止事件冒泡

2种方法:(1)return false; 阻止了事件冒泡,也阻止了默认行为

(2)event.stopPropagation();   阻止了事件冒泡,但不会阻击默认行为, event是函数对象

w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true。

4.如何阻止默认行为?

w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false                                      return false也能阻止默认行为。

5.简述javascript中this的指向

第一准则是:this永远指向函数运行时所在的对象,而不是函数被创建时所在的对象。

  • 普通的函数调用,函数被谁调用,this就是谁。 一般直接用的话就是window
  • 构造函数的话,如果不用new操作符而直接调用,那即this指向window。用new操作符生成对象实例后,this就指向了新生成的对象。
  • 匿名函数或不处于任何对象中的函数指向window 。
  • 如果是call,apply等,指定的this是谁,就是谁。

6.深拷贝和浅拷贝

简单来说:就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力。

  • 浅拷贝 只是 拷贝 一层,更深层次对象级别的只拷贝引用(地址)

  • 修改拷贝后的对象,会将原来的也修改 // 原生js 可以用 赋值的形式 实现浅拷贝

  • ES6 新增 语法糖实现浅拷贝 Object.assign(o, obj); // 将obj 拷给 o


<div>![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/7/5/1731cfb24e09094b~tplv-t2oaga2asx-image.image)</div>

    <script type="text/javascript">
        var obj = {
            id : 1,
            name: 'iPhone',
            price : {
                x : 8000,
                xr: 5000
            }
        }

        var phone = {};
        // 浅拷贝
        Object.assign(phone, obj);
        console.log(phone);
    </script>
  • 深拷贝拷贝,每一级别的数据都会被拷贝
![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/7/5/1731d0884f8ba2b1~tplv-t2oaga2asx-image.image)
function deepCopy(newObject,oldObject){

    for(var k in oldObject ){
        // 1, 获取属性值 oldObject[k] 表示 索引为k 的属性的值
        var item = oldObject[k];
        // 2,判断这个值是否是数组
        //    一定要先判断是数组,在判断对象,因为数组也是对象的一种
        if(item instanceof Array){
            newObject[k] = [];
             deepCopy(newObject,oldObject);
        }else if(item instanceof Object){
            newObject[k] = {};
             deepCopy(newObject,oldObject);
        }else{
        // 4, 简单类型
        newObject[k] = item;
        }
    }
}

7.请尽可能详尽的解释AJAX的工作原理

ajax简单来说是通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。

ajax的优点

  • 最大的一点是页面无刷新,在页面内与服务器通信,给用户的体验非常好。
  • 使用异步方式与服务器通信,不需要打断用户的操作,具有更加迅速的响应能力。
  • 可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本,ajax的原则是“按需取数据”,可以最大程度的减少冗余请求。
  • 基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。

ajax的缺点

  • ajax对浏览器后退机制造成了破坏,也就是说用户无法通过浏览器的后退按钮回到前一次操作的页面。虽然有些浏览器解决了这个问题,比如Gmail,但它也并不能改变ajax的机制,它所带来的开发成本是非常高的,和ajax框架所要求的快速开发是相背离的。这是ajax所带来的一个非常严重的问题。
  • 安全问题。技术同时也对IT企业带来了新的安全威胁,ajax技术就如同对企业数据建立了一个直接通道。这使得开发者在不经意间会暴露比以前更多的数据和服务器逻辑。
  • 对搜索引擎的支持比较弱。
  • 破坏了程序的异常机制。至少从目前看来,像ajax.dll,ajaxpro.dll这些ajax框架是会破坏程序的异常机制的。关于这个问题,我曾经在开发过程中遇到过,但是查了一下网上几乎没有相关的介绍。后来我自己做了一次试验,分别采用ajax和传统的form提交的模式来删除一条数据……给我们的调试带来了很大的困难。
  • 另外,像其他方面的一些问题,比如说违背了url和资源定位的初衷。例如,我给你一个url地址,如果采用了ajax技术,也许你在该url地址下面看到的和我在这个url地址下看到的内容是不同的。这个和资源定位的初衷是相背离的。
  • 一些手持设备(如手机、PDA等)现在还不能很好的支持ajax,比如说我们在手机的浏览器上打开采用ajax技术的网站时,它目前是不支持的,当然,这个问题和我们没太多关系。

8. get和post有什么区别?

其实,GET和POST本质上两者没有任何区别。他们都是HTTP协议中的请求方法。底层实现都是基于TCP/IP协议。所谓区别,只是浏览器厂家根据约定,做得限制而已。

  • get是通过明文发送数据请求,而post是通过密文;
  • get传输的数据量有限,因为url的长度有限,post则不受限;
  • GET请求的参数只能是ASCII码,所以中文需要URL编码,而POST请求传参没有这个限制
  • GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

9. 请解释变量声明提升 和函数声明提升

例1:[为什么先使用后声明不报错呢?]

console.log(a);     //  undefined
var a ='this is a';  // 

例2:[为什么给一个还没有声明的变量赋值会不报错呢?]

num = 6;
num++;
var num;
console.log(num) // 7 

原因:通过var声明的变量会被提升至作用域的顶端。不仅仅是变量,函数声明也一样会被提升。当同一作用域内同时出现变量和函数声明提升时,变量仍然在函数前面。

JS引擎会在正式执行代码之前进行一次”预编译“,预编译简单理解就是在内存中开辟一些空间,存放一些变量和函数

ps:我们在开发中,不应该使用这一特性,而是要规范我们的代码,做到可维护性和可读性。无论是变量还是函数,都必须先声明后使用。在开发中应该使用let来约束变量提升。

10,请指出document.onload和document.ready两个事件的区别

页面加载完成有两种事件,一是ready,表示文档结构已经加载完成(不包含图片等非文字媒体文件),二是onload,指示页面包含图片等文件在内的所有元素都加载完成。

11, 如何从浏览器的URL中获取查询字符串参数?

首先url的组成:

getUrlParam : function(name){
        //baidu.com/product/list?keyword=XXX&page=1
        var reg     = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
        var result  = window.location.search.substr(1).match(reg);
        return result ? decodeURIComponent(result[2]) : null;
    }
* 首先设置一个函数,给这个函数传递一个参数,也就是url的search部分的key值;* 设置一个正则表达式,以&开头或没有,中间是参数,后面以#或&结尾或没有;* 通过window.location.search.substr(1).match()匹配,返回一个数组* 如果数组不为空,返回数组的第3个值,也就是正则表达式的第二个子串

12.JavaScript里arguments究竟是什么?

类数组对象:arguments

当我们在js中在调用一个函数的时候,我们经常会给这个函数传递一些参数,js把传入到这个函数的全部参数存储在一个叫做arguments的东西里面,arguments 是类数组对象,和数组一样有length来表示长度,它也是一个特殊的对象,它的属性名是按照传入参数的序列来的,第1个参数的属性名是’0’,第2个参数的属性名是’1’,以此类推,并且它还有length属性,存储的是当前传入函数参数的个数,很多时候我们把这种对象叫做类数组对象。类数组对象和数组都是对象这个妈生的,但是数组是大哥比类数组对象多了很多其他的玩具(方法),类数组对象只是长得很像数组的弟弟而已。

arguments虽然有一些数组的性质,但其并非真正的数组,只是一个类数组对象。其并没有数组的很多方法,不能像真正的数组那样调用.jion(),.concat(),.pop()等方法。

13.什么是"use strict";?使用它的好处和坏处分别是什么?

在代码中出现表达式-"use strict"; 意味着代码按照严格模式解析,这种模式使得Javascript在更严格的条件下运行。

**好处:**
  • 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
  • 消除代码运行的一些不安全之处,保证代码运行的安全;
  • 提高编译器效率,增加运行速度;
  • 为未来新版本的Javascript做好铺垫。
**坏处:**
  • 同样的代码,在"严格模式"中,可能会有不一样的运行结果;
  • 一些在"正常模式"下可以运行的语句,在"严格模式"下将不能运行。

14.请解释一下JavaScript的同源策略

同源策略,即拥有相同的协议(protocol),端口(如果指定),主机(域名)的两个页面是属于同一个源

15.请解释JSONP的工作原理,以及它为什么不是真正的AJAX。

JSONP (JSON with Padding)是一个简单高效的跨域方式,HTML中的script标签可以加载并执行其他域的javascript,于是我们可以通过script标记来动态加载其他域的资源。例如我要从域A的页面pageA加载域B的数据,那么在域B的页面pageB中我以JavaScript的形式声明pageA需要的数据,然后在 pageA中用script标签把pageB加载进来,那么pageB中的脚本就会得以执行。JSONP在此基础上加入了回调函数,pageB加载完之后会执行pageA中定义的函数,所需要的数据会以参数的形式传递给该函数。JSONP易于实现,但是也会存在一些安全隐患,如果第三方的脚本随意地执行,那么它就可以篡改页面内容,截获敏感数据。但是在受信任的双方传递数据,JSONP是非常合适的选择。

AJAX是不跨域的,而JSONP是一个是跨域的,还有就是二者接收参数形式不一样!

16.通过new创建一个对象的时候,构造函数内部有哪些改变?

  • 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
  • 属性和方法被加入到 this 引用的对象中。
  • 新创建的对象由 this 所引用,并且最后隐式的返回 this 。

17. 什么是跨域?有什么方法解决跨域带来的问题?

跨域需要针对浏览器的同源策略来理解,同源策略指的是请求必须是同一个端口,同一个协议,同一个域名,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。

受浏览器同源策略的影响,不是同源的脚本不能操作其他源下面的对象。想要操作另一个源下的对象是就需要跨域。

解决方法: jsonp


在实际项目中,前后端分成两个不同的项目,各自部署在不同的域名下,这也就会遇到跨域问题了;  在浏览器同源策略限制下,向不同不同协议、不同域名或者不同端口)发送XHR请求,浏览器认为该请求不受信任,禁止请求,具体表现为请求后不正常响应;

18. 什么是闭包?使用场景是?

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

通常是函数嵌套时产生,它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在外部函数调用后被自动清除。

19.了解过Promise吗?与回调地狱有什么关系?

Promise 是一个对象,它在未来的某时会生成一个值:已完成(resolved)的值、或者一个没有完成的理由(例如网络错误)。一个 promise 会有 3 种可能的状态:fulfilled(已完成)、rejected(已拒绝)、pending(等待中)。Promise 的使用者可以附上回调函数来处理已完成的值或者拒绝的原因。

回调地狱:

JS异步编程,或使用大量回调函数时,其代码阅读起来晦涩难懂,并不直观;

使用Promise可以解决回调地狱的问题;

20.  js函数节流和函数防抖

举个栗子: 一个按钮被频繁点击,如何只提交一次,防止程序崩溃;

防抖(debounce)和节流(throttle)都是用来控制某个函数在一定时间内执行多少次的技巧,两者相似而又不同。 背后的基本思想是某些代码不可以在没有间断的情况下连续重复执行。

juejin.cn/post/684490…

21.  typeof函数

可以判断出'string','number','boolean','undefined','symbol'
但判断 typeof(null) 时值为 'object'; (这其实是早期留下来的一个bug)

 判断数组和对象时值均为 'object'

22. 数组去重的方法

这里说7种:

(1) set与解构赋值去重

ES6中新增了数据类型set,set的一个最大的特点就是数据不重复。Set函数可以接受一个数组(或类数组对象)作为参数来初始化,利用该特性也能做到给数组去重

<script type="text/javascript">
	function unique(arr){
		if(!Array.isArray(arr)){
			console.log('type error');
			return;
		}
		return [... new Set(arr)]
	}
	let arr1 = [11,11,11,23,45,23,55,76];
	let uarr1 = unique(arr1);
	console.log(uarr1);  //[11, 23, 45, 55, 76]
</script>

(2)双循环去重

双重for(或while)循环是比较笨拙的方法,它实现的原理很简单:先定义一个包含原始数组第一个元素的数组,然后遍历原始数组,将原始数组中的每个元素与新数组中的每个元素进行比对,如果不重复则添加到新数组中,最后返回新数组;因为它的时间复杂度是O(n^2),如果数组长度很大,那么将会非常耗费内存(一直有数组的修改)
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    let res = [arr[0]]
    for (let i = 1; i < arr.length; i++) {
        let flag = true
        for (let j = 0; j < res.length; j++) {
            if (arr[i] === res[j]) {
                flag = false;
                break
            }
        }
        if (flag) {
            res.push(arr[i])
        }
    }
    return res
}

(3)indexOf方法去重1

数组的indexOf()方法可返回某个指定的元素在数组中首次出现的位置。该方法首先定义一个空数组res,然后调用indexOf方法对原来的数组进行遍历判断,如果元素不在res中,则将其push进res中,最后将res返回即可获得去重的数组

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    let res = []
    for (let i = 0; i < arr.length; i++) {
        if (res.indexOf(arr[i]) === -1) {
            res.push(arr[i])
        }
    }
    return res
}

(4)indexOf方法去重2   (index + filter)

// 这种方法会有一个问题:[1,'1']会被当做相同元素,最终输入[1]
let arr = [1,1,2,3,4,5,5,6]
let arr2 = arr.filter(function(item,index) {
  // indexOf() 方法可返回某个指定的 字符串值 在字符串中首次出现的位置
  return arr.indexOf(item) === index
})

(5) 相邻元素去重

这种方法首先调用了数组的排序方法sort(),然后根据排序后的结果进行遍历及相邻元素比对,如果相等则跳过改元素,直到遍历结束

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    arr = arr.sort()
    let res = []
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            res.push(arr[i])
        }
    }
    return res
}

(6)利用对象属性去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    let res = [],
        obj = {}
    for (let i = 0; i < arr.length; i++) {
        if (!obj[arr[i]]) {
            res.push(arr[i])
            obj[arr[i]] = 1
        } else {
            obj[arr[i]]++
        }
    }
    return res
}

(7)Array.from与set去重

Array.from方法可以将Set结构转换为数组结果,而我们知道set结果是不重复的数据集,因此能够达到去重的目的

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    return Array.from(new Set(arr))
}

(8)reduce() 与 include()

let arr = [1,1,2,3,4,5,5,6]
let arr2 = arr.reduce(function(ar,cur) {
  if(!ar.includes(cur)) {
    ar.push(cur)
  }

  return ar
},[])

23.DOM 事件有哪些阶段?谈谈对事件代理的理解

分为三大阶段:捕获阶段--目标阶段--冒泡阶段

事件代理简单说就是:事件不直接绑定到某元素上,而是绑定到该元素的父元素上,进行触发事件操作时(例如'click'),再通过条件判断,执行事件触发后的语句(例如'alert(e.target.innerHTML)')

好处:(1)使代码更简洁;(2)节省内存开销

24. js 执行机制、事件循环

www.cnblogs.com/chenwenhao/…

JavaScript 语言的一大特点就是单线程,同一个时间只能做一件事。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。JavaScript 语言的设计者意识到这个问题,将所有任务分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous),在所有同步任务执行完之前,任何的异步任务是不会执行的。

25. ES6 的 class 和构造函数的区别

class 的写法只是语法糖,和之前 prototype 差不多,但还是有细微差别的,下面看看:

1. 严格模式

类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。

2. 不存在提升

类不存在变量提升(hoist),这一点与 ES5 完全不同。

复制new Foo(); // ReferenceError
class Foo {}
3. 方法默认是不可枚举的

ES6 中的 class,它的方法(包括静态方法和实例方法)默认是不可枚举的,而构造函数默认是可枚举的。细想一下,这其实是个优化,让你在遍历时候,不需要再判断 hasOwnProperty 了

4. class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有[[construct]],不能使用 new 来调用。
5. class 必须使用 new 调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用 new 也可以执行。
6. ES5 和 ES6 子类 this 生成顺序不同

ES5 的继承先生成了子类实例,再调用父类的构造函数修饰子类实例。ES6 的继承先 生成父类实例,再调用子类的构造函数修饰父类实例。这个差别使得 ES6 可以继承内置对象。

7. ES6可以继承静态方法,而构造函数不能

26. transform、translate、transition 分别是什么属性?

三者属性说明
transform 是指变换、变形,是 css3 的一个属性,和 width,height 属性一样;
translate 是 transform 的属性值,是指元素进行 2D(3D)维度上位移或范围变换;
transition 是指过渡效果,往往理解成简单的动画,需要有触发条件。

这里可以补充下 transition 和 animation 的比较,前者一般定义开始结束两个状态,需要有触发条件;而后者引入了关键帧、速度曲线、播放次数等概念,更符合动画的定义,且无需触发条件

27.javascript 的垃圾回收机制讲一下

定义:指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。

像 C 这样的编程语言,具有低级内存管理原语,如 malloc()和 free()。开发人员使用这些原语显式地对操作系统的内存进行分配和释放。
而 JavaScript 在创建对象(对象、字符串等)时会为它们分配内存,不再使用对时会“自动”释放内存,这个过程称为垃圾收集。

内存生命周期中的每一个阶段:

分配内存 —  内存是由操作系统分配的,它允许您的程序使用它。在低级语言(例如 C 语言)中,这是一个开发人员需要自己处理的显式执行的操作。然而,在高级语言中,系统会自动为你分配内在。
使用内存 — 这是程序实际使用之前分配的内存,在代码中使用分配的变量时,就会发生读和写操作。
释放内存 — 释放所有不再使用的内存,使之成为自由内存,并可以被重利用。与分配内存操作一样,这一操作在低级语言中也是需要显式地执行。

四种常见的内存泄漏:全局变量,未清除的定时器,闭包,以及 dom 的引用
  1. 全局变量 不用 var 声明的变量,相当于挂载到 window 对象上。如:b=1; 解决:使用严格模式
  2. 被遗忘的定时器和回调函数
  3. 闭包
  4. 没有清理的 DOM 元素引用

28. 对前端性能优化有什么了解?一般都通过那几个方面去优化的?

www.cnblogs.com/xiaohuochai…

  1. 减少请求数量
  2. 减小资源大小
  3. 优化网络连接
  4. 优化资源加载
  5. 减少重绘回流
  6. 性能更好的API
  7. webpack优化