前言
最近开始找工作啦,为了应付面试整理了一些常见的面试题,祝大家都能拿到Offer呀!
前端需要掌握的一些基础知识:
- HTML5,CSS,JavaScript
- webpack
- React/Vue/AngularJS
- 计算机网络基础
- Node.js
- 其他(操作系统、数据库、面向对象、数据结构…)
这些题可能有些答案不完整或错误,欢迎补充,一起进步!
HTML+CSS
- CSS实现元素水平垂直居中方法
方法一:(要求元素定宽高)
设置父元素为相对定位,给子元素设置绝对定位
left: 50%; top: 50%; 使元素的左上角移到父元素中间
margin-left: -元素宽度的一半px; margin-top: -元素高度的一半px;使元素往左上移一半元素的宽高。
#father {
width: 500px;
height: 300px;
position: relative;
}
#son {
width: 100px;
height: 100px;
position: absolute;
left: 50%;
top: 50%;
margin-left: -50px;
margin-top: -50px;
}
方法二:
设置父元素为相对定位,给子元素设置绝对定位
left: 50%; top: 50%;
transform: translateX(-50%) translateY(-50%);
原理与方法一相同,但是不定宽高
#father {
width: 500px;
height: 300px;
position: relative;
}
#son {
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
}
方法三:
设置父元素为相对定位,给子元素设置绝对定位
top: 0; right: 0; bottom: 0; left: 0;
margin: auto;
#father {
width: 500px;
height: 300px;
position: relative;
}
#son {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
方法四:
采用弹性盒子
#father {
width: 500px;
height: 300px;
display: flex;
justify-content: center;
align-items: center;
}
#son {}
方法五:
使用table-cell
#father {
width: 500px;
height: 300px;
display: table-cell;
text-align:center;
vertical-align:middle;
}
#son {}
- 为什么需要清除浮动,清除浮动的方法
为什么:
(1)父元素的高度无法被撑开,影响与父元素同级的元素
(2)与浮动元素同级的非浮动元素会跟随其后
(3)若浮动元素非第一个元素,那么该元素之前的元素也要浮动,否则会影响
清除方法:
(1)利用clear:both
.son{ clear:both; }
(2)在最后一个浮动标签后额外添加标签设置clear:both
<div class=”parent”>
浮动元素...
<div style=”clear:both;” ></div>
</div>
(3)父元素上使用after伪类
#parent:after{
content:’’;
clear:both;
height:0;
display:block;
}
(4)利用overflow
#parent{
overflow:auto;
display:inline-block;
}
- CSS中transition和animate有何区别,animate如何停留在最后一帧
transition一般用于做过渡,没有时间轴的概念,没中间状态,只触发一次;
animate是做动效的,有时间轴概念,帧是可控的,有中间状态,可以重复触发;
停留:
animation-fill-mode:forwards(最后一帧) | backwards(首帧)| both(轮流);
-
CSS伪元素
-
BFC
BFC一个特殊的块,内部有自己的布局方式,不受外边元素的影响,不与float重叠
实现:
(1)float不为none
(2)position:absolute | fixed
(3)display:inline-block | table-cell | flex | inline-flex | table-caption
(4)overflow:auto | hidden
用途:
①自适应布局
②清除浮动
③解决垂直边距重叠
- 什么是边距重叠
如果两个box都设置了边距,那么在垂直方向上边距会重叠。
解决办法就是生成BFC
(1)父子关系的边距重叠:当子元素设置了margin,在没有把父元素变为BFC的情况下,父元素也会产生外边距,给父元素添加overflow:hidden,隐藏溢出部分,就变为BFC,不会产生外边距,但是父元素的高会变化。
(2)同级关系的重叠:以绝对值大的那个为最终结果显示在页面上。
- 双栏布局
左边栏固定,右边栏自适应
<section id="layout">
<style media="screen">
#layout{
background: red;
}
#layout .left{
float: left;
width: 100px;
height: 100px;
background: green;
}
#layout .right{
height: 110;
background: blue;
overflow: auto;
}
</style>
<div class="left"></div>
<div class="right"></div>
</section>
- 三栏布局
方法一:
圣杯布局
遵循两侧宽度固定,中间宽度自适应,中间结构在DOM上优先,先渲染
<html>
<head>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="header"></div>
<div id="container">
<div id="center" class="column">center</div>
<div id="left" class="column">left</div>
<div id="right" class="column">right</div>
</div>
<div id="footer"></div>
</body>
</html>
body {
min-width: 550px;
}
#container{
padding-left:200px;
padding-right:150px;
background: red;
}
#container .column {
float: left;
position: relative;
}
/* 注意center写在最前面,优先渲染 */
#center {
width: 100%;
background: green;
}
#left {
width: 200px;
margin-left: -100%;
right: 200px;
background: blue;
}
#right {
width: 150px;
margin-right: -100%;
background: lightgray;
}
#footer {
clear: both;
background: orange;
}
方法二:
双飞翼布局
双飞翼布局的DOM结构与圣杯布局的区别是用container仅包裹住center,另外将.column类从center移至container上。
<html>
<head>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="header"></div>
<div id="container" class="column">
<div id="center">center</div>
</div>
<div id="left" class="column">left</div>
<div id="right" class="column">right</div>
<div id="footer"></div>
</body>
</html>
body {
min-width: 500px;
}
#container{
width: 100%;
background: red;
}
.column {
float: left;
}
/* 注意center写在最前面,优先渲染 */
#center {
margin-left: 200px;
margin-right: 150px;
background: green;
}
#left {
width: 200px;
margin-left: -100%;
background: blue;
}
#right {
width: 150px;
margin-right: -100%;
background: lightgray;
}
#footer {
clear: both;
}
方法三:
弹性布局
<html>
<head>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="container">
<div id="left"></div>
<div id="center"></div>
<div id="right"></div>
</div>
</body>
</html>
#container {
display: inline-flex;
flex-direction: row;
width: 100%;
background: red;
}
#left {
width: 200px;
background: blue;
}
#center {
flex: 1;
width: 100%;
background: green;
}
#right {
height: 300px;
width: 150px;
background: lightgray;
}
- CSS选择器权重
! important >行内样式>ID选择器>类选择器>标签选择器>通配符>继承>浏览器默认属性
- display属性
flex-direction属性:决定主轴的方向(即项目的排列方向)
flex-wrap属性:定义换行情况
justify-content属性:定义项目在主轴上的对齐方式。
align-items属性:定义在交叉轴上的对齐方式
align-content属性:定义多根轴线的对齐方式
- position属性
absolute:
生成绝对定位的元素,相对于 非static 定位的第一个父元素进行定位。
Fixed:
生成绝对定位的元素,相对于浏览器窗口进行定位。
relative:
生成相对定位的元素,相对于其正常位置进行定位。因此,left:20会向元素的 LEFT 位置添加 20 像素。
static:
默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明)。
inherit:
规定应该从父元素继承 position 属性的值。 元素的位置通过 “left”, “top”, “right” 以及"bottom" 属性进行规定。
- float:left和position:absolute的区别
使用float脱离文档流时,其他元素会无视它,但是盒内元素依然会为这个元素让位,环绕在周围;
而position脱离文档流就是被完全无视。
- 使用css实现一个三角形/自适应正方形
(1)三角形
当height:0,width:0时,边框呈现如下:
只要隐藏掉不需要的三角形,保留需要的就行,不需要的设置为透明transparent;
如果浏览器不支持透明,可设置border-style:dashed(点)
.triangle{
height: 0;
width: 0;
border: 10px solid transparent;
border-bottom-color: red;//下边框不透明,其余透明
}
(2)自适应正方形
方法一:
利用vw
.square{
width:50vw; /***(浏览器/屏蔽)视口宽度的50%***/
height:50vw;
background:red;
}
方法二:
利用padding-bottom
.square{
width:50%;
height:0;/***这个height设置为0,如果元素有内容,则溢出到padding-bottom上面***/
/***如果元素这时候未设置height(:0),则默认height:auto;如果有内容(子元素)的话,将撑开高度height(不为0的值),则垂直>水平背景了***/
padding-bottom:50%;/***这里不能用top,因为用top,且高度为0,如果有内容,则溢出到容器外了***/
background:red; /***背景颜色默认是从元素的border开始渲染绘制的***/
}
方法三:
利用padding-top/bottom、position:absolute结合;
.container{
width:70%;
background:yellow;
position:relative;
}
.square{
width:50%;
height:0;
padding-top:50%; /***构造正方形,并撑起/增加父元素的高度***/
}
.content{
position:absolute; /***绝对定位***/
left:0;
top:0;
background:blue;
width:50%; /***这个宽度是相对于.container的宽度;和.square大小一致***/
、 height:100%; /**这个高度也是相对于.container的高度,.container的高度是有.square撑起的**/
}
方法四:
利用伪元素(margin)padding-top/bottom撑开容器(父元素)
.container{
width:70%;
background:yellow;
position:relative;
}
.square::after{
content:""; /***利于伪元素撑起.container的高度****/
display:block;
padding-top:50%;/**padding-bottom也是可以的**/
width:50%;
}
.content{
width:50%; /***相对于.container元素***/
height:100%; /***同样相对于.container元素***/
position:absolute;
background:blue;
}
- 行内元素、块级元素
块级元素:
div , p , form, ul, li , ol, dl, address, fieldset, hr, menu, table
行内元素:
span, strong, em, br, img , input, label, select, textarea, cite
- html语义化
就是用具有具体语义的标签来书写HTML,因为HTML本身就是标记语言。
不仅对自己来说,容易阅读,书写。别人看你的代码和结构也容易理解,甚至对一些不是做网页开发的人来说,也容易阅读。
那么,我们以后再开发的过程中,一定要注意了,尽量使用官方的有语义的标签,不要再使用一堆无意义的标签去堆你的结构。
- rem,px,em的区别
px
相对于显示器屏幕分辨率而言的,无法适应页面大小而改变
em
相对于父元素的大小,相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸。
rem
相对的只是HTML根元素,只需要在根元素设置一个参考值
需要适配各种移动设备,使用rem,例如只需要适配iPhone和iPad等分辨率差别比较挺大的设备。
- doctype的作用
告诉浏览器使用哪种规范(HTML/XHTML)解析页面
- 隐藏页面中某个元素的方法
(1)完全隐藏
display: hidden | none;
(2)视觉上隐藏(共7种)
//1.将元素移出可视范围
position:absolute;
left:-99999px;
//或
margin-left:-99999px;
height:0;
//2.利用transform,平移、缩放、旋转
transform:scale(0);
height:0;
//或
transform:translateX(-9999999px);
height:0;
//或
transform:rotateY(90deg);
//3.设置其大小为0
height:0;
width:0;
overflow:hidden;
font-size:0;
//4.设置透明度为0
opacity:0;
//5.visibility属性
visibility:hidden;
//6.层级覆盖:
positive:relative;
z-index:-999;
//7.剪裁:
clip-path:polygon(0 0,0 0.0 0,0 0);
(3)语义上隐藏
<div aria-hidden=”true”></div>
- 盒模型
标准(W3C)盒模型为content-box
标准盒模型具有content,padding,border,margin
盒子尺寸:content+padding+border
设置width/height属性指的是content部分的宽/高
IE盒模型为border-box
盒子尺寸:content
设置width/height属性指的是border + padding + content
通过设置box-sizing:content-box | border-box可实现盒模型切换
获得盒模型宽高方法:
①window.getComputedStyle(dom).width/height
②dom.getBoundingClientRect().width/height
弹性盒子,通过设置display:flex|inline-flex实现
JavaScript、ES6
- 定义函数的方法,各有什么区别
(1)function 语句形式
function test1() {
alert("我是test1");
}
(2)函数直接量形式
var test2 = function() {
alert("我是test2");
};
(3)通过Function构造函数形式定义函数
var test3 = new Function("a", "b", "return a+b;");
区别:
①function语句式、函数直接量只会被解释一次,是静态的,有函数的作用域,Function构造函数式每次执行都动态new一次,说明有顶级作用域。
②解析机制不同,function语句式 相当于全局变量,会被优先解析,其他2种执行到哪解释到哪,顺序执行。
2. 判断变量的类型
(1)typeof
不能判断变量具体的数据类型,如数组、正则、日期、对象,因为都会返回object,但是可以判断function,判断null时返回的是object,判断NaN返回的是Number
(2)instanceof
用于检测这个变量是否是某种类型,返回布尔值,不能直接看出是什么类型
(3)constructor
指向构造函数,可使用num.constructor==Number判断一个变量是不是Number类型
(4)Object.prototype.toString.call(value)
得到对象的构造函数名,缺点是用户自定义的类型只会返回object
- 继承的方式,手写继承
父类例子:
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
(1)原型链继承:
将父类的实例作为子类的原型
缺点:
要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
无法实现多继承
来自原型对象的所有属性被所有实例共享
创建子类实例时,无法向父类构造函数传参
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
(2)构造继承:
使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
缺点:
实例并不是父类的实例,只是子类的实例
只能继承父类的实例属性和方法,不能继承原型属性/方法
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
(3)组合继承:
通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用(推荐!)即原型链继承+构造继承
缺点:
调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
// 组合继承也是需要修复构造函数指向的。
Cat.prototype.constructor = Cat;
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
(4)寄生组合继承:
通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点,但是实现较为复杂
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 创建一个没有实例方法的类
var Super = function(){};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
})();
// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.sleep());console.log(cat instanceof Animal); // trueconsole.log(cat instanceof Cat); //true
Cat.prototype.constructor = Cat; // 需要修复下构造函数
- call、apply、bind的区别
三者都是用于改变函数this指向,第一个参数都是this所要指向的对象。
call和apply的区别仅是第二个参数不同。
call的第二个参数是目标函数的第一个参数,第三个参数是目标函数的第二个参数,以此类推;
而apply的第二个参数是一个目标函数参数的数组。
bind是返回对应函数,便于稍后调用;而call与apply是立即调用。
- js连续赋值
var a = {n: 1};
ar b = a;
a.x = a = {n: 2};
console.log(a); //{n:2}
console.log(b); //{n:1}
console.log(a.x); //undefined
console.log(b.x);//{n:2}
- 防抖和节流,代码实现
(1)防抖:
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。
思路:每次触发事件时都取消之前的延时调用方法
function debounce(func, wait) {
let timeout = null;//存放定时器的返回值
return function () {
if (timeout) clearTimeout(timeout);//清理掉前一个
//重新创建一个新的延迟
timeout = setTimeout(() => {
func.apply(this.arguments);
}, wait);
}}
(2)节流:
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
思路:每次触发事件时都判断当前是否有等待执行的延时函数
function throttle(func, wait) {
let canRun = true;
return function() {
if (!canRun) //如果不能执行,直接返回
return;
canRun = false;//即将开始执行下一个,将canRun设为false,避免被打扰
setTimeout(() => {
func.apply(this.arguments);
canRun = true;//执行完毕,设为true,代表又可以执行下一个
}, wait);
}
}
- 异步加载js
(1)<script>标签内增加async或defer
<script src = “…js” async>
async和defer的区别:
defer要等到整个页面在内存中正常渲染结束,在window.onload之前执行,如果有多个defer脚本,会按照他们在页面出现的顺序加载
async一旦下载完,渲染引擎就会中断渲染,执行这个脚本后再渲染,多个async脚本不能保证加载顺序
(2)动态创建<script>标签
动态创建的script标签,设置src并不会立即开始下载,而是要添加到文档中,js文件才会开始下载
let script = document.creatElement(‘script’);
script.src = ‘XXX.js’;
document.body.append(script);//此时才开始下载
(3)采用XMLHttpRequest
let xhr = new XMLHttpRequest();
xhr.open(“get”, “js/XXX.js”,true);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
eval(xhr.responseText);
}
}
- Babel原理
就是JavaScript转译器,将高版本js转换成低版本,使浏览器能兼容。
过程:
(1)通过词法分析和语法分析,将代码源代码转换成抽象语法树
(2)遍历抽象语法树,通过插件babel-traverse,生成新的浏览器需要的抽象语法树
(3)再通过babel-generator将抽象语法树转换成目标代码。
- js中的强制类型转换指?
两种不同的内置类型的转换。
Boolean(value)——把给定的值转换成Boolean型;
Number(value)——把给定的值转换成数字(可以是整数或浮点数);
String(value)——把给定的值转换成字符串。
- set/map/weakset/weakmap的区别
set:
成员唯一,无序且不重复,只有键值没有键名,可以遍历
weakset:
成员是对象,成员都是弱引用,可被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄露,不能遍历
map:
本质上是键值对的集合,可以遍历
weakmap:
键名只能是对象,键名也是弱引用,可被垃圾回收,不能遍历
- 深浅拷贝
浅拷贝:
是会将对象的每个属性依次进行复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时,当前值也会改变。
深拷贝:
复制变量值,对于非基本类型的变量,递归至基本类型后再复制,深拷贝后的对象与原来的对象完全隔离,互不影响。
//深拷贝实现
function deepClone(obj,hash = new WeakMap()){
if(obj instanceof RegExp) return new RegExp(obj);
if(obj instanceof Date) return new Date(obj);
if(obj === null || typeof obj !== 'object'){
//不是复杂数据类型直接返回
return obj;
}
if(hash.has(obj)){
return hash.get(obj);
}
let t = new obj.constructor();
hash.set(obj,t);
for(let key in obj){
if(obj.hasOwnProperty(key)){
t[key] = deepClone(obj[key],hash);
}
}
return t;
}
- 数组去重
(1) 利用set
var uniqueArr = Array.from(new Set(arr));
//或
var uniqueArr = [...new Set(arr)];
(2)利用双重for循环,判断如果有相等,则删去一个
function unique(arr){
for(var i = 0;i<arr.length;i++){
for(var j=i+1;j<arr.length;j++){
if(arr[i] == arr[j]){
arr.splice(j,1);
j--;
}
}
}
}
(3)利用indexOf():新建一个空数组,不重复则加入
function unique(arr){
var arr2 = [];
for(var i=0;i<arr.length;i++){
if(arr2.indexOf(arr[i]) === -1){
arr2.push(arr[i]);
}
}
return arr2;
}
(4)利用sort,根据排序后的结果进行遍历,相邻元素比对
function unique(arr){
arr = arr.sort();
var arr2 = [arr[0]];
for(var i=0;i<arr.length;i++){
if(arr[i] !== arr[i-1]){
arr2.push(arr[i]);
}
}
return arr2;
}
(5)利用includes,也是新建一个空数组,判断是否有该值,没有则加入,想法和IndexOf一样
(6)利用filter:判断当前元素在数组中的第一个索引值是否与当前元素索引值相等
function unique(arr){
return arr.filter((item,index,arr)=>arr.indexOf(item,0) === index);
}
(7)利用map:map中不会出现相同的key值
function unique(arr){
let map = new Map();
let arr2 = new Array();
for(let i=0;i<arr.length;i++){
if(map.has(arr[i])){
map.ser(arr[i],true);
}
else{
map.set(arr[i],false);
arr2.push(arr[i]);
}
}
return arr2;
}
- 数组乱序
(1)利用随机数产生随机索引
(2)洗牌算法:
先从数组末尾开始,选取最后一个元素,与数组中随机一个位置的元素交换位置,然后在已经排好的最后一个元素以外的位置中,随机产生一个位置,让该位置元素与倒数第二个元素进行交换。
Array.prototype.shuffle = function() {
let m = this.length, i;
while (m) {
i = (Math.random() * m--) >>> 0;
[this[m], this[i]] = [this[i], this[m]]
}
return this;
}
- promise/promise.all/promise.race
promise用于异步操作,避免层层嵌套的回调地狱情况,可以让异步调用更优雅,使用promise无论成功或失败都有答复。
var promise = new Promise(function(resolve,reject){
if(...)
resolve(value);
if(...)
reject(error);
});
promise.then(function(value){
//success to do...
}).catch(function(error){
//failed to do...
});
Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败 promise 的结果。promise.all()适合用于所有的结果都完成了才去执行then()成功的操作,相当于“且”,promise.race只要完成一个就执行then()回调,相当于“或”
- 将一个同步callback包装成promise形式
var promisify = function promisify(fn, receiver) {
return function() {
for(var _len = argument.length, args = Array(_len), _key = 0; _key<_len; _key++) {
args[_key] = arguments[_key];
}
return new Promise(function (resolve, reject) {
fn.apply(receiver, [].concat(args,[function(err, res){
return err ? reject(err) : resolve(res);
}]));
});
};
};
- 实现instanceof
function instance_of(L,R){
const baseType = ['string','number','boolean','undefined','symbol'];
if(baseType.includes(typeof L)){return false;}
let RP = R.prototype;
L = L.__proto__;
while(true){
if(L === null){
return false;
}
if(L === RP){
return true;
}
L = L.__proto__;
}
}
- 实现new
(1)创建一个空对象,构造函数中的this指向这个空对象
(2)这个对象被执行原型连接
(3)执行构造函数方法,属性和方法被添加到this引用的对象中
(4)如果构造函数中没有返回其他对象,那么返回this,即创建的这个新对象,否则返回构造函数中返回的对象
function _new(){
let target = {};
let [constructor,...args] = [...arguments];
target.__proto__ = constructor.prototype;
let result = constructor.apply(target,args);
if(result && (typeof (result) == "object" || typeof (result) == "function")){
return result;
}
return target;
}
- let/const/var的区别
(1)变量提升
(2)暂时性死区
(3)相同作用域重复声明
(4)块作用域限制
const声明时必须赋值,是一个只读常量。不可改
- 箭头函数与普通函数的区别
(1)函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象
(2)不可以使用arguments对象,该对象在函数体内不存在,可以使用rest代替
(3)不可以使用yield命令,不能用作Generator函数
(4)不可以使用new命令,因为没有自己的this,不能用作构造函数
- promise/async await/Generator的区别
async await函数语法与generator函数相似,只要把 * 号换成async,然后把yield换成await就可以
async await返回值是一个promise,Generator是一个迭代器
- 闭包
可访问上一层函数作用域里变量的函数,可通过闭包来模拟私有方法,但是不当使用可能会造成内存泄露
- 原型链
每一个构造函数都设置一个prototype属性,这个属性就指向原型对象。其实原型对象就只是个普通对象,里面存放着所有实例对象需要共享的属性和方法,我们把需要共享的放到原型对象里,把那些不需要共享的属性和方法存在在构造函数里,修改prototype属性会影响它的所有实例里的值。
每当代码读取某个对象的某个属性的时候,都会执行一次搜索。首先从对象实例本身开始,如果在实例中找到了该属性,则返回该属性的值,如果没有找到,则顺着原型链指针向上,到原型对象中去找,如果如果找到就返回该属性值。
这里要提一点,如果为对象实例添加了一个属性与原型中同名,则该属性会屏蔽掉原型中的同名属性,不会去修改它!使用delete可以删除实例中的属性
_proto_:事实上就是原型链指针!,实例对象通过__proto__访问原型对象
prototype:上面说到这个是指向原型对象的,只有构造函数才有prototype
constructor:每一个原型对象都包含一个指向构造函数的指针,就是constructor
function foo(){
}
foo.prototype.sex = “female”;
var foooo = new foo();
foooo.proto.sex === “female”;
foo.prototype.constructor // function foo(){}
- 对js单线程的理解
js是一门单线程语言,所谓的异步是用同步方法去模拟的,事件循环是js实现异步的一种方法,也是js的执行机制。
如网页渲染过程就是同步任务,因此建立DOM树比较慢,而加载图片,音乐等占用资源大、耗时久的任务就是异步任务;
同步任务进入主线程,异步任务进入Event Table并注册函数。当异步任务指定的事件完成后(如延时3秒),Event Table会将函数移入Event Queu;只有当主线程内任务执行完毕后才会去Event Queue读取函数,进入主线程执行,因为上述过程会不断重复,因此叫做事件循环。
- 宏任务、微任务
对同步任务和异步任务进行更精细的定义
宏任务:包括整体代码(主线程)script,setTimeout,setInterval
微任务:包括Promise.then,process.nextTick()(类似node.js版的setTimeout)
执行机制:事件循环
执行完一个宏任务,如果当前微任务队列有微任务,那执行所有微任务;然后再执行下一个宏任务(可能该宏任务过程中又有新微任务入队)。
- null和undefined的区别
null表示"没有对象",即该处不应该有值。
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。
- link,js阻塞页面的渲染
link引入外部css时会阻塞页面的渲染,但是不会阻塞DOM的解析
js会先执行js,阻塞了DOM的解析
css解析和js执行是互斥的,但是不管是css阻塞还是js阻塞都不会阻塞浏览器加载外部资源
- 作用域,作用域链
作用域由变量和函数声明位置决定的,作用域链就是从当前作用域开始一层一层向上寻找某个变量,直到找到全局作用域还没找到就宣布放弃。
- this
一般是谁调用this,this就指向谁,除非对this进行绑定,箭头函数没有自己的this,因此继承外层上下文绑定的this
- Object和数组的区别
数组表示有序数据的集合,而对象表示无序数据的集合。如果数据的顺序很重要,就用数组,否则就用对象。
当然,数组和对象的另一个区别是,数组的数据没有”名称”(name),对象的数据有”名称”(name)
webpack
- webpack用过哪些loader和plugin,如何编写
CSS-Loader,Style-Loader,image-webpack-loader
- plugin与loader的区别
Loader:
用于对模块源码的转换,loader描述了webpack如何处理非javascript模块,并且在buld中引入这些依赖。loader可以将文件从不同的语言(如TypeScript)转换为JavaScript,或者将内联图像转换为data URL。比如说:CSS-Loader,Style-Loader等。
Plugin:
目的在于解决loader无法实现的其他事,从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。
- 使用import时,webpack对node_modules里的依赖会做什么
webpack会根据定义的引入方式判断模块类型,再进行相关的编译转化。当使用import引入时,babel会默认把ES6的模块转化为commonjs规范。然后会把node_module里的依赖打包成自执行函数的样子。在模块数组中将模块传入,在函数体中经过一系列操作最终将模块通过module.exports导出。
- Tree-shaking作用
清除无用的代码,减小打包后的体积
- …
React
- React生命周期及其相关钩子函数
Mounting:已插入真实 DOM
Updating:正在被重新渲染
Unmounting:已移出真实 DOM
React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。
componentWillMount()
componentDidMount()
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
componentWillUnmount()
还提供其他特殊状态的处理函数。
shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用
- 虚拟DOM
传统DOM的渲染过程是解析HTML和css构建渲染树,然后进行页面的布局和绘制,任何一点改变都可能造成页面的重绘和回流,频繁的重绘和回流严重影响页面性能。因此抽象出一个虚拟DOM,通过新旧虚拟DOM的比较,尽量批量的一次性的做最少的更新,减少重绘和回流,这样浏览器的刷新次数就少,性能就提高了。
- React diff算法
策略一(tree diff):
同级进行比较,Web UI中DOM节点跨层级的移动操作特别少,可以忽略不计。
(1)React通过updateDepth对Virtual DOM树进行层级控制。
(2)对树分层比较,两棵树只对同一层次节点进行比较。如果该节点不存在时,则该节点及其子节点会被完全删除,不会再进一步比较。
(3)只需遍历一次,就能完成整棵DOM树的比较。
diff只简单考虑同层级的节点位置变换,如果是跨层级的话,只有创建节点和删除节点的操作。因此官方建议不要进行DOM节点跨层级操作,可以通过CSS隐藏、显示节点,而不是真正地移除、添加DOM节点。
策略二(component diff):
拥有相同类的两个组件 生成相似的树形结构, 拥有不同类的两个组件 生成不同的树形结构。
(1)同一类型的两个组件,按原策略(层级比较)继续比较Virtual DOM树即可。
(2)同一类型的两个组件,组件A变化为组件B时,可能Virtual DOM没有任何变化,如果知道这点(变换的过程中,Virtual DOM没有改变),可节省大量计算时间,所以 用户 可以通过 shouldComponentUpdate() 来判断是否需要 判断计算。
(3)不同类型的组件,将一个(将被改变的)组件判断为dirty component(脏组件),从而替换 整个组件的所有节点。
注意:如果组件D和组件G的结构相似,但是 React判断是 不同类型的组件,则不会比较其结构,而是删除组件D及其子节点,创建组件G及其子节点。
策略三(element diff):
对于同一层级的一组子节点,通过唯一key区分,只要key相同,那就是同一个节点,再判断位置索引值,如果索引值变大,就做一个向后移动操作(索引值变小不用移,因为就相当于其他节点变大了),如果发现有key不存在,或者有key多出来了,那就做相应的插入和删除操作。
- react复合组件
由于react组件不支持扩展,因此采用复合组件,将多个组件结合从而进行功能扩展,如果组件中有公用的非UI逻辑,可抽取成JS模块导入使用(即采用组件复用方法),而不是继承。
- react原理,特点,优点,缺点
在Web开发中,我们需要将变化的数据实时反映到UI上,就需要对DOM进行操作,而复杂或频繁的操作DOM,会造成性能瓶颈,为此React引入了虚拟DOM机制,每当数据变化时,React都会重新构建整颗DOM树,然后React将当前整个DOM树和上一次的DOM进行对比,得到DOM结构的区别,然后实际仅更新变化的部分。
优点:
(1)能够实现服务器端的渲染
(2)能够很好和现有代码结合
(5)一切都是component,代码模块化强,代码复用更容易
(6)强调只从this.props和this.state生成HTML,写起来bug比较少
- jsx,如何使浏览器读取jsx
JavaScript XML,JSX是JS的一个语法扩展,在react中能够替代JS,去描述我们的视图,使用JSX可以编写看起来像HTML的内容。
为什么使用JSX:
(1)执行速度更快,直接写JS容易出错
(2)类型安全,由编译器将JSX进行严格的转换,进行一个安全检测,避免出错
(3)开发效率高
JSX和HTML使用上的注意点:
className用于代替class添加CSS类(classJavaScript中的保留关键字)。
JSX中的属性和方法为驼峰写法,onclick将变为onClick。
自闭合标签必须以斜杠结尾-例如<img />
- Render()的目的
render()返回一个react元素,是原生DOM组件的表示,此函数必须保持纯净
- state和props是什么?state和props的区别
虽然都包含影响渲染输出的信息,但是在组件方面的功能不同。
state是组件自己管理数据,控制自己的状态,可变;
props是外部传入的数据参数,不可变;
this.props 表示那些一旦定义,就不再改变的特性,而 this.state 是会随着用户互动而产生变化的特性。
- 在构造函数调用super并将props作为参数传入的作用是什么
在调用super()之前,子类构造函数无法使用this引用,将props参数传递给super()调用,使子类构造函数能够通过this.props获取传入的props值。
- 为什么不直接更新state
直接更新state不会重新渲染组件,需要用setState()来更新state
- 什么是高阶组件HOC
将组件装换成另一个组件,对功能进行提升扩展
- React中key的重要性是什么
key用于识别唯一的虚拟DOM元素
- 为什么使用react
模块化,方便管理和维护,复用性好
但是react本身不适合大型应用
- React组件间通信方式
父组件向子组件通讯:通过传props
子组件向父组件通讯:props+回调
用redux
- 为什么项目要用react-router
React路由有助于向应用程序添加新的屏幕和流,使URL与网页上显示的数据保持同步。负责维护标准化的结构和行为。
- 如何避免组件的重新渲染
React.memo()高阶组件,用于钩子组件
PureComponent()用于类组件
- react性能优化方案
重写shouldComponentUpdate来避免不必要的DOM操作
使用production版本的react.js
使用key来帮助React识别列表中所有子组件的最小变化。
- react组件props变化后会发生什么
会导致state改变,然后重新渲染组件
- Jquery与react设计思想有何不同
jquery开发:
监听事件=》判断当前业务状态=》直接操作dom元素
react开发:
监听事件=》判断当前业务状态=》修改state=》render修改dom元素(最小化修改)
计网
- 跨域是什么,实现方法
跨域由同源策略所导致,即规定不同源的策略无法进行资源交互,只有当协议(如http)、域名(如www.baidu.com)、端口(如8080)三者都一致时才算同源。
默认情况下http采用端口80,https采用端口443
(1)CORS:跨域资源共享
当进行跨域时,资源会发出一个跨域HTTP请求,告诉浏览器Web应用准许访问来自不同源服务器上的指定资源。
const cors = require(“koa-cors”);
app.use(cors());
(2) webpack代理
利用服务端请求不会跨域的特性,让接口和当前站点同域,这样所有的资源及请求都在一个域名下
在webpack中可以配置proxy来快速获得接口代理能力
并且由于现在是同域请求,因此修改前端接口请求方式,改为不带域名的。
const path = require(“path”);
module.exports = {
devServer: {
port:8080,
proxy: {
“/api”: {
target: “http://localhost:8080”
}
}
}
}
(3)Nginx反向代理
server{
listen 80;
server_name local.test;
location /api {
proxy_pass http://localhost:8080;
}
location {
proxy_pass http://localhost:8000;
}
}
(4)JSONP
主要利用了script标签没有跨域限制的特性来完成。仅支持get方法
流程:
1.前端定义解析函数(如jsonpCallback = function(){…})
2.通过params形式包装请求参数,并且声明执行函数(如cb = jsonpCallback)
3.后端获取前端声明的执行函数,并以带上参数并调用执行函数的方式传递给前端。
(5)Websocket
定义了一种API,可在网络浏览器和服务器之间建立“套接字”连接,简单说就是双方存在持久连接,随时可以发送数据,这种方式本质没有使用HTTP,所以也不存在跨域限制。
//前端
let socket = new WebSocket("ws://localhost:8080");
socket.onopen = function(){
socket.send("hello!");
};
socket.onmessage = function(e){
console.log(e.data);
}
//后端
const WebSocket = require("ws");
const server = new WebSocket.Server({port:8080});
server.on("connection",function(socket){
socket.on("message",function(data){
socket.send(data);
});
});
- Http和Https的区别
(1)http(默认80端口)
基于TCP/IP协议的超文本传输协议,采用客户端请求服务端应答的模式,以明文方式发送内容,不进行任何数据加密,因此不适合传输一些敏感信息。
结构:报文首部(起始行+首部字段)+空行(用于区分首部和实体)+报文实体(请求体、响应体)
(2)https(默认443端口)
超文本传输安全协议,可以认为是SSL+HTTP,经由HTTP进行通信,采用SSL对数据包进行加密,达到安全的目的
区别总结:
①HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
②使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。
③HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
④http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
⑤HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。
- Http中get和post的区别
get请求指定的页面信息,并返回实体主体,head类似于get,只不过返回的响应中没有具体内容,post向指定资源提交数据进行处理请求(如提交表单或上传文件),数据被包含在请求体中,post请求可能会导致新的资源建立和已有资源修改。
区别:
(1)get请求长度最多1024kb,post对请求数据没有限制
(2)通过get提交的数据会显示到url上,页面会被浏览器缓存,其他人查看历史记录会看到提交的数据,而post不会,另外get提交数据还可能造成CSRF攻击
(3)get只支持进行url编码,而post支持多种编码
(4)get产生1个tcp数据表,post产生2个
- url长度限制
虽然协议中未明确对url进行长度限制,但在真正实现中,url的长度还是受到限制的,一是服务器端的限制,二就是浏览器端的限制。
nginx服务器默认的限制是4K或者8K,不同浏览器对url的长度限制不同Chrome是8182字符数。
- cookie,session storage,local storage,session区别
(1)生命周期:
cookie在过期时间前有效,session Storage在浏览器关闭前有效,local Storage长期有效
(2)数据存储:
cookie会自动将数据传输到服务器,而其他两个不会
(3)大小:
cookie不超过4k,其他两个能达到5M
session基于cookie来工作,cookie有大小和数量限制,而且cookie太多会导致客户端和服务端传输量增加,因此采用session,只传一个唯一id,每次通过这个id直接在服务器找用户信息。
- 输入URL到按下Enter发生什么
(1)输入地址。
(2)DNS解析。
找浏览器缓存→找系统缓存→本地运营商DNS服务器→根域名服务器→通用顶级域名服务器→Name Server域名服务器→返回IP地址
(3)TCP连接。
三次握手,为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误
(4)发送http请求。
(5)返回http响应。
(6)浏览器解析渲染页面。
一个边解析边渲染的过程。首先浏览器解析HTML文件构建DOM树,然后解析CSS文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。这个过程比较复杂,涉及到两个概念: reflow(回流)和repaint(重绘)。
(7)断开连接。
四次挥手
- HTTP状态码
100(“continue”)客户端继续请求
200(“ok”)请求成功
400(“bad request”)客户端方面问题
500(“Internal Server Error”) 内部服务器错误
404(“Not Found”) 410(“gone”)当客户端请求的URI不对应任何应用资源时
- TCP协议三次握手和四次挥手
所谓三次握手,是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。
四次挥手:
- 什么是xss、csrf,如何预防
XSS
攻击是指攻击者在网站上注入恶意的客户端代码,通过恶意脚本对客户端网页进行篡改,从而在用户浏览网页时,对用户浏览器进行控制或者获取用户隐私数据的一种攻击方式。
防范:HttpOnly 防止劫取 Cookie,对输入输出进行检查
CSRF
攻击是攻击者借助受害者的 Cookie 骗取服务器的信任,可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击服务器,从而在并未授权的情况下执行在权限保护之下的操作。
防范:验证码,token验证
- DNS解析过程
找浏览器缓存→找系统缓存→本地运营商DNS服务器→根域名服务器→通用顶级域名服务器→Name Server域名服务器→返回IP地址
- http缓存
(1)强缓存:
浏览器缓存一种需要发送HTTP请求,一种不要,而强缓存正是不需要发送HTTP请求的缓存,在过期时间前可以直接从缓存中取数据
(2)协商缓存:
当强缓存超时失效后,浏览器需要在请求头中携带相应的缓存tag向服务器发送请求,服务器根据tag,来决定是否使用缓存,这就是协商缓存
首先通过cache-control验证强缓存是否可用,如果可用直接使用,否则进入协商缓存,发送HTTP请求,服务器通过请求头中的Last-Modified或者ETag字段检查资源是否更新,
若资源更新,返回资源和200状态码
否则,返回304,告诉浏览器直接从缓存获取资源
- 网络的7层模型
应用层
网络服务与最终用户的一个接口。
协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP
表示层
数据的表示、安全、压缩。(在五层模型里面已经合并到了应用层)
格式有,JPEG、ASCll、EBCDIC、加密格式等
会话层
建立、管理、终止会话。(在五层模型里面已经合并到了应用层)
对应主机进程,指本地主机与远程主机正在进行的会话
传输层
定义传输数据的协议端口号,以及流控和差错校验。
协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层
网络层
进行逻辑地址寻址,实现不同网络之间的路径选择。
协议有:ICMP IGMP IP(IPV4 IPV6)
数据链路层
建立逻辑连接、进行硬件地址寻址、差错校验 [3] 等功能。(由底层网络定义协议)
将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。
物理层
建立、维护、断开物理连接。(由底层网络定义协议)
- cookies的安全性
存储在浏览器端(用户本地),一些别有用心的人能够通过浏览器截获cookie(脚本、利用工具抓取等)。
第一步:设置cookie有效期不要过长,合适即可
第二步:设置HttpOnly属性为true
可以防止js脚本读取cookie信息,有效的防止XSS攻击。
第三步:设置复杂的cookie,加密cookie
(1)cookie的key使用uuid,随机生成;
(2)cookie的value可以使用复杂组合,比如:用户名+当前时间+cookie有效时间+随机数。
这样可以尽可能使得加密后的cookie更难解密,也是保护了cookie中的信息。
第四步:用户第一次登录时,保存ip+cookie加密后的token
每次请求,都去将当前cookie和ip组合起来加密后的token与保存的token作对比,只有完全对应才能验证成功。
第五步:session和cookie同时使用
sessionId虽然放在cookie中,但是相对的session更安全,可以将相对重要的信息存入session。
第六步:如果网站支持https,尽可能使用https
如果网站支持https,那么可以为cookie设置Secure属性为true,它的意思是,cookie只能使用https协议发送给服务器,而https比http更加安全。
- TCP与UDP的区别
(1)基于连接与无连接
(2)TCP要求系统资源较多,UDP较少;
(3)UDP程序结构较简单
(4)流模式(TCP)与数据报模式(UDP);
(5)TCP保证数据正确性,UDP可能丢包
(6)TCP保证数据顺序,UDP不保证
- 。。。
web优化
- 一个网页有很多图片,如何让用户体验好一点
(1)图片懒加载
即优先加载可视区域的内容,等其他部分进入了可视区域再加载,从而提高性能。
实现原理:在图片没有进入可视区域时,先不给<img>标签的src赋真实值,这样浏览器就不会发送请求,等到图片进入可视区域再赋值
①首先,不要将图片地址放到src属性中,而是放到其它属性(data-original)中
②页面加载完成后,在滚动事件中重复判断图片是否进入视野;如果进入,则将data-original属性中的值取出存放到src属性中
可使用JQuery的lazyload.js插件实现
(2)采用Webpack对图片进行压缩
先安装image-webpack-loader,然后在webpack配置文件中进行配置
- 如何web优化
(1)减少HTTP请求:
图片懒加载就是通过减少HTTP请求才提高效率的
(2)使用CDN
(3)添加Expires头:
页面的初次访问者会进行很多HTTP请求,但是通过使用一个长久的Expires头,可以使这些组件被缓存,下次访问的时候,就可以减少不必要的HTTP请求,从而提高加载速度。
(4)压缩组件gzip
(5)将css放在头部:
将样式表放在头部对于实际页面加载的时间并不能造成太大影响,但是这会减少页面首屏出现的时间,使页面内容逐步呈现,改善用户体验,防止“白屏”。
(6)将js脚本放在底部:
脚本放在底部对于实际页面加载的时间并不能造成太大影响,但是这会减少页面首屏出现的时间,使页面内容逐步呈现。js的下载和执行会阻塞Dom树的构建(严谨地说是中断了Dom树的更新),所以script标签放在首屏范围内的HTML代码段里会截断首屏的内容。
(7)避免CSS表达式
(8)使用外部的JavaScript和CSS:
在实际情况中,当脚本或者样式是从外部引入的文件,浏览器就有可能缓存它们,从而在以后加载的时候能够直接使用缓存,而HTML文档的大小减小,从而提高加载速度。
(9)减少DNS查找:
减少主机名的数量就可以减少DNS查找的数量,减少唯一主机名的数量会潜在减少页面中并行下载的数量,这样减少主机名和并行下载的方案会产生矛盾,需要大家自己权衡。建议将组件放到至少两个但不多于4个主机名下,减少DNS查找的同时也允许高度并行下载。
(10)精简JavaScript:
从代码中移除不必要的字符以减少文件大小,降低加载的时间。
(11)避免重定向:
重定向用于将用户从一个URL重新路由到另一个URL。
(12)删除重复js脚本:
重复的脚本会造成不必要的HTTP请求(如果没有缓存该脚本的话),并且执行多余的JavaScript浪费时间,还有可能造成错误。
(13)配置ETag
(14)使Ajax可缓存:
在进行Ajax请求的时候,可以选择尽量使用get方法,这样可以使用客户端的缓存,提高请求速度。
- CDN
在现有的网络中增加一层新的缓存层,将网站的内容发布到最接近用户的网络边缘节点,使用户能够就近取得所需内容,提高网站响应速度。
通过使用DNS来引导用户访问cache服务器,首先在CDN提供商注册加速域名后会得到一个CNAME域名,在DNS中添加CNAME记录,这样该域名的请求就会转向CDN节点,达到加速效果。
- SEO优化
利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。目的是让其在行业内占据领先地位,获得品牌收益。
优化策略:
(1)主题要明确,内容要丰富,尽量不要大幅度修改标题,否则可能降排名。标题也不要用关键词堆砌,会被百度认为是作弊,标题内容最好是能告诉别人网站的主旨,主要内容。
(2)优化网站分级结构。如在每个内页加面包屑导航栏,更有利于爬虫抓取信息。
(3)集中网站权重。因为爬虫针对每个页面的权重是一定的,这些权重也平均分配给每个a链接上,所以可以设置a链接的属性rel=”nofollow”,告诉爬虫无需抓取目标页,将权重分给其他链接,从而实现集中网站权重。
(4)使用文本强调标签<strong>,比用<b>更具有强调作用
(5)尽量给a标签加上title属性,有利于爬虫抓取信息
(6)给图片<img>标签加上alt属性,也是有利于爬虫抓取信息
(7)<h1>标签自带权重,所以使用要注意,一个页面最多只能有一个h1,放在页面最重要的标题上,比如首页的LOGO
- 回流和重绘
回流:
当对DOM的修改引发了DOM几何尺寸的变化时,浏览器需要重新计算元素的几何属性,然后再将计算结果绘制出来,这个过程就是回流
重绘:
当对DOM的修改导致了样式的变化,但并未影响几何属性,不需要重新计算元素的集合属性,直接绘制即可,就叫做重绘
重绘不一定导致回流,但是回流一定会导致重绘
- 如何解决内存泄露
可用Chrome的Performance检查内存泄露
避免死循环,减少不必要的全局变量,及时对无用的数据进行回收,避免创建过多的对象
Node.js
- 垃圾回收机制
把不用的内存视为垃圾进行回收
- V8垃圾回收机制
新生代内存回收:
在垃圾回收运行时时,会检查 From 中的对象,当某个对象需要被回收时,将其留在 From 空间,剩下的对象移动到 To 空间,然后进行反转,将 From 空间和 To 空间互换。进行垃圾回收时,会将 To 空间的内存进行释放
老生代垃圾回收:
将回收对象和不回收对象分开移到两边,然后对需要被回收的对象区域进行整体的垃圾回收
- Node.js默认最大内存
64位系统下位约为1.4G,在32位系统下约为0.7G
- Node.js的事件循环和浏览器的事件循环有什么不同
浏览器环境下,微任务的任务队列是每个宏任务执行完之后执行。而在Node.js中,微任务会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行微任务队列的任务。
- Node.js发明者
Brendan Eich
- Node.js和前端js的区别
前端的JS = 标准JS + webAPI
nodejs = 标准JS + 一些系统相关的API
其他
- 进程和线程的区别
进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。
进程之间的地址空间和资源是相互独立的
- 排序算法
//快排
void quick_sort(int s[], int l, int r)
{
if (l < r)
{
int i = l, j = r, x = s[l];
while (i < j)
{
while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
j--;
if(i < j)
s[i++] = s[j];
while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
s[j--] = s[i];
}
s[i] = x;
quick_sort(s, l, i - 1); // 递归调用
quick_sort(s, i + 1, r);
}
}
- 大文本文件排序用什么算法好
基本思路是:设文件共有m*n条记录, 内存中每次可以对m条记录进行排序
则排序过程分两步:
第一步:从磁盘中读入m条记录到内存; 在内存排序; 将排好序的数据写入新文件;
重复上述过程n次 共生成n个新文件
第二步:
分配一个文件指针数组,包含n个文件指针,分别指向n个新文件;
分配一个整数数组,大小为n,分别顺序读取各个文件的数据;
分配一个布尔数组,大小为n,指示各个文件是否读完;
将读入的整数数组中最小的值写入外存,并继续往后读取对应的文件(取得最小值的那个文件指针往该文件后面后移一位),然后重复这一步,直到所有的文件都读完。
- 给出出栈序列和入栈序列,判断是否正确
核心思路在于,判断栈顶元素是否等于此时出栈序列的第一个元素,若是,则执行出栈操作,同时指针后移,若否,继续入栈新的元素,继续执行判断操作。
import java.util.Stack;
public class Main {
public static void main(String[] args) {
String push = "12345";
String pop = "23541";
System.out.println(PopSerial(push, pop));
}
public static boolean PopSerial(String push, String pop) {
if (push == null || pop == null || (push.length() != pop.length())) {
return false;
}
int pushLen = push.length();
int popLen = pop.length();
Stack<Character> stack = new Stack<>();
int pushIndex = 0; //记录入栈指针
int popIndex = 0; //记录出栈指针
while (pushIndex < pushLen) {
stack.push(push.charAt(pushIndex));
pushIndex++;
//核心判断条件
while (!stack.isEmpty() && (stack.peek() == pop.charAt(popIndex))) {
stack.pop();
popIndex++;
}
}
//当指针走完整个序列长时,退出并给出结果
if (stack.isEmpty() && popIndex == popLen) {
return true;
} else {
return false;
}
}
}
-
树的前中后序遍历
-
死锁
(1)互斥条件:
指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
(2)请求和保持条件:
指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
(3)不剥夺条件:
指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
(4)环路等待条件:
指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
- 银行家算法
- 数据库索引,什么情况下不能用索引
- 如何进行移动端适配,默认宽度是多少
布局视口(view port)
布局视口就是装页面的盒子。默认情况下,布局视口的大小是 750 px。
这也是为什么一个width:375px盒子的宽度占整个屏幕的一半。
这个值可以通过meta标签来更改。
理想视口(ideal port)
显然,默认的宽度为750px的布局视口往往不能让我们获得最佳的浏览体验。那么需要将布局视口设置为多少呢?这个最佳值就是理想视口。
理想视口的值就是设备屏幕分辨率的值(与设备物理像素的值无关。举个例子,你屏幕的物理分辨率是19201080,但是你可以设置成800600,这个800*600相当于设备分辨率的值)。
当你把“装页面的盒子”的宽度设置成你的手机屏幕分辨率的宽度时,它在水平方向上就不会产生缩放了。然后在这样的情况下设计页面。
- 。。。