1.判断 js 类型的方式
-
typeof 可以判断出'string','number','boolean','undefined','symbol',但判断 typeof(null) 时值为 'object'; 判断数组和对象时值均为 'object'
-
instanceof 原理是 构造函数的 prototype 属性是否出现在对象的原型链中的任何位置
-
Object.prototype.toString.call() 常用于判断浏览器内置对象,对于所有基本的数据类型都能进行判断,即使是 null 和 undefined
-
Array.isArray() 用于判断是否为数组
2.字符串操作函数
-
toString() – 可以将任何类型的数据都转换为字符串。
-
concat() – 将两个或多个字符的文本组合起来,返回一个新的字符串。
-
indexOf() – 返回字符串中一个子串第一处出现的索引。如果没有匹配项,返回 -1 。
-
charAt() – 返回指定位置的字符。
-
lastIndexOf() – 返回字符串中一个子串最后一处出现的索引,如果没有匹配项,返回 -1 。
-
match() – 检查一个字符串是否匹配一个正则表达式。
-
substr() 函数 -- 起始位置开始截取,结束位置为第二个参数截取的字符串最大长度。
-
substring() – 返回字符串的一个子串。传入参数是起始位置和结束位置。
-
slice() – 提取字符串的一部分,并返回一个新字符串。
-
replace() – 用来查找匹配一个正则表达式的字符串,然后使用新字符串代替匹配的字符串。
-
search() – 执行一个正则表达式匹配查找。如果查找成功,返回字符串中匹配的索引值。否则返回 -1 。
-
split() – 通过将字符串划分成子串,将一个字符串做成一个字符串数组。
-
length – 返回字符串的长度,所谓字符串的长度是指其包含的字符的个数。
-
toLowerCase() – 将整个字符串转成小写字母。
-
toUpperCase() – 将整个字符串转成大写字母。
3.Array 相关的属性和方法
-
includes() – 找到匹配项返回布尔值,不能识别 undefined 和 NaN 。
-
concat() 连接两个或更多的数组,并返回结果。
-
join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。
-
pop() 删除并返回数组的最后一个元素。
-
shift() 删除并返回数组的第一个元素
-
push() 向数组的末尾添加一个或更多元素,并返回新的长度。
-
unshift() 向数组的开头添加一个或更多元素,并返回新的长度。
-
reverse() 颠倒数组中元素的顺序。
-
slice() 从某个已有的数组返回选定的元素
-
sort() 对数组的元素进行排序
-
splice() 删除元素,并向数组添加新元素。
-
toSource() 返回该对象的源代码。
-
toString() 把数组转换为字符串,并返回结果。
-
toLocaleString() 把数组转换为本地数组,并返回结果。
-
valueOf() 返回数组对象的原始值
4.闭包的概念?优缺点?
闭包的概念:闭包就是能读取其他函数内部变量的函数。
优点:
-
避免全局变量的污染
-
希望一个变量长期存储在内存中(缓存变量)
缺点:
-
内存泄露(消耗)
-
常驻内存,增加内存使用量
5.浅拷贝和深拷贝
浅拷贝
-
Object.assign()
-
Array.prototype.slice()
-
扩展运算符 ...
深拷贝
-
JSON.parse(JSON.stringify())
-
递归函数
function cloneObject(obj) {
var newObj = {} //如果不是引用类型,直接返回
if (typeof obj !== 'object' && !obj) {
return obj
}
//如果是引用类型,遍历属性
else {
for (var attr in obj) {
//如果某个属性还是引用类型,递归调用
newObj[attr] = cloneObject(obj[attr])
}
}
return newObj
}
6.数组去重的方法
ES6 的 Set
let arr = [1, 1, 2, 3, 4, 5, 5, 6]
let arr2 = [...new Set(arr)]
reduce()
let arr = [1, 1, 2, 3, 4, 5, 5, 6]
let arr2 = arr.reduce(function (ar, cur) {
if (!arr.includes(cur)) {
arr.push(cur)
}
return arr
}, [])
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
})
7.DOM 事件有哪些阶段?谈谈对事件代理的理解
分为三大阶段:捕获阶段--目标阶段--冒泡阶段
事件代理简单说就是:事件不直接绑定到某元素上,而是绑定到该元素的父元素上,进行触发事件操作时(例如'click'),再通过条件判断,执行事件触发后的语句(例如'alert(e.target.innerHTML)')
好处:(1)使代码更简洁;(2)节省内存开销
8.什么是跨域?跨域请求资源的解决方法有哪些?
由于浏览器同源策略,凡是发送请求 url 的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。存在跨域的情况:
-
网络协议不同,如 http 协议访问 https 协议。
-
端口不同,如 80 端口访问 8080 端口。
-
域名不同,如 qianduanblog.com 访问 baidu.com。
-
子域名不同,如 abc.qianduanblog.com 访问 def.qianduanblog.com。
-
域名和域名对应 ip,如 www.a.com 访问 20.205.28.90.
解决办法
-
porxy 代理
-
CORS
-
jsonp 缺点:这种方式无法发送 post 请求(这里),另外要确定 jsonp 的请求是否失败并不容易,大多数框架的实现都是结合超时时间来判定。
9.谈谈垃圾回收机制方式及内存管理
定义和用法:垃圾回收机制(GC:Garbage Collection),执行环境负责管理代码执行过程中使用的内存。
原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。
垃圾回收策略:标记清除(较为常用)和引用计数。
-
标记清除:
定义和用法:当变量进入环境时,将变量标记"进入环境",当变量离开环境时,标记为:"离开环境"。某一个时刻,垃圾回收器会过滤掉环境中的变量,以及被环境变量引用的变量,剩下的就是被视为准备回收的变量。
到目前为止,IE、Firefox、Opera、Chrome、Safari 的 js 实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。
-
引用计数:
定义和用法:引用计数是跟踪记录每个值被引用的次数。
基本原理:就是变量的引用次数,被引用一次则加 1,当这个引用计数为 0 时,被视为准备回收的对象。
10.内存泄漏
-
内存泄漏是指计算机可用的内存越来越少,主要是因为程序不能释放那些不再使用的内存。
-
无意的全局变量、循环、定时器、回调会触发内存泄漏
11.undefined 和 null 有什么区别
-
null 表示"没有对象",即该处不应该有值
-
undefined 表示"缺少值",就是此处应该有一个值,但是还没有定义
12.介绍一下 JavaScript 原型,原型链,它们有何特点
JavaScript 中所有的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链,所有原型链的终点都是 Object 函数的 prototype 属性。Objec.prototype 指向的原型对象同样拥有原型,不过它的原型是 null,而 null 则没有原型
13.avaScript 如何实现继承
- 原型链继承
function Animal() {}
Animal.prototype.name = 'cat'
Animal.prototype.age = 1
Animal.prototype.say = function () {
console.log('hello')
}
var cat = new Animal()
cat.name // cat
cat.age // 1
cat.say() // hello
- 构造继承
function Animal() {
this.species = '动物'
}
function Cat(name, age) {
Animal.call(this)
this.name = name
this.age = age
}
var cat = new Cat('豆豆', 2)
cat.name // 豆豆
cat.age // 2
cat.species // 动物
- 组合继承
function Animal() {
this.species = '动物'
}
function Cat(name) {
Animal.call(this)
this.name = name
}
Cat.prototype = new Animal() // 重写原型
Cat.prototype.constructor = Cat
- extends 继承 ES6 新增继承方式,Class 可以通过 extends 关键字实现继承
class Animal {}
class Cat extends Animal {
constructor() {
super()
}
}
14.new 操作符具体干了什么
-
创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型
-
属性和方法被加入到 this 引用的对象中
-
新创建的对象由 this 所引用,并且最后隐式的返回 this
15.冒泡排序
第一种
var arr = [89, 12, 8, 14, 23, 45, 9, 15, 33]
function bubble_sort(arr) {
var len = arr.length
if (arr.length == 1) {
return
}
for (var i = 0; i < len; i++) {
for (var j = 0; j < len - 1; j++) {
//如果前一个值比后一个值大,那么交换位置
if (arr[j] > arr[j + 1]) {
var temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
return arr
}
//测试一下代码
bubble_sort(arr)
//log [8, 9, 12, 14, 15, 23, 33, 45, 89]
第二种
function quickSort(arr) {
//如果数组<=1,则直接返回
if (arr.length <= 1) {
return arr
}
var pivotIndex = Math.floor(arr.length / 2)
//找基准,并把基准从原数组删除
var pivot = arr.splice(pivotIndex, 1)[0]
//定义左右数组
var left = []
var right = []
//比基准小的放在left,比基准大的放在right
for (var i = 0; i < arr.length; i++) {
if (arr[i] <= pivot) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
//递归
return quickSort(left).concat([pivot], quickSort(right))
}
第三种
function selectionSort(array) {
var length = array.length,
i,
j,
minIndex,
minValue,
temp
for (i = 0; i < length - 1; i++) {
minIndex = i
minValue = array[minIndex]
for (j = i + 1; j < length; j++) {
if (array[j] < minValue) {
minIndex = j
minValue = array[minIndex]
}
}
// 交换位置
temp = array[i]
array[i] = minValue
array[minIndex] = temp
}
return array
}
16.javascript 是弱类型语言吗
JavaScript 一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。
强类型定义语言:
- 强制数据类型定义的语言。也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。举个例子:如果你定义了一个整型变量 a,那么程序根本不可能将 a 当作字符串类型处理。强类型定义语言是类型安全的语言。
弱类型定义语言:
-
数据类型可以被忽略的语言。它与强类型定义语言相反, 一个变量可以赋不同数据类型的值。
-
强类型定义语言在速度上可能略逊色于弱类型定义语言,但是强类型定义语言带来的严谨性能够有效的避免许多错误。
17.箭头函数与普通函数的区别
-
箭头函数没有自己的 this,继承的是外层代码块的 this。
-
不可以当做构造函数,也就是说不可以使用 new 命令,否则会报错的。
-
不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
-
不可以使用 yield 命令,因此箭头函数不能用作 Generator(生成器) 函数。
-
因为没有 this,所以不能使用 call、bind、apply 来改变 this 的指向。
18.call,apply 和 bind 的区别
它们在功能上是没有区别的百,都是改变 this 的指向,它们的区别主要是在于方法的实现形式和参数传递上的不同。call 和 apply 方法都是在调用之后立即执行的。而 bind 调用之后是返回原函数,需要再调用一次才度行
- 写法的区别:
函数.call(对象问,arg1,arg2....)
函数.apply(对象,[arg1,arg2,...])
var ss=函数.bind(对象,arg1,arg2,....)
19.简单说一下你对 http 和 https 的理解
-
http 是一种超文本传输协议,传输的数据都是未加密的,也就是显示在明面上的,是现在互联网上应用最为广泛的一种网络协议,相对来说不太安全,但是所需成本很小。http 一般的端口号为 80.
-
https 则是具有安全性的 ssl 加密传输协议。简单来说,https 是一种安全版的 http,传输的数据是通过 SSL 加密的,比起 http 来说很安全,https 协议的主要作用就是:建立一个信息安全通道,来确保数组的传输,确保网站的真实性。但是相对来说,成本所需较高,尤其是所需 ca 证书等级越高,费用越高(ca 证书功能越强大,所需费用越高)。https 一般的端口号为 443。
-
http 和 https 各有各自的优缺点,http 花费少,安全性不高;https 花费稍高,但是更安全;
20.同步和异步
-
同步,是所有的操作都做完,才返回给用户结果。即写完数据库之后,在相应用户,用户体验不好。
-
异步,不用等所有操作等做完,就相应用户请求。即先相应用户请求,然后慢慢去写数据库,用户体验较好。
21.讲讲js数据类型?
-
基本类型: Sting Number Boolean null undefined Symbol bigint
-
引用类型: Object Date function RegExp Array
22.说说你对函数式编程的理解?
函数式编程是一种编程范式,可以理解为是利用函数把运算过程封装起来,通过组合各种函数来计算结果。函数式编程与命令式编程最大的不同其实在于,函数式编程关心数据的映射,命令式编程关心解决问题的步骤。
23.函数柯里化的理解?
是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
24.为什么需要柯里化?
-
把多个参数转化为单参数函数的级联,达到了动态确定参数的目的。
-
当某些参数不确定时,可以先保留一个存根。剩余的参数确定以后,就可以通过存根调用剩下的参数。
25.TCP 的三次握手和四次挥手
- 三次握手
1.客户端主动发起 SYN
2.服务端收到并返回 SYN 以及 ACK 客户端的 SYN
3.客户端收到服务端的 SYN 和 ACK 后,发送 ACK 的 ACK 给服务端,服务端收到后连接建立
Client -> SYN -> Server
Server -> SYN/ACK -> Client
Client -> ACK -> Server
- 四次挥手
1.客户端发送 FIN 给服务端
2.服务端收到后发送 ACK 给客户端
3.服务端发送 FIN 给客户端
4.客户端收到后,发送 ACK 的 ACK 给服务端,服务端关闭,客户端等待 2MSL 后关闭
Client -> FIN -> Server
Server -> ACK -> Client
Server -> FIN -> Client
Client -> ACK -> Server -> CLOSED
Client -> 2MSL 的时间 -> CLOSED
26.HTTP 的缓存策略知道吗?
- 强缓存
不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中可以看到该请求返回200的状态码,并且Size显示from disk cache或from memory cache。强缓存可以通过设置两种 HTTP Header 实现:Expires 和 Cache-Control。
- 协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况: 1. 协商缓存生效,返回304和Not Modified 2. 协商缓存失效,返回200和请求结果
27.从URL输入到页面展现到底发生什么?
DNS 解析:将域名解析成 IP 地址
TCP 连接:TCP 三次握手
发送 HTTP 请求
服务器处理请求并返回 HTTP 报文
浏览器解析渲染页面
断开连接:TCP 四次挥手
28.浏览器的渲染机制
解析HTML,生成DOM树,解析CSS,生成CSSOM树
将DOM树和CSSOM树结合,生成渲染树(Render Tree)
Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
Display:将像素发送给GPU,展示在页面上。
29.九种跨域方式实现原理
JSONP原理 利用 script 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。
CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。 服务端设置 Access-Control-Allow-Origin 就可以开启 CORS)
postMessage 允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
Websocket Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。
Node中间件代理(两次跨域) 实现原理:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。
nginx反向代理 实现原理类似于Node中间件代理,需要你搭建一个中转nginx服务器,用于转发请求。
window.name + iframe window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
location.hash + iframe 实现原理: a.html欲与c.html跨域相互通信,通过中间页b.html来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。