不论是金三银四,还是金九银十,面试题总是我们避不开的必备项;但我认为这并不是我们需要面试时,才可以打开看的。这系列知识点,可以是我们日常查漏补缺的宝典,也可以是我们对自己前端知识系统的完善,也能是放松时,偶尔瞥两眼的积累!
第二章我想聊聊前端三剑客之一,关于Javascript相关问题。
1.Js基本数据类型有哪些?
字符串 String 数值 Number 布尔 boolean null undefined 对象 数组
2.Ajax如何使用?
一个完整的 AJAX 请求包括五个步骤:创建 XMLHTTPRequest 对象使用 open 方法创建 http 请求,并设置请求地址xhr.open(get/post,url,async,true(异步),false(同步))经常使用前三个参数设置发送的数据,用 send 发送请求注册事件(给 ajax 设置事件)获取响应并更新页面。
3.如何判断一个数据是 NaN?
NaN 非数字 但是用 typeof 检测是 number 类型;利用 NaN 的定义 用 typeof 判断是否为number 类型并且判断是否满足 isnan;利用 NaN 是唯一一个不等于任何自身的特点 n!==n;利用 ES6 中提供的 Object.is()方法(判断两个值是否相等) n==nan;
Js 中 null 与 undefined 区别:
相同点:用 if 判断时,两者都会被转换成 false
不同点:number 转换的值不同 number(null)为 0 number(undefined)为 NaN;Null 表示一个值被定义了,但是这个值是空值;Undefined 变量声明但未赋值
4.闭包是什么?有什么特性?对页面会有什么影响?
闭包可以简单理解成:定义在一个函数内部的函数。其中一个内部函数,在包含它们的外部函数之外被调用时,就会形成闭包。
特点:
- 1.函数嵌套函数。
- 2.函数内部可以引用外部的参数和变量。
- 3.参数和变量不会被垃圾回收机制回收。
使用:
- 1.读取函数内部的变量;
- 2.这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除。
优点:
- 1:变量长期驻扎在内存中;
- 2:避免全局变量的污染;
- 3:私有成员的存在 ;
缺点:会造成内存泄露。
5. Js中常见的内存泄漏?
- 1.意外的全局变量
- 2.被遗忘的计时器或回调函数
- 3.脱离 DOM 的引用
- 4.闭包
6.事件委托是什么?如何确定事件源(Event.target谁调用谁就是事件源)?
JS 高程上讲:事件委托就是利用事件冒泡,只制定一个时间处理程序,就可以管理某一类型的所有事件。事件委托,称事件代理,是 js 中很常用的绑定事件的技巧,事件委托就是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务,事件委托的原理是 DOM 元素的事件冒泡。
7.什么是事件冒泡?
一个事件触发后,会在子元素和父元素之间传播,这种传播分为三个阶段,捕获阶段(从 window 对象传导到目标节点(从外到里),这个阶段不会响应任何事件),目标阶段,(在目标节点上触发),冒泡阶段(从目标节点传导回 window 对象(从里到外)),事件委托/事件代理就是利用事件冒泡的机制把里层需要响应的事件绑定到外层。
8.本地存储与 cookie的区别?
Cookie 是小甜饼的意思。顾名思义,cookie 确实非常小,它的大小限制为 4KB 左右。它的主要用途有保存登录信息,比如你登录某个网站市场可以看到“记住密码”,这通常就是通过在 Cookie 中存入一段辨别用户身份的数据来实现的。
localStorage是 HTML5 标准中新加入的技术,它并不是什么划时代的新东西。早在 IE 6 时代,就有一个叫 userData 的东西用于本地存储,而当时考虑到浏览器兼容性,更通用的方案是使用 Flash。而如今,localStorage 被大多数浏览器所支持,如果你的网站需要支持 IE6+,那以 userData 作为你方案是种不错的选择。
sessionStorage 与 localStorage 的接口类似,但保存数据的生命周期与 localStorage 不同。做过后端开发的同学应该知道 Session 这个词的意思,直译过来是“会话”。而 sessionStorage 是一个前端的概念,它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但当页面关闭后,sessionStorage 中的数据就会被清空。
三者的异同:
一般由服务器生成,可设置失效时间。如果在浏览器端生成 Cookie,默认是关闭浏览器后失效,除非被清除,否则永久保存,仅在当前会话下有效,关闭页面或浏览器后被清除,存放数据大小,4K 左右,一般为 5MB,与服务器端通信,每次都会携带在 HTTP 头中,如果使用 cookie 保存过多数据会带来性能问题,仅在客户端(即浏览器)中保存,不参与和服务器的通信,易用性,需要程序员自己封装,源生的 Cookie 接口不友好,源生接口可以接受,亦可再次封装来对 Object 和 Array 有更好的支持。
9.ES6新特性?
const 和 let、模板字符串、箭头函数、函数的参数默认值、对象和数组,解构、for...of 和for...in、ES6 中的类。
10.Let与 var与 const的区别?
- Var 声明的变量会挂载在 window 上,而 let 和 const 声明的变量不会
- Var 声明的变量存在变量提升,let 和 const 不存在变量提升
- 同一作用域下 var 可以声明同名变量,let 和 const、不可以
- Let 和 const 声明会形成块级作用域
- Let 暂存死区
- Const 一旦声明必须赋值,不能用 null 占位,声明后不能再修改,如果
- 声明的是复合类型数据,可以修改属性
11.数组方法有哪些请简述?
- push() 从后面添加元素,返回值为添加完后的数组的长度
- arr.pop() 从后面删除元素,只能是一个,返回值是删除的元素
- arr.shift() 从前面删除元素,只能删除一个 返回值是删除的元素
- arr.unshift() 从前面添加元素, 返回值是添加完后的数组的长度
- arr.splice(i,n) 删除从 i(索引值)开始之后的那个元素。返回值是删除的元素
- arr.concat() 连接两个数组 返回值为连接后的新数组
- str.split() 将字符串转化为数组
- arr.sort() 将数组进行排序,返回值是排好的数组,默认是按照最左边的数字进行排序,不是按照数字大小排序的
- arr.reverse() 将数组反转,返回值是反转后的数组
- arr.slice(start,end) 切去索引值 start 到索引值 end 的数组,不包含 end索引的值,返回值是切出来的数组
- arr.forEach(callback) 遍历数组,无 return 即使有 return,也不会返回任何值,并且会影响原来的数组
- arr.map(callback) 映射数组(遍历数组),有 return 返回一个新数组
- arr.filter(callback) 过滤数组,返回一个满足要求的数组
12.什么是面向对象请简述?
面向对象是一种思想,是基于面向过程而言的,就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节;这种思想是将数据作为第一位,这是对数据一种优化,操作起来更加的方便,简化了过程。Js 本身是没有 class 类型的,但是每个函数都有一个 prototype 属性,prototype 指向一个对象,当函数作为构造函数时,prototype 就起到类似于 class 的作用.
面向对象有三个特点:封装(隐藏对象的属性和实现细节,对外提供公共访问方式),继承(提高代码复用性,继承是多态的前提),多态(是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象)
13.普通函数和构造函数的区别?
- 1.构造函数也是一个普通函数,创建方式和普通函数一样,但是构造函数习惯上首字母大写
- 2.调用方式不一样,普通函数直接调用,构造函数要用关键字 new 来调用
- 3.调用时,构造函数内部会创建一个新对象,就是实例,普通函数不会创建新对象
- 4.构造函数内部的 this 指向实例,普通函数内部的 this 指向调用函数的对象(如果没有对象调用,默认为 window)
- 5.构造函数默认的返回值是创建的对象(也就是实例),普通函数的返回值由 return 语句决定
- 6.构造函数的函数名与类名相同
14.请简述原型/原型链/(原型)继承?
什么是原型?
任何对象实例都有一个原型,也叫原型对象,这个原型对象由对象的内置属性_proto_指向它的构造函数的 prototype 指向的对象,即任何对象都是由一个构造函数创建的,但是不是每一对象都有 prototype,只有方法才有 prototype。
什么是原型链?
原型链基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。我们知道,每个构造函数都有一个原型对象,每个原型对象都有一个指向构造函数的指针,而实例又包涵一个指向原型对象的内部指针。原型链的核心就是依赖对象的_proto_的指向,当自身不存在的属性时,就一层层的扒出创建对象的构造函数,直至到 Object 时,就没有_proto_指向了。因为_proto_实质找的是 prototype,所以我们只要找这个链条上的构造函数的 prototype。其中 Object.prototype 是没有_proto_属性的,它==null。每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含指向原型对象内部的指针。
15.Promise的理解?
什么是 Promise?
我们都知道,Promise 是承诺的意思,承诺它过一段时间会给你一个结果。Promise 是一种解决异步编程的方案,相比回调函数和事件更合理和更强大。从语法上讲,promise 是一个对象,从它可以获取异步操作的消息;
promise 有三种状态:pending 初始状态也叫等待状态,fulfiled,成功状态,rejected 失败状态;状态一旦改变,就不会再变。创造 promise实例后,它会立即执行。
Promise 的两个特点:
- 1)、Promise 对象的状态不受外界影响
- 2)、Promise 的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可以逆,
Promise 的三个缺点:
1)无法取消 Promise,一旦新建它就会立即执行,无法中途取消
2)如果不设置回调函数,Promise 内部抛出的错误,不会反映到外部
3)当处于 pending(等待)状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成。
16.我们用 Promise来解决什么问题?
promise 是用来解决两个问题的:
1.回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象;
2.promise 可以支持多并发的请求,获取并发请求中的数据,这个 promise 可以解决异步的问题,本身不能说 promise 是异步的。
17.请简述 async的用法?
Async 就是 generation 和 promise 的语法糖,async 就是将 generator的*换成 async,将 yiled 换成 await函数前必须加一个 async,异步操作方法前加一个 await 关键字,意思就是等一下,执行完了再继续走,注意:await 只能在 async 函数中运行,否则会报错,Promise 如果返回的是一个错误的结果,如果没有做异常处理,就会报错,所以用 try..catch 捕获一下异常就可以了。
18.一个页面从输入 URL到页面加载显示完成,这个过程中都发生了什么?
分为 4 个步骤:
- 1. 当发送一个 URL 请求时,不管这个 URL 是 Web 页面的 URL 还是 Web 页面上每个资源的 URL,浏览器都会开启一个线程来处理这个请求,同时在远程 DNS 服务器上启动一个 DNS 查询。这能使浏览器获得请求对应的 IP 地址。
- 2. 浏览器与远程 Web 服务器通过 TCP 三次握手协商来建立一个TCP/IP 连接。该握手包括一个同步报文,一个同步-应答报文和一个应答报文,这三个报文在 浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,然后服务器响应并接受客户端的请求,最后由客户端发出该请求已经被接受的报文。
- 3. 一旦 TCP/IP 连接建立,浏览器会通过该连接向远程服务器发送HTTP 的 GET 请求。远程服务器找到资源并使用 HTTP 响应返回该资源。
- 4. 此时,Web 服务器提供资源服务,客户端开始下载资源。
19.get请求传参长度的误区?
误区:我们经常说 get 请求参数的大小存在限制,而 post 请求的参数大小是无限制的。实际上 HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对 get 请求参数的限制是来源与浏览器或 web服务器,浏览器或 web 服务器限制了 url 的长度。为了明确这个概念,我们必须再次强调下面几点:
- HTTP 协议 未规定 GET 和 POST 的长度限制;
- GET 的最大长度显示是因为 浏览器和 web 服务器限制了 URI 的长度不同的浏览器和 WEB 服务器,限制的最大长度不一样要支持IE,则最大 长度为 2083byte,若只支 持 Chrome,则最大 长度8182byte。
20.补充 get和 post请求在缓存方面的区别?
post/get 的请求区别,具体不再赘述。
补充补充一个 get 和 post 在缓存方面的区别:get 请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
post 不同,post 做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此 get 请求适合于请求缓存。
21.说一下闭包?
一句话可以概括:闭包就是能够读取其他函数内部变量的函数,或者子函数在外调用, 子函数所在的父函数的作用域不会被释放。
为什么要用:
匿名自执行函数:我们知道所有的变量,如果不加上 var 关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的)。除了每次使用变量都是用 var 关键字外,我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,可以用闭包。
22.说说前端中的事件流?
HTML 中与 javascript 交互是通过事件驱动来实现的,例如鼠标点击事件 onclick、页面的滚动事件 onscroll 等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。
什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2 级事件流包括下面几个阶段。事件捕获阶段处于目标阶段事件冒泡阶段addEventListener:addEventListener 是 DOM2 级事件新增的指定事件处理程序的操作,这个方法接收 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是 false,表示 在冒泡阶段调用事件处理程序。IE 只支持事件冒泡。
23.说一下事件委托?
简介:事件委托指的是,不在事件的发生地(直接 dom)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素 DOM 的类型,来做出不同的响应。
举例:最经典的就是 ul 和 li 标签的事件监听,比如我们在添加事件时候,采用事件委托机制,不会在 li 标签上直接添加,而是在ul 父元素上添加。
好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发机制。
24.JS的 new操作符做了哪些事情?
new 操作符新建了一个空对象,这个对象原型指向构造函数的prototype,执行构造函数后返回这个对象。
25.改变函数内部 this指针的指向函数(bind,apply,call的区别)?
通过 apply 和 call 改变函数的 this 指向,他们两个函数的第一个参数都是一样的表示要改变指向的那个对象,第二个参数,apply 是数组,而 call 则是 arg1,arg2...这种形式。通过 bind 改变 this 作用域会返回一个新的函数,这个函数不会马上执行。
26.JS的各种位置,比 clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop的区别?
- clientHeight:表示的是可视区域的高度,不包含 border 和滚动条
- offsetHeight:表示可 视区域的高度,包 含了 border 和滚动条
- scrollHeight:表示了所有区域的高度,包含了因为滚动被隐藏的部分。
- clientTop:表示边框 border 的厚度,在未指定的情况下一般为
- 0scrollTop:滚动后被隐藏的高度,获取对象相对于由 offsetParent 属性指定的父坐标(css 定位的元素或 body 元素)距离顶端的高度。
27.JS拖拽功能的实现?
首先是三个事件,分别是 mousedown,mousemove,mouseup 当鼠标点击按下的时候,需要一个 tag 标识此时已经按下,可以执行mousemove 里面的具体方法。clientX,clientY 标识的是鼠标的坐标,分别标识横坐标和纵坐标,并且我们用 offsetX 和 offsetY 来表示元素的元素的初始坐标,移动的举例应该是:鼠标移动时候的坐标-鼠标按下去时候的坐标。也就是说定位信息为:鼠标移动时候的坐标-鼠标按下去时候的坐标+元素初始情况下的 offetLeft.还有一点也是原理性的东西,也就是拖拽的同时是绝对定位,我们改变的是绝对定位条件下的 left 以及 top等等值。补充:也可以通过 html5 的拖放(Drag 和 drop)来实现。
28.JS中的垃圾回收机制?
必要性:由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript 程序每次创建字符串、数组或对象时,解释器都必 须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,
JavaScript 的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。这段话解释了为什么需要系统需要垃圾回收,JS 不像 C/C++,他有自己的一套垃圾回收机制(Garbage Collection)。JavaScript 的解释器可以检测到何时程序不再使用一个对象了,当他确定了一个对象是无用的时候,他就知道不再需要这个对象,可以把它所占用的内存释放掉了。例如:
var a="hello world"; var b="world";var a=b; // 这时,会释放掉"hello world",释放内存以便再引用垃圾回收的方法:
标记清除:
这是最常见的垃圾回收方式,当变量进入环境时,就标记这个变量为”进入环境“,从逻辑上讲,永远不能释放进入环境的变量所占的内存,永远不能释放进入环境变量所占用的内存,只要执行流程进入相应的环境,就可能用到他们。当离开环境时,就标记为离开环境。垃圾回收器在运行的时候会给存储在内存中的变量都加上标记(所有都加),然后去掉环境变量中的变量,以及被环境变量中的变量所引用的变量(条件性去除标记),删除所有被标记的变量,删除的变量无法在环境变量中被访问所以会被删除,最后垃圾回收器,完成了内存的清除工作,并回收他们所占用的内存。
引用计数法:
另一种不太常见的方法就是引用计数法,引用计数法的意思就是每个值没引用的次数, 当声明了一个变量,并用一个引用类型的值赋值给改变量,则这个值的引用次数为 1,;相反的,如果包含了对这个值引用的变量又取得了另外一个值,则原先的引用值引用次数就减 1,当这个值的引用次数为 0 的时候,说明没有办法再访问这个值了,因此就把所占的内存给回收进来,这样垃圾收集器再次运行的时候,就会释放引用次数为 0 的这些值。
用引用计数法会存在内存泄露,下面来看原因:
function problem() {
var objA = new Object(); var objB = new Object();
objA.someOtherObject = objB; objB.anotherObject = objA;
}
在这个例子里面,objA 和 objB 通过各自的属性相互引用,这样的话,两个对象的引用次数都为 2,在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,因为计数不为 0,这样的相互引用如果大量存在就会导致内存泄露。特 别 是 在 DOM 对 象 中 , 也 容 易 存 在 这 种 问 题 :
var element=document.getElementById ('');
var myObj=newObject();
myObj.element=element; element.someObject=myObj;
这样就不会有垃圾回收的过程。
29.能来讲讲 JS的语言特性吗?
- 运行在客户端浏览器上;
- 不用预编译,直接解析执行代码;
- 是弱类型语言,较为灵活;
- 与操作系统无关,跨平台的语言;
- 脚本语言、解释性语言;
30.JS的全排列?
function permutate(str) {
var result =
[];
if(str.length
> 1) { var
left = str[0];
var rest = str.slice(1,
str.length); var
preResult =
permutate(rest);
for(var i=0;
i<preResult.length; i++)
{ for(var j=0;
j<preResult[i].length; j++) {
var tmp = preResult[i],slice(0, j) + left + preResult[i].slice(j,
preResult[i].length);result.push(tmp);
}
}
} else if (str.length
== 1) { return
[str];
}
return result;
}
人生海海,码途徐徐,在每一段经历中,留下成长的印记,爱自己爱生活爱思考!