耗时一周整理的前端面试题,干货为主

49次阅读

共计 7911 个字符,预计需要花费 20 分钟才能阅读完成。

websocket
Websocket 同 http 一样都是是基于 tcp 的, 可靠性的双向通信协议,是建立在 tcp 之上的,并且是持久化的协议。
websocket 和 http 区别?

相同点

都是应用层的协议
都是基于 tcp, 可靠的协议

不同点

websocket 是持久化的协议.
websocket 是双向通信协议,模拟 socket 协议, 可以双向发送信息,而 HTTP 是单向的
websocket 可以在服务器端主动向客户端发送信息,而 http 的服务端, 只能通过客户端主动请求

请描述一下 cookie、sessionStorage 和 localStorage 的区别?

相同点:都存储在客户端

不同点

存储大小 cookie 数据大小不能超过 4k。sessionStorage 和 localStorage 虽然也有存储大小的限制,但比 cookie 大得多,可以达到 5M 或更大。
有效时间 localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据;sessionStorage 数据在当前浏览器窗口关闭后自动删除。cookie 设置的 cookie 过期时间之前一直有效,即使窗口或浏览器关闭
数据与服务器之间的交互方式 cookie 的数据会自动的传递到服务器,服务器端也可以写 cookie 到客户端 sessionStorage 和 localStorage 不会自动把数据发给服务器,仅在本地保存

JS 的类型?

基本类型

undefined
null
Number
String
Boolean

复杂类型
Object

js 变量按照存储方式区分,有哪些类型,并表述其特点

值类型:

undefined string number Boolean
拷贝形式,

引用类型:

array , function
指针指向,共用空间
可无限扩展属性,极大节省命名空间。

特殊引用类型:function

同引用类型↑
特殊引用类型只有 function,由于 function 的特殊性,使得它有点不同

JS 中的 typeof 能得到的那些类型?6 种

number
string
undefined
object : null 和数组 都是 object
function
boolean

注意:typeof 无法详细区分引用类型的类型,除了 function. 只能准确区分值类型的类型比如:
typeof {} //object
typeof [] //object
typeof null //object
typeof console.log // function
function 是一个比较特殊的类型,所以 typeof 能够区分
何时使用 === 何时使用 ==?

尽可能使用 === 原因如下

一致性: 使用 == 对一致性没有任何好处,所以提前避免
一般来说,=== 是最简单的操作符,因为它不用类型转换,所以相对来说,速度也会更快。
== 会进行类型转换,很容易懵逼

== 的使用情况, 可参考
判断对象的属性是否存在
var obj = {};
if(obj.a == null){
// 这里相对于:obj.a === null || obj.a === undefined 的简写形式,JQ 源码的推荐写法
}
判断函数的参数是否存在
function fn(a, b){
if(b == null){
// 这里相当于 b===null || b === undefined 的简写
}
}

如何理解 JSON?

从纯 JS 的角度看,JSON 就是对象,并且只有两个 API
JSON.stringify({a:10,b:30}) // 将对象转为字符串
JSON.parse(‘{“a”:10,”b”:30}’) // 将 JSON 格式的字符串转为 对象

JSON 也是一种轻量级的文本数据交换格式.

js 中有哪些内置函数 9 种

Object
Array
Boolean
Number
String
Function
Date
RegExp
Error

判断一个变量会被当做 true 还是 false
var a = 100; console.log(!!a); //true
window.onload 和 DOMContentLoaded 的区别?

window.onload: 页面中所有数据加载完成后,才会执行,包括图片,css 等
DOMContentLoaded: DOM 结构加载完成后执行,需要等待图片等其他资源加载完成

简述如何实现一个模块加载器,实现类似 requires.js 的基本功能
可参看这篇博文:https://github.com/youngwind/…
实现数组的随机排序
// 该方法最简单,效果一般,每个元素仍然有很大机率在它原来的位置附近出现
arr.sort(function () {
return Math.random() – 0.5;
});

//Fisher–Yates shuffle 费雪耶兹随机置乱算法 )!!!推荐

// 算法思想:从 0~i(i 的变化为 n- 1 到 0 递减)中随机取得一个下标,和最后一个元素(i)交换。
var arr = [5,8,59,56];
function shuffle(arr) {
var i = arr.length, t, j;
while (i)
{
j = Math.floor(Math.random() * i–);
t= arr[i];
arr[i] = arr[j];
arr[j]= t;
}
}
shuffle(arr)
console.log(arr);//[56, 8, 5, 59]
原型和原型链
什么叫原型链
原型链是针对构造函数的,比如我创建了一个函数并通过变量 new 了一个函数,那这个函数就会继承创建处理函数的属性,如果访问这个函数的属性时,并没有在 new 处理的变量中写该属性,那么就会往上, 根据 protype 逐级向上查找,这个查找的过程就叫原型链。
原型规则

所有的引用类型(数组,对象,函数),都具有对象的特殊,即可自由扩展属性(除了 Null,纯属意外)
所有的引用类型(数组,对象,函数),都有一个__proto__属性,也可以称为隐式原型,属性值是一个普通的对象
所有的函数,都有一个 prototype 属性,也可称之为显式原型,属性值是一个普通的对象
所有的引用类型(数组,对象,函数),__proto__属性值指向它的构造函数的 prototype 属性值
当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__中去找。

由于它的隐式原型等于它的显式原型,所以也会去 prototype 中去找。
构造函数
function Foo(name,age){
this.name = name;
this.age = age;
}
var foo = new Foo(‘h1’,25);
var foo2 = new Foo(‘h1’,250);
console.log(foo,foo2);

// 循环对象自身的属性
var item;
for(item in foo)
{
// 只遍历对象自身的属性,过滤掉该对象的显式原型
if(foo.hasOwnProperty(item))
{
console.log(item)
}

}

描述 new 一个对象的过程

创建一个对象
this 指向这个新对象
执行代码,即对 this 赋值
return this。默认有 return,不用写

如何判断一个变量是数组类型
var arr = [1,2,3];
console.log(Array.isArray(arr)); //true

//instanceof 运算符用于测试构造函数的 prototype 属性是否出现在对象的原型链中的任何位置
console.log(arr instanceof Array) //true
写一个原型继承的例子
function Elem(id){
this.dom = document.getElementById(id);
}

Elem.prototype.html = function(val){
var dom = this.dom;
if(val){
dom.innerHTML = val;
return this; // 用来链式调用
}else{
return dom.innerHTML;

}
}

Elem.prototype.on = function(type ,fn){
var dom = this.dom;
dom.addEventListener(type , fn);

}
var h1 = new Elem(‘h1’);
h1.html(“ 你被修改了 ”).on(‘click’, function(){
console.log(this)
})

作用域和闭包
什么叫作用域?
` 作用域是针对变量的,比如我创建了一个函数,这个函数中包含了另外一个函数。那么该变量中就有 3 个作用域全局作用域》函数作用域》内层函数的作用域作用域的特点就是,先在自己的变量范围中查找,如果找不到,就会沿着作用域往上找。`
变量提升的理解

变量定义
函数声明(注意和函数表达式的区别)
预解析

this 的使用场景
注意:this 要在执行时才能确认值,定义时无法确认

作为构造函数执行
作为对象属性执行
作为普通函数执行
call apply bind

function f1(name,age){
console.log(name,age)
console.log(this); //this 为 x 对象
}

f1.apply({x:’ 我是 this’}, [“seek”,20]);
f1.call({x:’ 我是 this’}, “seek”,20);

// 使用 bind 改变 this 时,需用函数表达式
var f1 = function (name,age){
console.log(name,age)
console.log(this); //this 为 x 对象
}.bind(‘ 我是被绑定的 this’)

f1(“seek”,20)

闭包
` 当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数内部的其它变量,如果返回的这个函数在外部被执行,就产生了闭包。表现形式:使函数外部能够调用函数内部定义的变量。`

闭包的使用场景

函数作为返回值
function fn(){
var a = 10;
return function(){
console.log(a); // a 是自由变量,从父作用域开始找。
}
}
var f1 = fn();
var a = 20;
f1(); //10

函数作为参数来传递
function fn(){
var a = 10;
return function(){
console.log(a);
}
}
var fn1 = fn();

function fn2(fn){
var a =20;
fn();
}
fn2(fn1); //10

如何理解作用域?

自由变量
作用域链,即自由变量的查找
闭包的两个场景

JS 创建 10 个 a 标签,点击时弹出对应的序号(考点:作用域 )
var str,a;
for(a=0; a<10;a++){
str = document.createElement(“a”);
str.innerHTML = a + “ 点我 ” + “<br/>”;

document.body.appendChild(str);

(function(a){
str.addEventListener(“click”,function(e){
e.preventDefault();
console.log(a)
})
})(a)
}
什么叫异步, 什么叫同步?
同步是阻塞模式,异步是非阻塞模式。

异步:不需要等操作做完,就响应用户请求. 比如:ajax,img 的加载,setTimeout,setInterval
同步:必须等待操作做完,才返回结果.

数组 API
var arr= [2,3,9,0];
forEach 遍历所有元素
arr.forEach(function(item,index){
console.log(item) // 2390
console.log(index) //0123

})
every 判断所有元素是否都符合条件
var result = arr.every(function(item,index){
if(item < 4)
{
return true;
}
})
console.log(result); //false,因为 9 并不小于 4
some 判断是否有至少一个元素符合条件
var result = arr.some(function(item,index){
if(item < 4)
{
return true;
}
})
console.log(result); //true 因为 2,3,0 小于 4
sort 排序
var result = arr.sort(function(a,b){
// return a-b; // 正序
return b-a; // 倒序
// return return Math.random() – 0.5; // 最简单的随机数组排序,并不推荐

})
console.log(result); //  [9, 3, 2, 0]
map 对元素重新组装,生成新数组
//map 适用范围还是较广的,学会思考
var result = arr.map(function(item,index){
return ‘<h1>’ + item + ‘</h1>’;
})
console.log(result); // [“<h1>2</h1>”, “<h1>3</h1>”, “<h1>9</h1>”, “<h1>0</h1>”]
filter 过滤符合条件的元素, 较为常用
var result = arr.filter(function(item,index){
if(item >=3){
return true;
}
})
console.log(result); // [3, 9]
获取 2019-03-23 格式的日期
function formatDate(dt) {
if (!dt) {
// 如果不传参数,则默认为当前时间
dt = new Date();
}

var year = dt.getFullYear();
var month = dt.getMonth() + 1;
var day = dt.getDate();

if (month <= 10) {
month = ‘0’ + month;
}

if (day <= 10) {
day = ‘0’ + day;
}

return year + ‘-‘ + month + ‘-‘ + day;
}

var date = formatDate();
console.log(date); //2019-03-23

获取随机数,要求长度一致的字符串格式
var random = Math.random();
random = random + ‘0’.repeat(10); //repeat 重复 10 个 0, 防止随机数出现少于 10 位数的情况
random = random.slice(0,10)
console.log(random); //0.70728618 每次返回的只有 10 位数的字符串
写一个能遍历对象和数组的 foreach 函数
function foreach(info, fn)
{
// 数组处理
if(info instanceof Array)
{
info.forEach(fn)
}else{
// 对象处理
for(key in obj){
fn(key, obj[key])
}
}

}

// 使用方法
var obj = {x: ‘ 我是 x ’,y: ‘ 我是 y ’};
foreach(obj, function(key,value){
console.log(value); // 我是 x, 我是 y
})

var arr = [5,8,9];
foreach(arr, function(elem,index){
console.log(elem);//5,8,9
})

Web_API
编写一个通用的事件监听函数
function bindEvent(elem,type,fn){
elem.addEventListener(type ,fn)
}
// 使用方法
bindEvent(id,’click’, function(e){
console.log(e)
})

bindEvent(a,’click’, function(e){
e.preventDefault(); // 阻止默认事件
})
对于一个无限加载流的页面,如何给每个特定的标签添加事件
// 使用代理,由父级帮忙去做

<div id=”div1″>
<a href=”#”>a1</a>
<a href=”#”>a2</a>
<span>ddd</span>
<a href=”#”>a5</a>
<!– 动态加载更多 –>
</div>

div1.addEventListener(‘click’, function(e){
if (e.target.nodeName == “A”){
alert(e.target.innerHTML)
}
})
完善通用绑定事件的函数,包括代理
//HTML 结构
<div id=”div1″>
<a href=”#”>a1</a>
<a href=”#”>a2</a>
<span>ddd</span>
<a href=”#”>a5</a>
<!– 动态加载更多 –>
</div>

<div id=”div2″> 不使用代理 </div>

//
function bindEvent(elem,type,selector, fn){
if(fn == null){
fn=selector;
selector =null;
}
elem.addEventListener(type ,function(e){
var target;
if(selector){
target = e.target;
//matches() 方法用于检测字符串是否匹配给定的正则表达式。
if(target.matches(selector)){
fn.call(target,e);
}
}else{
fn.call(e);
}
})
}

// 使用代理
bindEvent(div1,’click’,’a’,function(e){
console.log(this)
})
// 不使用代理
bindEvent(div2,’click’,function(e){
//call 改变了 this 指向为 e
console.log(this.toElement.innerHTML)
})

可以跨域的三个标签
<img src=”” alt=””> // 用于打点统计
<link rel=”stylesheet” href=””> // 使用 CDN
<script></script> // 使用 JSONP
3 月 10 日面试
闭包的优缺点
JS 中,在函数外部无法访问函数内部的值,使用闭包就可以做到。

优点:

使用闭包能够让局部变量模拟全局变量一样, 但是,只能被特定函数调用。
全局变量可能会造成命名冲突,使用闭包不用担心这个问题,因为它是私有化,加强了封装性。

缺点
由于闭包是驻留在内存中的, 会增大内存使用量,使用不当很容易造成内存泄露, 降低程序的性能。

按需引入,模块引入的实现?
http 请求缓存头详解

Expires: http1.0 推出的,指服务器返回的文件有效期,但其实这是有缺陷的,如果把本地的时间改为 2118 年,那 Expires 的时间怎么都会过期。
Last-Modified: http1.0 推出的,指服务器文件的最后修改时间,浏览器会带上 If-Modified-Since 向服务器发送请求,与服务器文件修改时间 Last-Modified 做对比,如果时间不同,则获取数据返回 200,否则返回 304 后调用浏览器本地硬盘的缓存。

Cache-Control: http1.1 推出,指文件缓存的有效期。

.max-age: 单位是 s,设置文件最大缓存时间,用得最多。
public: 缓存可以被多用户共享,例如 360 浏览器可以登录不同账号,电脑系统可以切换不同账号
private: 仅单用户私有,不被多用户共享
no-cache: 不会被缓存。
no-store: 不允许被存储

ETag: http1.1 推出,该版本号是由服务端随机生成的,浏览器会带上 If-None-Match 向服务器发送请求,与服务器文件修改版本 ETag 做对比,如果版本号不同,则获取数据返回 200,否则返回 304 后调用浏览器本地硬盘的缓存,这种方式比 Last-Modified 靠谱。

正文完
 0