前端功课复习手册
1. 一些面试题
1.1 seo搜索引擎优化
语义化书写html代码
少使用iframe(搜索引擎抓取不到)
为图片加上alt属性
页面尽量不要做flash,视频
1.2 性能优化的方法
- 减少htttp请求
- 压缩格式化代码
- 把css放在顶部,js放在底部。尽量外链,利于维护
- css雪碧图
- 利用cdn
1.3 线程和进程,异步编程
一个程序一定会开启一个进程
多个进程之间存储空间独立,线程是进程的基本执行单位,任务运行必须依赖与线程,同进程下的线程共享地址空间
例子:浏览器的一个窗口就是一个进程,浏览器会包含一个主进程
每个进程下面包含多个线程,其中主要包含GUI渲染线程和Js引擎线程,js是单线程,因为如果是多线程就会有如同时操作dom,锁逻辑之类的。但是js可以通过事件循环机制执行异步编程。
当Gui渲染线程碰到js线程的时候,会把GUI渲染线程挂起,不然当渲染线程渲染后在处理js就可能导致不一致。这也是为什么js会阻塞html解析的原因,也是为什么要把js代码放到body底部的原因
1.3.1同步任务,异步任务
- 同步任务:自上向下运行,依赖于上一行执行结果。主线程首先执行同步代码
- 异步任务:执行时不会等待上一行代码完成,而是将这部分代码放入异步队列,继续执行后续的代码。
1.3.3 宏任务,微任务
异步任务分了宏任务和微任务,微任务先执行,宏任务后执行
- 宏任务:setTimeOut,setInterval,requestAnimationFrame,addEventLister
- 微任务:promise回调
1.3.2 执行栈,任务队列,事件循环机制
- 执行栈:先进后出
比如多个函数嵌套使用a {b {c {,那么栈中顺序a在栈底,c在栈顶,当c执行完,弹出c。最后a执行完弹出a - 任务队列(消息队列):先进先出
最先添加最先执行,任务队列就是用来存放异步方法的,分为了微任务队列和宏任务队列。 - 事件循环机制(Event Loop):
当主程序的执行栈(同步任务)执行完清空后。先去查询主程序执行(一个宏任务的调用)产生的微任务,执行微任务队列内的所有任务。执行后再去查询最先添加到宏任务队列里面的任务,将宏任务添加到执行栈中执行,执行完毕后立即去执行产生的微任务队列,微任务队列清空后再执行下一个宏任务。如此循环直到两个任务队列清空。
1.4 输入url到渲染界面过程
- 浏览器接受到url后,开启一个进程
- 提取url中协议,地址域名等,浏览器向dns服务器发送请求,dns进行解析。找到对应服务器ip地址
- 浏览器与服务器创建tcp三次握手协议
- 浏览器发送http请求,包含请求头等
- 服务器接受并返回,包括响应数据状态码等
- 浏览器分析状态码等,如果成功开始html解析(执行下述2.1流程)
- 链接结束,断开tcp链接
1.5 回收机制垃圾
垃圾回收机制是js管理内存的一个过程,当一个变量赋值给一个对象的时候就会被标记引用,当赋值为null或者在作用域通过其他方式删除后,之前的引用计数就会被删除,当引用计数为的时候就会被回收。
常见导致内存泄漏的类型:
- 全局对象不会被回收(var定义的变量
- 闭包导致的(变量引用不会被回收
1.6 算法相关
2.HTML
2.1 浏览器加载页面的顺序
- 获取html,解析成dom树
- 解析css,生成cssom树。来源包含:浏览器样式,通过link或@import引入外部CSS,style标签内联样式
- 解析js(defer和async可以改变执行时间,defer是文档加载解析完后执行,async是文件加载后立即执行)
- 结合dom树和cssom树生成布局树
- 布局和绘制(重排,重绘),布局过程涉及到分层
2.2 html5常见新特性
2.3 画布,水印
/**
* 设置水印
* @param width 画布宽度
* @param height 画布高度
* @param fillStyle 文字颜色
* @param font 字体样式
* @param insertDom 插入dom
* @param ZIndex 层级
* @param className 添加节点的class
* @param args 显示文本(多行)
*/
function set(
{
fillStyle = 'rgba(0, 0, 0, 0.1)',
font = 'normal 14px Microsoft YaHei',
insertDom = document.body,
ZIndex = 9996,
className = 'watermark',
rotate = 15
} = {}, ...args
) {
const canvas = document.createElement('canvas')
let lensArr = [...args].map(item => (item.length))
let sortArr = lensArr.sort((a, b) => (b - a))
let max = sortArr[0]
canvas.width = max * 14 + 164
canvas.height = rotate === 0 ? 126 : max * 14 * Math.sin(rotate * Math.PI / 180) + 112
canvas.style.display = 'none'
const context = canvas.getContext('2d')
// 控制文字的旋转角度和上下位置
context.rotate(-rotate * Math.PI / 180)
// context.translate(0, 0)
// 文字颜色
context.fillStyle = fillStyle
// 文字样式
context.font = font
args.forEach((text, index) => {
context.fillText(text, 0, index === 0 ? (canvas.height - 12) : (canvas.height + 40 * index), canvas.width - 100)
})
// 新建一个用于填充canvas水印的div标签,考虑到z-index对个别内容影响,故而不用body
const waterMark = document.createElement('div')
const styleStr = `left: 0px; top: 0px; width: 100%; height: 100%; position: absolute; z-index: ${ZIndex}; pointer-events: none; background-image: url(${canvas.toDataURL('image/png')}); background-repeat: repeat; mix-blend-mode: multiply;`
waterMark.setAttribute('style', styleStr)
waterMark.classList.add(`${className}`)
insertDom.appendChild(waterMark)
}
/**
* 关闭页面的水印,即要移除水印标签
*/
function close(insertDom = document.body, className = 'watermark') {
const waterMark = document.querySelector(`.${className}`)
// deleteFlag = true
insertDom.removeChild(waterMark)
}
const watermark = {
set,
close
}
export default watermark
2.4 拖拽
元素可拖拽:draggable="true"
开始拖拽事件:ondragstart=drag(event)
设置被拖数据的数据类型和值:dataTransfer.setData("type",data)
接收拖拽元素:ondragover="allowDrop(event)"
,方法中设置:event.preventDefault()
放置元素后:ondrop="drop(event)"
<body>
//接收拖拽的dom`在这里插入代码片`
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
//可以拖动的dom
<img id="drag1" src="/images/logo.png" draggable="true" ondragstart="drag(event)" width="336" height="69">
<script>
//开始拖拽
function drag(event){
event.dataTransfer.setData("Text",event.targrt.id)
};
//允许接收拖拽元素
function allowDrop(event){
event.preventDefault()
};
//拖拽后
function drop(event){
event.preventDefault();
var data=event.dataTransfer.getData("Text");
event.target.appendChild(document.getElementById(data));
}
</script>
</body>
2.5 地理位置
必须客户同意。
var x=document.getElementById("demo");
function getLocation(){
if (navigator.geolocation){
navigator.geolocation.getCurrentPosition(showPosition);
}else{
x.innerHTML="该浏览器不支持获取地理位置。";
}
}
function showPosition(position){
x.innerHTML="纬度: " + position.coords.latitude +
"<br>经度: " + position.coords.longitude;
}
- 音频,视频,插件
浏览器将使用第一个 可识别的格式:
<video width="320" height="240" controls>
<source src="movie.mp4" type="video/mp4">
<source src="movie.ogg" type="video/ogg">
您的浏览器不支持Video标签。
</video>
<audio controls>
<source src="horse.ogg" type="audio/ogg">
<source src="horse.mp3" type="audio/mpeg">
您的浏览器不支持 audio 元素。
</audio>
//<object> 元素定义了在 HTML 文档中嵌入的对象。
<object width="400" height="50" data="bookmark.swf"></object>
<object width="100%" height="500px" data="snippet.html"></object>
<object data="audi.jpeg"></object>
//<embed> 元素表示一个 HTML Embed 对象 。
<embed width="400" height="50" src="bookmark.swf">
<embed width="100%" height="500px" src="snippet.html">
3.CSS复习+Scss
3.1 link和import区别
link:属于html标签,在界面加载时会同步加载
import:属于css语法,界面加载后在加载。会导致样式闪烁
3.2 动画 ( @keyframes )
//设置动画名
div{
animation: myfirst 5s;
-moz-animation: myfirst 5s; /* Firefox */
-webkit-animation: myfirst 5s; /* Safari 和 Chrome */
-o-animation: myfirst 5s; /* Opera */
}
//设置动画效果
@keyframes myfirst{//可用半分比
from {background: red;}
to {background: yellow;}
}
@-moz-keyframes myfirst /* Firefox */{
from {background: red;}
to {background: yellow;}
}
@-webkit-keyframes myfirst /* Safari 和 Chrome */{
from {background: red;}
to {background: yellow;}
}
@-o-keyframes myfirst /* Opera */{
from {background: red;}
to {background: yellow;}
}
3.3 scss
4.js 复习
4.1 类型判断,转换
4.1.1 typeof
4.1.2 instanceof
4.1.3 Object.prototype.toString.call
4.1.4 转换成布尔Boolean
4.1.5 转换成数值Number
4.1.6 转换成字符串String
4.1.7 判断是不是数组,对象和类数组转换成数组
4.2 数字的相关方法
value.parseInt
valu.pareFloat
Math.PI
Math.ceil
Math.floor
Math.round
Math.random
- i++,++i计算:
变量后写++:计算后,变量本身在+
变量前写++:变量本身先加1,在计算
4.3 字符串转换比大小
字符串和字符串比较,会转换成编码比较
"23"<"3" // true
"23"<3 // false
运算符优先级:
1.括号()
2.一元运算符号 ++,–,!
3.乘除取余*,/,%
4.加减法 +,-
5.移位 << >>
6.关系 > < >= <=
7.相等 == !=
8.且 &&
9.或 ||
10.条件运算符?
11.赋值+=,=等
4.4 false,null,undifined与0的关系
console.log(false == 0); // true
console.log('' == 0); // true
console.log(0==undefined); // false
console.log(0== null); // false
console.log(null == undefined); // true
console.log(null === undefined); // false
4.5 数组常见方法,去重,解构
concat
pop
push
shift
unshift
sort
map
filter
foreach
some
every
- 数组去重
Array.from(new Set([1,2,3,4,3,4,5,6]))
- 数组最大最小值
Math.max(...[-10,-1,0,1,2,'100'])
Math.min(...['-10',-1,0,1,2,100])
- 数组解构
es6新增flat方法
4.6 堆和栈, 深拷贝浅拷贝
栈:操作系统自行分布,先进先出
基础数据类型,存放在栈内存中,占据固定大小的空间。内存地址大小的固定的
堆:程序手动分布,先进后出
引用数据类型的指针存在栈中,对象存在堆中
深拷贝:
赋值一份对象值存储到堆内存内,不是只赋值指针
4.7 作用域,this,闭包
作用域分为:全局作用域window,函数作用域,块级作用域(es6新增)
- 作用域链:保证对执行环境有权访问的所有变量和函数的有序访问
- 全局执行环境是window对象,每个函数都有自己的执行环境
- 内部环境环境可以通过作用域链访问所有的外部环境,但是外部环境不能访问内部环境的任何变量和函数
- 对象不构成单独的作用域
闭包:一个函数内部创建另外一个函数(内部函数使用外部函数活动对象,所以外部函数执行完后,作用域链被销毁,但是活动对象还在)
function out(a){
var b = 1
return function(c, d){
var e = a+b+c+d
console.log(e)
return {
b,
e
}
}
}
out(1)(1,1)
// 定义一个out函数,会创建它的执行环境。
// 调用函数后会创建它的活动对象,活动对象包含arguments,a为1,b为1
// 函数执行中,需要作用域链查找变量,此时作用域链第一位是out的活动对象,第二位是全局执行环境的活动对象
// 匿名函数返回后,会创建匿名函数的执行环境
// 调用函数后会创建它的活动对象,活动对象包含arguments,c为1,d为1,e为4
// 此时作用域链第一位是匿名函数的活动对象,第二位是out函数的活动对象,第三位是全局执行环境的活动对象
// 当out执行后,它的作用域链被销毁,但是它的活动对象没被销毁,因为匿名函数的作用域链任然在引用out的活动对象
this指向问题:this对象是在运行时基于函数执行环境定的
- 在全局函数中,this指向window
- 当函数作为对象方法被调用时,指向该对象
- 匿名函数执行环境具有全局性,this指向window
- new构造函数的this指向实例对象
- setTimeout普通函数写法指向window
- 箭头函数()=>指向定义时上层作用域中的this
4.8 面向对象相关
4.8.1 函数定义方式
1.函数声明式(会有函数提升)
function name(){}
2.函数表达式(匿名函数赋值给变量)
const name = function(){}
4.8.2 函数的四个内置属性,call,apply,bind
prototype,this,arguments,length
在特定的作用域下调用函数
var color = "bule"
function func(){
console.log(this.color)
}
var obj = {
color:"red"
}
// apply :第二个参数是参数数组
func.apply(obj,[parm1,parm2])
// call:参数要逐个列出
func.call(obj,parm1,,parm2)
// bind:创建函数实例
const colorFun = func.bind(obj)
color()
4.8.3 对象定义的方式
1.对象构造函数法
const person = new Object()
person.name = '1'
2.对象字面量法
const person = {
name:"1"
}
4.8.4 对象的数据属性,访问器属性。object.defineProperty
数据属性:
value:值
writable:能否修改属性值
enumerable:能否使用for in
configurable:能否使用delete删除属性
访问器属性:(必须通过object.defineProperty)
get:获取值触发
set:设置值
enumerable:能否使用for in
configurable:能否使用delete删除属性
设置属性
object.defineProperty(obj,'name',{
value:'123'
writable:false
})
object.defineProperties(obj,{
name:{
value:'123'
writable:false
},
color:{
get:function(){
return 'red'
},
set:function(value){
if(value=='bule'){
return 'yellow'
}
}
}
})
4.8.5 toString,valueOf方法
- toString方法将对象转换为字符串并返回字符串
var obj = {
a:1
}
console.log(obj.toString()) // [object Object]
console.log(['1',''].toString()) // 1,
- valueOf:返回对象的原始值
var obj = {
a:1,
valueOf:function(){
return this.a + 1
}
}
console.log(obj.valueOf()) // 2
4.8.6 New关键字
const person1 = new Person()
1.创建一个空对象
const person1 = {}
2.构造函数的作用域赋值给新对象,this指向新对象
Person.apply(person1,arguments)
2.执行构造函数代码(属性和方法加到this指向的对象内)
person1.属性=value
3.返回新对象
return person1
4.8.7 原型模式
// 构造函数
const person = function(){}
// 构造函数有个prototype属性,prototype指向原型对象,这句话是给原型对象内新增了值为123的name。原型对象内有一个constructor属性指向构造函数
person.prototype.name="123"
// 创建一个实例,实例中有一个_proto_指向原型对象
const person1 = new person()
// 实例中找不到name会去原型对象上找
console.log(person1)
console.log(person1.name)
4.8.8 原型链
让一个构造函数的原型等于另外一个构造函数的实例
- 原型链的原理依赖于对象的_proto_隐式属性
- 实例对象的_proto_指向原型对象=构造函数的prototype
- 原型对象的_proto_指向Object原型对象=Object.prototype
- Object对象的_proto_等于null,是终点
4.9 事件冒泡,事件捕获
事件冒泡:事件由最开始具体的元素接受想上传播。直到根节点
阻止冒泡:e.stopPropagation()
事件捕获:不大具体的节点应该更早接收到事件,具体的节点最后收到事件
DOM事件流(event flow )存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段
// 第三个参数默认false,冒泡阶段执行,true为捕获阶段执行
event.addEventListener(事件类型,回调函数,true||false)
4.10 防抖,节流
防抖:一定时间内,触发多次函数,等待结束时间执行一次
一般用于输入框搜索,实时获取数据
var obj1 = {
id:null,
method1:function(){
console.log('666')
},
click1:function(){
clearTimeout(this.id);
var that=this;
this.id=setTimeout(function(){
that.method1()
},1000)
}
}
window.onresize = function(){obj1.click1()}
function debounce(fun,time){
let timeId = null;
return function(){
if(timeId){
clearTimeout(timeId);
}
var that = this;
timeId = setTimeout(function(){
timeId = null
fun.apply(that,arguments);
}, time)
}
}
window.addEventListener('resize',
debounce(function(){
console.log(window.innerWidth)
},1000)
)
节流:一定时间内,无论函数触发多少次,都只在开始触发一次
一般终于窗口缩放触发函数
function throttle(fn, time) {
let timeId = null;//声明一个定时器
return function () {
var width1 = this.innerWidth;
if (!timeId) {
var that = this;
timeId = setTimeout(function () {
fn.call(that,width1);
timeId = null;
}, time)
}
}
}
window.addEventListener('resize',
throttle(function(width){
console.log('输出第一次调整时候的窗口宽度',width)
},5000)
)