html基础面试题302
1.如何理解HTML语义化?
两端代码比较:
<div>标题</div>
<div>
<div>一段文字</div>
<div>
<div>列表1</div>
<div>列表2</div>
</div>
</div>
}
<h1>标题</h1>
<div>
<p>一段文字</p>
<ul>
<li>列表1</li>
<li>列表2</li>
</ul>
</div>
}
- 让人更容易读懂(增加代码可读性)
- 让搜索引擎更容易读懂(SEO)
2.哪些HTML标签是块级元素,哪些是内联元素?
- display:block/table;有 div h1 h2 table ul ol p 等
- display:inline/inline-block;有span img input button 等
css常见面试题
css布局
1.盒子模型的宽度如何计算?
<!--如下代码,请问div1的offsetWidth是多大?-->
<style>
#box{
width: 100px;
padding: 10px;
border: 1px solid #ccc;
padding:10px;
margin:10px;
}
</style>
<div id="box"></div>
offsetWidth=(内容宽度 + 内边距 + 边框),无外边距
答案:122px
box-sizing: border-box/ content-box;
2.margin纵向重叠的问题?
<p>AAA</p>
<p></p>
<p></p>
<p></p>
<p>BBB</p>
<!--如下代码,AAA和BBB之间的距离是多大?-->
<style>
p{
font-size: 16px;
line-height: 1;
margin-top: 15px;
margin-bottom: 15px;
}
</style>
- 相邻元素的margin-top和margin-bottom会发生重叠
- 空白内容的p标签也会重叠 答案是:15px
3.margin负值的问题?
- margin-top和margin-left负值,元素向上,向左移动
- margin-right负值,右侧元素左移,自身不受影响
- margin-bottom负值,下方元素上移,自身不受影响
4.BFC理解和应用?
- Block format context,块级格式化上下文
- 一块独立渲染区域,内部元素的渲染不会影响边界以外的元素 形成BFC的常见条件:
- float不是none
- position是absolute 或fixed
- overflow不是visible
- display是flex inline-block等 BFC常见应用:
- 清除浮动
// 如果img 添加float:left;会形成bfc 使图片脱离文档流;
// 加上bfc(oveflow:hidden),可以实现bfc
<div class="container bfc">
<img src="./logo.png" alt="">
<p>一段文字</p>
</div>
<style>
.container img {
float:left; /* 形成bfc */
}
.bfc {
overflow: hidden; /* 触发元素BFC */
}
</style>
5.float 布局的问题,以及clearfix?
圣杯布局和双飞翼布局的目的:
- 三栏布局,中间一栏最先加载和渲染(内容最重要)
- 两侧内容固定,中间内容随着宽度自适应
- 一般用于pc 圣杯布局和双飞翼布局的技术总结:
- 使用float布局
- 两侧使用margin负值,以便和中间内容横向重叠
- 防止中间内容被两侧覆盖,一个用padding一个用margin
// 圣杯布局
<style>
.container {
padding-left: 200px;
padding-right: 150px;
}
.center{
width: 100%;
background: #ccc;
}
.left{
position: relative;
width: 200px;
margin-left: -100%;
background: yellow;
right: 200px;
}
.right
{
width: 150px;
background: pink;
margin-right: -150px;
}
.column{
float: left;
}
</style>
<div class="header"></div>
<div class="container">
<div class="center column">this is center</div>
<div class="left column">this is left</div>
<div class="right column">this is right</div>
</div>
<div class="footer"></div>
// 双飞翼布局
<style>
.main {
width: 100%;
height: 200px;
background-color: #ccc;
}
.main-warp{
margin-left: 200px;
margin-right: 150px;
}
.left{
width: 200px;
height: 200px;;
background: yellow;
margin-left: -100%;
}
.right
{
width: 150px;
background: pink;
height: 200px;
margin-left: -150px;
}
.col{
float: left;
}
/* 手写clear fix */
.clearfix:after{
content:"";
display: table;
clear: both;
}
.clearfix {
*zoom:1 /* 兼容IE低版本 */
}
</style>
<div class="container clearfix">
<div class="main col ">
<div class="main-warp ">this is main</div>
</div>
<div class="left col">this is left</div>
<div class="right col">this is right</div>
</div>
6.flex布局的问题?
7.css定位absolute和resolve分别依据什么定位?
- resolve依据自身定位,对外界无影响
- absolute依据最近一层元素定位 定位元素: absolute fixed resolve body
8.居中对齐有哪些实现方式?
水平居中:
- inline元素: text-align:center
- block元素:margin: auto
- absolute元素: left:50%; margin-left 负值 垂直居中:
- inline元素: line-height的值等于height值
- absolute元素: top:50%; margin-top 负值
- absolute元素: transform(-50%, -50%)
- absolute元素:left,right,top,bottom=0 + margin:auto;
// bsolute元素: transform(-50%, -50%)
<style>
.container{
width: 500px;
height: 300px;
border:1px solid #ccc;
position: relative;
}
.item{
width: 200px;
height: 100px;
line-height: 100px;
background-color: #ccc;
text-align: center;
}
.item-1{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
<div class="container clearfix">
<div class="item item-1">this is item</div>
</div>
// - absolute元素:left,right,top,bottom=0 + margin:auto;
<style>
.container{
width: 500px;
height: 300px;
border:1px solid #ccc;
position: relative;
}
.item{
width: 200px;
height: 100px;
line-height: 100px;
background-color: #ccc;
text-align: center;
}
.item-1{
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
</style>
<div class="container clearfix">
<div class="item item-1">this is item</div>
</div>
效果:
9.line-height 如何继承?
- 写具体数值,如30px, 则继承该值
- 写比例,如 2/1.5, 则继承该值
- 写百分比,如200%, 则继承算出来的值(考点)
10.css响应式
(1)rem是什么?
rem: 是一个长度单位
- px:绝对长度单位,最常用
- em:相对长度单位,相对于父元素,不常用
- rem:相对长度单位,相对于根元素,常用于响应式
<style>
html{
font-size: 100px;
}
div{
font-size: 0.16rem; /* 0.16rem = 16px */
}
</style>
<p style="font-size: 0.1rem;">rem1</p>
<p style="font-size: 0.2rem;">rem2</p>
<p style="font-size: 0.18rem;">rem3</p>
<div>rem4</div>
<div>rem5</div>
<div>rem6</div>
(2)响应式布局的常用方案?
- media-query, 根据不同的屏幕宽度设置根元素的font-size
- rem,基于根元素的相对单位
<style>
@media only screen and (max-width:374px){
/* iphone5 或者更小的尺寸,以iPhone5 的跨度(320px)比例设置 fonts-size */
html{
font-size: 86px;
}
}
@media only screen and (min-width:375px) and (max-width: 431px){
/* iphone6/7/8/x */
html{
font-size: 100px;
}
}
@media only screen and (min-width:414px){
/* iphone6 或者更大尺寸,以iPhone6p 的跨度(414px)比例设置font-size */
html{
font-size: 110px;
}
}
body{
font-size: 0.16rem;
/* 根元素html文字大小 * 0.16rem = 绝对长度 px */
}
.div1{
width: 1rem;
}
</style>
<div class="div1">this is div1</div>
(3)响应式 vw/vh ?
- rem弊端
- 网页视口尺寸
- vw/vh rem弊端: “阶梯” 性
@media only screen and (max-width:374px){
/* iphone5 或者更小的尺寸,以iPhone5 的跨度(320px)比例设置 fonts-size */
html{
font-size: 86px;
}
}
@media only screen and (min-width:375px) and (max-width: 431px){
/* iphone6/7/8/x */
html{
font-size: 100px;
}
}
@media only screen and (min-width:414px){
/* iphone6 或者更大尺寸,以iPhone6p 的跨度(414px)比例设置font-size */
html{
font-size: 110px;
}
}
网页视口尺寸: window.screen.height// 屏幕视口高度 window.innerHeight // 网页视口高度 document.body.clientHeight // body 高度
vw/vh: vh 网页视口高度的 1/100 vw 网页视口宽度的 1/100 vmax 取两者中间最大值,vmin 取两者之间最小值
window.innerWidth === 100vh
window.innerHeight === 100vw
10. css3动画?
Javascript面试题
变量类型和计算
1.typeof 能判断那些类型?
- 识别所有的值类型
- 识别函数
- 判断是否是引用类型(不可再细分)
判断值类型:
const a; // typeof undefined
const b = 'abc'; // typeof string
const c = 10; // typeof number
const d = true; // typeof boolean
const f = Symbol('5); // typeof Symbol
判断函数:
typeof console.log() // function
typeof function() {} // function
// 能识别应用类型 (不能再继续识别)
typeof null; // Object
typeof [12,23] // Object
typeof {} // Object
2.何时使用 === 何时使用== ?
除了null之外,其他一律都用 ===,例如
const obj = { x:100 };
if(obj.a == null) {}
// 相当于:
// if(obj.a === null || obj.a === undefined) {}
3. if语句与逻辑运算符
- truly变量 !!a === true 的变量
- falsely变量 !!b === false 的变量
!!0 === false
!! undefined === false
!! false === false
!! null === false
!! '' === false
!! NaN === false
!! {} === true
!! [] === true
// truly类变量:
const a = true;
const a = 100;
if(a) {
...
}
// falsly类变量:
const b = '';
const b = null;
if(b) {
...
}
// 逻辑判断规则
console.log( 0 && 10 ) // 0
console.log( 10 || 0 ) // 10
console.log(!window.abc) // true
4.值类型和引用类型的区别?
引用值类型的赋值可能会存在干扰
const obj = {
a: 1
}
const obj1 = obj;
obj1.a = 2;
console.log(obj.a) // 2
5.手写深拷贝?
function deepClone(obj = {}){
if(typeof obj != 'object' || typeof obj == null) {
// obj是null,或者不是object直接返回
return obj
}
// 初始化返回结果
let result;
if(obj instanceof Array) {
result = []
} else {
result = {}
}
for(let key in obj) {
// 保证key不是原型的属性
if(obj.hasOwnProperty(key)) {
// 递归调用!!!
result[key] = deepClone(obj[key])
}
}
return result
}
6.值类型和引用类型?
值类型:
let a = 20;
let b = a;
b = 10;
console.log(a) // 20
引用类型:
let a = { age: 20 } let b = a; b.age = 21; console.log(a.age) // 21
值类型: 存储在栈中
| 栈 | |
| --- | --- |
|key | value |
|a | 100 |
|b | 200 |
| 栈 | |
| --- | --- |
|key | value |
|a | 内存地址1 |
|b | 内存地址2 |
引用值类型:
| 堆 | |
| --- | --- |
|key | value |
|内存地址1 | {age: 20} |
|key | val |
| 堆 | |
| --- | --- |
|key | value |
|内存地址2 | {age: 21} |
|key | val |
使用引用类型和值类型的原因:
- 值类型存储空间小,赋值不会对内存造成影响。
- 引用值内存地址太大,不好管理
- 引用直接赋值会非常慢,基于内存空间,计算机性能进行区分。
常见的值类型:
const a = undefined;
const b = 'abc';
const c = 10;
const d = true;
const f = Symbol('5);
常见的引用类型:
const a = { x: undfined };
const b = [1,2,3,4];
const c = null; // 特殊引用值类型,指针指向空地址
// 特殊的引用类型,但不用于存储数据,所以没有“拷贝,赋值函数”
function() fn {} // function
6.变量计算-类型转换?
- 字符串拼接
- ==
- if语句和逻辑运算
//字符串拼接
const a = 100 + 10; // 110
const b = 100 + '10'; // 10010
const c = true + '10'; // true10
// == 运算符
100 == '100' // true
0 == '' // true
0 == false // true
false == '' // true
null == undefined // true
7.typeof 和 instanceof区别?
- typeof 会返回一个运算数的基本类型,instanceof 返回的是布尔值
- instanceof 可以准确判断引用数据类型,但是不能正确判断基本数据类型
- typeof 虽然可以判断基本数据类型(null 除外),但是无法判断引用数据类型(function 除外) 扩展:
Object.prototype.toString.call()
typeof和instanceof都有一定的弊端,并不能满足所有场景的需求。如果需要通用检测数据类型,可以使用Object.prototype.toString.call()方法:
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(666); // "[object Number]"
Object.prototype.toString.call('xxx'); // "[object String]"
扩展封装:
function getType(value) {
let type = typeof value;
console.log(type)
if(type !== 'object') {
// 如果是基本数据类型,直接返回
return type
}
return Object.prototype.toString.call(value).replace(/\[object (\S+)]$/,'$1')
}
getType({name: 123}) // object
原型和原型链
1.如何判断一个变量是不是数组?
a instanceof Array
2.手写jquery,考虑插件和扩展性?
/*
* jQuery demo
*/
class jQuery {
constructor(selector) {
const result = document.querySelectorAll(selector);
const length = result.length;
for(let i = 0; i < length; i++ ) {
this[i] = result[i];
}
this.length = length;
this.selector = selector;
}
get(index) {
return this[index]
}
each(fn) {
for(let i = 0; i < this.length; i++) {
const elem = this[i];
fn(elem)
}
}
on(type, fn) {
return this.each(elem => {
elem.addEventListener(type, fn, false)
})
}
// 扩展更多dom操作
}
// 扩展
jQuery.prototype.dailog = function(info) {
alert(info)
}
// 复用
class MyjQuery extends jQuery{
constructor(selector) {
super(selector);
}
// 扩展自己的插件
addClass(classname) {
}
}
// 使用插件
const $p = new jQuery('div');
$p.get(1)
$p.each(elem => console.log(elem.nodeName));
$p.on('click', () => alert('clicked'))
3.class的原型本质,怎么理解?
- 原型和原型链的图示
- 属性和方法的执行规则
知识点:
4.class继承
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHi() {
console.log(this.name ,this.age);
}
}
const student1 = new Student('mary', 13);
student1.sayHi();
继承:
- extends
- super
- 扩展或重写方法
5.instanceof 类型判断
xiaoluo instanceof Student // true
xiaoluo instanceof People // true
xiaoguo instanceof Object // true
[] instanceof Array // true
[] instanceof Object // true
{} instanceof Object // true
- 判断继承
- 判断数组类型
- 判断对象类型
.6原型和原型链
原型
原型关系:
- 每个class都有显示原型prototype
- 每个实例都有隐式原型__proto__
- 实例的__proto__指向对应的class的prototype 原型的执行规则: 获取属性xiaoluo.name 或执行方法xiaoluo.sayhi() 时 先在自属性和方法寻找 如果找不到则自动去__proto__中查找
原型链
console.log(Student.prototype.__proto__)
console.log(People.prototype)
console.log(People.prototype === Student.prototype.__proto__)
hasOwnProperty() // 判断是自身原型
instanceof原理:
- 顺着原型链上查找,对应的到class的prototype(显示原型),那么intanceof成立。
xiaoluo instanceof Student // true
xiaoluo instanceof People // true
xiaoguo instanceof Object // true
作用域和闭包
1. this的不同应用场景,如何取值?
- 作为普通函数
- 使用call apply bind
- 作为对象方法调用
- 在class方法中调用
- 箭头函数
2. 手写bind函数?
function fn1(a, b, c) {
console.log('this', this.x);
console.log(a, b, c);
return 'this is fn1';
}
const fn2 = fn1.bind({x: 100}, 10, 20, 53);
const res = fn2();
console.log(res);
// 模拟bind
Function.prototype.MyBind = function() {
// 将参数插接为数组
const args = Array.prototype.slice.call(arguments);
// 获取this (数组第一项)
const t = args.shift();
// this指向 fn1.bind(...) 中的fn1
const self = this;
// 返回一个函数
return function() {
return self.apply(t, args)
}
}
const fn2 = fn1.MyBind({x: 100}, 10, 20, 53);
const res = fn2();
console.log(res);
3. 实际开发中闭包的应用场景,举例说明?
- 隐藏数据
- cache工具
// 闭包隐藏数据,只提供api
function createCache() {
const data = {}; // 闭包中的数据,被隐藏,不被外接访问。
return {
set: function(key, val) {
return data[key] = val
},
get(key) {
return data[key]
}
}
}
const c = createCache();
c.set('name', 'bob');
console.log(c.get('name'))
// 外部无法直接访问data作用域中的数据
4. 创建 10 个 a标签,点击时候弹出对应的序号?
知识点:
5.作用域和自由变量
作用域:
- 如果所示,作用域就是控制变量访问的范围。
- 全局作用域
- 局部作用域
- 块级作用域(ES6 新增)
es6块作用域
if(true) {
let x = 100
}
console.log(x) // 会报错
自由变量: 一个变量在当前作用域没有定义,但被使用了,会向上级作用域,一层一层一次寻找,直到找到为止 如果到全局作用域都没找到,则报错 xxx is not defined
6.闭包
作用域应用的特殊情况,有两种表现:
- 函数作为参数被传递
- 函数作为返回值被返回
// 函数作为返回值
function create() {
let a = 100;
return function() {
console.log(a)
}
}
let fn = create();
let a = 200;
fn()
// 函数作为参数
function print(fn) {
let a = 100;
fn()
}
let a = 200;
function fn() {
console.log(a)
}
print(fn);
7.this
this取值在函数执行的时候决定的
- 作为普通函数
- 使用call apply bind
- 作为对象方法调用
- 在class方法中调用
- 箭头函数
function f1() {
console.log(this)
}
f1(); // window
f1.call({x: 100 }) // {x: 100 }
const f2 = f1.bind({x: 200});
f2() // {x: 200 }
const zhangsan = {
name:"张三",
sayhi:function() {
// this当前对象
console.log(this)
},
wait() {
setTimeout(function() {
// this === window
console.log(this)
}, 100)
}
}
const zhangsan = {
name:"张三",
sayhi:function() {
// this当前对象
console.log(this)
},
wait() {
setTimeout(() => {
// 箭头函数取值,取上层作用域的值
// this当前对象
console.log(this)
}, 100)
}
}
class People{
constructor(name) {
this.name = name ;
}
say() {
console.log(this.name)
}
}
const p1 = new People('bobo');
p1.say(); // this指向'bobo'对象
异步和单线程
1.同步和异步的区别是什么?
- 异步是基于js单线程语言
- 异步不会阻塞代码执行
- 同步回阻塞代码执行
2.手写promise加载一张图片?
const url = 'xxx.png';
function loadImg(src) {
return new Promise((resolve,reject) => {
const img = document.createElement('img');
img.onload = () => {
resolve(img)
}
img.onerror = () => {
reject(new Error(`图片加载失败) ${src}`)
}
img.src = src;
})
}
loadImg(url).then(img => {
console.log(img.width);
return img
}),then(img => {
console.log(img.height);
}).catch((error) => console.log(error)
3.前端使用异步的场景有哪些?
- 网络请求,如ajax 图片加
ajax 请求
console.log('start)
$.get('./data.json', function(res) {
console.log(res)
})
console.log('end')
图片加载
console.log('start');
let img = document.createElement('img');
img.onload = funtion() {
console.log('loaded')
}
img.src = 'xxx'
console.log('end');
- 定时任务,如setTimeout
// 定时任务
console.log(100)
setTimeOut(function() {
console.log(200)
}, 1000)
console.log(300);
4.一下代码什么顺序打印出来?
13542
知识点:
4.单线程和异步
- js是单线程语言,只能同时做一件事儿
- 浏览器和nodejs已支持js启动进程,如web worker
- js和dom 渲染共同一个线程,因为js可修改dom结构
- 遇到等待(网络请求,定时任务)不能卡主
- 需要异步
- 回调callback函数形式 异步和同步:
- 基于js是单线程语言本质
- 异步不会阻塞代码执行
- 同步回阻塞代码执行
// 异步 (callback 回调函数)
console.log(100)
setTimeOut(function() {
console.log(200)
}, 1000)
console.log(300);
// 同步
console.log(100)
alert(200)
console.log(300)
5.应用场景
- 网络请求,如ajax 图片加载
- 定时任务,如setTimeout
6.callback hell 和promise
promise 解决 callback hell
callback hell
promise
管道串联形式展示回调嵌套
异步 - 进阶
- event-loop
- pormise 进阶
- async/await
- 微任务/宏任务
1.请描述evet-loop (事件循环/事件轮询)的机制,可画图
以几点分析机制: (1)自行回顾eventloop的过程 (2) 和dom渲染的关系 (3)微任务和宏任务在event loop过程中的不同处理
- js是单线程运行的
- 异步要基于回调来实现
- evet-loop 就是异步回调的实现原理 js如何执行?
- 从前到后,一行一行执行
- 如果某一行执行报错,则停止下面代码的执行
- 先把同步代码执行完,再执行异步
event loop 过程:
- 同步代码,一行一行放在Call Stack(调用栈) 执行
- 遇到异步,会先“记录”下,等待时机(定时、网络请求等)
- 时机到了,就移动到Callback Queue (回调队列)
- 如 Call Stack为空(即同步带吗执行完)Event Loop开始工作
- 轮询查找Callback Queue, 如有则移动到call stack执行
- 然后继续轮询查找(永动机一样)
Dom 事件和event loop
- 先将click内回调函数放入 web apis中
- 当点击时,启动event loop执行,放入callback queue 中,再放入call Stack中执行该回调内函数 DOM事件 和event loop
- js是单线程的
- 异步(settimeout ajax等)使用回调,基于event loop
- DOM事件也使用回调,基于event loop,但是dom事件不是异步
2.什么是宏任务和微任务,两者有什么区别?
- 宏任务: setTimeout setInterval Ajax dom事件
- 微任务: Promise async/awiat
- 微任务执行时机比宏任务早
- 宏任务在dom渲染之后触发,微任务在dom渲染之前触发
3.Promise 有那几种状态?如何变化?
三种状态:
- pending resolved rejected
- pending --> resolved 或 pending --> rejected
- 变化不可逆 状态的表现和变化
- pending状态,不会处罚then 和catch
- resolved状态,会触发后序的then回调函数
- rejected状态,会触发后序的catch回调函数
const p1 = new Promise(() => {});
console.log('p1',p1); // p1 Promise {<pending>}
const p2 = Promise.resolve('100');
console.log('p2',p2) //p2 Promise {<fulfilled>: '100'}
const p3 = Promise.reject('100');
console.log('p3',p3) //p3 Promise {<rejected>: '100'}
then和catch对状态的影响
- then正常返回resolved, 里面有报错则返回rejected
- catch正常返回resolved, 里面有报错则返回rejected
// then正常返回resolved, 里面有报错则返回rejected
const p1 = Promise.resolve().then(() => {
return 100
});
console.log('p1', p1) // fulfilled 触发then回调
p1.then(() => {
console.log('p1 then') // 打印
}).catch(() => {
console.log('p1 catch')
})
// then正常返回resolved, 里面有报错则返回rejected
const p2 = Promise.resolve().then(() => {
throw new Error('err')
});
p2.then(() => {
console.log('p2 then')
}).catch(() => {
console.log('p2 catch') // 打印
})
console.log('p2', p2) // rejected 触发catch回调
const p3 = Promise.reject('my error p3').catch((err) => {
console.log(err)
})
console.log('p3',p3) // fulfilled 注意! 触发then 回调
p3.then(() => {
console.log('p3 then') // 打印
}).catch(() => {
console.log('p3 catch')
})
const p4 = Promise.reject('my error p4').catch((err) => {
throw new Error('errerr')
})
console.log('p4',p4) // rejected 注意! 触发catch回调
p4.then(() => {
console.log('p4 then')
}).catch(() => {
console.log('p4 catch') // 打印
}) // 返回 fulfilled 的promise
4.场景题 - promise then和 catch连接的问题?
结果:1 3
结果:1 2 3
结果:1 2
5.场景题 - async/await 语法
async/await 和 promise的关系:
- 和Promise 并不互斥
- 反而,两者相辅相成
- 执行async函数,返回的是Promise对象
- await相当于Promise的then
- try...catch 可捕获异常,代替了Promise的catch
异步的本质
- async/await 是消灭异步回调的终极武器
- js还是单线程,但得是有异步,还得是基于event loop
- async/await 只是一个语法糖
async function async1() {
console.log('async1 start'); // 2
await async2(); // undefined
// await 的后面,都可以看做是callback 里的内容,即异步
// 类似event loop,settimeout(callback1)
// 类似 settimeout(function() {console.log('async1 end'})
// Promise.resolve().then(() => console.log('async1 end')
console.log('async1 end'); //5
}
async function async2() {
console.log('async2) // 3
}
console.log('script starts') // 1
async1()
console.log('script end') //4 同步代码执行完,(event loop 启动)执行异步代码
结果:a = promise fulfilled; b = 100
结果:start; a 100; b 200; Uncaught (in promise) 300
6. 场景题 - promise 和settimeout 的顺序
结果: 100; 400;300;200
7. 场景题 - 外加 async/await 的顺序问题
结果:
script start
test.html:56 async1 start
test.html:61 async2
test.html:69 promise1
test.html:74 script end
test.html:58 async1 end
test.html:72 prmise2
test.html:65 setTimeout
知识点:
1.for...of
- for...in(以及forEach for 是常规的同步遍历
- for...of常用于异步的遍历
function muti(num) {
return new Promise(resolve => {
setTimeout(() => {
resolve(num * num)
},1000)
})
}
const nums = [1, 2, 3];
nums.forEach(async (item) => {
const res = await muti(item);
console.log(res)
})
// 执行结果: 一秒后一次性打印出 1 3 9
// 如何依次每隔1秒后打印 1 3 9?
(async function() {
for (const item of nums) {
const res = await muti(item);
console.log(res)
}
})()
2.宏任务macroTask 与微任务microTask
- 什么是宏任务,什么是微任务
- 宏任务: setTimeout setInterval Ajax dom事件
- 微任务: Promise async/awiat
- 微任务执行时机比宏任务早
- event loop 和DOM渲染
- 再次回归一遍event loop的过程
- js是单线程的,而且和dom渲染共用一个线程
- js执行的时候,得留一些时机供dom渲染
evet loop和dom渲染过程:
- 每次callstack清空(及每次轮询结束),即同步任务执行完
- 都是dom重新渲染的机会,dom结构如果有改变则重新渲染
- 然后再触发下一次 Event loop
微任务,宏任务,dom渲染的关系:
- 微任务在dom渲染之前
- 宏任务在dom渲染之后
const $p1 = '<p>这是一段文字</p>';
const $p2 = '<p>这是一段文字</p>';
const $p3 = '<p>这是一段文字</p>';
const container = document.getElementById('container');
console.log(container)
container.append($p1)
container.append($p2)
container.append($p3)
console.log('length', container.childNodes.length)
// 宏任务:Dom渲染后触发
setTimeout(() => {
alert('Dom渲染后触发SetTimeout')
}, 0);
// 微任务:Dom渲染前触发
Promise.resolve('Dom渲染前触发,如Promise').then((res) => alert(res))
alert('本次call stack结束,dom结构已更新,但尚未触发渲染')
// alert会阻断js执行,也会阻断dom渲染,便于查看效果
// 这就是js执行和dom 渲染关系
宏任务,微任务和dom渲染,在event loop的过程:
从event loop解释,为何微任务比宏任务执行要早?
- 微任务是es6语法规定的
- 宏任务是浏览器规定的
JS-Web-Api
1.题目一: DOM是那种数据结构
2.题目二:一DOM操作常用api
3.题目三:一次性插入多个DOM节点,考虑性能
4.题目四:attr和property的区别
5.题目五:一次性插入多个DOM节点,考虑性能
6.题目六:如何识别浏览器的类型
7.题目七:分析拆解url各个部分
8.题目八:绑定一个通用的事件绑定函数
function bindEvnt(elem, type, selector, fn) {
if(fn === null) {
fn = selector;
selector = null;
}
elem.addEventListener(type, function(event) {
const target = event.target;
if(selector) {
// 代理
if(target.matches(selector)) {
fn.call(target, event)
}
} else {
// 普通绑定
fn.call(target, event)
}
alert(this.innerHTML);
};
}
const btn1 = document.getElementByIBd('btn1');
bindEvent(btn1, 'click', 'a', function(event) {
event.preventDefault();
const target = e.target;
if(target.nodeName === 'A') {
alert(target.innerHTML)
}
});
9.题目九:描述事件冒泡的流程
- 基于dom树形结构
- 事件会顺着触发元素网上冒泡
- 应用场景:代理
10.题目十:无限下拉的图片列表,如何监听每个图片的点击?
- 事件代理
- 用e.target获取触发元素
- 用matches来判断是否是触发元素
11.题目十一:手写一个简易的ajax
function ajax(url) {
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadyStateChange = function() {
if(xhr.readyState === 4){
if(xhr.status === 200) {
resolve(JSON.parse(xhr.responseTest))
} else if(xhr.status === 404){
reject(new Error('404 not found))
}
}
}
xhr.send(null)
})
return p;
}
const url = '/data/test.json';
ajax(url)
.then(res => {
console.log(res)
})
12.题目十二:跨域的常用实现方式
- JSONP
- CORS
13.题目十三:描述cookie loaclStorage sessionStorage?
- 容量的区别
- API易用性
- 是否跟随http请求发送出去
知识点:
1.DOM (document object model)
DOM本质: html语言和html文件解析出来的一棵树(dom树)
DOM节点操作
var div1 - document.getElementById('div1'); // 元素
var divs = document,getElementByTagName('div'); //集合
var container = document.getElementByClassName('container'); // 元素
var pList = document.querySelectAll('p');// 集合
DOM的property / attribute
- property属性:修改对象属性,不会体现到HTML结构中
- attribute属性:修改html属性,会体现到html结构中(一定会触发dom重新渲染)
- property / attribute都可能引发dom重新渲染 (nodeType: 0 文本节点 1 元素)
var pList = document.querySelectAll('p');// 集合
var p = pList[0];
console.log(p.style.width); // 获取样式
p.style.width = '10px'; // 修改样式
console.log(p.className); // 获取className
p.className = 'p1';
// 获取nodeName 和 nodeType
console.log(p.nodeName); // p
console.log(p.nodeType); // 1
// 修改attribute
p.setAttribute('className','list')
p.setAttribute('data', 'data1')
p.setAttribute('style', 'font-size: "12px"')
DOM结构操作
// 新增/插入节点
const div1 = document.getElementById('div1');
// 添加新节点
const p1 = document.createElement('p');
p1.innertHTMl = '新增子元素';
div1.appendChild(p1); // 插入新节点元素
// 获取父元素
console.log(p.parentNode)
// 获取子元素
console.log(div1.childNodes)
// 过滤p标签
const pNodes =Array.prototype.slice.call(div1.childNodes)
.filter(child => {
if(child.nodeType == 1 ) {
return true;
}
return false;
})
// 删除节点
div1.removeChild(p1);
DOM性能
- dom操作非常昂贵,避免频繁的dom操作
- 对dom查询作缓存
// 不做缓存
for(var i = 0; i<document.getElementByTagName('p').length; i++) {
// 每次循环都会计算length,频繁进行dom查询
}
// 缓存dom查询结果
const pList = document.getElementByTagName('p');
const length = pList.length;
for(var i = 0; i<length; i++) {
// 缓存length,只进行一次dom查询
}
- 频繁操作改为一次性操作
2.BOM(browser object model)
navigator
// navigator
const ua = navigator.userAgent;
const isChrome = ua.indexOf('chrome');
console.log(isChrome)
// screen
console.log(screen.width)
console.log(screen.height)
screen location
// 获取网址
location.href // https://baidu.com/try/try.php?name=sdfds#PART2
// 获取协议
location.protocl // https
// 获取主机名称和端口号
location.host // baidu.com
// url上获取参数
location.search // ?name=sdfdsf
// URL 的锚部分
location.hash. // #PART2
// URL 的路径部分
location.pathname // /try/try.php
//
history
// 回退
history.back()
// 前进
history,forward()
3.事件绑定
事件绑定
// 通用的事件绑定函数
function bindEvnt(elem, type, fn) {
elem.addEventListener(type,fn)
}
const btn1 = document.getElementByIBd('btn1');
bindEvent(btn1, 'click', function(event) {
console.log(event.target) // 获取触发元素
event.preventDefault(); // 阻止默认行为
});
事件冒泡
const btn1 = document.getElementByIBd('btn1');
bindEvent(btn1, 'click', function(event) {
console.log(event.target) // 获取触发元素
event.stopPropagation(); // 阻止事件冒泡
});
事件代理
- 基于事件冒泡机制,将事件绑定到父元素上
- 代码简介
- 减少浏览器内存占用
- 但是,不要滥用
const btn1 = document.getElementByIBd('btn1');
bindEvent(btn1, 'click', function(event) {
event.preventDefault();
const target = e.target;
if(target.nodeName === 'A') {
alert(target.innerHTML)
}
});
4.ajax
XMLHttpRequest
const xhr = XMLHttpRequest();
xhr.open('GET','/api',true); // true 异步请求,false 同步请求
xhr.onreadystatechange = function() {
// 这里的函数异步执行,可参考之前JS基础中的函数模块
if(xhr.readyState == 4) {
if(xhr.status === 200) {
alert(xhr.responseTest);
} else {
console.log('其他情况')
}
}
}
xhr.send(null);
const xhr = XMLHttpRequest();
xhr.open('POST','/api',true);
xhr.onreadystatechange = function() {
// 这里的函数异步执行,可参考之前JS基础中的函数模块
if(xhr.readyState == 4) {
if(xhr.status === 200) {
alert(xhr.responseTest);
} else {
console.log('其他情况')
}
}
}
const postData = {
userName: 'zhangsan',
password: 'xxx'
};
xhr.send(postData)
状态码 xhr.readyState: (存有 XMLHttpRequest 的状态。从 0 到 4 发生变化)
- 0 - 未初始化,还没有调用send()方法
- 1 - 载入,已调用send()方法,正在发送请求
- 2 - 载入完成,send()方法执行完成,已经接收到去全部响应内容
- 3 - 交互,正在解析响应内容
- 4 - 完成,响应内容解析完成,可以在客户端调用
xhr.state:
- 2xx - 表示成功处理请求,如200
- 3xx - 需要重定向,浏览器直接跳转,如301(永久重定向) 302(临时重定向)304(资源未改变)
- 4xx - 客户端请求错误,如403 404
- 5xx - 服务器端错误
跨域:同源策略
- 同源策略 ajax请求时,浏览器要求当前页面和server必须同源(域名,协议,端口号三者必须一致)安全。
如:前端:http://a.com:8080 server: https//b.com/api/xx
加载图片css js可无视同源策略:
<img src="跨域的图片地址" /> // 图片可以服务器端做访问现在
<link href="跨域的css地址" />
<script src="跨域的js地址" > </script>
利用加载图片css js可无视同源策略:
- img可用于统计打点,可使用第三方统计服务器
- link可以使用cdn,cdn一般都是外域
- script 可以实现jsonp
- 什么是跨域
- 所有的跨域,必须经过server端允许和配合
- 未经server端允许就实现跨域,说明浏览器有漏洞,危险信号
- JSONP
- CORS(服务器支持) 跨域的解决方案
- JSONP
- script可绕过跨域限制
- 服务器可以任意拼接数据返回
- 所以script 就可以获得跨域的数据,只要服务端愿意返回
abc:[12,2]
window.abc = function(data) {
console.log(data)
}
<script src="https://xxxxx?callback=abc"></script>
- CORS(服务器支持)
// 第二个参数填写允许跨域的域名称,不建议直接写“*”
response.setHeader('Access-Control-Allow-Origin','https://locakhost:9090');
response.setHeader('Access-Control-Allow-Headers','X-Requested-With');
response.setHeader('Access-Control-Allow-methods','PUT,POST,GET,DELETE,OPTIONS');
// 接受跨域的cookie
response.setHeader('Access-Control-Allow-Credentials','true')
- 常见的请求库
- jquery 的ajax
- fetch api
- axios
5.存储
- cookie
- 本身基于浏览器和server通讯,
- 被“借用”到本地存储来,
- 可以通过document.cookie='a=100,b=366'修改
- 每次修改,是追加和覆盖的过程 cookie缺点:
- 存储大小,最大只有4kb
- http请求时需要发送到服务器端,增加请求数据量
- 只能用document.cookie = '....'来修改,🇹太过简陋 localStorage和sessionStorage:
- HTML5专门为存储而设计,最大可存5M
- API简单易用 setItem getItem
- 不会随着http请求被发送出去
- locationStorage数据会永久存储,除非代码或手动删除
- sessionStorage数据只会存在当前会话,浏览器关闭则清空
location.setItem('a',200);
location.getItem('a);
HTTP面试题
1. http常用的状态码有哪些?
状态码分类:
- 1xx 服务器收到请求
- 2xx 请求成功,如200
- 3xx 重定向,如302
- 4xx 客户端错误,404
- 5xx 服务端错误,50 常见状态码:
- 200 成功
- 301 永久重定向(配合location,浏览器自动处理)
- 302 临时重定向(配合location,浏览器自动处理)
- 304 资源未被修改
- 404 资源未找到
- 403 没有权限
- 500 服务器错误
- 504 网关超时
302状态配合location重定向
关于协议和规范:
- 就是一个约定
- 要求大家都跟着执行 不要违反规定,例如IE浏览器
2. http常用的methods有哪些?
传统的methods:
- get获取服务器的数据
- post向服务器提交数据
- 简单的页面功能,就这两操作 现在的methods:
- get获取数据
- post新建数据
- patch/put更新数据
- delete删除数据
3. 什么是Restful Api?
- 一种新的API设计方法(早已推广使用)
- 传统API设计:把每个url当做一个功能
- Restful API设计:把每个url当做一个唯一的资源 如何设计成一个资源?
- 尽量不用url参数 传统API设计: /api/list?pageIndex=2
RestFul API设计: /api/list/2
- 用method表示操作类型 传统的method操作:
post 请求 /api/create-blog
patch 请求 /api/update-blog?id=100
get 请求 /api/get-blog?id=100
RestFul的method操作:
post 请求 /api/blog
patch 请求 /api/blog/100
get 请求 /api/create/100
4. http常用的headers有哪些?
常见的Request Headers
- Accept浏览器可接收的数据格式
- Accept-Encoding浏览器可接收的压缩算法,如gzip
- Accept-Language 浏览器可接收的语言,如zh-CN
- Connection: keep-alive 一次TCP连接重复使用
- cookie(同域请求浏览器会自动携带cookie)
- Host (请求的域名是什么)
- User-Agent(简称UA)浏览器信息
- Content-type发送数据的格式,如application/json 常见的Response Headers
- Content-type发送数据的格式,如application/json
- Content-length 返回数据的大小,多少字节
- Content-Encoding返回数据的压缩算法,如gzip
- set-Cookie,修改客户端cookie
自定义header
缓存相关的headers
- Cache-Control
max-age最大过期时间
no-cache不缓存
no-store不用强制缓存,也不用服务端缓存,直接返回数据 private允许最终用户缓存,电脑 手机 public允许中间代理,路由缓存 - Expires
同在response headers中
同为控制缓存过期
已被cache-control代替 - Last-Modified If-Modified-Since
- Etag If-None-Match
5. 描述一下http缓存机制(重要)?
1. 关于缓存的介绍
- 什么是缓存? 没有必要重新获取的,不重新获取,暂存一份,不浪费资源
- 为什么要缓存? 通过缓存,减少网络请求的体积和数量。
- 那些资源可以被缓存?如静态资源 img js css
2. http缓存策略(强缓存 + 协商缓存)
webpack打包,打包文件生成hash值,当文件修改生成新的hash时,浏览器缓存会更新
强缓存
- 浏览器第一次访问,请求资源时,服务端判定那些资源可以被缓存,会加上cache-Control
- 第二次访问浏览器,如果本地有缓存,则直接返回
本地缓存
3.第三次访问,如果缓存失效,会再次发起http请求
协商缓存
- 服务器端缓存策略
- 服务器判断客户端资源,是否和服务端资源一样
- 一致则返回302,否则返回200和最新的资源
资源标识有哪些?
- 在Response Headers中,有两种
- Last-Modified资源的最后修改时间
- Etag资源的唯一表示(一个字符串,类似人类的指纹)
last-Modified 和Etag
- 会优先使用Etag
- Last-Modified只能精确到秒级
- 如果资源被重复生成,而内容不变,则Etag更精确
3. 刷新操作方式,对缓存的影响:
- 正常操作:地址栏输入url,跳转连接,前进后退等 强制缓存有效,协商缓存有效
- 手动刷新:F5, 点击刷新按钮,右击菜单刷新 强制缓存失效,协商缓存有效
- 强制刷新: ctrl + f5 强制缓存失效,协商缓存失效
开发环境
git
调试工具
抓包
- 移动端h5页,查看网络请求,需要用抓包工具
- windows一般用fiddler
- max os 一般用charles 抓包过程:
- 手机和电脑必须同一个局域网
- 将手机代理到电脑上
- 手机浏览网页,即可抓包
抓包内容: - 查看网络请求
- 网址代理
- https
webpack babel
- ES6 模块化,浏览器暂不支持
- ES6 语法,浏览器并不完全支持
- 压缩代码,整合代码,以让网页加载更快
初始化环境: npm init -y
安装webpack : npm i webpack webpack-cli
linux常用命令
运行环境
运行环境即浏览器(server端有nodeJs)
下载网页代码,渲染出页面,期间会执行若干JS
保证代码在浏览器中:稳定且高效
网页的加载过程
题目1:从输入url到渲染出页面的整个过程?
- 下载资源;各个资源类型,下载过程
- 渲染页面:结合html css js 图片等
题目2:window.onload 和DOMContentLoaded的区别?
- window.onload 资源全部加载完成才执行,包括图片
- DOMContentLoaded ODM渲染完成即可,图片可能尚未下载
知识点:
1.加载资源的形式
- html代码
- 媒体文件,图片视频等
- JavaScript css
2.加载资源过程
- DNS解析:域名 -> IP地址
- 浏览器根据IP地址向服务器发起http请求
- 服务器处理http请求,并返回给浏览器
3.渲染页面的过程
- 根据HTML代码生成DOM Tree
- 根据css代码生成CSSOM
- 将DOM Tree 和CSSOM整合形成Render Tree
- 根据Render Tree 渲染页面
- 遇到Script 则暂停渲染,优先加载并执行JS代码,完成再继续
- 直至把Render Tree渲染完成 网页加载监听:
window.onload 和 DOMContentLoaded
window.addEventListenter('load',function() {
// 页面全部资源加载完成才会执行,包括图片,视频等
})
window.addEventListenter('DOMContentLoaded',function() {
// DOM 渲染完即可执行,此时图片,视频还可能没有加载完
})
性能优化:
- 手写防抖,节流
- 多使用内存,缓存或其他方法
- 减少CUP计算量,减少网络加载耗时
- 适用于所有编程的性能优化-----空间换时间
1.让加载更快
- 减少资源体积:压缩代码
- 减少访问次数:合并代码,SSR服务器渲染,缓存
- 使用更快的网络: CDN
2.让渲染更快
- css放在head,js放在body最下面
- 尽早开始执行js,用DOMContentLoaded触发
- 懒加载(图片懒加载,上滑加载更多)
- 对DOMchaxun进行缓存
- 频繁DOM操作,合并到一起插入DOM结构
- 节流throttle 防抖 debounce
节流防抖
<input id="input1" />
const input1 = document.getElementById('input1');
let timer;
input1.addEventListener('keyup',function() {
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
console.log(input1.value)
// 清空定时器
timer = null;
},500)
})
<input id="input1" />
const input1 = document.getElementById('input1');
/**
* @desc 函数防抖
* @param func 回调函数
* @param wait 延迟执行毫秒数
*/
function debounce(func, wait) {
let timeout;
return function () {
let context = this;
let args = arguments;
//如果timeout存在,先清除定时器(其实就是每次执行都清除定时器,判断是否存在只是为了严谨)
timeout?clearTimeout(timeout):null;
timeout = setTimeout(() => {
//是为了让 debounce 函数最终返回的函数 this 指向不变以及依旧能接受到 e 参数。
//不使用apply绑定this func执行时this是window
func.apply(context, args)
}, wait);
}
}
document.body.onclick= debounce(function () { console.log(this) },1000)
input1.addEventListener('keyup',debounce(() => {
console.log(input1.value)
}, 9000))
const div1 = document.getElementById('div1');
function throttle(fn, delay = 500) {
let timer;
return function() {
if(timer) {
return;
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null
},delay)
}
}
div1.addEventListener('click', throttle(() => {
console.log(1)
}, 5000));
安全
常见web前端攻击方式有哪些?
- XSS 跨站请求攻击
- XSRF 跨站请求伪造