一、JS异步加载的方式
1)同步加载
<script src="http://yourdomain.com/script.js"></script>
又称阻塞模式,会阻止浏览器的后续处理,停止后续解析,只有当当前加载完成,才能进行下一步操作。有可能输出dociment内容,及修改dom,重定向等行为,故建议放置在<body>结尾处,这样尽可能减少页面阻塞。
2)异步加载
又称非阻塞模式,在下载执行js的同时,还会继续进行后续的页面处理。
【1】Script DOM Element
(function(){
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = 'xxx.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga,s);
})();
会阻止onload事件的触发,而代码可能在onload时还执行额外的渲染工作,所以会阻塞部分页面的初始化处理。
【2】onload时异步加载
(function(){
if(window.attachEvent) window.attachEvent("load",asyncLoad);
else window.addEventListener("load",asyncLoad);
var asyncLoad = function(){
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = 'xxx.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga,s);
}
})();
在onload方法中执行,解决上面的问题。
注意:DOMContentLoaded与load的区别。前者document已经解析完成,页面中的dom元素可用,但是页面中的图片,视频,音频等资源未加载完成。后者是所有的资源加载完毕。
【3】其他方法 XHR Injection(XHR 注入) XHR Eval Script In Iframe Script defer document.write(script tag)
XHR Injection:XHR返回数据处理方式 ===>
var script = document.createElement("script"); script.text = xmlHttp.responseText; document.getElementByTagName("head")[0].appendChild(script);
XHR Eval:XHR返回数据处理方式 ===>
eval(xmlHttp.responseText);
Script In Iframe:在父窗口插入一个iframe元素,然后在iframe执行加载js的操作。 ===>
var insertJS = function(){alert(2)}; var iframe = document.createElement("iframe"); document.body.appendChild(iframe); var doc = iframe.contentWindow.document; doc.open(); doc.write("<script>var insertJS = function(){};</script><body onload='insertJS()'></body>"); doc.close();
async和defer属性:
defer声明脚本中将不会有document.write和dom修改。浏览器会并行下载其他的defer属性的script。注意:所有defer脚本必须保证按顺序执行的。 <scripte type="text/javascript" defer></script>
async属性:脚本将在加载后尽快执行,作用同defer,但是不能保证脚本按顺序执行,他们将在onload事件之前执行。 <scripte type="text/javascript" async></script>
3)延迟加载
页面初始化时只加载需要立即执行的JS,然后其他JS的加载延迟到第一次需要用到的时候再加载。
二、CSS动画与js动画的区别
1)JS动画:
优点:控制能力强,效果丰富,JS兼容性好
缺点:主线程运行,可能会造成阻塞。复杂度高于CSS动画
2)CSS动画:
优点:浏览器可以优化。
缺点:控制能力弱,代码冗长。
三、XSS与CSRF两种跨站攻击
XSS:跨站脚本,是注入攻击的一种。不对服务器端造成任何伤害,而是通过一些正常的站内交互途径,提交含有JavaScript的内容文本。主要是前端层面的,用户在输入层面插入攻击脚本,改变页面的显示,或则窃取网站cookie,预防方法:不相信用户的所有操作,对用户输入进行一个转义,不允许js对cookie的读写。
CSRF:跨站请求伪造。XSS是CSRF的诸多途径的一种。以你的名义,发送恶意请求,通过cookie加参数等形式过滤。没有办法彻底杜绝攻击,只能提高攻击门槛。
四、事件委托,目的,功能,写法
把一个或者一组元素的事件委托到它的父层或者更外层元素上
优点,减少内存消耗,动态绑定事件
target是触发事件的最具体的元素,currenttarget是绑定事件的元素(在函数中一般等于this)
五、线程,进程
线程是最小的执行单元,进程是最小的资源管理单元
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少一个线程
六、负载均衡
当系统面临大量用户访问,负载过高的时候,通常会使用增加服务器数量来进行横向扩展,使用集群和负载均衡提高整个系统的处理能力
七、什么是CDN缓存
Content Delivery Network,即内容分发网络。各地部署多套静态存储服务,本质上是空间换时间。自动选择最近的结点内容,不存在再请求原始服务器,适合存储更新很少的静态内容,文件更新慢。
八、闭包的写法,闭包的作用,闭包的缺点
闭包:是指有权访问另一个函数作用域中的变量函数。常见形式:就是在一个函数内部创建另一个函数。闭包是由函数以及声明该函数的词法环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。
使用闭包的目的==>隐藏变量,间接访问一个变量,在定义函数的词法作用域外,调用函数。
1.闭包可以封装这个函数内的私有变量。
function create_counter(initial){ var x = initial || 0; return { inc:function(){ x += 1; return x; } }}
var c1 = create_counter(); c1.inc(); //1 c1.inc();//2 c1.inc();//3
var c2 = create_counter(10); c2.inc(); //11 c2.inc();//12 c2.inc();//13
闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。
2.闭包还可以把多参数的函数变成单参数的函数。
function make_pow(n){ return function(x){ return Math.pow(x,n) } }
var pow2 = make_pow(2); var pow3 = make_pow(3); console.log(pow2(5)); console.log(pow3(7));
本质上,make_pow是一个函数工厂====>他创建了将指定的值和它的参数相加求和的函数。
循环错误:
闭包只能取得包含函数中任何变量的最后一个值。
function count(){ var arr = []; for(var i = 1; i <= 3; i++){ arr.push(function(){ return i*i; });} return arr; }
var results = count(); var f1 = results[0]; var f2 = results[1]; var f3 = results[2];
f1();//16 f2();//16 f3();//16
原因就在于返回的函数引用了变量i,但它并非立即执行。
修改方法一:
function count(){ var arr = []; for(var i = 1; i <= 3; i++){ arr.push(function(n){ return function () { return n * n; } })(i);} return arr; }
var results = count(); var f1 = results[0]; var f2 = results[1]; var f3 = results[2];
f1();//1 f2();//4 f3();//9
再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变。
修改方法二:
直接将for循环内的声明变量i的var改为let。因为var在for循环内部声明的值,在外部可以获取到。而let会使for循环变成一个封闭的作用域。
闭包的缺点:
闭包在处理速度和内存消耗方面对脚本性能具有负面影响。
九、跨域问题,谁限制的跨域,怎么解决
由于浏览器的同源策略,在出现域名、端口、协议有一种不一致时,就会出现跨域,属于浏览器的一种安全限制。
解决跨域的几种方式:
1.jsonp跨域:动态创建script,再请求一个带参网址实现跨域通信。缺点就是只能实现get一种请求
2.document.domain + iframe跨域:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。但是仅限于主域名相同,子域名不同的跨域应用场景
3.跨域资源共享CORS:只需要服务端设置Access-Control-Allow-Origin即可,前端无需设置。若要带cookie请求,前后端都需要设置CORS需要浏览器和后端同时支持。
4.nginx反向代理接口跨域:同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨域问题
postMessage:这种方式通常用于获取嵌入页面中的第三方页面数据。一个页面发送信息,另一个页面判断来源并接受信息
Websocket协议跨域:
十、JavaScript中4种常见的内存泄漏陷阱
内存泄漏会导致一系列问题,比如:运行缓慢,崩溃,高延迟。
1.意外的全局变量
function foo(arg){ bar = "this is a hidden global variable"; }
它实际上是:function foo(arg){ window.bar = "this is a hidden global variable"; }
2.被遗漏的定时器和回调函数
setInterval 及 对象观察者
3.DOM之外的引用
将DOM结点存储到数据结构中,程序中将会保留同一个结点的两个引用。如果要移除,则需要将所有的引用清除。
对DOM树子结点的引用,如果保留子结点,javascript代码对整个结点都会保存在内存中。
4.闭包
十一、babel把ES6转成ES5或者ES3之类的原理是什么
它就是个编译器,输入语音是ES6+,编译目标语言是ES5
babel官方工作原理:
解析:将代码字符串解析成抽象语法树
变换:对抽象语法树进行变换操作
再建:根据变换后的抽象语法树再生成代码字符串
十二、promise 放在try catch里面有什么结果
try-catch主要用于捕获异常,这里的是指同步函数的异常,如果try里面的异步方法出现了异常,此时catch是无法捕获到异常的。
十三、网站性能优化
第一次访问您网站,尚未在浏览器cache中缓存您网站内容的用户:
1.减少一个页面访问所产生的http连接次数
2.使用gzip压缩网页内容
3.将CSS放在页面顶端,JS文件放在页面低端 可以使内容尽早显示出来。
4.使JS文件内容最小化
5.尽量减少外部脚本的使用,减少DNS查询时间
网站的经常性访问用户,优化思路是最大限度利用用户浏览器的cache来减少服务器开销:
1.在header中添加过期时间
a.第一次访问url时,用户从服务器段获取页面内容,并把相关的文件放在高速缓存中。
b.用户重复访问url时,浏览器首先看高速缓存中是否有本站同名且未过期的文件,如果有则读取文件,不再访问服务器。
c.如果不存在或者已过期,则从服务器获取文件
2.css和js文件放在独立外部文件中引用 可以单独缓存起来
3.去掉重复的脚本
4.避免重定向的发生
十四、get与post通讯的区别
Get请求能缓存,Post不能
Post相对于Get安全一点点,因为Get请求能包含在URL里。Post可以通过request body来传输比Get更多的数据,Get没有这个技术。
URL有长度限制,会影响Get请求,但是这个长度限制是浏览器规定的,不是RFC规定的
Post支持更多的编码类型且不对数据类型限制
十五、webpack的原理和机制
webpack的作用:
1.webpack作为一个模块打包工具,本身就可以解决模块化代码打包问题,将零散的JavaScript代码打包到一个JS文件中。
2.对于有环境兼容问题的代码,Webpack可以在打包过程中通过Loader机制对其实现编译转换,然后再进行打包。
3.对于不同类型的前端模块,Webpack支持在JavaScript中以模块化的方式载入任意类型的资源文件。
4.Webpack还具备代码分拆的问题,它能够将应用中所有的模块按照我们的需要分块打包。
配置webpack的打包过程:
默认情况:按照src/index.js 作为打包入口,最终打包结果放到 dist/main.js
自定义情况:根目录下添加webpack.config.js
const path = require("path");
module.exports = {
entry:'./src/main.js', //打包的入口
output:{ //打包出口
filename:'bundle.js', //打包出口的文件名
path:path.join(__dirname,'output') //打包出口所在文件路径
}
}
Webpack 工作模式:
production模式下,启动内置优化插件,自动优化打包结果,打包速度偏慢;
development模式下,自动优化打包速度,添加一些调试过程中的辅助插件;
none模式下,运行最原始的打包,不做任何额外处理。
webpack常用的加载器使用实例:
webpack想要实现的是整个前端项目的模块化,项目中的各种资源都应该属于需要被管理的模块。
CSS样式loader:webpack是用Loader(加载器)来处理每个模块的,而内部默认的Loader只能处理JS模块,如果需要加载其他类型的模块就是需要配置不同的Loader
module:{rules:[{
test:/\.css$/, //根据打包过程中所遇到文件路径匹配是否使用这个 loader
use:["style-loader","css-loader"], //指定具体的 loader
}]}
css-loader 只会把CSS模块加载到JS代码中,而并不会使用这个模块。style-loader的作用总结一句话就是,将css-loader所加载到的所有样式模块,通过创建style标签的方式添加到页面上。
图片文件的处理loader:
url-loader:将文件发送到输出文件夹,并且返回URL;但是如果文件小于限制,可以返回data URL
file-loader:将文件发送到输出文件夹,并且返回URL
webpack插件机制
插件机制的目的是为了增强Webpack在项目自动化构建方面的能力。
Loader就是负责完成项目中各种各样资源模块的加载,从而实现整体项目的模块化,而Plugin则是用来解决项目中除了资源模块打包以外的其他自动化工作。
常用场景:
1.实现自动在打包之前清除dist目录
2.自动生成应用所需要的HTML文件
3.根据不同环境为代码注入类似API地址这种可能变化的部分
4.拷贝不需要参与打包的资源文件的输出目录
5.压缩Webpack打包完成后输出的文件
6.自动发布打包结果到服务器实现自动部署
插件机制的基本原理:
Webpack的插件机制就是我们在软件开发中最常见的钩子机制。在Webpack整个工作过程会有很多环节,为了便于插件的扩展,Webpack几乎在每一个环节都埋下了一个钩子。在开发插件的时候,通过往这些不同节点上挂载不同的任务,就可以轻松扩展Webpack的能力。
Webpack工作过程简介:
Webpack可以通过打包,将它们最终聚集在一起。Webpack在整个打包的过程中:1.通过Loader处理特殊类型资源的过程,例如加载样式、图片;2.通过Plugin实现各种自动化的构建任务,例如自动压缩、自动发布。
Webpack工作原理剖析:
1.Webpack CLI启动打包流程;
2.载入Webpack核心模块,创建Compiler对象
3.使用Compiler对象开始编译整个项目;
4.从入口文件开始,解析模块依赖,形成依赖关系图
5.递归依赖树,将每个模块交给对应的Loader处理
6.合并Loader处理完成的结果,将打包结果输出到dist目录
十六、ES6模块与CommonJS模块的差异
CommonJS模块输出的是一个值得拷贝,ES6模块输出的是一个值得引用
CommonJS模块是运行时加载,ES6模块是编译时输出接口
ES6输入的模块变量,只是一个符号链接,所以这个变量是只读的,对它进行重新赋值就会报错
十七、Node事件循环,js事件循环差异
1.JavaScript中事件循环
宏任务:setTimeOut、setInterval、setImmediate、I/O、各种callback、UI渲染、messageChannel等
优先级:主代码块 ==> setImmediate ==> postMessage ==> setTimeOut/setInterval
微任务:process.nextTick、Promise、MutationObserver、async(实质上也是promise)
优先级:process.nextTick ==> Promise ==> MutationObserver
2.Nodejs中事件循环
Node.js采用V8作为js的解析引擎,而I/O处理方面使用了自己设计的libuv。事件循环包含下面的6个阶段。
1.timers阶段:这个阶段执行timer(setTimeout、setInterval)的回调
2.I/O callbacks阶段:执行一些系统调用错误,比如网络通信的错误回调
3.idle、prepare阶段:仅node内部使用
4.poll阶段:获取新的I/O事件,适当的条件下node将阻塞在这里
5.check阶段:执行setImmediate()的回调
6.close callbacks阶段:执行socket的close事件回调
3.Node.js与浏览器的Event Loop差异
1.浏览器下,microtack的任务队列是每个macrotack执行完成之后执行

2.Node.js中,microtack会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会执行microtack队列的任务

4.总结
1.Node.js的事件循环分为6个阶段
2.浏览器和Node环境下,microtack任务队列的执行时机不同:Node.js中microtack在事件循环的各个阶段之间执行;浏览器端,microtack在事件循环的macrotack执行完之后执行
3.递归的调用process.nextTick()会导致I/O starving,官方推荐使用setImmediate()
十八、浅拷贝和深拷贝的问题
深拷贝和浅拷贝是只针对Object和Array这样的复杂类型的
也就是说a和b指向了同一块内存,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝
浅拷贝,“Object.assign()”方法用于将所以可枚举的属性的值从一个或多个源对象复制到目标对象,它将返回目标对象
深拷贝,JSON.parse()和JSON.stringify()给了我们一个基本的解决方法,但是函数不能被正确处理