JS进阶篇(前端面试题整合)(一)

js的垃圾回收机制是什么原理

垃圾回收机制有两种方法

  • 第一种是标记清除法:当变量进入执行环境时,就标记这个变量为”进入环境”,当变量离开环境的时候,则将其标记为”离开环境”,垃圾收集器在运行的时候会给储存在内存中的所有变量都加上标记,然后它会去掉环境中的标量以及被环境中的变量引用的标记,而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了,最后,垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间
  • 第二种是引用计数法:当声明了一个变量并将一个引用类型赋值给改变量是,则这个值得引用次数就是1,相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值得引用次数就减1,当这个引用次数变成0时,则说明没有办法在访问这个值了,因而就可以将其所占的内存空间给收起来,这样垃圾收集器再下次运行时,它就会释放那些引用次数为0的值所占的内存

哪些操作会造成内存泄露,怎样避免内存泄露

会造成内存泄漏的操作:

  • 意外的全局变量引起的内存泄露
  • 闭包引起的内存泄露
  • 控制台日志
  • 没有清理的DOM元素引用
  • 被遗忘的定时器或者回调 

避免内存泄露的操作:

  • 减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收
  • 注意程序逻辑,避免“死循环”之类的
  • 避免创建过多的对象  原则:不用了的东西要及时归还(置为null)

AMD\CMD区别

  • AMD即Asynchronous Module Definition,翻译过来就是异步模块化定义(RequireJS)
  • CMD即 common moudle definition,翻译过来即通用模块定义(SeaJS)
  • RequireJS在主文件里是将所有的文件同时加载,然而SeaJS强调一个文件一个模块。
  • AMD推崇依赖前置,CMD推崇依赖就近。
  • AMD加载完模块后,就立马执行该模块;CMD加载完某个模块后没有立即执行而是等到遇到require语句的时再执行
  • 所以,他们两者的不同导致各自的优点是AMD用户体验好,因为模块提前执行了;CMD性能好,因为只有用户需要的时候才执行。

面向对象的三个特性

封装 : 屏蔽内部细节   用户直接调用被封装的功能
继承 : 子类拥有父类的所有属性或方法
多态 (js中不存在多态概念)

原型,原型链的理解

所有的构造函数都有一个prototype属性,这个属性也叫 原型对象   构造函数.prototype    所有的构造函数new出来的对象也都有一个原型对象   实现 :对象.__proto__
原型链就是实例对象和原型之间的链接
原型对象的执行流程:

  • 首先去实例上查找,如果找到了就返回
  • 如果没做查找到,就去改构造函数的原型上查找,如果找到了就返回,如果没找到,就去Object.prototype的原型上查找,找到了就返回,否则返回undefined

继承的方式

1.通过改变父类的执行环境来实现
2.通过call
3.通过apply
4.原型继承
5.混合继承
6.es6构造函数

作用域链的理解

作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的
安全隐患:污染全局环境,或者造成内存泄露的问题,变量的提升

闭包的理解

什么是闭包
一个函数内部返回一个匿名函数,这个函数就称为闭包
闭包中this指向 window
特点
(1)函数嵌套函数
(2)函数可以引用外层的参数和变量
(3)参数和变量不会被垃圾回收机制回收
闭包的缺点:常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
为何要使用闭包:为了设计私有方法和变量,避免全局变量污染 希望一个变量长期驻扎在内存中

下面这个ul,如何点击每一列的时候alert其index(闭包解决方式)

<ul id="test">
    <li>这是第一条</li>
    <li>这是第二条</li>
    <li>这是第三条</li>
</ul>

方法一:

//将i属性绑定到标签对象中的index
var liItems=document.getElementById('test').getElementsByTagName('li');
for(var i=0;i<liItems.length;i++)
{
    liItems[i].index=i;
    liItems[i].onclick=function(){
        alert(this.index);
    };
}

方法二:

//将i属性通过参数传递至function作用域中,立即执行函数在下一次循环之前先将i绑定至作用域
var liItems=document.getElementById('test').getElementsByTagName('li');
for(var i=0;i<liItems.length;i++)
{
     liItems[i].onclick=(function(a){
        return function() {
            alert(a);
        }
    })(i);

}

方法三(es6的let产生暂时性死区,与声明的变量所在的块级作用域(for循环内)都不会造成闭包,var只受function的作用域影响,let受所有带‘{}‘大括号的作用域影响):

//通过let
var liItems=document.getElementById('test').getElementsByTagName('li');
        for (let i = 0; i < liItems.length; i++) {
            liItems[i].onclick = function () {
                alert(i);
            }
        }

高内聚低耦合的理解

高内聚 :模块内部高内聚 。 一个系统有多个模块组成,在划分模块式,要把功能关系紧密的放到一个模块中,这就叫做高内聚低耦合:功能关系远的放到其它模块中。模块之间的联系越少越好,接口越简单越好,这叫做低耦合,也称为细线通信

TCP和UDP的最完整的区别

1.基于连接与无连接
2.TCP要求系统资源较多,UDP较少;
3.UDP程序结构较简单
4.流模式(TCP)与数据报模式(UDP);
5.TCP保证数据正确性,UDP可能丢包
6.TCP保证数据顺序,UDP不保证 

JS处理异步的方式

  • 利用回调函数(es5常用方法)
  • 用async和await来处理异步(es7-8中新增)
  • promise(es6新增)
  • 发布/订阅 我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)(和事件原理一样)
  • 事件 触发一个事件也可以作为异步的处理  当触发某个事件再来执行某件事(js底层解决异步常用方法)

深拷贝浅拷贝的理解

浅拷贝:只是复制当前的对象,该对象内部的引用(Object,Array等堆内存数据)不能复制
深拷贝:对对象内部的引用均复制,是创建一个新的实例
简言之:是否复制了子对象,修改了克隆后的对象属性值,影响到原对象-浅拷贝  不影响-深拷贝

常见的HTTP请求返回状态码

  • 200成功
  • 304请求浏览器缓存的内容
  • 400语义有误,当前请求无法被服务器理解
  • 401当前请求需要用户验证
  • 404未找到
  • 403服务器已经理解请求,但是拒绝执行它
  • 500服务器错误
  • 503服务器端暂时无法处理请求

1开头的(信息类):表示接收到请求并且继续处理,用于指定客户端应相应的某些动作
2开头的(响应成功):表示动作被成功接收,理解和接受。
3开头的(重定向):为了完成指定的动作,必须接受进一步处理,用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息
4开头的(客户端错误类):请求包含错误语法或不能正确执行
5开头的(服务器端错误):服务器遇到错误,无法完成请求

html页面怎么解析的?它加载顺序是什么?

  1. 用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件
  2. 浏览器开始载入html代码,如果发现<head>标签内有一个<link>标签引用外部CSS文件
  3. 浏览器又发出CSS文件的请求,服务器返回这个CSS文件
  4. 浏览器继续载入html中<body>部分的代码,并且CSS文件已经加载完成了,开始渲染页面
  5. 如果浏览器在代码中发现一个<img>标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是异步渲染后面的代码
  6. 服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码
  7. 如果浏览器发现了一个包含一行Javascript代码的<script>标签,会立即运行它
  8. 如果Javascript脚本执行了浏览器隐藏掉代码中的某个<style>(style.display=”none”),浏览器就得重新渲染这部分代码
  9. 如果这时用户点了一下界面中的“换肤”按钮,Javascript让浏览器换了一下<link>标签的CSS路径,那么浏览器就会向服务器请求了新的CSS文件,重新渲染页面

总结:最好将无论内部或是外部JS文件放到所有html内容之后,这样会令用户感觉页面加载速度变快了,否则如果将所有外部文件(包括css和JS)引用都放到<head>中,意味着必须等到全部的JS代码都被下载解析和执行完毕后,才能开始呈现页面的内容(当浏览器遇到<body>),这样会导致呈现页面时出现明显的延迟,二延迟期间的浏览器窗口将是一片空白。

谈谈web攻击技术

  • XSS攻击
  • CSRF攻击
  • 网络劫持攻击
  • 控制台注入代码
  • 钓鱼

同步和异步的区别

同步:阻塞的,A需要等待B完成任务后再开始任务
异步:非阻塞的,A,B同时开始任务

打个简单的比方:Ajax请求数据渲染页面操作,需要使用同步方式渲染,因为js执行的时间很短,几乎可以忽略不计,而Ajax请求需要等待时间,所以,需要等待Ajax请求完毕,收到响应信息后再渲染页面

解释a = b||c,fn&&fn(),a=(b,c),a?b:c的作用或含义

a = b||c:和if(!b) {a=c}效果一致,如果b存在,把b的值赋给a,否则把c的值赋给a(短路求值,提升效率)
fn&&fn():和上面的效果一样,但条件相反,如果fn不存在,则不执行,否则将执行(短路求值,提升效率)
a=(b,c):这里是逗号运算符的用法之一,先执行运算符左侧的操作数,然后再执行右侧的操作数,最后返回右侧操作数的值,即a=c
a?b:c:条件表达式(三元表达式),若a为真,返回b,若a为假,则返回c

对if语句的优化

  • 把次数多的条件和执行结果放到最前面
  • 减少第一次无用的判断,可以用嵌套判断
  • 判断语句禁止出现三次嵌套

对switch的理解

  • switch的括号里面放的是一个变量
  • case相对应的值是关于这个变量的一个值
  • switch里面的这个变量和case里面这个变量不会进行隐式类型的一个转换,而是进行了恒等比较。所以一定要注意这个变量和这个case里 面的值是不是一个类型
  • 关于switch里面的case会有一个穿透效果,这个效果有的时候会给我们带来好处(详情请看最后一个案例),有的时候会给我们带来坏处, 如果不需要这种穿透效果的时候加break
  • swicth里面如果这个变量没有匹配到case里面这个值,那么就需要返回一个信息。所以在case的末尾一定要加上一个default;这样既给用 户的体验比较完美,另一方面对代码的今后维护也有很大的帮助
  • 比较的值是固定值

if和swicth的应用场景

if :
1、具体的值进行判断
2、区间的判断
3、对运算的结果是boolean类型表达式进行判断 true false
switch:
1、对具体的值进行判断
2、值的个数是固定的
对于几个固定的值判断,建议使用switch 语句。因为switch 语句会将具体的答案都加载进内存,效率相对高一点 基于代码的可读性:如果条件较少时,if-else容易阅读,而条件较多时switch更容易阅读

do-while循环的使用及while的区别

do-while() 无论条件是否成立至少执行一次,和while规则一样,唯一不同的是do{}while会先执行一次(先执行后判断

while和for的区别

for循环是知道了循环次数,while是不知道循环次数
for限定了循环次数
while是条件循环

break和continue return的区别

continue只是中止本次循环,接着开始下一次循环 ,只能出现在循环中
break用于完全结束一个循环,跳出循环体 不在执行break下面的代码,只能出现在选择或者循环中
return作用是返回函数的值,不在执行return下面的代码,只能出现函数中

函数的作用

减少代码的编写(代码重复利用)
隐藏处理细节,便于今后的修改和维护
控制执行时机

对参数的理解

参数分为:形参和实参
有了参数以后可以使函数变的更加灵活
形参和实参要一一对应
如果对应的形参没有传值,那么值是undefined

对arguments的了解

  • 函数内部自带的一个对象
  • 存储的是所有的实参
  • 可以使用[ ]及下标访问arguments中的内容 arguments[0] 访问第一个实参
  • 可以使用 arguments.length 确定传入实参的个数
  • 最常用的用途: 判断传入参数的个数(根据参数个数做不同的事情)

请说一下js的编译和执行

js的预编译:
把var 和 function 定义的变量提升到script的最上方
赋值语句不会被提升,哪怕等号后面是一个function

js执行:代码从上往下执行

递归与循环的区别

递归算法:
优点:代码简洁、清晰,并且容易验证正确性。
缺点:
它的运行需要较多次数的函数调用,如果调用层数比较深,每次都要创建新的变量,需要增加额外的堆栈处理,会对执行效率有一定影 响,占用过多的内存资源。
递归算法解题的运行效率较低。在递归调用的过程中系统为每一层的返回点、局部变量等开辟了栈来储存。递归次数过多容易造成栈溢出 等
注意:递归就是在过程或函数里调用自身;
使用递归策略时要注意的几个条件:
必须有一个明确的递归结束条件,称为递归出口。
递归需要有边界条件、递归前进段和递归返回段。
当边界条件不满足时,递归前进。当边界条件满足时,递归返回。

循环算法:
优点:速度快,结构简单。
缺点:并不能解决所有的问题。有的问题适合使用递归而不是循环。如果使用循环并不困难的话,最好使用循环

 

posted @ 2022-12-25 18:51  阿宇的编程之旅  阅读(36)  评论(0编辑  收藏  举报