简述:
- 个人的基本信息
我叫梅敏君,江西九江人,13年毕业于江西科技学院计算机科学与技术专业,有1年后端开发经验,5年前端开发经验,目前为止累计完成了上百个业务需求
-
目前任职及主要工作职责 目前在平安银行任职,主要负责的项目是平安银行官方App的前端开发工作 我的工作内容主要有四块
- 参与平安银行App前端开发基础架构设计工作
- 参与前端公共组件、模块定制的研发工作
- 参与前端工作流工具、推广优秀的前端技术
- 每个月的迭代业务需求的调研与常规开发工作 从只有10几个业务模块,做到了几百个模块,从以前的每日100万的用户活跃度做到了每日平均1000万的用户活跃度 所用的前端技术也经历了从传统的mvc+svn+jquery时代升级到了现在流行的mvvm+git+前端工程化阶段
-
以往任职及简要工作职责
在平安之前的工作经历也是一直围绕的是,从事移动端的跨平台项目的项目开发,主要的工作职责是帮助其他企业实现定制的类OA系统、电商系统等等
- 个人软硬件能力
硬件能力: 在大学前2年玩,第三年顿悟了一下,学了下.net ,考了一个数据库工程师
案例:出来做过近一年.net 开发,.net 的思想很超前,所要求的技术栈很全面,前端+后端+dba都要会一些,有些类似现在的全栈
软件能力: 之后随着HTML5的趋势,开始专注前端开发,在软通的时候基本节奏很快,1个前端+1个后端+1个安卓+1个ios+1个设计,一周完成一个项目需求
时间长了,发现重复造轮子到了一个技术瓶颈,除了解决问题能力有些提高,技术能力好像没什么长进,前端技术每年都在变化,有点学不过来,我开始做自己的产品和写博客分享总结经验,慢慢形成前端的结构性思维体系
- 对职位的理解并与自身匹配
其实在传统的业务驱动型的公司工作还是有局限性的,老板不太关注技术,只是要求业务的快速实现,我们现在银行git 没法上,只能在家写点东西
我比较喜欢bat这种互联网技术浓厚的公司,会有更多时间去研究技术,开源一些产品,在推动互联网的技术上更进一步,又能让最新最好的技术驱动业务需求增长
https://segmentfault.com/a/1190000014321635?utm_source=index-hottest
何为闭包:有权访问另一个函数作用域中的变量的函数
闭包特性:可实现函数外访问函数内变量,外层变量可以不被垃圾回收机制回收
https://juejin.im/entry/57f765a7da2f60004f841d98
参考:1、https://juejin.im/entry/57f765a7da2f60004f841d98
简述:原型简单的讲,就类似于Java 中的父类(因为Javscript 中没有类的概念,用prototype模拟的),对象的原型可以理解为对象的父对象(prototype)
原型链是什么?
简述: 可以类比类的继承链条,子类继承父类,即每个对象的原型往上追溯,一直到Object为止,组成的一个链条,叫做原型链
作用和特性:当前对象中访问变量, 如果没有则根据其__proto__
指针指向其prototype 对象(也就是父类)获取,如果获取不到,在往上层查找,一直到Object的原型, 找不到返回undefined
注意点:
- 原型链的最后一层是Object,但是Object 的
__proto__
指针指向的是null - 任意对象(包括function)都有原型的,并指向其原型(类似于父类概念)的
__proto__
,对象是没有prototype属性只有指向其原型的私有变量__proto__
, 方法有prototype、__proto__
属性 - 由于
__proto__
是运行环境私有属性,所以最好不要用,推荐用es6的新方法,Object.getPrototypeOf()方法来获取对象的原型
作用域:作用域就是变量和函数的可访问范围,在js中分为全局作用域(又称:全局执行上下文)、局部作用域(局部执行上下文)
- 全局作用域(
全局执行上下文
):最外层定义的函数和最外层的变量以及未用var 声明的都拥有全局作用域 - 局部作用域(
局部执行上下文
):局部作用域一般只在固定的代码片段内可访问到,一般就是函数中定义的函数和方法
全局和局部作用域的关系: 在函数体内,局部变量的优先级高于同名的全局变量。(后面的调用栈会讲到为什么
)
注意点:
如果在函数内声明的一个局部变量或者函数参数中带有的变量和全局变量重名,那么全局变量就被局部变量所覆盖(后面的调用栈会讲到为什么
)
作用域链:当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。 作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。 作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即 arguments 对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象
我们都知道Js就是单线程的,但是Js语言设计者意识到这个问题(因为IO设备传入数据很慢,比如ajax获取网络数据),不得不等到结果数据返回,再往下执行。所以他认为可以现将将主线程的耗时的IO设备任务挂起,先运行排在后面的任务。等IO设备返回了结果,再去执行挂起的任务,这就是所谓的非阻塞特性
于是,所有任务分为两种:
同步任务
异步任务
- 执行栈是什么(
同步任务
)?
在浏览器中的 JavaScript 解释器是单线程的。这意味着,在浏览器中一次只会发生一件事,其他行为或者事件在所谓的执行栈 中排队等待,只有前一个任务执行完毕,才能执行后一个任务。
js在浏览器的过程:
加载js,进入全局执行上下文
(全局环境
),如果在全局中调用了一个函数,程序创建一个新的局部执行上下文
(局部环境
),并且将这个环境压入执行栈
中,如果当前函数中又调用的其他函数,也会发生同样事情,浏览器永远执行当前栈最顶部的局部执行上下文
,一旦在当前执行上下文
中执行完毕,它会被弹出执行栈
- 什么是任务队列,也称事件队列(
异步任务
)?
js引擎遇到一个异步事件(例如ajax
请求、setTimeout)后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务,当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列
被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码,如此反复,这样就形成了一个无限的循环。这就是这个过程被称为“事件循环(Event Loop)”的原因
参考如图:
图中stack
表示我们所说的执行栈,web apis
则是代表一些异步事件,而callback queue即事件队列
异步任务分为两类:
- 微任务(micro task)
- 宏任务(macro task)
微任务:当执行栈
中的代码执行完毕,会在执行宏任务队列之前先看看微任务队列中有没有任务,如果有会先将微任务队列中的任务清空才会去执行宏任务队列
宏任务:等待执行栈和微任务队列都执行完毕才会执行,并且在执行完每一个宏任务之后,会去看看微任务队列有没有新添加的任务,如果有,会先将微任务队列中的任务清空,才会继续执行下一个宏任务
以下事件属于宏任务:
setInterval() setTimeout()
以下事件属于微任务
new Promise() new MutaionObserver()
我们只需记住当当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。
参考:
- https://segmentfault.com/a/1190000017224799
- http://www.ruanyifeng.com/blog/2014/10/event-loop.html
- https://zhuanlan.zhihu.com/p/33058983
- https://juejin.im/post/5b63b4cb6fb9a04fb4017f5a
事件流怎么产生的?在浏览器出现的第四代时候,浏览器开发者团队遇到了一个问题:页面的哪一部分会拥有没某个特定的事件?在一张白纸上有一组同心圆,你把手放在同心圆上,那么你的手指指向的不是一个圆,而是所有的圆,两家浏览器厂商的开发者走了截然相反的事件流概念
网景(事件捕获流
): 与上面相反,事件从document传播到具体节点
IE(事件冒泡流
):事件最开始由具体的元素向上层节点扩散,传播到document
目前大部分都是事件冒泡流
,不过老的一些浏览器也支持事件捕获流
什么是DOM事件流?事件流描述的是从页面中接收事件的顺序,DOM2级事件流包括下面几个阶段。
- 事件捕获阶段
- 处于目标阶段
- 事件冒泡阶段
-
事件处理程序?响应某个事件的函数 DOM0:元素本身的事件方法(
onclick
) DOM2:addEventListener可以增加多个事件方法,这个方法接收3个参数:要处理的事件名、事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序,IE只支持事件冒泡 -
如何阻止事件冒泡?
event.stopPropagation()
- 写出兼容IE的事件程序
var EventUnit = {
addHandler: function(element,type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false)
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler)
} else {
element['on' + type] = handler
}
},
removeHandler: function (element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false)
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler)
} else {
element["on" + type] = null
}
}
}
事件委托是将事件监听器添加到父元素,而不是每个子元素单独设置事件监听器。当触发子元素时,事件会冒泡到父元素,监听器就会触发。这种技术的好处是:
- 内存占用减少,因为只需要一个父元素的事件处理程序,而不必为每个后代都添加事件处理程序。
- 无需从已删除的元素中解绑处理程序,也无需将处理程序绑定到新元素上。
简单的讲就是,函数的调用方式决定了this
的值
this取值的几个规则:
- 在调用函数时使用new关键字,函数内的this指的是当前实例对象
- 如果apply、call或bind方法用于调用、创建一个函数,函数内的 this 就是作为参数传入这些方法的对象
- 当函数作为对象里的方法被调用时,函数内的this是调用该函数的对象。比如当obj.method()被调用时,函数内的 this 将绑定到obj对象
- 如果调用函数不符合上述规则,那么this的值指向全局对象(global object)。浏览器环境下this的值指向window对象,但是在严格模式下('use strict'),this的值为undefined
- 如果符合上述多个规则,则较高的规则(第1 个最高,第4个最低)将决定this的值
- 如果函数是箭头函数,将忽略以上规则,this指的是它被创建时候的上下文
这段话引自《JavaScript权威指南(第四版)》: 由于引用类型没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃
堆内存(Heap)区的一个对象,如果没有被引用,它就是垃圾,将Heap区的这部分内存释放,就叫“垃圾回收”
垃圾回收的方式?
1、标记清除 2、引用计数
基本类型:Undefined,Null,Boolean,Number,String,Symbol 引用类型:Object(Array 也是Object)
如果遇到基本类型的声明,就在栈内存(stack)里开辟一个空间,将简单类型的值放进去;
如果遇到引用类型的声明:就在放在栈内存(stack),堆内存(heap)开辟一个空间。将自身的值放进堆内存,并将指向堆内存的这块空间的地址,放进栈内存。(有个形象的比喻:桌面的快捷方式,里面保存的是地址,地址打开的是真实数据内容
)
-
草图一分为三,左代码,中Stack,右Heap
-
每逢声明语句,基本类型值写入Stack
-
每逢声明语句,对象类型地址写入Stack,值写入Heap,并用箭头连接两者
-
每逢赋值语句,值类型将Stack区的内容照抄;引用类型Stack的指针照抄,指向同一处Heap
https://juejin.im/post/5c6bbf0f6fb9a049ba4224fd
简要:就是用来装页面上的元素的矩形区域
块元素:独占一行,并且有自动填满父元素,可以设置margin和pading以及高度和宽度 行元素:不会独占一行,width和height会失效,并且在垂直方向的padding和margin会失效
- 采用google性能优化原则:
- 避免使用重定向 每个重定向都会添加一次往返(HTTP 请求-响应);而在最坏的情况下,除了额外的 HTTP 请求-响应周期外,它还可能会让更多次的往返执行 DNS 查找、TCP 握手和 TLS 协商
- 启用压缩功能-gzip
- 缩短服务器响应时间
- 将服务器的接口响应时间控制在200ms以内
- 剔除掉无用的响应数据
- 使用浏览器缓存策略
- Cache-Control 指定了浏览器和其他中间缓存可如何缓存单项响应以及可缓存多长时间
- ETag 提供了一个重新验证令牌,该令牌是由浏览器自动发送的,用于检查自上次请求相应资源后该资源是否发生了变化
- 缩减资源大小(html、js、css)
- 优化图片大小
- 优化css发送过程
- 内嵌较小 CSS 文件
- 请勿内嵌较大数据 URl
- 请勿内嵌 CSS 属性(导致代码重复)
- 优先加载可见内容
案例: 优先加载首屏内容,图片的话可以先给一个占位的默认图片,下一屏幕可以通过
promise
的方式来预加载图片
// 预处理图片
function preLoadImg(source){
let pr = [];
source.forEach(url => {// 预加载图片
let p = loadImage(url)
.then(img => this.images.push(img))
.catch(err => console.log(err))
pr.push(p);
})
// 图片全部加载完
Promise.all(pr)
.then(() => {
// do sth
});
}
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path; // 占位图片
});
};
- 移除会阻止内容展示的javascript
- 尽量不在头部添加外部脚本,可少量内嵌脚本
- 将javascript设为异步加载
- 按需加载javascript
- 使用http2
- 通过Lighthouse 进行性能分析,找出需要优化的项
描述::跨站脚本攻击(Cross Site Scripting),这是是最常见和基本的攻击WEB网站方法,攻击者通过注入非法的html标签或者javascript代码,从而当用户浏览该网页时,获取用户信息,控制用户浏览器。
分3类: domxss,反射型,存储型
带来的影响是什么? 用虚假输入表单骗取用户个人信息。 利用脚本窃取用户的Cookie值,被害者在不知情的情况下,帮助攻击者发送恶意请求。
显示伪造的文章或图片。
如何防御xss?
httpOnly:服务器设置httpOnly,禁止读取cookie 过滤:
- 输入检查(按照规定的格式和类型进行检验)
- 前后端同时检验机制
- 对html标签和url进行encode处理
https://tech.meituan.com/fe_security.html
csrf:跨站点请求伪造(Cross-Site Request Forgeries),也被称为one-click attack 或者session riding。
描述:冒充用户发起请求(在用户不知情的情况下),完成一些违背用户意愿的事情(如修改用户信息,删初评论等)
https://juejin.im/entry/5bed80afe51d45170811060a
MVC: 数据模型层(Model) + 视图层(View) + 控制器层(Controller)
MVVM: 数据模型层(Model) + 视图层(View) + 视图模型(ViewModel)
https://juejin.im/post/57dcd394a22b9d00610c5ec8
https://sekaiamber.github.io/react-dom-diff/
Javascript处于安全角度考虑,规定不允许跨域调用其他页面的数据(同源策略
)
注意:只要不满足
协议
、子域域
、主域名
、端口号
的其中之一,都算跨域
缺点:必须是>IE10
- jsonp
只支持get方式,会有长度限制
- websocket: Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议
- postMessage: HTML5为了解决父子的跨窗口通信
postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送
原理: 通过<script>标签可以跨域的策略,利用url参数添加回调函数的方式来传递数据
本地写一个回调函数:
function jsonpcallback(json) {
console.log(json)
}
把前面的方法稍微改改参数:
<script src="http://xxx.com/xxx?callback=jsonpcallback" />
服务器根据约定的回调函数名,返回一个/多个函数调用表达式,jsonpcallback({"Email":"zhww@outlook.com","Remark":"我来自遥远的 东方"}),正好执行了我们的jsonpcallback方法
https://juejin.im/post/5872309261ff4b005c4580d4
https://juejin.im/post/5b44a485e51d4519945fb6b7
概述:浏览器首先下载html、css、js。 接着解析生成DOM tree、CSS rule tree(样式规则树
), 之后浏览器引擎会通过DOMtree和CSS rule tree构造出rendering tree。 再通过layout后渲染页面
主要有两个步骤:
解析过程:
- 浏览器通过url,下载接收资源(HTML、CSS、JS、Images等等)
- HTML 文件加载后,开始构建 DOM Tree
- CSS 样式文件加载后,开始解析和构建 CSS Rule Tree
- Js脚本文件加载后, 通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree
浏览器渲染:
- 浏览器引擎通过 DOM Tree 和 CSS Rule Tree 构建 Rendering Tree
Rendering Tree 并不与 DOM Tree 对应,比如像 标签内容或带有 display: none; 的元素节点并不包括在 Rendering Tree 中
- layout/flow:通过 CSS Rule Tree 匹配 DOM Tree 进行布局的过程,例如:定位坐标和大小,是否换行,以及 position、overflow、z-index 等等属性
- paint:最终通过调用Native GUI 的 API 绘制网页画面的过程
当用户在浏览网页时进行交互或通过 js 脚本改变页面结构时,以上的部分操作有可能重复运行,此过程称为 Repaint 或 Reflow。
Repaint:当页面元素不会影响dom结构的变化时(例如:background,color),
Reflow:当元素改变的时候,将会影响文档内容或结构,或元素位置
Reflow 的成本比 Repaint 的成本高得多的多。一个结点的 Reflow 很有可能导致子结点,甚至父点以及同级结点的 Reflow 。在一些高性能的电脑上也许还没什么,但是如果 Reflow 发生在手机上,那么这个过程是延慢加载和耗电的。----浏览器的渲染原理简介
以下行为将有可能产生 Reflow
- 增加、删除、或改变 DOM 节点
- 增加、删除 ‘class’ 属性值
- 元素尺寸改变
- 文本内容改变
- 浏览器窗口改变大小或拖动
- 动画效果进行计算和改变 CSS 属性值
- 伪类激活(:hover)
这就是为什么visual DOM 的优势,尽量减少Reflow 的成本
1、粗暴型,禁用缩放
<meta name="viewport" content="width=device-width, user-scalable=no">
- 利用FastClick,其原理是
检测到touchend事件后,立刻出发模拟click事件,并且把浏览器300毫秒之后真正出发的事件给阻断掉
chrome32之后,和IOS9以上基本可以不关心
参考:https://thx.github.io/mobile/300ms-click-delay
https://juejin.im/post/5aaa37c8f265da23945f365c
一个快速构建的全栈脚手架工具,支持vue,react,ng,koa,我的目标的打造完成后能构建跨平台项目,桌面,小程序,h5
- 低版本的兼容性问题和卡顿问题:开起硬件加速
- webpack的构建缓慢问题?
1、增量部署 2、sass 预编译 3、用webpack4