JavaScript执行上下文(context)主要指代码执行环境的抽象概念。执行上下文分为三种:
全局执行上下文
函数执行上下文
eval执行上下文
每一段js代码执行,都会先创建一个上下文环境。
前沿知识:js代码执行前会创建上下文环境,这个上下文环境包含了变量、作用域链和this.
简单理解就是从当前环境向父级一层一层查找变量的过程称之为作用域链。
var name = '前端未来'; function hello(){ console.log(name); }解释:当我们在函数hello里面打印name的时候,会先在hello作用域中查找,如果没有找到就去hello的父级作用域中查找。
每个函数都拥有一个prototype属性,每个函数实例对象都拥有一个__proto__属性,而这个属性指向了函数的prototype,当我们访问实例对象的属性或者方法时,会先从自身构造函数中查找,如果没有就通过__proto__去原型中查找,这个查找的过程我们称之为原型链。(跟作用域链有点像)
// 定义动物 - 父类 function Animal(){ this.age = 10; this.say = function(){ return 'hello tom'; } } // 定义猫 - 子类 function Cat(){ this.name = 'tom'; } // 通过原型继承动物类 Cat.prototype = new Animal() // 实例化一个cat对象 var cat = new Cat(); // 打印返回true cat.__proto__ === Cat.prototype // 打印age,会先查找cat,再查找Animal console.log(cat.age)通过截图,我们可以看到cat实例对象__proto__指向了Animal,当cat没有age的时候,会通过__proto__到原型上查找,如果原型上依然没有,会继续向Object上查找。
简单理解就是函数中嵌套函数。我们都知道局部变量我们是无法访问的,但是通过闭包可以做到。
// 正常访问 var lan = 'zh'; function hello(){ var name = '前端未来'; } console.log(name)//很明显'undefined' // 换成闭包 function hello(){ var name = '前端未来'; function demo(){ console.log(name)//打印:前端未来 } }以上几个基础面试题都是密切关联的,理解其中一个,基本就能理解剩下几个
原型继承
构造继承
实例继承
call/apply继承(组合继承)
ES6 使用class extends继承
JS数据类型分别基本数据类型和引用数据类型,基本数据类型保存的是值,引用类型保存的是引用地址(this指针)。浅拷贝共用一个引用地址,深拷贝会创建新的内存地址。
浅拷贝方法
直接对象复制
Object.assign
深拷贝
JSON.stringify转为字符串再JSON.parse
深度递归遍历
面试官希望的答案:Object.prototype.toString.call([]) 返回 "[object Array]"
扩展答案
[].slice (能力判断 )
[] instanceof Array(类型判断)
[].proto === Array.prototype
Array.isArray([]) 存在兼容问题
数组也是引用类型,通过typeof依然返回object
这个非常多,说起来也很快,我主要考察你会多少,另外也为了引出下一个问题,slice和splice区别
push 末尾添加
pop 末尾删除
shift 首部删除
unshift 首部添加
concat 数组合并
join 数组元素 通过连接符 连接
reverse 数组反转
sort 数组排序
map/forEach/filter/indexOf/includes/slice/splice
slice表示截取,slice(start,end)不改变原数组,返回新数组。
splice表示删除,splice(start,length,item),会改变原数组,从某个位置开始删除多个元素,并可以插入新的元素。
他们都可以改变函数的作用域。
call/apply可以直接执行该函数,而bind不会立刻执行
call/apply作用类似,都可以改变指针和执行函数,区别在于传参不同,call需要单个传参,apply通过数组传参
他们都属于观察者模式,只不过有不同的实现方法。发布订阅相比于观察者多了一个调度中心,发布者通过调度中心向订阅者发布消息。观察者模式中目标和观察者相互依赖,观察者订阅目标主题,当目标发生变化后,会通知对应观察者。
大家可参考我另一篇公众号文章。
解析HTML,生成DOM树
解析CSS,生成CSSOM树
将DOM树和CSSOM树关联,生成渲染树(Render Tree)
布局render树(Layout/reflow),负责各元素尺寸、位置的计算
绘制render树(paint),绘制页面像素信息
将像素发送给GPU,展示在页面上。(Display)
这其中大家需要了解几个概念:调用栈、同步/异步任务、宏任务/微任务
JavaScript本身是单线程,也就是同一时刻只能干一件事,JS任务包含了同步任务和异步任务,遇到执行函数会将其放入调用栈(先进后出)中,遇到setTimeout/setInterval等异步任务时,会把它放入到消息队列中,等主线程的任务执行完成以后,再回过头执行消息队列中的异步任务,如果异步任务中仍然有异步任务,会继续放入消息队列,以此类推,便形成了一个事件循环。
异步任务:
setTimeout
setInterval
异步任务又分为宏任务和微任务,promise就属于微任务.
大小方面
GET传输一般2K-8K,IE限制2K,,POST没有大小限制
安全方面
GET通过url明文传输,POST通过body传输,本身都不安全,因为HTTP就是明文传输。
浏览器记录
GET请求浏览器会记录,POST不会
浏览器后退
GET无害,POST会再次提交
浏览器收藏
GET可以收藏,POST不可以
浏览器缓存
GET可以缓存,POST不会
编码方式
GET通过url编码,POST支持多种编码
TCP数据包
GET产生一个数据包,POST产生2个数据包
使用方式(习惯上讲)
GET主要拉取数据,POST主要提交保存数据
防抖和节流都是希望在同一时间内,不要重复触发请求。一般场景用在搜索和网页滚动事件中。
区别:
防抖主要是在规定时间内只触发一次,如果再次调用,时间从新计算。
节流主要是在固定时间内只触发一次。比如每间隔1秒触发一次。
ES6 Set去重
利用Object key去重
两层循环逐一对比,生成新数组
indexOf去重
sort排序,再单层循环前后对比
数组sort排序
冒泡排序(两层循环,两两互换)
//冒泡排序 function bubbleSort ( data ) { var temp = 0; for ( var i = data.length ; i > 0 ; i -- ){ for( var j = 0 ; j < i - 1 ; j++){ if( data[j] > data[j + 1] ){ temp = data[j]; data[j] = data [j+1]; data[j+1] = temp; } } } return data; }选择排序
//选择排序 function selectionSort( data ) { for( var i = 0; i< data.length ; i++){ var min = data[i]; var temp; var index = i; for( var j = i + 1; j< data.length; j++){ if( data[j] < min ){ min = data[j]; index = j; } } temp = data[i]; data[i] = min; data[index]= temp; } return data; }插入排序
//插入排序 function insertionSort( data ) { var len = data.length; for (var i = 1; i < len; i++) { var key = data[i]; var j = i - 1; while ( j >= 0 && data[j] > key) { data[j + 1] = data[j]; j--; } data[j + 1] = key; } return data; }单例模式
工厂模式
观察者模式
适配器模式
在Vue中通过观察者模式触发视图更新。Vue2.x通过Object.defineProperty劫持data数据,当数据变化后触发setter,setter内部通过订阅器来notify消息,notify会调用watcher更新视图。
当一套前端对接不同后端服务时,会出现数据解构不一致情况,这个时候可以使用适配器模式来兼容不同后端,使他以统一的数据解构对接前端。
for...of 是ES2015版本引入的语法,它可以遍历数组、类数组、以及Map/set/字符串等
数组迭代
for (const number of [1,2,5]) { }类数组迭代
for (const number of arguments) { }字符串迭代
const message = 'hello'; for (const character of message) { }map迭代
const map = new Map(); map.set("name", '前端未来'); map.set("author", '河畔一角'); for(const item of map){ } // 或者 for(const [key,val] of map){ }Set迭代
const names = new Set(['Tom', 'Jack', 'Lily']); for (let name of names) { }for...of非常灵活,还有其它例子,我这儿不再列出。
XSS攻击
CSRF攻击
Sql注入
html脚本注入
攻击原理和方案这儿不再阐述
JSONP跨域(本质是JS调用)
CORS(后台设置)
Nginx反向代理(运维配置)
跨域是浏览器做出的安全限制,必须同协议、同域名、同端口否则会被浏览器block
不常用跨域方案,我不进行列出
优化策略:减少请求次数、减小资源大小、提高响应和加载速度、优化资源加载时机、优化加载方式
合并、压缩、混淆html/css/js文件(webpack实现,减小资源大小)
Nginx开启Gzip,进一步压缩资源(减小资源大小)
图片资源使用CDN加速(提高加载速度)
符合条件的图标做base64处理(减小资源大小)
样式表放首部,JS放尾部(JS单线程,会阻塞页面;资源加载方式)
设置缓存(强缓存和协商缓存,提高加载速度)
link或者src添加rel属性,设置prefetch或preload可预加载资源。(加载时机)
如果使用了UI组件库,采用按需加载(减小资源大小)
SPA项目,通过import或者require做路由按需(减小资源大小)
服务端渲染SSR,加快首屏渲染,利于SEO
页面使用骨架屏,提高首页加载速度(提高加载速度)
使用 JPEG 2000, JPEG XR, and WebP 的图片格式来代替现有的jpeg和png,当页面图片较多时,这点作用非常明显
使用图片懒加载-lazyload
