共计 9457 个字符,预计需要花费 24 分钟才能阅读完成。
1. JS 有哪些数据类型?
依据 JavaScript 中的变量类型传递形式,分为 根本数据类型 和援用数据类型 两大类七种。
根本数据类型包含 Undefined、Null、Boolean、Number、String、Symbol (ES6 新增) 六种 。
援用数据类型只有 Object 一种,次要包含对象、数组和函数。
判断数据类型采纳 typeof
操作符,有两种语法:
typeof 123;// 语法一
const FG = 123;
typeof FG;// 语法二
typeof(null) // 返回 object;
null == undefined // 返回 true,因为 undefined 派生自 null;
null === undefined // 返回 false。
2. 根本数据类型和援用数据类型有什么区别?
(1)两者作为函数的参数进行传递时:
根本数据类型 ** 传入的是数据的正本 **,原数据的更改不会影响传入后的数据。援用数据类型 ** 传入的是数据的援用地址 **,原数据的更改会影响传入后的数据。
(2)两者在内存中的存储地位:
根本数据类型 ** 存储在栈中 **。援用数据类型在 ** 栈中存储了指针 **,该指针指向的 ** 数据实体存储在堆中 **。
3. 判断数据类型的办法有哪些?
(1)利用 typeof
能够判断数据的类型;
(2)A instanceof
B 能够用来判断 A 是否为 B 的实例,但它不能检测 null 和 undefined;
(3)B.constructor == A
能够判断 A 是否为 B 的原型,但 constructor 检测 Object 与 instanceof 不一样,还能够解决根本数据类型的检测。
不过函数的 constructor 是不稳固的,这个次要体现在把类的原型进行重写,在重写的过程中很有可能呈现把之前的 constructor 给笼罩了,这样检测进去的后果就是不精确的。
(4)Object.prototype.toString.call()
Object.prototype.toString.call()
是最精确最罕用的形式。
4. 与深拷贝有何区别?如何实现?
浅拷贝只复制指向某个对象的指针,而不复制对象自身 。浅拷贝的实现形式有:
(1)Object.assign()
:需注意的是 指标对象只有一层的时候,是深拷贝;
(2)扩大运算符;
深拷贝就是在拷贝数据的时候,将数据的所有援用构造都拷贝一份 。深拷贝的实现形式有:
(1)手写遍历递归赋值;
(2)联合应用JSON.parse()
和JSON.stringify()
办法。
5. let、const 的区别是什么?
var
、let
、const
都是用于申明变量或函数的关键字。其区别在于:
var | let | const | |
---|---|---|---|
作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
作用域内申明晋升 | 有 | 无(暂时性死区) | 无 |
是否可反复申明 | 是 | 否 | 否 |
是否可反复赋值 | 是 | 是 | 否(常量) |
初始化时是否必须赋值 | 否 | 否 | 是 |
6. 什么是执行上下文和执行栈?
变量或函数的执行上下文,决定了它们的行为以及能够拜访哪些数据 。每个上下文都有一个关联的 变量对象 ,而这个上下文中定义的所有变量和函数都存在于这个对象上(如 DOM 中全局上下文关联的便是window
对象)。
每个函数调用都有本人的上下文。当代码执行流进入函数时,函数的上下文被推到一个执行栈中。在函数执行完之后,执行栈会弹出该函数上下文,在其上的所有变量和函数都会被销毁,并将控制权返还给之前的执行上下文。JS 的执行流就是通过这个执行栈进行管制的。
7. 什么是作用域和作用域链?
作用域能够了解为一个独立的地盘,能够了解为 标识符所能失效的范畴 。作用域最 大的用途就是隔离变量 ,不同作用域下同名变量不会有抵触。ES6 中有 全局作用域、函数作用域和块级作用域 三层概念。
当一个变量在以后块级作用域中未被定义时,会 向父级作用域 (创立该函数的那个父级作用域) 寻找 。如果父级仍未找到,就会再一层一层向上寻找,直到找到全局作用域为止。这种一层一层的关系,就是 作用域链。
8. 作用域和执行上下文的区别是什么?
(1)函数的执行上下文只在函数被调用时生成,而其作用域在创立时曾经生成;
(2)函数的作用域会蕴含若干个执行上下文(有可能是零个,当函数未被调用时)。
9. this 指向的各种状况都有什么?
this 的指向只有在调用时能力被确定 ,因为this
是执行上下文的一部分。
(1)全局作用域中的函数:其外部 this
指向window
:
var a = 1;
function fn(){console.log(this.a)
}
fn() // 输入 1
(2)对象外部的函数:其外部 this
指向对象自身:
var a = 1;
var obj = {
a:2,
fn:function(){console.log(this.a)
}
}
obj.fn() // 输入 2
(3)构造函数:其外部 this
指向生成的实例:
function createP(name,age){
this.name = name //this.name 指向 P
this.age = age //this.age 指向 P
}
var p = new createP("老李",46)
(4)由 apply
、call
、bind
革新的函数:其 this
指向第一个参数:
function add(c,d){return this.a + this.b + c + d}
var o = {a:1,b:2)
add.call(o,5,7) // 输入 15
(5)箭头函数:箭头函数没有本人的 this
,看其外层的是否有函数,如果有,外层函数的this
就是外部箭头函数的 this
,如果没有,则this
是window
。
10. 如何扭转 this 指针的指向?
能够应用 apply
、call
、bind
办法扭转 this
指向 (并不会扭转函数的作用域)。比拟如下:
(1)三者第一个参数都是this
要指向的对象,也就是想指定的上下文,上下文就是指调用函数的那个对象 (没有就指向全局 window);
(2)apply
的第二个参数是数组或者类数组对象,bind
和 call
接管多个参数并用逗号隔开;
(3)apply
和 call
只对原函数做改变,bind
会返回新的函数(要失效还得再调用一次)。
console.log(Math.max.apply(null, [1,2,3])) //--apply 输入 3
console.log(Math.max.apply(null, new Array(1,2,3))) //--apply 输入 3
console.log(Math.max.call(null, 1,2,3)) //--call 输入 3
console.log(Math.max.bind(null, 1,2,3)()) //--bind 输入 3
11. 什么是闭包?
闭包就是 援用了其余函数作用域中变量的函数,这种模式通常在函数嵌套构造中实现。外面的函数能够拜访里面函数的变量,里面的变量的是这个外部函数的一部分。闭包有如下作用:参考:前端进阶面试题具体解答
(1)增强封装,模仿实现公有变量;
(2)实现常驻内存的变量。
闭包不能滥用,否则会导致内存泄露,影响网页的性能。闭包应用完了后,要立刻开释资源,将援用变量指向 null。
12. 什么是原型、原型链?
原型:JS 申明 构造函数 (用来实例化对象的函数) 时,会在 内存中创立一个对应的对象 ,这个对象就是原函数的 原型 。 构造函数默认有一个 prototype 属性 ,prototype
的值 指向函数的原型 。同时 原型中也有一个 constructor
属性 ,constructor
的值 指向原函数 。
通过构造函数实例化进去的对象,并不具备 prototype
属性,其 默认有一个 __proto__
属性 ,__proto__
的值 指向构造函数的原型对象。在原型对象上增加或批改的属性,在所有实例化出的对象上都可共享。
当在实例化的对象中拜访一个属性时,首先会在该对象外部寻找,如找不到,则会向其 __proto__
指向的原型中寻找,如仍找不到,则持续向原型中 __proto__
指向的下级原型中寻找,直至找到或 Object.prototype
为止,这种链状过程即为 原型链。
13. 何为防抖和节流?如何实现?
防抖和节流都是避免短时间内高频触发事件的计划。
防抖的原理是:如果肯定工夫内屡次执行了某事件,则只执行其中的最初一次。
节流的原理是:要执行的事件每隔一段时间会被冷却,无奈执行。
利用场景有:搜寻框实时搜寻,滚动扭转相干的事件。
//@fn: 要执行的函数
//@delay: 设定的时限
// 防抖函数
function debunce(fn,delay){
let flag = null;
return function(){if(flag) clearTimeout(flag)
// 利用 apply 扭转函数指向,使得封装后的函数能够接管 event 自身
flag = setTimeout(()=>fn.apply(this,arguments),delay)
}
}
// 节流函数
function throttle(fn,delay){
let flag = true;
return function(){if(!flag) return false;
flag = false;
setTimeout(()=>{fn.apply(this,arguments)
flag=true
},delay)
}
}
14. 如何了解同步和异步?
同步:依照代码书写程序一一执行解决指令的一种模式,上一段代码执行完能力执行下一段代码。
异步:能够了解为一种并行处理的形式,不用期待一个程序执行完,能够执行其它的工作。
JS 之所以须要异步的起因在于 JS 是单线程运行的。罕用的异步场景有:定时器、ajax 申请、事件绑定。
15. JS 是如何实现异步的?
JS 引擎是单线程的,但又能实现异步的起因在于事件循环和工作队列体系。
事件循环:
JS 会创立一个相似于 while (true)
的循环,每执行一次循环体的过程称之为 Tick
。每次 Tick
的过程就是查看是否有待处理事件,如果有则取出相干事件及回调函数放入执行栈中由主线程执行。待处理的事件会存储在一个工作队列中,也就是每次 Tick
会查看工作队列中是否有须要执行的工作。
工作队列:
异步操作会将相干回调增加到工作队列中。而不同的异步操作增加到工作队列的机会也不同,如 onclick
, setTimeout
, ajax
解决的形式都不同,这些异步操作是由浏览器内核的 webcore
来执行的,浏览器内核蕴含 3 种 webAPI,别离是 DOM Binding
、network
、timer
模块。
onclick
由 DOM Binding
模块来解决,当事件触发的时候,回调函数会立刻增加到工作队列中。setTimeout
由 timer
模块来进行延时解决,当工夫达到的时候,才会将回调函数增加到工作队列中。ajax
由 network
模块来解决,在网络申请实现返回之后,才将回调增加到工作队列中。
主线程:
JS 只有一个线程,称之为主线程。而事件循环是主线程中执行栈里的代码执行结束之后,才开始执行的。所以,主线程中要执行的代码工夫过长,会阻塞事件循环的执行,也就会阻塞异步操作的执行。
只有当主线程中执行栈为空的时候(即同步代码执行完后),才会进行事件循环来察看要执行的事件回调,当事件循环检测到工作队列中有事件就取出相干回调放入执行栈中由主线程执行。
16. 什么是 AJAX?如何实现?
ajax
是一种可能实现部分网页刷新的技术,能够使网页异步刷新。ajax
的实现次要包含四个步骤:
(1)创立外围对象 `XMLhttpRequest`;(2)利用 `open` 办法关上与服务器的连贯;(3)利用 `send` 办法发送申请;("POST" 申请时,还需额定设置申请头)(4)监听服务器响应,接管返回值。
//1- 创立外围对象
// 该对象有兼容问题,低版本浏览器应应用 ActiveXObject
const xthhp = new XMLHttpRequest();
//2- 连贯服务器
//open(method,url,async)
xhttp.open("POST","http://localhost:3000",true)
// 设置申请头
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");//3- 发送申请
//send 办法发送申请参数,如为 GET 办法,则在 open 中 url 后拼接
xhttp.send({_id:123})
//4- 接管服务器响应
//onreadystatechange 事件,会在 xhttp 的状态发生变化时主动调用
xhttp.onreadystatechange =function(){
// 状态码共 5 种:0- 未 open 1- 已 open 2- 已 send 3- 读取响应 4- 响应读取完结
if(xhttp.readyState == 4 && xhttp.status == 200){alert("ajax 申请已实现")
}
}
17. 实现异步的形式有哪些?
(1)回调函数模式 :将须要异步执行的函数作为回调函数执行,其毛病在于解决简单逻辑异步逻辑时,会造成回调天堂(回调嵌套层数太多,代码构造凌乱);
(2) 事件监听模式 :采纳事件驱动的思维,当某一事件产生时触发执行异步函数,其毛病在于整个代码全副得变为事件驱动模式,难以分辨主流程;
(3) 公布订阅模式 :当异步工作执行实现时公布音讯给信号核心,其余工作通过在信号核心中订阅音讯来确定本人是否开始执行;
(4)Promise(ES6):Promise
对象共有三种状态 pending
(初始化状态)、fulfilled
(胜利状态)、rejected
(失败状态)。
(5)async/await(ES7):基于Promise
实现的异步函数;
(6)利用 生成器 实现。
18. 怎么了解 Promise 对象?
Promise
对象有如下两个 特点 :
(1) 对象的状态不受外界影响 。Promise
对象共有三种状态 pending
、fulfilled
、rejected
。状态值只会被异步后果决定,其余任何操作无奈扭转。
(2) 状态一旦成型,就不会再变,且任何时候都可失去这个后果 。状态值会由pending
变为 fulfilled
或rejected
,这时即为resolved
。
Promise 的毛病有如下三个毛病:
(1)Promise
一旦执行便无奈被勾销;
(2)不可设置回调函数,其外部产生的谬误无奈捕捉;
(3)当处于 pending
状态时,无奈得悉其具体倒退到了哪个阶段。
Pomise
中罕用的办法有:
(1)Promise.prototype.then()
:Promise
实例的状态产生扭转时,会调用 then
外部的回调函数。then
办法承受两个参数(第一个为 resolved
状态时时执行的回调,第一个为 rejected
状态时时执行的回调)
(2)Promise.prototype.catch()
:.then(null, rejection)
或 .then(undefined, rejection)
的别名,用于指定产生谬误时的回调函数。
19. 怎么了解宏工作,微工作???
宏工作有:script(整体代码)
、setTimeout
、setInterval
、I/O
、页面渲染;
微工作有:Promise.then
、Object.observe
、MutationObserver
。
执行程序大抵如下:
主线程工作——> 宏工作——> 微工作——> 微工作里的宏工作——>…….——> 直到工作全副实现
20. 什么是跨域?怎么解决跨域问题?
跨域问题理论是由同源策略衍生出的一个问题,当传输协定、域名、端口任一部分不统一时,便会产生跨域问题,从而拒绝请求,但 <img src=XXX> <link href=XXX><script src=XXX>
; 人造容许跨域加载资源。解决方案有:
(1)JSONP
原理:利用<script>
; 标签没有跨域限度的破绽,使得网页能够失去从其余起源动静产生的 JSON 数据(前提是服务器反对)。
长处:实现简略,兼容性好。
毛病:仅反对 get 办法,容易受到 XSS 攻打。
(2)CORS
原理:服务器端设 置 Access-Control-Allow-Origin
以开启 CORS。该属性示意哪些域名能够拜访资源,如设置通配符则示意所有网站均可拜访。
实现实例(express):
//app.js 中设置
var app = express();
//CORS 跨域 -------------------------------------------------------------------------------------
// CORS:设置容许跨域中间件
var allowCrossDomain = function (req, res, next) {// 设置容许跨域拜访的 URL(* 示意容许任意 URL 拜访)
res.header("Access-Control-Allow-Origin", "*");
// 设置容许跨域拜访的申请头
res.header("Access-Control-Allow-Headers", "X-Requested-With,Origin,Content-Type,Accept,Authorization");
// 设置容许跨域拜访的申请类型
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
// 设置容许服务器接管 cookie
res.header('Access-Control-Allow-Credentials', 'true');
next();};
app.use(allowCrossDomain);
//------------------------------------------------------------------------------------
(3)Node 中间件代理
原理:同源策略仅是浏览器须要遵循的策略,故搭建中间件服务器转发申请与响应,达到跨域目标。
/* server1.js 代理服务器(http://localhost:3000)*/
const http = require('http')
// 第一步:承受客户端申请
const server = http.createServer((request, response) => {
// 代理服务器,间接和浏览器间接交互,须要设置 CORS 的首部字段
response.writeHead(200,{
'Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Methods':'*',
'Access-Control-Allow-Headers':'Content-Type'
})
// 第二步:将申请转发给服务器
const proxyRequest = http.request({
host:'127.0.0.1',
port:4000,
url:'/',
method:request.method,
headers:request.headers
},
serverResponse =>{
// 第三步:收到服务器的响应
var body = ''serverResponse.on('data', chunk =>{body += chunk})
serverResponse.on('end',()=> {console.log('The data is'+ body)
// 第四步:将响应后果转发给浏览器
response.end(body)
})
})
.end()})
server.listen(3000,()=>{console.log('中间件服务器地址:http://localhost:3000')})
// server2.js(http://localhost:4000)
const http = require("http");
const data = {title: "fontend", password: "123456"};
const server = http.createServer((request, response) => {if (request.url === "/") {response.end(JSON.stringify(data));
}
});
server.listen(4000, () => {console.log("The server is running at http://localhost:4000");
});
(4)nginx 反向代理
原理:相似 Node 中间件服务器,通过 nginx 代理服务器实现。
实现办法:下载安装 nginx,批改配置。
21. 实现继承的办法有哪些???
实现继承的办法有:
(1)class+extends 继承(ES6)
// 类模板
class Animal {constructor(name){this.name = name}
}
// 继承类
class Cat extends Animal{// 重点。extends 办法,外部用 constructor+super
constructor(name) {super(name);
//super 作为函数调用时,代表父类的构造函数
}//constructor 可省略
eat(){console.log("eating")
}
}
(2)原型继承
// 类模板
function Animal(name) {this.name = name;}
// 增加原型办法
Animal.prototype.eat = function(){console.log("eating")
}
function Cat(furColor){this.color = color ;};
// 继承类
Cat.prototype = new Animal()// 重点:子实例的原型等于父类的实例
(3)借用构造函数继承
function Animal(name){this.name = name}
function Cat(){Animal.call(this,"CatName")// 重点,调用父类的 call 办法
}
(4)寄生组合式继承(重点)
22. DOM 事件模型和事件流?
DOM 事件模型包含 事件捕捉 (自上而下触发) 与事件冒泡 (自下而上触发,ie 用的就是冒泡) 机制。基于事件冒泡机制能够实现 事件代理。
事件捕捉 事件冒泡
DOM 事件流包含三个阶段事件捕捉阶段、处于指标阶段、事件冒泡阶段。
23. EventLoop 事件循环是什么?
js 是一门单线程的须要,它的异步操作都是通过事件循环来实现的。整个事件循环大体由执行栈、音讯队列和微工作队列三个局部组成。
同步代码会间接在执行栈中调用执行。
定时器中的回调会在执行栈被清空且定时达成时推入执行栈中执行。promise
、async
异步函数的回调会被推入到微工作队列中,当执行栈被清空且异步操作实现时立刻执行。
24. require
/import
之间的区别?
(1)require
是 CommonJS 语法,import
是 ES6 语法;
(2)require
只在后端服务器反对,import
在高版本浏览器及 Node 中都能够反对;
(3)require
引入的是原始导出值的复制,import
则是导出值的援用;
(4)require
时运行时动静加载,import
是动态编译;
(5)require
调用时默认不是严格模式,import
则默认调用严格模式.
前端面试指南系列传送门:
前端面试指南之 Vue 面试题总结
前端面试指南之 HTML 面试题总结
前端面试指南之 CSS 面试题总结