对于搞技术的来说,面试题绝对是一个有意思并且值得学习的一门艺术,技术大牛给你面试时,真的是会让你受益匪浅,觉得学海无涯;渣渣大牛给你面试时,你会觉得自己是在听一个教音乐的老师在和你说他的画价(搞)值(笑)连(装)城(逼)(边面试边教学,主要教的都不对,搞的有点耐人寻味skr~)。duang~duang~duang!虽然那些渣渣大牛们面试中问到的问题觉得很脑残,但是不要怀疑面试题,面试题真的算是一个武功秘籍,因为渣渣们面试中问的那不叫面试题。
ok 开始我们的面试题吧(以下都是个人的整理,回答面试题和学习面试题,会持续的更新滴,建议先看右下角的目录,比较多)
一、关于HTML的面试题
1、html5哪些操作可以SEO优化
- title标签;meta标签;header标签;footer标签
元标签(meta标签);
- 导航标签(nav标签);文章标签(article标签);左或右侧标签(aside标签)
2、页面导入样式时,使用link和@import有什么区别?
- 1.link属于HTML标签,除了加载CSS外,还能用于定义RSS,定义rel连接属性等作用;而@import是CSS提供,只能加载CSS;
- 2.页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载;
- 3.import是CSS2.1提出的,只在IE5以上才能被识别,而link是HTML标签,无兼容问题;
3、行内元素和块状元素的区别?行内快元素的兼容性使用?(IE8以下)
- 行内元素:会在水平方向排列,不能包含快级元素,设置width无效,height无效(可以设置line-height),margin上下无效,padding上下无效
- 块级元素:各占据一行,垂直方向排列。从新行开始结束接着一个断行
兼容性:display:inline-block;display:inline;zoom:1;
ok 关于html的面试题个人觉得有用的并不多,然后什么doctype的意义,HTML5带来了哪些变化?,html,xhtml,html5之间有什么关系?我觉得正常的面试官不会问你这些的,这也不是重头,先热热身,那我们继续!
二、关于CSS的面试题(持续更新中。。。)
1、如何清除浮动,以及清除浮动的方法
-
1)添加额外标签
在浮动元素末尾添加一个空的标签例如 <div style=”clear:both”></div>,其他标签br等亦可。<div class="warp" id="float"> <h2>1)添加额外标签</h2> <div class="main left">.main{float:left;}</div> <div class="side left">.side{float:right;}</div> <div style="clear:both;"></div> </div>
优点:通俗易懂,容易掌握
缺点:可以想象通过此方法,会添加多少无意义的空标签,有违结构与表现的分离。 -
2)父元素设置 overflow:hidden
通过设置父元素overflow值设置为hidden;在IE6中还需要触发 hasLayout ,例如 zoom:1;<div class="warp" id="float" style="overflow:hidden; *zoom:1;"> <h2>2)父元素设置 overflow </h2> <div class="main left">.main{float:left;}</div> <div class="side left">.side{float:right;}</div> </div>
优点:不存在结构和语义化问题,代码量极少
缺点:内容增多时候容易造成不会自动换行导致内容被隐藏掉,无法显示需要溢出的元素; -
3)父元素设置 overflow:auto 属性
同样IE6需要触发hasLayout,演示和2差不多优点:不存在结构和语义化问题,代码量极少
缺点:多个嵌套后,firefox某些情况会造成内容全选;IE中 mouseover 造成宽度改变时会出现最外层模块有滚动条等,firefox早期版本会无故产生focus等。 -
4)使用:after 伪元素
需要注意的是 :after是伪元素,不是伪类(某些CSS手册里面称之为“伪对象”),很多闭合浮动大全之类的文章都称之为伪类,不过csser要严谨一点,这是一种态度。由于IE6-7不支持:after,使用 zoom:1触发 hasLayout。
.clearfix:before{ content:'';display:table; } .clearfix:after {content:""; display:block; height:0; visibility:hidden; clear:both; } /* 简洁写法 */ .clearfloat:after{display:block;clear:both;content:'';}
优点:结构和语义化完全正确,代码量居中
缺点:复用方式不当会造成代码量增加 -
小结
通过对比,我们不难发现,其实以上列举的方法,无非有两类:
其一,通过在浮动元素的末尾添加一个空元素,设置 clear:both属性,after伪元素其实也是通过 content 在元素的后面生成了内容为一个点的块级元素;
其二,通过设置父元素 overflow。
面试的时候的时候面试官跟想听到伪类的方法
2、说说flex布局
内容太多,彻底学习去这里:http://www.ruanyifeng.com/blo...
-
面试重点回答需要知道这些,平时开发这些用的的最多
怎么横向居中?justify-content-属性定义了项目在主轴上的对齐方式。
.box { justify-content: flex-start | flex-end | center | space-between | space-around; }
怎么竖向居中?align-items-属性定义项目在交叉轴上如何对齐。
.box { align-items: flex-start | flex-end | center | baseline | stretch; }
align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
.box { align-content: flex-start | flex-end | center | space-between | space-around | stretch; }
3、垂直居中的方法
- margin:0 auto;
- justify-content: center;
- text-algin:center
4、水平居中的方法
- flex布局的align-items: center或者margin:auto;
- position: absolute;top: calc( 50% -需要居中的一般高度);
平时用的一些小技巧样式(项目一定会用到但用的很少,还不能忘)
1、p标签段落前面缩进
p{text-indent:2em}
2、ul去掉前面的点
ul{list-style:none}
关于JS的面试题(持续更新中。。。)
1、数据类型和引用类型/javascript的typeof返回哪些数据类型,参考地址
- 基本数据类型:Number、String、Boolean、Null、 Undefined、Symbol(ES6),这些类型可以直接操作保存在变量中的实际值。
- 引用数据类型:Object(在JS中除了基本数据类型以外的都是对象,数据是对象,函数是对象,正则表达式是对象)
2、JS中深克隆和浅克隆详解,参考地址
- 浅复制(浅克隆):直接将存储在栈中的值赋值给对应变量,如果是基本数据类型,则直接赋值对应的值,如果是引用类型,则赋值的是地址 <br/>
- 深复制(深克隆):就是把数据赋值给对应的变量,从而产生一个与源数据不相干的新数据(数据地址已变化)。深拷贝,是拷贝对象各个层级的属性
2.1浅克隆
-
1.基本数据类型的赋值(以String为例)
let a = "hello world"; let b = a; alert( b ); // 'hello world' a = "changed"; alert( b ); // 'hello world' // 在这段代码中,把a赋值给b,当a的值发生变化时,并不影响b
-
2引用数据类型的赋值(以Array为例)
let arr1 = [1, 2, 3, 4]; let arr2 = arr1; console.log( arr2 ); // [10, 2, 3, 4] arr1[0] = 10; console.log( arr2 ); // [10, 2, 3, 4] // 在这段代码中,把arr1赋值给arr2,当arr1的值改变时,arr2对应的值也会改变
对以上两个例子的分析
对于基本数据类型而言,把a的值赋值给b后,a的修改,不会影响到b<br/>
对于引用数据类型而言,把arr1赋值给arr2后,arr1的修改,会影响到arr2对应的值<br/>
基本数据类型是直接存储在 栈内存 中的,而引用数据类型,则仅仅是把地址存储在 栈内存 中,真正的数据是存储在 堆内存 中的,赋值操作时,仅仅把地址进行了赋值。
2.2 深克隆
-
方法一: 递归
function deepClone( obj ){ let objClone = Array.isArray(obj) ? [] : {}; if( obj && typeof obj==="object"){ for(key in obj){ if(obj.hasOwnProperty(key)){ if(obj[key]&&typeof obj[key] ==="object"){ objClone[key] = deepClone(obj[key]); }else{ objClone[key] = obj[key]; } } } } return objClone; } let a=[1,2,[2, 3],4], b=deepClone(a); a[2][0]=10; console.log(a,b);
-
方法二: JSON.stringify 和 JSON.parse
function deepClone(obj){ let _obj = JSON.stringify(obj), objClone = JSON.parse(_obj); return objClone } let a=[0,1,[2,3],4], b=deepClone(a); a[0]=1; a[2][0]=1; console.log(a,b);
-
方法三: jQuery中的 $.extend
let a=[0,1,[2,3],4], b=$.extend(true,[],a); a[0]=1; a[2][0]=1; console.log(a,b);
$.extend( [deep ], target, object1 [, objectN ] )<br/>
deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝<br/>
target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。<br/>
object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。<br/> -
方法四:var newArray = Array.slice(0) 用来生成新得数组
这个是vue源码中生成新数组的方法,偷偷学到的
3、js 宏任务和微任务,参考地址
宏任务(macrotask )和微任务(microtask )
macrotask 和 microtask 表示异步任务的两种分类。
在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。
把下面的代码运行一下,加速你的理解:
//主线程直接执行
console.log('1');
//丢到宏事件队列中
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
//微事件1
process.nextTick(function() {
console.log('6');
})
//主线程直接执行
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
//微事件2
console.log('8')
})
//丢到宏事件队列中
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
4、重写js push函数(因为vue2.+的object.defineproperty中重写了push、pop、unshift、shift、reverse、sort)
let _push = Array.prototype.push
Array.prototype.push = function(){
for(let i of arguments){
_push.call(this, i);
}
// _push.call(this, arguments);
// console.log('参数为', arguments);
console.log('this', this);
}
let a = [1,2,3]
a.push(4,5)
5、this(this的4种指向和改变this指向的方式),参考地址和apply、call、bind区别、用法,参考地址
6、从输入URL到页面呈现的过程,参考地址
7、split() join() 的区别
- 前者是将字符串切割成数组的形式,后者是将数组转换成字符串
8、数组方法pop() push() unshift() shift()
- push()尾部添加 pop()尾部删除 unshift()头部添加 shift()头部删除
9、闭包是什么,有什么特性,对页面有什么影响
- 闭包就是能够读取其他函数内部变量的函数,使得函数不被GC回收,如果过多使用闭包,容易导致内存泄露
10、如何阻止事件冒泡
- ie:阻止冒泡ev.cancelBubble = true;非IE ev.stopPropagation();
11、如何阻止默认事件
- (1)return false;(2) ev.preventDefault();
12、事件委托是什么
- 利用事件冒泡的原理,让自己的所触发的事件,让他的父元素代替执行!
13、Javascript的事件流模型都有什么?
- “事件冒泡”:事件开始由最具体的元素接受,然后逐级向上传播
- “事件捕捉”:事件由最不具体的节点先接收,然后逐级向下,一直到最具体的
- “DOM事件流”:三个阶段:事件捕捉,目标阶段,事件冒泡
14、回答以下代码,alert的值分别是多少?
<script>
var a = 100;
function test(){
alert(a);
a = 10; //去掉了var 就变成定义了全局变量了
alert(a);
}
test();
alert(a);
</script>
正确答案是: 100, 10, 10
15、判断 js 类型的方式
- 1、typeof
可以判断出'string','number','boolean','undefined','symbol'
但判断 typeof(null) 时值为 'object'; 判断数组和对象时值均为 'object'
- 2、instanceof
原理是 构造函数的 prototype 属性是否出现在对象的原型链中的任何位置
function A() {}
let a = new A();
a instanceof A //true
//因为 Object.getPrototypeOf(a) === A.prototype;
- Object.prototype.toString.call()
常用于判断浏览器内置对象,对于所有基本的数据类型都能进行判断,即使是 null 和 undefined
- Array.isArray()
用于判断是否为数组
16、统计字符串中字母个数
var str = "I think javascript is good";
//解:方法不唯一
str = str.toLowerCase();
var result = {};
for(var i in str) {
if(str[i] in result) {
result[str[i]]++;
} else {
result[str[i]] = 1;;
}
}
17、给数组去重
var arrays = [1,2,3,2,4,6,5,7]
function unique (arrray) {
var result=[]
//请编程实现数组去重
return result;
}
//1.利用es6的set对象
result = [...new Set(array)];//[...new Set (array)]
//2.利用对象属性的唯一性
let obj = {};
for (let i of array) {
if (!obj[i]) {
result.push(i);
obj[i]=1;
}
}
//3.利用sort方法排序去重
var newarray = array.sort();
result = [newarray[0]];
var length = newarray.length;
for( var i = 1 ;i<length; i++) {
newarray[i] !== newarray[i-1] && result.push(newarray[i]);
}
//4.利用for...of+includes()循环
for(var i of array) {
!result.includes(i) && result.push(i);
}
//5.用filter结合indexOf
result = array;
return result.filter((item, index)=> {
return result.indexOf(item) === index
})
//6.利用双层for循环(6分)类似
var length = array.length;
for(var i =0; i<length; i++) {
var only = true;
for(var j =i+1; j<length; j++){
if(!only) continue;
if(array[i] == array[j]) only = false;
}
if(only) result.push(array[i]);
}
18、js中forEach,for in,for of循环的用法,参考地址
19、js防抖和节流(防抖事件)
在进行窗口的resize、scroll,输入框内容校验等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少调用频率,同时又不影响实际效果。
-
函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。如下图,持续触发scroll事件时,并不执行handle函数,当1000毫秒内没有触发scroll事件时,才会延时触发scroll事件。
function debounce(fn, delay) { let timer = null //借助闭包 console.log('a') return function () { console.log('b') if (timer) { clearTimeout(timer) } timer = setTimeout(fn, delay) // 简化写法 } } // 然后是旧代码 function showTop() { var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; console.log('滚动条位置:' + scrollTop); } window.onscroll = debounce(showTop, 1000)
当持续触发scroll事件时,事件处理函数handle只在停止滚动1000毫秒之后才会调用一次,也就是说在持续触发scroll事件的过程中,事件处理函数handle一直没有执行。
-
函数节流(throttle):当持续触发事件时,保证一定时间段内只调用一次事件处理函数。节流通俗解释就比如我们水龙头放水,阀门一打开,水哗哗的往下流,秉着勤俭节约的优良传统美德,我们要把水龙头关小点,最好是如我们心意按照一定规律在某个时间间隔内一滴一滴的往下滴。如下图,持续触发scroll事件时,并不立即执行handle函数,每隔1000毫秒才会执行一次handle函数。
节流throttle代码(时间戳):
function throttle(fn, delay) { var prev = Date.now(); return function() { var context = this; var args = arguments; var now = Date.now(); if (now - prev >= delay) { fn.apply(context, args); prev = Date.now(); } } } // 以下照旧 function showTop() { var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; console.log('滚动条位置:' + scrollTop); } window.onscroll = throttle(showTop,1000)
当高频事件触发时,第一次会立即执行(给scroll事件绑定函数与真正触发事件的间隔一般大于delay,如果你非要在网页加载1000毫秒以内就去滚动网页的话,我也没办法o(╥﹏╥)o),而后再怎么频繁地触发事件,也都是每delay时间才执行一次。而当最后一次事件触发完毕后,事件也不会再被执行了 (最后一次触发事件与倒数第二次触发事件的间隔小于delay,为什么小于呢?因为大于就不叫高频了呀(╹▽╹))。
节流throttle代码(定时器):
function throttle(fn, delay) { let valid = true return function () { if (!valid) { //休息时间 暂不接客 return false } // 工作时间,执行函数并且在间隔期内把状态位设为无效 valid = false setTimeout(() => { fn() valid = true; }, delay) } } // 以下照旧 function showTop() { var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; console.log('滚动条位置:' + scrollTop); } window.onscroll = throttle(showTop,1000)
当触发事件的时候,我们设置一个定时器,再次触发事件的时候,如果定时器存在,就不执行,直到delay时间后,定时器执行执行函数,并且清空定时器,这样就可以设置下个定时器。当第一次触发事件时,不会立即执行函数,而是在delay秒后才执行。而后再怎么频繁触发事件,也都是每delay时间才执行一次。当最后一次停止触发后,由于定时器的delay延迟,可能还会执行一次函数。
节流中用时间戳或定时器都是可以的。更精确地,可以用时间戳+定时器,当第一次触发事件时马上执行事件处理函数,最后一次触发事件后也还会执行一次事件处理函数。
节流throttle代码(事件戳+定时器):
function throttle(fn, delay) { var timer = null; return function() { var context = this; var args = arguments; if (!timer) { timer = setTimeout(function() { fn.apply(context, args); timer = null; }, delay); } } } // 以下照旧 function showTop() { var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; console.log('滚动条位置:' + scrollTop); } window.onscroll = throttle(showTop,1000)
在节流函数内部使用开始时间startTime、当前时间curTime与delay来计算剩余时间remaining,当remaining<=0时表示该执行事件处理函数了(保证了第一次触发事件就能立即执行事件处理函数和每隔delay时间执行一次事件处理函数)。如果还没到时间的话就设定在remaining时间后再触发 (保证了最后一次触发事件后还能再执行一次事件处理函数)。当然在remaining这段时间中如果又一次触发事件,那么会取消当前的计时器,并重新计算一个remaining来判断当前状态。
- 总结:防抖和节流的区别,防抖就是将几次操作合并为一此操作进行触发,节流就是每隔固定的时间进行触发(类似几技能冷却)
20、js设计模式(个人觉得这个不应该是一个面试题)
自行百度吧,我觉得就是自己写代码的思路,可以参看参考设计模式,再结合自己的思路,也挺不错的(而我被一个渣渣大牛蔑视了,我还以为设计模式是个多么厉害的东西,回头一百度,what?设计模式很难么?对不起,我有我的设计模式)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。