前情提要: 这篇是偏向应用类的题目,关于涉及到的知识点,配合我的上一篇JavaScript知识点整理实用更佳喔~
HTML+CSS
布局
假定高度已知,请写出三栏布局,其中左栏、右栏宽度各为300px,中间自适应
- 浮动
- 绝对定位
中间元素 position:absolute;left:300px;right:300p
- 弹性盒子flex
容器 display:flex; 中间元素 flex:1;
- 表格布局table
容器 display:table; 中间元素 display: table-cell;
- 网格布局grid
容器 display:grid;grid-template-rows:300px;// 行高 中间元素 grid-template-columns:300px auto 300px;
五种方案的优缺点
- 浮动后脱离文档流,需清除浮动,兼容性较好
- 绝对定位会脱离文档流且子元素也会脱离文档流,可使用性较差
- flex布局较完美
- 表格布局兼容性好,但使用较麻烦且一行中所有表格等高不够灵活
- 网格布局新技术,有兼容性问题 高度未知后,这几种方案还有哪些可使用 flex布局和表格布局
水平垂直居中的几种实现方式
- flex,div内子元素居中
div{
display: flex;
justify-content: center; // 水平居中
align-items: center; // 垂直居中
}
复制代码
- CSS3中的transform属性
div{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
复制代码
- 绝对定位和margin-left/margin-top(仅适用宽高固定)
div{
position: absolute;
top: 50%;
left: 50%;
margin-left: -0.5*宽度;
margin-top: -0.5*高度;
}
复制代码
- 绝对定位和margin:auto(仅适用宽高固定)
div{
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
复制代码
居中为什么要用transform(为什么不用margin-left/margin-top)
如果使用margin-left/margin-top需要固定元素宽/高,而transform不需要,100%就是元素自身的宽高。
flex布局
display: flex;
// 主轴方向
flex-direction: row|row-reverse|column|column-reverse;
// 换行:wrap 不换行:nowrap
flex-wrap:nowrap|wrap|wrap-reverse;
// 主轴对齐方式
justify-content:flex-start|flex-end|center|space-between|space-around;
// 副轴对齐方式
align-items:flex-start|flex-end|center|baseline|stretch;
// 多根轴线对齐方式
align-content:flex-start|flex-end|center|space-between|space-around|stretch;
复制代码
网格布局grid
// display: grid;
// grid-template-rows // 定义行轨道
// grid-template-columns // 定义列轨道
// grid-column-gap和grid-row-gap定义网格间隙
// 实现上下网格高度为50px,100px,中间自适应
grid{
display:grid;
grid-template-columns:50px auto 100px;
}
//实现第一列,第二列,第三列宽度为90px,50px,150px
grid{
display:grid;
grid-template-rows:90px 50px 150px;
}
// 实现项目1占1/4,项目2占1/4宽度,项目3占1/2宽度
grid{
display: grid;
grid-template-rows: 1fr 1fr 2fr;
}
复制代码
position
position有哪些值?有什么作用
- static:默认值,不脱离文档流
- relative:相对定位,相对自身原来的位置定位,不会脱离文档流
- absolute:绝对定位,会脱离文档流,如果有设置为relative的父容器则相对父容器定位,如果没有就相对body定位
- fixed:固定定位,相对浏览器浏览器定位,会脱离文档流
- sticky:粘性定位,基于用户滚动的位置,正常的时候像relative,当页面滚动超出目标区域时,就像fixed(IE15之前不支持)可以用来做吸顶效果
- inherit:继承父级的position属性
- initial:设置该属性为默认值
盒模型
每个HTML元素都可以叫盒模型,盒模型包括margin,border,padding,content。
标准盒模型和IE盒模型的区别
- 标准盒模型内容大小为content的大小
box-sizing:content-box;
- IE盒模型内容大小为content+padding+border的大小
box-sizing:border-box
获取盒模型宽高 - 获取内联元素的宽高,但样式一般不以内联方式写,所以不适用大多数开发
dom.style.width/height
- 只适用于在IE浏览器获取元素宽高
dom.currentStyle.width/height
- 获取元素的宽高,这个方法兼容性较好,支持Firefox,chrome
window.getComputedStyle(dom).width/height
- 根据元素在视窗中的绝对位置来获取宽高
dom.getBoundingClientRect().width/height
- 最常用,兼容性最好
dom.offsetWidth/offsetHeight
行内元素和块元素
BFC
BFC块级格式化上下文,是一种边距解决方案
实例题(根据盒模型解释边距重叠)
两个box都设置了边距,垂直方向上两个box的边距会重叠,以绝对值大的为最终结果显示
BFC的原理
BFC元素不会与float的元素重叠,BFC上下的边距不会重叠,BFC是一个独立的容器和外面的容器互不干扰,计算BFC高度的时候浮动子元素也会参与运算
如何创建BFC
- 根元素
- float属性不为none
- position:absolute/fixed;
- display:inline-block,table-cell,table-caption,flex,inline-flex;
- overflow不为visiable,为hidden/auto
BFC的使用场景
- 自适应两栏布局
- 清除内部浮动
- 防止垂直margin重叠
animation
使用CSS写一个幻灯片效果
考察点:animation
<style>
.ani{
width:100px;
heigth:100px;
background-size:cover;
background-position:center;
animation: loop 20s infinite;
-webkit-animation: loop 20s infinite;
}
@keyframes "loop"{
0%{background-image: url('1.jpg')}
25%{background-image: url('2.jpg')}
50%{background-image: url('3.jpg')}
75%{background-image: url('4.jpg')}
100%{background-image: url('5.jpg')}
}
@-webkit-keyframes "loop"{
0%{background-image: url('1.jpg')}
25%{background-image: url('2.jpg')}
50%{background-image: url('3.jpg')}
75%{background-image: url('4.jpg')}
100%{background-image: url('5.jpg')}
}
</style>
<div class="ani"></div>
复制代码
伪类伪元素
写一个表格,使表格奇数行为红色背景,偶数行为黄色背景,鼠标移过去为橘色背景
<style>
#table tr:nth-child(odd){
background: red;
}
#table tr:nth-child(even){
background: yellow;
}
#table tr:hover{
background: orange
}
</style>
<table id="table">
<tr><td>1</td></tr>
<tr><td>2</td></tr>
<tr><td>3</td></tr>
<tr><td>4</td></tr>
<tr><td>5</td></tr>
</table>
复制代码
兼容性问题
移动端适配1px的问题
- 伪类+transform实现
.scale-1px{
position:relative;
border:none;
}
// 下边框
.scale-1px:after{
content:'';
position:absolute;
left:0;
bottom:0;
background:#000;
width:100%;
height:1px;
-webkit-tranform:scaleY(0.5);
transform:scaleY(0.5);
-webkit-transform-origin:0 0;
transform-origin:0 0;
}
复制代码
position:fixed;在android下无效怎么处理?
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/>
DOM
选择器(改变页面内容)
获取所有的checkbox
<input type="checkbox" />
<script type="text/javascript">
var arr=[]''
var inputs = document.getElementsByName('input');
for(var i=0; i<inputs.length;i++){
if(inputs[i].type="checkbox"){
arr.push(inputs[i])
}
}
</script>
复制代码
找到html中id相同的元素
使用document.querySelectorAll('#id')
已知id的input输入框,如何获取输入框输入值?
var inp = document.getElementById('id');
console.log(inp.value)
复制代码
设置已知id的div的html内容为xxxx,字体颜色设为黑色
var ele=document.getElementById('id');
ele.innerHTML="xxxx"
ele.style.color="#000"
复制代码
遍历DOM树
<div class="root">
<div class="container">
<section class="sidebar">
<ul class="menu"></ul>
</section>
<section class="main">
<article id="post"></article>
<p class="copyright"></p>
</section>
</div>
</div>
<script type="text/javascript">
var node = document.getElementsByClassName('root')[0];
var arr=[showNode(node)];
function mapNode(node){
var nodeList=node.children;
// console.log(node,nodeList)
for(var i=0;i<nodeList.length;i++){
var childNode=nodeList[i];
console.log(childNode)
arr.push(showNode(childNode))
if(childNode.children.length>0){
mapNode(childNode)
}
}
return arr;
}
function showNode(node){
if(node.className){
return node.nodeName+" ."+node.className
}else{
return node.nodeName+" #"+node.id;
}
}
</script>
复制代码
事件
动态根据下拉列表的选项变化,更新图片显示
<select name="changeImg" id="select">
<option value="1.jpg">1</option>
<option value="2.jpg">2</option>
<option value="3.jpg">3</option>
</select>
<script>
var select=document.getElementById('select')
var img=document.getElementById('img')
img.src=select.value;
select.onchange=function(){
img.src=select.value;
}
</script>
复制代码
给按钮添加事件,确认用户是否退出当前页面,确认之后关闭窗口
考察点:window方法
<button onclick="closeWin()">关闭</button>
<script>
function closeWin(){
if(confirm('是否退出当前页面?'){
window.close()
}
}
</script>
复制代码
现有一个效果,有显示用户头像,用户昵称,用户其他信息;当用户鼠标移到头像上,会弹出用户的所有信息;如何实现此功能?
考察点:事件类型 鼠标的移入移出: mouseover和mouseout(会冒泡);mouseenter和mouseleave(不冒泡)
编写通用的事件监听函数
考察:对事件的了解
var EventUtil={
addEvent:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler)
}else if(element.attachEvent){
element.attachEvent('on'+type,handler)
}else{
element['on'+type]=handler
}
},
removeEvent:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler)
}else if(element.detachEvent){
element.detachEvent('on'+type,handler)
}else{
element['on'+type]=null
}
},
getEvent:function(event){
return event||window.event
},
getTarget:function(event){
return event.target||event.srcElement
},
preventDefault:function(event){
if(event.preventDefault){
event.preventDefault()
}else{
event.returnValue=false
}
},
stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation()
}else{
event.cancelBubble=true
}
}
}
复制代码
设计基于观察者模式的事件绑定机制
观察者模式简单说就是当一个对象被修改时,会自动通知它的依赖对象,发布-订阅模式。
var Event={
// 通过on接口监听事件eventName
// on相当于订阅
on: function(eventName, handler){
if(!this.handler){
this.handler={}
}
if(!this.handler[eventName]){
this.handler[eventName]=[]
}
this.handler[eventName].push(handler)
},
// 触发事件eventName
// emit相当于发布
emit: function(eventName){
// 若存在eventName,则调用所有事件处理函数
if(this.handler[eventName]){
for(var i=0;i<this.handler[eventName].length;i++){
this.handler[eventName][i](arguments[1])
}
}
}
}
Event.on('test', function(a){
console.log(a)
})
Event.emit('test',78) //78
Event.on("test", function(a){
console.log("我的数字是"+a)
});
Event.emit("test", 88);
// 88
//我的数字是88
复制代码
存储
请实现tip组件功能,描述:1.用户第一次进来显示,同一天访问该页面不显示 2.用户点击我知道了然后访问此页面不再显示tip?
<div id="tip">提示</div>
<div id="know">我知道了</div>
<script type="text/javascript">
var tip=document.getElementById('tip');
var storage=window.localStorage;
// 存储的日期
var date=storage.getItem('date');
// 用户是否点击过我知道了
var ifKnow=storage.getItem('know');
// 今天日期
var today=new Date();
today=today.getFullYear()+'-'+today.getMonth()+'-'+today.getDate();
// 点击我知道了不再显示
var know=document.getElementById('know');
know.onclick=function(){
storage.setItem('know', true)
tip.style.display="none";
}
// 如果点击过我知道了,则不显示
if(ifKnow){
tip.style.display="none";
}else{
if(date!=null){
if(date==today){
// 今日已登录过,不显示
tip.style.display="none"
}else{
// 今日尚未登录过
storage.setItem('date',today)
}
}else{
// 第一次访问网站,还没有存储日期
storage.setItem('date', today)
}
}
</script>
复制代码
ECMAScript
数据类型和变量提升
看代码输出结果,解释原因
var a;
alert(typeof a);
alert(b);
b=10;
alert(typeof b);
复制代码
a使用了var声明变量未赋值所以为undefined,b由于未声明将报错。 若b=10改为var b=0;则返回undefined因为var会提升声明。
看代码输出结果,解释原因
var foo=1;
function test(){
console.log(foo);
var foo=2;
console.log(foo);
}
test();
复制代码
原因:函数内部var声明会提升到函数内部顶部,所以就相当于
var foo=1;
function test(){
var foo;
console.log(foo);
foo=2;
console.log(foo)
}
复制代码
回答输出结果
if(!("a" in window)){
var a=1
}
console.log(a)
复制代码
因为var存在声明提升,所以"a" in window为true,就不执行if语句中的赋值了,于是为undefined
回答输出结果
var foo=1;
function bar(){
if(!foo){
var foo=10
}
console.log(foo)
}
bar(); // 10
复制代码
var存在声明提升,所以if(!foo)中的foo为undefined,会被执行,结果为10
回答输出结果
var a=10,b=11,c=12;
function test(a){
a=1;
var b=2;
c=3;
}
test(14);
console.log(a); // 10
console.log(b); // 11
console.log(c); // 3
复制代码
个人认为,test(a)传入的参数a,会被解析成var a=14,所以a也是局部变量
回答输出结果
function fn(){
f=ff()
return f;
function ff(){
return 'f' in window;
}
var f;
}
console.log(fn());
复制代码
函数会被整个提升到最上方,所以ff()中会执行,但由于函数作用域,window下没有f属性,返回false;
回答输出结果
console.log(a);
var a=13;
function fn(){
console.log(a);
var a=1;
}
fn();
console.log(a);
复制代码
输出undefined,undefined,13。原因:js引擎会先把var声明提到顶部,然后再把函数声明整个函数提到var的前面,以上代码等于
function fn(){
var a;
console.log(a);
a=1;
}
var a;
console.log(a);
a=13;
fn();
console.log(a)
复制代码
回答输出结果
console.log(a);
var a=13;
function fn(){
console.log(a);
a=1;
}
fn()
console.log(a)
复制代码
输出undefined,13,1。因为fn中的a=1没有var,所以不存在变量提升,所以fn()中console.log(a)
可以访问到外部的,等fn()
之后,a=1
没有var,不是全局变量改变了原先a的值,所以最后一个输出1
console.log(a);
a=13;
function fn(){
console.log(a);
a=1;
}
fn()
console.log(a)
复制代码
报错,13,1
引用类型
回答代码输出结果,解释原因
var a=new Object();
a.value=1;
b=a;
b.value=2
console.log(a.value)
复制代码
对象参数是按引用传递的
var a=[1];
var b=a;
b=[2];
console.log(a);
复制代码
b=[2]指向了其他的地址,不是原引用了
回答代码输出结果,解释原因
function changeObj(o){
o.name="first";
o = new Object();
o.name="second";
}
var obj=new Object();
changeObj(obj);
console.log(obj.name);
复制代码
o=new Object()切断了原先的引用,此时obj.name仍是指向原来的,新的就只是一个另外的对象
回答代码输出结果
var a=b=c=[1,2,3,4];
b=9;
a[1]=0;
console.log(b[0])
a=[1,2,3,4]
c=[1,2,3,4]
a[0]=b;
console.log(c);
console.log(a);
复制代码
b=9
时已经被切断引用了,a,c也是被切断了,a[0]=b=9,所以a=[9,2,3,4]
实现一个函数clone,可以对JavaScript中的5种主要数据类型(Number,String,Object,Array,Boolean)进行值复制?
function clone(obj){
var o;
switch (typeof obj){
case "undefined":
break;
case "string":
o=obj+"";
break;
case "number":
o=obj-0;
break;
case "boolean":
o=obj;
break;
case "object":
if(obj===null){
o=null
}else{
if(Object.prototype.toString.call(obj)==="[object Array]"){
o=[];
for(var i=0;i<obj.length;i++){
o.push(clone(obj[i]))
}
}else{
o={};
for(var k in obj){
o[k]=clone(obj[k])
}
}
}
break;
default:
o=obj;
break;
}
return o;
}
复制代码
运算符
看代码输出结果,解释原因
function show(){
var b=1;
a=++b;
}
show();
alert(a);
复制代码
a为2,++b就是b=b+1然后返回b,b++就是先返回b,然后b=b+1
字符串
已知有字符串"get-element-by-id",将其转换为getElementById
var str='get-element-by-id';
function formatString(str){
var arr=str.split('-');
var newStr=arr[0]
for(var i=1;i<arr.length;i++){
var first=arr[i].slice(0,1).toUpperCase();
var other=arr[i].slice(1)
newStr=newStr+first+other
}
return newStr
}
formatString(str)
复制代码
清除字符串前后的空格(兼容所有浏览器)
function myTrim(str){
if(String.prototype.trim){
return str.trim()
}else{
return str.replace(/^\s+/, "").replace(/\s+$/,"")
}
}
console.log(myTrim(" hello "))
复制代码
统计一个字符串中出现次数最多的字符和出现次数
function test(str){
var obj={};
// 先将出现过的字符和次数记录下来
for(var i=0; i<str.length;i++){
var key = str[i]
if(!obj[key]){
obj[key]=1
}else{
obj[key]++;
}
}
var max=0;
var idx=0;
// idx为字符,max为出现次数
for(var i in obj){
if(obj[i]>max){
max=obj[i]
idx=i;
}
}
console.log(idx,max)
}
var str="abcdefgaddda";
test(str);
复制代码
删除与某个字符相邻且相同的字符,比如fdaffdaaaklfjklja转换成fdafdaklfjklja
function test(str){
var newStr="";
for(var i=0;i<str.length;i++){
if(str.indexOf(str[i])!==i){
newStr=newStr+""
}else{
newStr=newStr+str[i]
}
}
return newStr;
}
复制代码
求一个字符串的字节长度
假设中文字符占两个字节,英文字符占一个字节
function getByteLen(str){
var reg=/[\u4e00-\u9fa5]/;
var len=0;
for(var i=0;i<str.length;i++){
if(reg.test(str.charAt(i))){
len+=2
}else{
len++
}
}
return len;
}
复制代码
将url的各个参数提取出来,按key-value形式返回一个对象
var url='http://test.com?a=1&b=2&cd=344'
function formatUrl(url){
var urlArr=url.split('?');
url=urlArr[1];
var arr=url.split('&')
var obj={}
for(var i=0;i<arr.length;i++){
var res=arr[i].split('=');
var key=res[0];
var val=res[1];
obj[key]=val;
}
return obj;
}
formatUrl(url)
复制代码
正则
如何将浮点数左边的数每三位添加一个逗号,如12000000.11转换为12,000,000.11?
function formatNum(num){
var newStr="";
var count=0;
var str=num.toString();
for(var i=str.indexOf('.')-1;i>=0;i--){
if(count%3==0 && count!=0){
newStr=str.charAt(i)+','+newStr;
}else{
newStr=str.charAt(i)+newStr;
}
count++;
}
str=newStr+str.substr((str).indexOf('.'));
return str;
}
formatNum(123456566.222)
复制代码
匹配邮箱和手机号
function phone(str){
var reg=/^1[3|4|5|7|8|9]\d{9}$/;
if(reg.test(str)){
console.log('yes')
}else{
console.log('no')
}
}
function email(str){
var reg=/^[0-9a-zA-Z_\.\-]+\@+[0-9a-zA-Z_\.\-]+\.(com|com.cn|edn|hk|cn|net)$/;
if(reg.test(str)){
console.log('yes')
}else{
console.log('no')
}
}
复制代码
去除字符串中HTML标签
function replace(str){
var newStr=str.replace(/<\/?\w+>/gi, "")
console.log(newStr)
}
复制代码
将<,>,&,"进行转义
function test(str){
return str.replace(/[<>"&]/g, function(match){
switch(match){
case "<":
return '<'
case ">":
return ">"
case "&":
return "&"
case "\"":
return """;
}
})
}
复制代码
将字符串"<tr><td>{$id}</td><td>{$name}</td></tr>"
中的{$id}
替换成10,${name}
替换成chen
function test(str){
return str.replace(/{\$id}/g,'10').replace(/{\$name}/g, "chen")
}
复制代码
匹配输入的字符:第一个必须是字母或下划线开头,后面就是字母和数字或者下划线构成,长度5-20
function testStr(str){
var reg=/^[a-zA-Z_]+[0-9a-zA-Z_]{5,20}/;
if(reg.test(str)){
console.log('yes')
}else{
console.log('no')
}
}
复制代码
假设有一篇文章var content="....",涉及到一些敏感词["死了","20","放屁"]等,如何在文章中发现这些敏感词,并将背景置为红色或改变字体颜色标识出来。
<div id="content">
我真的要丢脸死了,我今天在课堂上放屁了而且很大声,这是我18年来最丢脸的一次。
</div>
<script type="text/javascript">
function testStr(id){
var node=document.getElementById(id);
var content=node.innerHTML;
var reg=/死了|18|放屁/g;
var res=content.replace(reg, function(match){
return "<span style='color:red'>"+match+"</span>"
})
node.innerHTML=res;
}
testStr('content')
</script>
复制代码
Math对象
获取一个长度一致的随机的字符串
// 获取随机两位整数
var random=Math.random();
random=Math.floor(random*100);
// 获取随机三位小数
var random=Math.random()+'00000';
random=random.slice(0,5);
复制代码
如何实现数组的随机排序?
// 方法1
function randomArr(arr){
for(var i=0;i<arr.length;i++){
var random=Math.floor(Math.random()*arr.length);
var val=arr[random];
arr[random]=arr[i];
arr[i]=val;
}
return arr;
}
// 方法2
arr.sort(function(){
return Math.random-0.5;
})
复制代码
实现随机10-100之间的10个数字,存入一个数组并排序
function randomArr(){
var arr=[]
for(var i=0;i<5;i++){
var random=Math.floor(Math.random()*(100-10)+10);
arr.push(random)
}
return arr.sort();
}
复制代码
数组
实现对数组的倒序
// 方法1
arr.reverse()
// 方法2
function test(arr){
var newArr=[];
for(var i=0;i<arr.length;i++){
newArr.unshift(arr[i])
}
}
复制代码
实现数组的降序排列
// 方法1
function test(arr){
arr.sort(function(x,y){
return y-x;
})
}
// 方法2
var arr=[1,2,34,40,56]
function test(arr){
for(var i=0;i<arr.length-1;i++){
for(var j=0;j<arr.length-1-i;j++){
if(arr[j]<arr[j+1]){
var val=arr[j]
arr[j]=arr[j+1];
arr[j+1]=val
}
}
}
return arr;
}
复制代码
数组去重
// 方法1 利用indexOf
function test(arr){
var newArr=[];
for(var i=0;i<arr.length;i++){
if(arr.indexOf(arr[i])===i){
newArr.push(arr[i])
}
}
console.log(newArr)
}
// 方法2 利用indexOf
function test1(arr){
var newArr=[];
for(var i=0;i<arr.length;i++){
if(newArr.indexOf(arr[i])===-1){
newArr.push(arr[i])
}
}
console.log(newArr)
}
// 方法3 利用splice和indexOf
function test(arr){
var newArr=[];
for(var i=0;i<arr.length;i++){
if(arr.indexOf(arr[i])!==i){
arr.splice(i,1);
i--;
}
}
console.log(arr)
}
// 方法4 利用ES6的set
function test1(arr){
return Array.from(new Set(arr))
}
复制代码
找到数组arr中重复出现过的元素(若给出多种方式,请分别给出它们的复杂度)
// 方法1
var arr=[5,1,18,4,1,3,4,3];
function test(arr){
var newArr=[];
for(var i=0;i<arr.length;i++){
for(var j=i+1;j<arr.length;j++){
if(arr[i]===arr[j] && newArr.indexOf(arr[i])==-1){
newArr.push(arr[i])
}
}
}
console.log(newArr)
}
复制代码
不用循环,创建一个长度为100的数组,并且每个元素的值等于它的下标
// 方法1
[...new Array(100).keys()]
// 方法2
Array.from(new Array(100),(item,idx)=>idx)
复制代码
实现一个函数,函数参数是一个数组,返回一个新数组,新数组的每一项是原数组的值的两倍,如[2,4,6]返回[4,8,12]
function test(arr){
return arr.map(function(item,index,array){
return item*2;
})
}
function test1(arr){
var newArr=[];
arr.forEach(function(item,index,array){
newArr.push(item*2)
})
return newArr;
}
function test2(arr){
for(var i=0;i<arr.length;i++){
arr[i]=arr[i]*2;
}
return arr;
}
复制代码
阅读代码,分析结果
var arr=new Array(1,3,5);
arr[4] = 'z';
arr2=arr.reverse();
arr3=arr.concat(arr2);
console.log(arr3)
复制代码
["z", undefined, 5, 3, 1, "z", undefined, 5, 3, 1] 因为reverse()会改变原数组
写一个能遍历对象和数组的通用forEach函数
function forEach(obj,fn){
// 判断是否为数组
if(obj instanceof Array){
obj.forEach(function(item,index){
fn(index,item)
})
}else{
for(var key in obj){
fn(key, obj[key])
}
}
}
复制代码
判断一个给定10位字符串是否为合法的日期,例如:'2016-10-31'是一个合法日期则返回true,'2016-10-31'不是一个合法日期,则返回false
function test(str){
var reg=str.match(/^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2})$/);
if(!reg){
console.log('日期格式错误');
return;
}
var d=str.split('-');
var year=parseInt(d[0]);
var month=parseInt(d[1]);
var date=parseInt(d[2]);
if(year<=0){
console.log('日期错误')
return;
}
if(month>12||month<=0){
console.log('日期错误')
return;
}
if((year%4===0&&year%100!==0)||year%400===0||year%500==0){
// 为闰年
if(month==2&&(date>29||date<=0)){
console.log('日期错误')
return;
}
}else{
if(month==2&&(date>28||date<=0)){
console.log('日期错误')
return;
}
}
if(month==4||month==6||month==9||month==11){
if(date>30||date<=0){
console.log('日期错误')
return;
}
}else{
if(date>31||date<=0){
console.log('日期错误')
return;
}
}
return str;
}
复制代码
Function
回答输出结果
// 第二步执行
var test=(function(a){
// a=1,但是b还没有定义因为函数作用域
this.a=a;
return function(b){
// 第三步执行,接收test(4)的参数,所以b=4,this.=1
return this.a+b;
}
}
// 第一步执行
(function(a,b){
// a=1,b=2
return a;
}(1,2))
)
console.log(test(4))
复制代码
返回5
回答代码输出结果
function foo(){
foo.a=function(){
console.log(1);
}
this.a=function(){
console.log(2)
}
a=function(){
console.log(3)
}
var a=function(){
console.log(4)
}
}
foo.prototype.a=function(){console.log(5)}
foo.a=function(){console.log(6)}
// 此时foo还没真正执行,所以只有上面这个属性a,所以返回6
foo.a(); // 执行了foo()
var obj=new foo();
// new方法调用之后this就是指向obj的,所以obj.a就相当于this.a
obj.a()
// 上面执行了foo()之后,foo.a改变了,所以输出1
foo.a()
复制代码
6,2,1
输出结果
var a=5
function test(){
a=0;
console.log(a)
console.log(this.a);
var a;
console.log(a);
}
test();
new test();
复制代码
第一个test()是直接调用的所以this指向window,第二个new test()中this会被绑定一个空对象上,所以this.a为undefined
输出结果:
var myObj={
foo: 'bar',
func: function(){
var self=this;
console.log(this.foo)
console.log(self.foo)
(function(){
console.log(this.foo)
console.log(self.foo)
}())
}
}
myObj.func()
复制代码
bar,bar,undefined,bar
myObj.func()所以this绑定在myObj上,然后在func()里的函数this就会指向func,所以返回undefined。
关于this绑定不太清楚的,可以看木易杨说的JavaScript深入之史上最全--5种this绑定全面解析
定义一个log方法,让它代理console.log的方法
var log=console.log.bind(console)
复制代码
arguments
写一个函数可以计算sum(5,0,-5)输出0;sum(1,2,3,4)输出10;
可以用...或arguments获取参数
function sum(...arr){
var sum=0;
arr.forEach(function(item){
sum=sum+item;
})
return sum;
}
复制代码
只列举一种,方法有很多。
闭包
回答代码输出结果,解释原因
var foo="hello"
(function(){
var bar="world"
console.log(foo+bar)
})()
console.log(foo+bar)
复制代码
"helloworld", 报错,原因:函数作用域
输出结果
var z=10;
function foo(){
console.log(z);
}
(function(funArg){
var z=20;
funArg()
})(foo);
复制代码
函数自动执行,函数参数是值传递,funArg引用外部foo函数,而foo函数作用域没有z变量,所以找到全局变量z,输出结果10
输出结果:
(function(){
var a=b=3
})();
console.log("a defined?"+(typeof a !== 'undefined'))
console.log("b defined?"+(typeof b !== 'undefined'))
复制代码
var a=b=3可以解析出var a=b,b=3;所以b是全局变量,所以typeof b为number,typeof对于不存在的变量a返回undefined
输出结果
function fun(n, o){
console.log(o)
return{
fun: function(m){
return fun(m,n)
}
}
}
var a=fun(0); a.fun(1); a.fun(2); a.fun(3);
var b=fun(0).fun(1).fun(2).fun(3);
var c=fun(0).fun(1); c.fun(2); c.fun(3);
复制代码
这里是一个闭包的概念,保存变量的值 第一行
var a=fun(0); // 这里o是undefined,n=0,会保存在a中
a.fun(1); // m=1,n=0 => n=1,o=0
a.fun(2); // m=2,n=0 => n=2,o=0
a.fun(3); // m=3,n=0 => n=3,o=0
复制代码
从第二行开始,var b=fun(0).fun(1).fun(2).fun(3),可以转化为
var b=fun(0); // 在这里面o是undefined,n=0会保存在b中
var b1=b.fun(1); // o=undefined,m=1,n=0 => n=1,o=0保存到b1中
var b2=b1.fun(2); // o=0,m=2,n=1 => n=2,o=1 保存到b2中
var b3=b2.fun(3); // o=1,m=3,n=2 => n=3,o=2
复制代码
第三行,转化为
var c=fun(0); // 在这里面o是undefined,n=0会保存在c中
var c1=c.fun(1); // o=undefined,m=1,n=0 => n=1,o=0保存到c1中
c1.fun(2); // o=0,m=2,n=1 => n=2,o=1
c1.fun(3); // o=0,m=3,n=1 => n=3,o=1
复制代码
回答输出结果,如果不正确如何修改
for(var i=0;i<5;i++){
setTimeout(function(){
console.log(i)
},100*i)
}
复制代码
会输出5个5
// 方法1
for(let i=0;i<5;i++){
setTimeout(function(){
console.log(i)
},100*i)
}
// 方法2
for(let i=0;i<5;i++){
(function(i){
setTimeout(function(){
console.log(i)
},100*i)
})(i)
}
复制代码
写一段代码实现tab的切换
考察点:闭包
<style>
.hidden{
display: none;
}
</style>
<ul id="tabs">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<div id="content">
<div>第一</div>
<div>第二</div>
<div>第三</div>
</div>
<script>
var tabs=document.getElementById('tabs');
var tabList=tabs.children;
var content=document.getElementById('content');
var conList=content.children;
// 方式1
for(var i=0;i<tabList.length;i++){
tabList[i].index=i;
tabList[i].onclick=function(){
for(var j=0;j<conList.length;j++){
conList[j].style.display="none"
}
conList[this.index].style.display="block"
}
}
// 方式2 用闭包
for(var i=0;i<tabList.length;i++){
tabList[i].onclick=function(i){
return function(){
for(var j=0;j<conList.length;j++){
conList[j].style.display="none"
}
conList[i].style.display="block"
}
}(i)
}
</script>
复制代码
创建10个a标签点击时弹出对应的序号
// 1
for(let i=0;i<10;i++){
var a=document.createElement('a');
a.innerHTML=i+"<br>";
a.onclick=function(){
console.log(i)
}
document.body.appendChild(a)
}
// 2
for(var i=0;i<10;i++){
(function(i){
var a=document.createElement('a');
a.innerHTML=i+"<br>";
a.onclick=function(){
console.log(i)
}
document.body.appendChild(a)
})(i)
}
复制代码
异步
输出结果:
(function(){
console.log(1);
setTimeout(function(){
console.log(2)
},1000)
setTimeout(function(){
console.log(3)
},0);
console.log(4)
})()
复制代码
1,4,3,2 原因:会先将同步代码执行完毕,再执行异步代码
看代码输出结果,解释原因
var a=6
setTimeout(function(){
console.log(a);
a=666
}, 1000)
a=66;
复制代码
66 原因:会先执行同步代码,再执行异步代码
EventLoop
看代码输出结果,解释原因
//请写出输出内容
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end')
复制代码
- JS分为同步任务和异步任务,同步任务在主线程上,主线程外有一个任务队列,一旦同步任务执行完毕就会读取任务队列将可运行的异步任务添加到执行栈中,开始执行。
- EventLoop中可以有一个或多个任务队列,任务队列是一系列有序任务task的集合,每个任务都有一个任务源。不同任务源会被分配到不同task队列中,任务源分为微任务microtask和宏任务macrotask。
- 宏任务包含script(整体代码),setTimeout,setInterval,I/O,UI交互时间,postMessage,MessageChannel,setImmediate
- 微任务包含promise,MutationObserver,process.nextTick
- EventLoop运行机制 1.执行一个宏任务 2.执行过程如果遇到微任务就添加到微任务的任务队列中 3.宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行) 4.当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染 5.渲染完毕后,JS线程继续接管,开始下一个宏任务
- 关于Promise 写在Promise中的代码是被当做同步任务立即执行,而then和catch是异步的
- 关于await await是一个让出线程的标志,await后面的代码会先执行一遍,将await后面的代码加入microtask,然后就会跳出整个async函数来执行后面的代码
async function a1(){
console.log('a1 start')
await a2() // 执行,执行完毕才会回来执行a1 end
cosnole.log('a1 end') // 会被注册为microtask
}
复制代码
- 更多的内容可以看从一道题浅说 JavaScript 的事件循环
实现一个简易版Promise
const PENDING='pending'
const RESOLVED='resolved'
const REJECTED='rejected'
function myPromise(fn){
const that=this;
that.state=PENDING; // Promise当前状态
that.value=null; // Promise的值
that.resolvedCallbacks=[]
that.rejectedCallbacks=[]
function resolve(value){
if(value instancof myPromise){
return value.then(resolve, reject)
}
setTimeout(() => {
if(that.state===PENDING){
that.state===RESOLVED;
that.value===value
// 依次执行成功之后的函数栈
that.resolvedCallbacks.map(cb => cb(that.value))
}
},0)
}
function reject(error){
setTimeout(() => {
if(that.state===PENDING){
that.state===REJECTED
that.value=error;
// 依次执行失败之后的函数栈
that.rejectedCallbacks.map(cb => cb(that.error))
}
},0)
}
try{
fn(resolve, reject)
}catch(e){
reject(e)
}
}
myPromise.prototype.then = function(onFulfilled, onjected){
const that=this;
onFulfilled=typeof onFulfilled === 'function' ? onFulfilled: v => v;
onRejected=typeof onRejected === 'function' ? onRejected : r => {throw r}
if(that.state === PENDING){
that.resolvedCallbacks.push(onFulfilled)
that.rejectedCallback.push(onRejected)
}
if(that.state === RESOLVED){
onFulfilled(that.value)
}
if(that.state === REJECTED){
onRejected(that.value)
}
}
new myPromise((resolve,reject) => {
setTimeout(() => {
resolve(1)
}, 0)
}).then(value => {
console.log(value)
})
复制代码
关于优化
关于图片优化
- 对于修饰图片可以用CSS替代
- 对于移动端不必加载原图,可以使用CDN加载,计算出适配屏幕的宽度,然后请求相应裁剪好的图片
- 小图使用base64格式
- 将多个图标文件整合到一张图片中(雪碧图)
- 选择正确的图片格式,小图使用png,图标可以使用svg替代,照片使用jpeg
怎么控制一次加载一张图片,加载完后再加载下一张?
监控图片是否加载完成,加载完再加载下一张
<img src="">
<img src="">
<img src="">
<img src="">
var imgList=document.getElementsByTagName('img');
var urlList=[
'http://pica.nipic.com/2007-07-15/200771515512480_2.jpg',
'http://www.zyzw.com/sjms/sjmst/sjmsdg021.jpg',
'http://pic11.nipic.com/20100803/4038389_093502059852_2.jpg',
'https://img03.sogoucdn.com/app/a/100520093/ac75323d6b6de243-6a8470ba18ff4002-5f37053dc99bdc42f6f306e09de5e133.jpg'
]
// 加载图片
function loadImg(i){
var obj=new Image();
obj.src=urlList[i];
obj.onload=function(){
imgList[i].src=urlList[i];
i=i+1;
if(i<imgList.length){
loadImg(i)
}
}
}
loadImg(0);
复制代码
图片懒加载和预加载
- 预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染
- 懒加载:主要是为了减少请求数或延迟请求数,不在可视区域的图片先不加载。
节流与防抖
- 防抖:防止手抖,短时间内连续点击只会执行一次
// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait=50) => {
// 缓存一个定时器id
let timer=0
// 返回的参数就是每次用户实际调用的防抖函数
return function(...args){
// 清空上一次的定时器
if(timer) clearTimeout(timer)
timer=setTimeout(() => {
func.apply(this,args)
}, wait)
}
}
function sayHi(){
console.log('防抖成功')
}
var inp=document.getElementById('inp');
inp.addEventListener('input', debounce(sayHi, 500));
复制代码
- 节流:高频事件触发,只会在n秒内执行一次,稀释函数的执行频率(防抖是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行)
const throttle(fn, wait=500) => {
let canRun=true;
return function(...args){
if(!canRun) return;
canRun=false;
setTimeout(()=>{
fn.apply(this, args)
canRun=true
}, wait)
}
}
function resizeWindow(e){
console.log(e.target.innerWidth, e.target.innerHeight)
}
window.addEventListener('resize', throttle(resizeWindow))
复制代码
算法
已知如下数组:
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10]; 编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
// 方法1
// 扁平化数组
function flat(arr){
var newArr=[];
arr.forEach(function(item){
if(Array.isArray(item)){
newArr=newArr.concat(flat(item))
}else{
newArr.push(item)
}
})
return newArr;
}
// 去重和排序
function formatArr(arr) {
var newArr=flat(arr);
newArr.sort(function(a,b){
return a-b;
});
for(var i=0;i<newArr.length;i++){
if(newArr.indexOf(newArr[i])!==i){
newArr.splice(i,1)
}
}
return newArr;
}
formatArr(arr);
// 方法2
function flat(arr){
arr=arr.toString().split(',') // 扁平化数组
arr=arr.sort(function(a,b){
return a-b;
})
arr=arr.map(Number) // 逐个将每个元素转为数字类型
arr=Array.from(new Set(arr)) // 数组去重
return arr;
}
复制代码
几种排序算法的实现
冒泡排序
相邻的两个数进行比较,如果前者大于后者就交换位置,这样一来,第一轮就可以选出一个最大的数放在最后面;那么经过n-1轮,就会完成所有数的排序。
function bubbleSort(arr){
for(var i=0;i<arr.length-1;i++){
for(var j=0;j<arr.length-1-i;j++){
if(arr[j]>arr[j+1]){
var temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
return arr;
}
复制代码
选择排序
- 在未排序的数组中找到最小元素,存放到数组起始位置
- 从剩余未排序元素中继续寻找最小元素,放到已排序序列的末尾
- 重复第二步,直到所有元素排序完毕 好处:不占用额外的内存空间,坏处:时间复杂度为O(n^2),所以数据规模越小越好
function selectSort(arr){
for(var i=0; i<arr.length-1;i++){
var minIdx=i;
// 找到未排序序列中的最小值
for(var j=i+1;j<arr.length;j++){
if(arr[j]<arr[minIdx]){
minIdx=j;
}
}
// 将最小值放到已排序序列的后面
var temp=arr[i];
arr[i]=arr[minIdx];
arr[minIdx]=temp;
}
return arr;
}
复制代码
插入排序
- 插入排序跟整理扑克牌是一样的,即每次拿到一个数,按大小顺序将其插入到有序的数组中。
- 首先初始化第一个元素为有序数列,取出一个无序数列中的元素按大小顺序插入有序数组中。
function insertSort(arr){
for(var i=1;i<arr.length;i++){
// 将要插入的数
let temp=arr[i];
// 有序数列
for(var j=i-1;j>=0;j--){
// 要插入的数与有序数列一一比较
if(temp<arr[j]){
arr[i]=arr[j]
arr[j]=temp;
}
}
}
return arr;
}
复制代码
希尔排序
- 希尔排序也叫递减增量排序算法,是插入排序的升级版。
- 先将无序数组分割成若干子序列,子序列是相隔特定增量的子序列,对每个子序列进行插入排序
- 然后再选择一个更小的增量,再将之前排序后的数组按这个增量分割成多个子序列
- 不断选择更小的增量,直到增量为1时,再对序列进行一次插入排序,使序列最终成为有序序列
function shellSort(arr){
var gap=Math.floor(arr.length/2);
while(gap>0){
for(var i=gap; i<arr.length;i++){
var temp=arr[i];
for(var j=i; j-gap>=0 && arr[j-gap]>temp; j=j-gap){
arr[j]=arr[j-gap];
}
arr[j]=temp;
}
gap=Math.floor(gap/2);
}
return arr;
}
复制代码
归并排序
先递归分解数列再合并数列,将一个数组拆分成A,B两个小组,一直拆到每个小组只有一个元素为止。 看小册
快速排序
- 先取出一个数作为基准
- 然后把大于这个数的放到它右边,小于等于这个数的放到左边
- 再对左右区间重复第二步,直到各区间只有一个数
function quickSort(arr){
if(arr.length<=1){
return arr;
}
// 基准位置(理论上可任意选取)
var idx=arr.length-1;
// 基准值
var num=arr[idx];
var left=[];
var right=[];
for(var i=0;i<arr.length-1;i++){
if(arr[i]<=num){
left.push(arr[i])
}else{
right.push(arr[i])
}
}
// 会先将左边的排序好再开始对右边进行排序
return quickSort(left).concat([num],quickSort(right));
}
复制代码