JavaScript最全面试题

1.闭包

什么是闭包?

MDN 的解释:闭包是函数和声明该函数的词法环境的组合。
按照我的理解就是:闭包 = 『函数』和『函数体内可访问的变量总和』
说白了就是函数嵌套函数,内部函数能够访问外部函数的变量

闭包的作用

闭包最大的作用就是隐藏变量,闭包的一大特性就是 内部函数总是可以访问其所在的外部函数
中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后
基于此特性, JavaScript 可以实现私有变量、特权变量、储存变量等
我们就以私有变量举例,私有变量的实现方法很多,有靠约定的(变量名前加 _ , 有靠 Proxy
代理的,也有靠 Symbol 这种新数据类型的。

闭包的优点

可以隔离作用域,不造成全局污染

闭包的缺点

由于闭包长期驻留内存,则长期这样会导致内存泄露
如何解决内存泄露:将暴露全外部的闭包变量置为 null

适用场景

封装组件, for 循环和定时器结合使用 ,for 循环和 dom 事件结合 . 可以在性能优化的过程
, 节流防抖函数的使用 , 导航栏获取下标的使用

一般函数的词法环境在函数返回后就被销毁,但是闭包会保存对创建时所在词法环境的引用,即便创建时所在的执行上下文被销毁,但是创建时所在词法环境仍然存在,以达到延长变量生命周期的目的

柯里化函数
柯里化的目的在于避免频繁的调用具有相同参数函数的同时,又能轻松的重用

注意事项: 
如果不是某些特定任务需要使用闭包,在其他函数中创建函数是不明智的,因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响

2.谈谈你对原型链的理解?

原型对象

绝大部分的函数(少数内建函数除外)都有一个 prototype属性,这个属性是原型对象用来创建新对象实例,而所有被创建的对象都会共享原型对象,因此这些对象便可以访问原型对象的属性

js常被描述为一个基于原型的语言--每个对象拥有一个原型对象

当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次向上搜索,直到找到一个名字匹配的属性或者达到原型链的顶端

原型链

原因是每个对象都有 __proto__ 属性,此属性指向该对象的构造函数的原型

原型对象也可能拥有原型,并从中继承方法和属性,一层一层,以此类推,这种关系被称为原型链,他解释了为何一个对象会拥有定义在其他对象中的属性和方法

在对象实例和他的构造器之间建立一个链接,_proto_属性,是从构造函数的prototype属性中派生的,之后可以通过上溯原型链,在构造器中找到这些属性和方法

总结:

一起对象都是继承自object对象,object对象直接继承根源对象null

一切的函数对象,都是继承自Function对象

object对象直接继承自Function对象

Function对象的_proto_会指向自己的原型对象,最总还是继承自object对象

3.说一下JS继承(含ES6的)

JS 继承实现方式也很多,主要分 ES5 ES6 继承的实现

继承的优点:

继承可以使得子类具有父类别的各种属性和方法,而不需要再次编写相同的代码

再子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得于父类别不同的功能

先说一下 ES5 是如何实现继承的
ES5 实现继承主要是基于 prototype 来实现的,具体有三种方法
一是 原型链继承 :即 B.prototype=new A()
二是借 用构造函数继承 (call 或者 apply 的方式继承 )
是组合继承

实现方式:
原型链继承:是比较常见的一种继承方式之一,其中设计的构造函数、原型和实例,三者之间存在一定的关系,即每一个构造函数中都会有一个原型对象,原型对象有包含一个指向构造函数的指针,而实例则包含一个原型对象的指针

构造函数继承(借助 call)借助call调用Parent函数,父类原型对象中一旦存在父类之前自己定义的方法,那么子类将无法继承这些方法,父类的引用属性不会被共享,优化了原型链继承的弊端,但是它只能继承父类的实例属性和方法,不能继承原型属性或者方法

组合继承:组合继承就是将原型链继承和构造函数继承结合起来

原型式继承:借助Object.create方法实现普通对象的继承:因为它实现的是浅拷贝,多个实例的引用类型属性指向相同的内存,存在篡改的可能

寄生式继承:就是利用浅拷贝的能力进行增强,添加一些其他方法

寄生组合式继承:借助解决普通对象的继承问题的Object.create 方法,在前面几种继承方式的优缺点基础上进行改造,这也是所有继承方式里面相对最优的继承方式

4.说一下JS原生事件如何绑定

JS 原生绑定事件主要为三种:
一是 html 事件处理程序
二是 DOM0 级事件处理程序
三是 DOM2 级事件处理程序
html 事件现在早已不用了,就是在 html 各种标签上直接添加事件,类似于 css 的行内样
式,缺点是不好维护,因为散落在标签中 , 也就是耦合度太高

javascript中的事件,可以理解就是在HTML文档或者浏览器中发生的一种交互操作,使得网页具备互动性, 常见的有加载事件、鼠标事件、自定义事件等

事件流都会经历三个阶段:

事件捕获阶段(capture phase)
处于目标阶段(target phase)
事件冒泡阶段(bubbling phase)
 事件冒泡是一种从下往上的传播方式,由最具体的元素(触发节点)然后逐渐向上传播到最不具体的那个节点,也就是DOM中最高层的父节点

事件捕获与事件冒泡相反,事件最开始由不太具体的节点最早接受事件, 而最具体的节点(触发节点)最后接受事件

事件模型可以分为三种:

原始事件模型(DOM0级)html代码直接绑定,js代码绑定

特性:

绑定速度快

只支持冒泡,不支持捕获

同一个类型的事件只能绑定一次

标准事件模型(DOM2级)

事件捕获阶段:事件从document一直向下传播到目标元素, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行
事件处理阶段:事件到达目标元素, 触发目标元素的监听函数
事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行
特性:

可以在一个DOM元素上绑定多个事件处理器,各自并不会冲突
当第三个参数(useCapture)设置为true就在捕获过程中执行,反之在冒泡过程中执行处理函数

IE事件模型(基本不用

IE事件模型共有两个过程:

事件处理阶段:事件到达目标元素, 触发目标元素的监听函数。
事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行

 

5.说一下JS原生常用dom操作方法?

查找:

getElementByid,
getElementsByTagName,
querySelector,
querySelectorAll

插入:

appendChild,insertBefore

删除:

removeChild

克隆:

cloneNode

设置和获取属性:

setAttribute(“ 属性名 ”,” ”)
getAttibute(“ 属性名 ”)

6.说一下ES6新增特性?

1. 新增了块级作用域 (let,const)
2. 提供了定义类的语法糖 (class)
3. 新增了一种基本数据类型 (Symbol)
4. 新增了变量的解构赋值
5. 函数参数允许设置默认值,引入了 rest 参数,新增了箭头函数
6. 数组新增了一些 API ,如 isArray / from / of 方法 ; 数组实例新增了 entries() keys() values()
方法
7. 对象和数组新增了扩展运算符
8. ES6 新增了模块化 (import/export)
9. ES6 新增了 Set Map 数据结构
10. ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例
11. ES6 新增了生成器 (Generator) 和遍历器 (Iterator)

7.JS设计模式有哪些(单例模式观察者模式等)

JS 设计模式有很多,但我知道的有单例模式,观察者模式
单例模式:
就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果
不存在就创建了再返回,这就确保了一个类只有一个实例对象。在 JavaScript 里,单例作为一个命
名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。
观察者模式 :
观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象
需要改变的时候,就应该考虑使用观察者模式。
总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而 使得各自的变化都不会影响到另一边的变化

8.说一下你对JS面试对象的理解

JS 面向对象主要基于 function 来实现的,通过 function 来模拟类,通过 prototype 来实现类方法的共享, 跟其他语言有着本质的不同,自从有了ES6 后,把面向对象类的实现更像后端语言的实现了,通过 class 来定义类,通过extends 来继承父类,其实 ES6 类的实现本质上是一个语法糖,不过对于开发简单了好多

9.说一下JS数组常用方法(至少6个)

1. Array.push(),向数组的末尾添加一个或多个元素,并返回新的数组长度。原数组改变
2. Array.pop(),删除并返回数组的最后一个元素,若该数组为空,则返回undefined。原数组改变
3. Array.unshift(),向数组的开头添加一个或多个元素,并返回新的数组长度。原数组改变。
4. Array.shift(),删除数组的第一项,并返回第一个元素的值。若该数组为空,则返回undefined。原数组改变。
5. Array.concat(arr1,arr2…),合并两个或多个数组,生成一个新的数组。原数组不变。
6. Array.join(),将数组的每一项用指定字符连接形成一个字符串。默认连接字符为 “,” 逗号。
7. Array.reverse(),将数组倒序。原数组改变。
8. Array.sort(),对数组元素进行排序。按照字符串UniCode码排序,原数组改变。
9.Array.map(function),原数组的每一项执行函数后,返回一个新的数组。原数组不变。(注意该方法和forEach的区别)。
10.Array.slice() 按照条件查找出其中的部分内容
11.Array.splice(index,howmany,arr1,arr2…) ,用于添加或删除数组中的元素。从index位置开始删除howmany个元素,并将arr1、arr2…数据从index位置依次插入。howmany为0时,则不删除元素。原数组改变。
12.Array.forEach(function),用于调用数组的每个元素,并将元素传递给回调函数。原数组不变。(注意该方法和map的区别,若直接打印Array.forEach,结果为undefined)。
13.Array.filter(function),过滤数组中,符合条件的元素并返回一个新的数组
14.Array.every(function),对数组中的每一项进行判断,若都符合则返回true,否则返回false。
15.Array.some(function),对数组中的每一项进行判断,若都不符合则返回false,否则返回true。
16.Array.reduce(function),reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
17.indexOf() 检测当前值在数组中第一次出现的位置索引

原数组改变的方法有:push pop shift unshift reverse sort splice forEach
不改变原数组的方法有:concat map filter join every some indexOf slice

10.说一下JS数组内置遍历方法有哪些和区别

forEach

这个方法是为了取代 for 循环遍历数组的,返回值为 undefined
item 代码遍历的每一项 ,
index: 代表遍历的每项的索引,
arr 代表数组本身

filter

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值