前端面试题(各种基础)
HTML
-
Doctype的作用,标准模式和混杂模式有什么区别
- < ! D o c t y p e>声明位于HTML文档的第一行,出在html标签的前面,告诉浏览器的解析器用什么文档标准解析这个文档,当不存在或者格式错误的时候会采用兼容模式来加载这个网页
- 标准模式的排版和js运作模式都是以改浏览器支持的最高标准运行,在兼容模式中,页面以一种宽松的向后兼容的方式显示,模拟老师浏览器的行为以防止站点无法工作
-
html语义化的理解
- 用正确的标签做正确的事情
- 让页面的内容结构化,结构更加清晰,便于对浏览器、搜索引擎解析
- 即时没有css的情况下,也已一种文档格式显示,并且容易阅读
- 便于seo,因为搜索引擎的爬虫是依赖于html标记来确定各个关键字的权重
-
html5的离线存储怎么使用,工作原理
在用户和因特网没有连接时,可以正常访问站点和应用,在用户和因特网链接的时候可以更新用户机器上的缓存文件
原理: html5是一种基于新建一个appcache的文件的缓存机制,通过这个文件上的解析清单离线存储资源,这些资源就会响cookie一样被存储下来之后当网络处于离线状态下,浏览器会通过离线存储的数据进行页面展示
使用:
- 在页面头部下面加入一个mainfest的属性
- 在cache.mainfest文件的编写先存储的资源
- 在离线状态下,操作window.applicationCache进行需求实现。
-
iframe有那些缺点?
- iframe会阻塞主页面的onload事件,搜索引擎的检索程序无法解读这种页面, 会影响seo。iframe和主页面之间是共享连接池的,所以会影响页面的并行加载。
- 为了解决这些缺点,我们需要给iframe动态加上src,这样子的话可以绕开上面的问题
-
laber的作用是什么?要怎么用
- laber标签定义表单控制,当用户选择该标签时,浏览器会自动吧焦点转到和标签相关的表单控件上。
<laber for = "Name">点击这里和点击输入框一样 </laber> <input type = "texte" name = "bal"/> <laber>data:<input type ="text" name ="B"/></laber>
-
对页面元素进行编辑
- 对页面整体编辑,需要设置designMode on/off
- 对页面元素编辑,需要设置contentEditable
-
实现不用border,实现1px高的线
<div style="height:1px;overflow:hidden;background:red"></div>
-
行内元素和块级元素,行内元素不会换新航,块级元素会格式化为块状,会换行,行内元素不呢能设置宽度和高度,熟悉的行内元素有span,i,em等,img是行内元素,但是可以设置宽度和高度。主要原因是置换元素和非置换元素。
置换元素和非置换元素不仅行内元素有区分,块级元素也有区分,比如iframe以及个别时候的audio和canvas也是置换元素。
置换元素是根据标签的属性来决定元素显示的具体内容,比如img标签就是根据src属性来显示,input标签是根据type属性来显示数据框,这些元素往往没有实际内容,也就是一个空元素。但是大多数元素都是不可替换元素,比如div p,他们都把实际的内容展示出来。
可置换元素有img|input|select|textarea|button。他们区别于一般的inline元素,这些元素拥有内在尺寸,可以设置高度和宽度,他们的性质和设置了inline-block的属性一样,
行内置换元素设置上下padding,可以撑大父元素,而行内非置换元素设置上下padding只是范围扩大,不会影响父元素。
-
语义化的标签基本都是h5的,比如header footer nav article
-
li标签之前看不见的空白间隔是什么原因引起的,有什么解决办法
行框的排列会受到中间空白(回车换行的影响)因为空格也属于字符,这些空白也会被应用样式,占据空间,把字符大小设置为0或者负边距,或者删除空白就行。
CSS
-
w3c标准中,块元素的总宽度就是content,而ie标准盒模型中块元素的总宽度就是content+2 * padding+2 * border。导致他们采用那个标准,是html文件顶部的doctype
-
行内元素有 br input span b i
-
BFC是块级格式化上下文,是css2.1的规范,决定了元素如何对其内容进行定位,以及与其他元素的关系和作用。。BFC提供了一个环境,html元素在这个环境中按照固定的规则进行布局,一个环境中的元素不会影响到其它环境的布局。浮动元素内部子元素主要受该浮动元素影响。
触发BFC
- display的值是table-cell ,table-caption,inline-blockk
- overflow为hidden,反正不能visible
- 只要元素触发了BFC,就不需要clearboth来清除浮动
- float的值不能为none
- position的值不是relative和static
-
css盒子模型中的padding是透明的,可以显示背景
-
canvas和svg的区别
- canvas
- 依赖分辨率
- 不支持事件处理器
- 弱的文本渲染能力
- 能够以jpg和png保存图像
- 适合图形密集的游戏
- 挨个像素进行渲染
- 图形完成后不会继续得到浏览器的关注
- SVG
- 不依赖分辨率
- 支持时间处理器
- 适合带有大型渲染区域的应用程序(地图)
- 复杂度高会减慢渲染速度
- 不适合游戏
- canvas
-
垂直居中的方法
-
flex
.flex { display : flex; align-items : center; align-items : center; }
-
单行文字垂直居中,只需要height和line-height一样高
-
多对象的垂直居中技巧,将多个元素用一个大的div包裹起来,并且设置display是inline-block设置高度。
-
absolute + margin负值
-
absolute+ translate
-
flex+margin
-
-
import和link的区别
- link
- 加载页面之前吧css加载完毕
- link在老版本ie也兼容
- 当使用javascript控制dom去改变样式的时候,只能使用link标签
- import
- 读完文件之后加载,所以一开始会没有css,会导致短暂的闪烁
- 老版本不支持
- 只能加载css
- 不可以被dom控制
- link
-
position定位
- static:默认布局,按照文档流进行排布
- relative:相对布局,按照本身的位置进行偏移
- absolute:绝对定位,从当前节点向上找寻第一个不是static的元素进行定位
- fixed:固定定位,固定在页面的某个位置
- sticky:粘性定位,==relative+fixed,先按照普通文档流进行定位,然后相对于该元素在流中的BFC和块级祖先元素定位,然后元素表现为在跨域特定阈值的时候为相对定位,之后为绝对定位,必须结合left,right,top或者bottom一个使用,可以实现导航栏那些功能。
-
display
- inline行内布局,表现为各元素在一行显示,比如span元素
- block块级元素,表现为每个元素独占一行,比如p和div
- inline-block行内块元素,表现为每个元素有自己的宽度和高度,但是他们在一行显示,会存在空白间隙现象,处理方法有负边距,font-size=0
- flex弹性布局,可以实现动态分配宽度,比如实现双飞翼那样的布局
-
双飞翼和圣杯布局
圣杯布局和双飞翼布局基本上是一致的,都是两边固定宽度,中间自适应的三栏布局,其中,中间栏放到文档流前面,保证先行渲染。解决方案大体相同,都是三栏全部float:left浮动,区别在于解决中间栏div的内容不被遮挡上,圣杯布局是中间栏在添加相对定位,并配合left和right属性,效果上表现为三栏是单独分开的(如果可以看到空隙的话),而双飞翼布局是在中间栏的div中嵌套一个div,内容写在嵌套的div里,然后对嵌套的div设置margin-left和margin-right,效果上表现为左右两栏在中间栏的上面,中间栏还是100%宽度,只不过中间栏的内容通过margin的值显示在中间。
-
圣杯
DOM: <body> <div id="hd">header</div> <div id="bd"> <div id="middle">middle</div> <div id="left">left</div> <div id="right">right</div> </div> <div id="footer">footer</div> </body> <style> #hd{ height:50px; background: #666; text-align: center; } #bd{ /*左右栏通过添加负的margin放到正确的位置了,此段代码是为了摆正中间栏的位置*/ padding:0 200px 0 180px; height:100px; } #middle{ float:left; width:100%;/*左栏上去到第一行*/ height:100px; background:blue; } #left{ float:left; width:180px; height:100px; margin-left:-100%; background:#0c9; /*中间栏的位置摆正之后,左栏的位置也相应右移,通过相对定位的left恢复到正确位置*/ position:relative; left:-180px; } #right{ float:left; width:200px; height:100px; margin-left:-200px; background:#0c9; /*中间栏的位置摆正之后,右栏的位置也相应左移,通过相对定位的right恢复到正确位置*/ position:relative; right:-200px; } #footer{ height:50px; background: #666; text-align: center; } </style>
-
行内元素是否可以设置margin和padding
看html8 置换元素和非置换元素的区别
-
清除浮动的几种方式。
-
父级元素设置伪类
.clearfloat{zoom:1} .clearfloat:after { content : ''; display:block; clear:both; visibility:hidden; } /* 原理:利用css提高的clear:both清除浮动,让父级div能自动获取到高度 优点:浏览器支持比较好,不容易出现兼容性问题 */
-
在底部添加一个空div,添加属性
.newDiv { clear:both; } /* 原理:添加一个空div,利用css提高的clear:both清除浮动,让父级div能自动获取到高度 缺点:添加了一个div,增加了dom节点 */
-
父级div设置高度
/* 缺点明显,只适合固定高度的div */
-
父级div设置overflow:hideen
/* 必须定义width或zoom:1,同时不能定义height,使用overflow:hidden时,浏览器会自动检查浮动区域的高度 不能使用position */
-
父级元素设置float
/* 形成了bfc,浏览器会检测高度 */
-
-
媒体查询
@media screen and (min-width:1200px) @media screen and (min-width:992px) @media screen and (min-width:768px) @media screen and (min-width:480px)
-
移动端开发过程中,因为手机的dpr(设备像素比不同),需要根据dpr来修改图标的大小,判断使用@2x 图 还是 @3x 图,解决高清的适配。
/*less,sass这类css预处理语言中的混合,可以理解成自定义了一段代码,后面可以用@include调用*/ @mixin bg-image($url) { background-image: url($url + "@2x.png"); @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3){ background-image: url($url + "@3x.png"); } } /*用@include调用*/ div{ width:30px; height:20px; background-size:30px 20px; background-repeat:no-repeat; @include bg-image('../../../../static/image/map_loading'); } //scss @mixin box-size($width:100px,$height:100px) { width: $width; height: $height; } .demo{ @include box-size; } .demo2{ @include box-size(200px,300px); }
-
0.5px实现的几种方式
-
利用渐变来做
<div class = "bd-t"> </div> .bd-t { position:relative; } .bd-t :after { content : ''; position : absolute; top:0; left:0; width:100%; height:1px; background-image: linear-gradient(0deg, transparent 50%, #e0e0e0 50%); } /* 为了实现盒子顶部边框0.5px的伪代码:border-top:0.5px solid #e0e0e0; 的效果,使用after,作为一个钩子,宽度100%,高度1px,背景渐变,一半透明,一半有颜色,这么干是可以的。 */
-
使用缩放
<div class="bd-t"></div> .bd-t { position :relative; } .bt-d:after { content:''; position ;absolute; left:0; top:0; width:100%; height:1px; transform:scaleY(0.5); background:black; /*或者 border-top 1px solid black; */ }
-
使用缩放解决四边都是0.5
<div class= "bd-t"></div> .bd-t { position:relative; } .bd-t:after { content:""; position:absolute; z-index:-1; width:200%; height:200%; transform:sacle(0.5,0.5); border:1px solid black; }
-
使用background-img
<div class="bd-t"> </div> .bd-t { position:relative; } .bd-t:after { content : ''; position: position; left:0; top:0; width:100%; border-top:1px solid transparent; border-image: url('pic.png') 2 1 1 1 stretch; }
-
使用同周边相似的浅色,利用视觉效果,让用户产生错觉。
-
-
在浏览器中我们可以开启硬件加速,使得gpu发挥作用。cssanimations,transform,transitions不会自动开启gpu加速,而是由浏览器的缓慢的软件渲染引擎来执行。现代浏览器检测到页面中某个dom元素应用了某些css规则的时候就会开启,显著的是元素的3d变换,我们可以欺骗浏览器开启硬件加速。
transform:translateZ(0);
-
jquery和css3做动画的区别
- css3动画和过度都是基于css实现机制的,没有任何语言操作,效率高于jquery,兼容性不好
- jquery的animate函数可以简单的理解为css样式的逐桢动画,是css样式不同状态快速切换的结果,兼容性好
- css3使用gpu加速,jquery使用cpu,所以css3更加的流畅
- css 动画建议transoform,而不是单独计算width等,因为tansform会吧元素独立出来单独计算。
$("p").animate({"bottom":p*9},3000,function(){ // 回调函数 $("p").animate({"bottom":-p},6000); //css3 @keyframes myfirst { from {background: red;} to {background: yellow;} } div { animation: myfirst 5s; -moz-animation: myfirst 5s; /* Firefox */ -webkit-animation: myfirst 5s; /* Safari 和 Chrome */ -o-animation: myfirst 5s; /* Opera */ }
-
不可以继承的, display,margin,padding,border,backgroud,height,width,position等
-
css多列等高是如何实现的。
利用padding-bottom(+)和marin-bottom(-)正负相抵。设置父容器overflow:hidden,这样父容器的高度就是它里面子元素没有设定padding-boddom的高度,当里面的任何一列高度增加了,那么父容器的高度就被撑到里面最高的那列的高度了。 其它的比这列矮的列会用他们的padding-bottom补偿这份高度差
-
全屏滚动的原理
比如要显示五个页面,可以设置高度为500%; 然后只显示100%;通过transform来使用
-
响应式布局就是一个网站可以兼容多个终端,而不是为每一个终端写一个版本,有点事面对不同分辨率设备灵活性强。但是他会工作量大,而且效率有点低,代码累赘,加载时间长。可以通过媒体查询来做
@media 设备名 only(选取条件) not(选取条件) and(选取条件)
-
视觉感动就是网页向下滑动的时候,控制背景的移动速度比前景移动速度慢来创建出令人惊叹的3d效果。可以用css实现
-
::before 和 :after中双冒号和单冒号 有什么区别?解释一下这2个伪元素的作用。
单冒号是用于css伪类,双冒号是css微元素, ::befare就是一个子元素的存在,定义在元素主体内容之前的一个微元素,并不存在dom中, after生成的会比before的靠后。
-
修改chrome记住密码之后自动填充表单的黄色背景,第一种情况是针对文本框是纯色背景的
input:-webkit-autofill, textarea:-webkit-autofill, select: -webkit-autofill { background-color:rgb(250, 255, 189); background-image :none, color:rgb(0,0,0); } /* 设置背景色为白色 */
还有一种情况是,input使用背景图片,图片不复杂的情况下可以使用第一种情况解决,纯色内阴影覆盖,
-
line-height
行高是只一行文字的高度,具体说的是两行文字极限之间的距离,css起高度作用的主要是height和line-height,一个没有定义height属性,最终表现一定是line-height。单行文本垂直居中只需要吧line-height设置为何height一样高。多行文本垂直居中需要设置display为inline-block
-
设置元素浮动之后,他的display的值会自动变成block,ie出现双边框设置inline就可以解决bug
-
position:fixed在安卓中无效怎么办。
fixed的元素是相对整个页面固定位置的,你在屏幕上滑动只是移动站合格所以的viewport,但是原来的网页还好好的在哪里,fixed的内容也没有变化位置,。需要在head中加入代码
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/>
-
如果要手写动画,最小的时间间隔。
因为大多数显示器的默认分辨率是60hz,也就是1s刷新60次,所以莅临赏最小的时间间隔是1/60*1000 也就是16.7ms
-
有一个高度自适应的div,里面有两个div,一个高度是100px;希望另一个填满剩下的高度,
.con { height: auto; } .top { width:100%; height:100px; backgroun:red; } .bottom { position: absolute; top:100px; width:100%; bottom:0; left:0; right:0; }
-
png, jpg, gif
png是无损的,压缩比高,色彩好,透明
jpg是针对相片的失真压缩,会皮怀的,
gif是一种位图格式,可以有动画
-
cookie隔离,看下面的cookie优化
-
移动端最小触控区域时44px*44px
-
css强制不换行,超出部分用省略号
{ white-space: nowrap; //文本强制不换行; text-overflow:ellipsis; //文本溢出显示省略号; overflow:hidden; //溢出的部分隐藏; }
-
颜色渐变
background: linear-gradient(to right, dodgerBlue, skyBlue); -webkit-background-clip: text;
-
rem实现页面兼容
例如:设计稿宽度是750px,有一个元素设计稿上的宽度是50px,设备物理宽度是375px,那么我们在页面上应该设置宽度为 width:50rem,相当于宽度是:50*(375/750)=25px;这里能正确算出在375px的设备上刚好占一半,其实可以想象为 rem=(375/750)。
2.一般浏览器的最小字体是12px,如果html的font-size=(375/750)px,相当于font-size=0.5px,那么这个数值就小于12px,会造成一些计算的错误,和一些奇怪的问题,*100后,font-size是50px,就可以解决这种字体小于12px的问题。
- 为了计算方便 我们后面把比率乘以了100,(375/750)*100,那么相对应这个元素在设置数值的时候就需要除以100了(50/100),这样可以保证最后出来的数值不变.
在header中写标签设置font-size的大小。
<script type="text/javascript"> document.documentElement.style.fontSize = document.documentElement.clientWidth / 750*100 + 'px'; </script>
js
-
移动端ios开发和andriod的区别
-
物理返回andriond会刷新,ios不会刷新
-
ios和安卓对json对象遍历的顺序相反
-
ios弹出键盘的时候,viewport的高度并不会边,但是安卓变,所以iosfixed在底部的元素显示不出来。
-
ios自带的浏览器是有双击缩放功能的。比如用户在ios里面点击了一个连接,由于用户可以进行双击缩放和双击滚动的操作,浏览器并不能确定用户是要打开这个链接还是要放大,所以需要等待三百ms来判断用户是判断用户是否再次点击。 所以就有了300ms延迟
-
解决方法有
- 添加viewpoint meta标签,不允许缩放
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
- 使用fastclick代码
移动端事件触发的顺序是,touchstart-》touchmove-》touchend-》click,所以fastclick的原理就是检测到touch事件的时候,通过dom自定义事件立即模拟click事件,同时把300ms之后的事件阻止掉
-
与之相关的还有点击穿透事件,比如页面有两个元素a和b,b元素在a元素的上面,我们在b元素的touchstart事件上注册了一个回调函数,这个回调函数的作用是隐藏b元素,我们发现,当点击b元素的时候,b被隐藏了,同时a元素触发了click事件。这是因为事件的处理顺序,click有300ms的延迟,touchstart是立即发生的,然后300ms之后,浏览器触发了click事件,但是这时候b已经没了,那就触发了a事件,同样用fastclick解决
-
-
移动端为什么用zepto而不是jquery
- 因为jquery适用于桌面环境,桌面环境更加复杂,jquery需要考虑的因素非常多,尤其是在兼容性上面,相对于pc端,移动端的复杂度就比不上pc端了,pc端下载jquery只需要1-3s,但是移动端就慢了很多,zepto比jquery小很多,只有不到10k,表现也不比jquery差很多
-
cookie,sessionStorage,localStorage的区别
- cookie是网站为了标识用户身份而存储在用户本地终端上的数据,通常是加密的。是由服务器生成的。
- cookie的数据始终在同源的http请求中携带,即使不需要,也就是说他们会在浏览器和服务器之间来回传递
- localstorage和sessionstorage仅仅在本地保存,不会发给服务器
- cookie的大小是4kb,sessionStorage和loaclStorage虽然也有大小限制,但是他们比cookie要大得多,可以达到5m
- localStorage可以永久保存数据,浏览器关闭之后不会丢失的,除非主动删除
- sessionStroage 数据在当前浏览器窗口关闭之后自动删除
- cookie 设置的cookie过期时间之前一直有效,即使窗口或者浏览器关闭
- http-only属性标识一个客户端jas能否操作改cookie
- max-age属性表示缓存时间,单位为秒,domain设置可以访问该cookie的域名
- cookie的优化方案有
- 去除没有必要的cookie,如果页面不需要cookie就完全禁用
- 将ciikie的大小减少到最小,由于cookie在访问对应于明霞的资源时候都会通过http请求发送到服务器,所以减少cookie的代销,可以减少http请求报文的大小,提高相应速度。
- 设置合适的过期时间,较长的过期时间可以提高访问速度。
- 通过不同的domain来减少cookie的使用,在访问js,css和图片是,大多数cookie是多余的,可以使用不同的domain来存储这些静态资源,这样访问这些资源时就不会发送多余的cookie,从而提高响应速度举个例子来说,我们的主网站是 w w w . sunrobin.com/default.aspx,那么图片类的静态文件应该放到w w w.sunrobinimg.com域名下边。而且这两个域名其实最终可以指向同一个server,但是浏览器发送cookie的原则只是根据域名是否一样,不关心最终在哪个server,而且还有一种方式,就是使用CDN
- 那么是cookie的名称,value是cookie的值,domian字段设置cookie的域名,path字段可以访问cookiede页面路径
-
获取鼠标点击的位置
- 相对于屏幕 event.screenX
- 相对于浏览器窗口 event.clientX
- 相对于文档 event.pageX ||event.clientX + event.scrollX
-
判断一个对象是不是数组
- typeof一定不行,会返回一个object
- instanceof判断一个变量是否是某个对象的实例
- Object.prototype.toString.call(obj) ===’[object Array]’
- es5 有个isArray()方法
-
ajax请求数据的流程
- 创建ajax对象
- 发送http请求
- 接受服务器返回的数据
- 更新网页数据
-
浏览器事件
事件就是文档或者浏览器窗口中发生的一些特定的交互瞬间,可以用监听器来监听事件。部分不支持冒泡的事件有
- ui事件
- load
- unload
- scroll
- resize
- 焦点事件
- blur
- focus
- 鼠标事件
- mouseleave
- mouseenter
- ui事件
-
事件冒泡会从当前触发的目标事件一级一级向上传递,依次触发,知道document为止,事件捕获会从document开始触发,一级一级向下传递,依次触发,直到真正到目标为止
我们给一个dom同时绑定两个点击事件,一个用捕获,一个用冒泡。会执行几次事件,会先执行冒泡还是捕获?
会执行两次,先捕获,后冒泡
事件触发的顺序是事件捕获-> 事件触发 -> 事件冒泡
- 事件冒泡的用处有很多,比如可以给ui标签里面每一个li标签绑定事件,也可以给ul绑定事件,监听li标签,增加效率
- 有些时候需要阻止冒泡,比如一个模态框,框里有一个按钮,我点击按钮需要跳转页面,点击模态框没有反应,点击模态框外面的需要把模态框给藏起来,这个时候就需要阻止冒泡,要不然点击模态框会导致隐藏。。。
- 阻止冒泡的方法。dom中提供stopProgpagation方法,ie不支持这个方法,所以ie需要用cancelbubble方法,默认为false,当设置为true的时候就可以阻止冒泡了,也就是用event对象在事件函数中调用。
- 取消默认事件,在w3c中时preventDefault,ie是设置e.returnValue= true
-
js基本数据类型有 null,undefined,string number boolean symbol
-
复杂数据类型 array object function
-
延迟js的运行,需要加上在script中加上defer属性
-
日期类转换到原始值能使用什么方法,toString
-
typeof Date.now() 的返回值是number
-
oxff^33 是异或的意思。
-
client是content+ 2*padding。 offset是content+ 2 * padding + 2 * border
-
scrollheight是真实的高度,自身高度加上滑动高度
-
[] == 0; [1]==1; [0] == 0
-
a标签不跳转有三种方式,第一种src=#,第二种 onclick函数return false,第三种href= javascript:void(0)
-
parseInt(string,radix)有两个参数,第一个参数是要解析的字符串,第二个参数是要解析的基数, 这个值在2-36之间, 省略的话会当做10来处理,小于2或者大于36会NaN
-
javascript有许多保留字不嫩用作标识符,比如arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield
-
事件循环队列的只是,是主线任务完成后,会完成所有微队列内的异步任务(promise),再去处理宏队列的任务,settimeout
-
每个对象都会在其内部初始化一个属性也就是prototype,当我们范文正对对象属性时,如果这个对象内部不存在这个属性,那么它就会去prototype中去找这个属性,这个prototype也会有自己的prototype。于是这样一直找下去,就是我们说的原型链的概念, instance.constructot.prototype = instance.proto
-
window对象是指浏览器打开的窗口,document对象时document对象的一个制度引用,是window对象的一个属性
-
全局函数无法查看局部函数,但是局部函数可以查看全局函数,当需要从局部函数查找某一属性和方法的时候,如果当前作用域没有找到,就会上诉到上层作用域查找,直到全局函数,这就是作用域链
-
上下文是基于对象的,上下文总是关键字this的值,this总是指向函数的直接调用者,当函数作为对象的方法被调用,this指向调用方法的对象 、 如果有new创建一个对象的实例的时候,那么this就指向new出来的这个对象,当调用一个未绑定函数,this默认指向全局上下文或者浏览器的window对象,如果在严格模式下,this指向undefined,当对象调用属性的时候,this指向调用的对象。call,bind,apply可以改变this的指向
-
当函数执行的时候,会创建一个称为执行上下文的内部对象,不是25的上下文,一个执行上下文就定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用同一个函数会导致创建多个执行上下文, 当js代码被浏览器载入后,默认最先进入的是一个全局的执行上下文,当在全局上下文中调用一个执行函数的时候,程序流就进入了该调用函数内,此时,引擎就会为该函数新建一个执行上下文,并且将其压入执行栈顶部,(作用域链)浏览器总是执行位于执行栈顶部的执行上下文,一旦执行完毕,该执行上下文就会从执行栈顶部弹出,并且控制权进入下一个执行上下文,这样子,执行栈中的指向上下文就会被一次执行并且弹出, 直到回到全局的执行上下文。
-
作用域链其实就是我们访问对象的属性和方法,当找不到的时候,就会通过scope,进行查找,scope连存储的执行上下文的集合,这个集合呈现链式连接,我们称之为作用域链
-
原型链就是我找一个属性首先会在f.proto中找,如果找不到,就去f.protot.proto中去找,直到找到null位置,这个串起来的链就是原型链
-
什么是闭包,为什么要用
闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。
-
javaScript中hasOwnProperty函数方法是返回一个布尔值,指出一个对象是否具有指定名称的属性。此方法无法检查该对象的原型链中是否具有该属性;该属性必须是对象本身的一个成员。
使用方法:
object.hasOwnProperty(proName)
其中参数object是必选项。一个对象的实例。
proName是必选项。一个属性名称的字符串值。
如果 object 具有指定名称的属性,那么JavaScript中hasOwnProperty函数方法返回 true,反之则返回 false。 -
[].forEach.call($$(""),function(a){a.style.outline=“1px solid #”+(~~(Math.random()(1<<24))).toString(16)}) 给页面每一个dom元素加上一个彩色border
-
防抖和节流
针对响应跟不上触发频率这种问题的两种解决方案, 再给dom绑定事件的时候,有些时间我们是无法控制频率的。如鼠标移动事件,滚动事件onscroll,窗口大小改变事件onresize,瞬间的操作都会导致这些事件被高频触发。如果事件的回调函数较为复杂, 就会导致响应跟不上触发,出现页面卡顿、假死现象。针对这种连续触发和不可控的高频触发问题,可以用去抖动和节流。防抖的方法是,当时间触发的时候,设定一个周期延迟执行,若期间又被触发,那么重新设定周期,直到周期结束,执行动作。如果周期内有事件发生,那么周期就重新设定。节流的方法是固定周期内,只执行一次动作,如果有新的事件触发,那么就不执行,周期结束之后,执行动作。
-
去抖方法一,有新事件触发,就清除旧定时器
// 暴力版: 定时器期间,有新操作时,清空旧定时器,重设新定时器 var debounce = (fn, wait) => { let timer, timeStamp=0; let context, args; let run = ()=>{ timer= setTimeout(()=>{ fn.apply(context,args); },wait); } let clean = () => { clearTimeout(timer); } return function(){ context=this; args=arguments; let now = (new Date()).getTime(); if(now-timeStamp < wait){ console.log('reset',now); clean(); // clear running timer run(); // reset new timer from current time }else{ console.log('set',now); run(); // last timer alreay executed, set a new timer } timeStamp=now; } }
-
方法二周期内有新事件触发时,重置定时器开始时间撮,定时器执行时,判断开始时间撮,若开始时间撮被推后,重新设定延时定时器
/ 优化版: 定时器执行时,判断start time 是否向后推迟了,若是,设置延迟定时器 var debounce = (fn, wait) => { let timer, startTimeStamp=0; let context, args; let run = (timerInterval)=>{ timer= setTimeout(()=>{ let now = (new Date()).getTime(); let interval=now-startTimeStamp if(interval<timerInterval){ // the timer start time has been reset, so the interval is less than timerInterval console.log('debounce reset',timerInterval-interval); startTimeStamp=now; run(timerInterval-interval); // reset timer for left time }else{ fn.apply(context,args); clearTimeout(timer); timer=null; } },timerInterval); } return function(){ context=this; args=arguments; let now = (new Date()).getTime(); startTimeStamp=now; if(!timer){ console.log('debounce set',wait); run(wait); // last timer alreay executed, set a new timer } } } //简单版 function debounce(fn) { let timeout = null; // 创建一个标记用来存放定时器的返回值 return function () { clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉 timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数 fn.apply(this, arguments); }, 500); }; } function sayHi() { console.log('防抖成功'); } var inp = document.getElementById('inp'); inp.addEventListener('input', debounce(sayHi)); // 防抖
-
节流方法一
简单版: 定时器期间,只执行最后一次操作 var throttling = (fn, wait) => { let timer; let context, args; let run = () => { timer=setTimeout(()=>{ fn.apply(context,args); clearTimeout(timer); timer=null; },wait); } return function () { context=this; args=arguments; if(!timer){ console.log("throttle, set"); run(); }else{ console.log("throttle, ignore"); } } } //另一种简单版 function throttle(fn) { let canRun = true; // 通过闭包保存一个标记 return function () { if (!canRun) return; // 在函数开头判断标记是否为 true,不为 true 则 return canRun = false; // 立即设置为 false setTimeout(() => { // 将外部传入的函数的执行放在 setTimeout 中 fn.apply(this, arguments); // 最后在 setTimeout 执行完毕后再把标记设置为 true(关键) 表示可以执行下一次循环了。当定时器没有执行的时候标记永远是 false,在开头被 return 掉 canRun = true; }, 500); }; } function sayHi(e) { console.log(e.target.innerWidth, e.target.innerHeight); } window.addEventListener('resize', throttle(sayHi));
-
节流方法二
/// 增加前缘 var throttling = (fn, wait, immediate) => { let timer, timeStamp=0; let context, args; let run = () => { timer=setTimeout(()=>{ if(!immediate){ fn.apply(context,args); } clearTimeout(timer); timer=null; },wait); } return function () { context=this; args=arguments; if(!timer){ console.log("throttle, set"); if(immediate){ fn.apply(context,args); } run(); }else{ console.log("throttle, ignore"); } } }
-
-
创建,移除,移动,复制,查找节点
- 创建新节点
- createDocumentFragment()创建一个dom片段
- createElement创建元素
- createTextNode创建一个文本节点
- 添加,移除,替换,插入
- appendChild()
- removeChild()
- replaceChild()
- insertBefore()
- 查找
- getElementsByTagName()
- getElementsByName()
- getElementById()
- 创建新节点
-
Document.write会导致重绘整个页面,innerHTML只重绘页面的一部分
-
不在任何函数里定义的变量就具有全局作用域,js有一个全局对象window,全局作用域的变量实际上就是被绑定到window上的一个属性,window对象的内置属性都有全局作用域。 在一个函数里定义的变量就只对这个函数内部可见,这就是函数(局部)作用域
-
判断两个对象相等, 可以用遍历来做,也可以转换为字符串
Json.strungify(obj) == Json,Stringify(obj2);
-
用户验证
<script language = "javascript"> function checkSubmit() { if ((document.form1.name.value()) == '') { window.alert('姓名必须填写'); document.form1.name.select(); document.form1.name.focus(); return false; } else { return false; } } </script> <form name = "form1" "javascript:return checkSubmit()"> <input type = "text" name = "name"> </form>
-
变量提升问题
compute(10,100); var compute = function(A,B) { console.info(A * B) ; }; function compute(A,B){ console.info(A + B); } function compute(A,B){ console.info((A + B)*2); } compute(2,10); 等同于220 20 var compute; function compute(A, B) { console.info(A + B); } function compute(A, B) { console.info((A + B) * 2); } compute(10, 100); //赋值 compute= function(A, B) { console.info(A * B); }; compute(2, 10);
-
json.parse 对null和undefined解析会报错
let a = { age: undefined, jobs: function() {}, name: 'yck' } let b = JSON.parse(JSON.stringify(a)) console.log(b) // { age: undefined, jobs: function() {}, name: 'yck'} // json.parse对null,undefined报错
-
数组字符串操作
给定一个字符串,数字或者大小写字母。找出最长的对称的子串,如果有多个,输出任意一个
例如,输入abbaad,输出 abba
function reverseStr(str) { return str.solit("").reverse().join(""); } function findStr(str) { var maxStrings = ""; if (str.length == 1 || str == reverseStr(str)) { return str; } for (var i = 0; i != str.length; i++) { for (j = str.length; j > i; j--) { var subString = str.subString(i,j) { if (subString == reverseStr(subString)) { if (subString.length > maxString.length) { maxStrings = subStrings; } } } } } return maxStrings; } console.log(findStr(readline()));
-
给定两个数字(0-9)字符串,求它们的乘积
function multiply (num1, num2) { return parseInt(num1) * parseInt(num2); } multiply(readline(),readline());
-
立即执行函数的作用域
var i = 0; (function fn() { console.log(i++); setTimeout(fn, 1000); })(); //所以他不会因为堆栈段溢出而停止,因为每一个都是单独的作用域
-
编写一个函数,使得mysort传入的参数按照从小到大的顺序显示出来
function mySort() { var tags = new Arrya(); for (i = 0 ,j = arguments.length; i != j; i++) { tags.push(arguments[i]); } tags.sort(function(num1, num2) { return num1 - num2; }) return tags; } var result = mySort(3,4,5,6,1,2,8); console.log(result);
-
给array本地对象增加一个原型方法,用于删除数组条目中重复的条目,返回值是一个包含被删除的重复条目的新数组。
Array.prototype.distinct = function() { var ret = []; for (var i = 0; i != this.length; i++) { for (var j = i + 1; j != this.length; j++) { if (this[i] == this[j]) { ret.push(this.splice(j, 1)[0]); } else { j++; } } } return ret; }
-
js实现继承的几种方式 参考:构造函数的继承,非构造函数的继承;
https://mp.weixin.qq.com/s/U1V9CXApoKFcw08Rkzmgmw
要实现继承,首先我们需要有个父类比如。
functuon animal(name) { this.name = name || 'Animal'; this.sleep = function() { console.log("正在睡觉!"); } } animal.protorype.eat = function(food) { console.log(this.name + '正在吃' + food); }
-
原型链继承,核心是把父类的实例作为子类的原型
function Cat() { } Cat.prototype = new Animal(); Cat.prorotype.name = 'cat'; var cat = new Cat(); console.log(cat.name); console.log(cat.eat('fish')); console.log(cat.sleep()); console.log(cat instanceof Cat) //true; console.log(cat instanceof animal) //true;
特点是非常纯粹的继承关系,实例是子类的实例,也是父类的实例,父类新增原型方法和属性,子类都能访问到,简单,抑郁实现。 但是想要为子类新增属性和方法,必须在new Animal之后才能执行,不能放到构造器中,无法实现多继承。 来及原型对象的所有属性被所有实例,创建子类市里的时候,无法向父类构造函数传参。
-
构造继承,核心是使用父类的构造函数来增强子类实例,等于是吧父类的实例都复制给子类,没用到原型
function Cat(name) { Animal.call(this); this.name = name || 'Tom'; } var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); //false console.log(cat instanceof Cat); //true
特点,解决了1中子类实例共享父类引用属性的问题。创建子类实例时,可以向父类传递参数,可以实现多继承。但是实例并不是父类的实例,只是子类的实例。只能继承父类的实例属性和方法。不能继承原型属性和方法。无法实现函数的复用,每个子类都是父类实例函数的副本,影响性能
-
实例继承 核心是为父类实例增加新的特性,作为子类实例返回
function Cat(name){ var instance = new Animal(); instance.name = name || 'Tom'; return instance; } var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); //true console.log(cat instanceof Cat); //false
特点是不限制调用方式,不管是new子类()还是子类();返回的对象都具有同样的效果,但是缺点是,实例是父类的实例,不是子类的实例, 不支持多继承
-
拷贝继承,支持多继承
function Cat(name) { var animal = new Animal(); for (var p in animal) { Cat.prototype[p] = animal[p]; } Cat.prototype.name = name || 'Tom'; } var car = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); false console.log(cat instanceof Cat ) true
特点是效率低,内存占用高,需要拷贝父类的属性,而且无法获取父类不可枚举的方法
-
组合继承,核心是通过调用父类构造,继承父类的属性并且保留传参的有点,然后通过把父类实例作为自雷原型,实现函数复用。
function Cat(name) { Animal.call(this); this.name = name || 'Tom'; } Cat.prototype = new Animal(); var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal) true console.log(cat instanceof Cat) true
特点是可以继承实例属性和方法,也可以继承原型属性和方法,既是自雷的实例,也是父类的实例,不存在引用属性共享问题,可以传参,函数可以复用
-
寄生组合继承核心是通过寄生方式,砍掉父类的实例属性,这样在调用两次父类的构造的时候,不会初始化两次实例方法属性,避免了组合继承的缺点
function Cat(name) { Animal.call(this); this.name = name || 'Tom'; } (function() { var super = function() { super.prototype = Animal.prototype; Cat.prototype = new Super(); } }) // 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
-
-
随机排序
-
sort函数
var arr = [1,2,3,4,5,6,7,8,9,10]; arr.sort(function() { retrun Math.random() - 0.5; })
-
按序
var arr = [1,2,3,4,5,6,7,8,9,10] function randSort(arr) { for (var i = 0, lne = arr.length; i != j; i++){ var rand = parseInt(MAth.random() * len); var temp = arr[rand]; arr[rand] = arr[i]; arr[i] = temp } return arr; }
-
-
如何把浮点数左边的数每三位添加一个逗号
function commafy(num){ return num && num .toString() .replace(/(\d)(?=(\d{3})+\.)/g, function($1, $2){ return $2 + ','; }); }
-
创建对象
//对象字面量 var person = { firstname : "firstname", latname : "lastname" } //function模拟无参的构造函数 function Person(){} var person = new Person(); person.firstname = 'firstname'; person,age = 28; //用function来模拟构造函数实现 function Person(name, age) { this.name = name; this.age = age; this.eat = function() { console.log("eat"); } } var jontyy = new Person("jontyy", 28); //工厂方式创建 var dog = new Object(); dog.name = "wwa"; dog.age = 3; dog.work = function() { console.log("work"); } //原型方式创建 function dog() { } dog.prototype.name = "wang"; dog.prototype.eat = function () { console.log("eat"); } var dogg = new dog(); //混合方式调用 function Car (name,price) { this.name = name; this.price = price; } Car.prototype.eat = function() { console.log("eat"); }
-
自定义事件
- 方法一
<button id="button" type="button">你好</button> <script> var button = document.getElementById("button"); button.onclick = function(){ trigger(save); } var trigger = function(){ for(var i in save){ save[i](); } } var save1 = function(){ alert("save1"); } var save2 = function(){ alert("save2"); } var save = [save1,save2]; </script>
- 方法二
//定义一个Box类 function Box(){ //other code this.handlers = {};//存储事件的对象 } Box.prototype = { constructor: Box, //显示方法 show: function (){ //code //this.fire('show'); }, //关闭方法 close: function (){ //code //this.fire('close'); }, //监听方法,模拟addEventListener,其中type为事件函数,handler为需要触发的函数。 addListener: function (type,handler){ if (typeof this.handlers[type] == 'undefined')this.handlers[type] = []; this.handlers[type].push(handler);//将要触发的函数压入事件函数命名的数组中 }, //手动触发方法 fire: function (type){ if ( this.handlers[type] instanceof Array ){ var handlers = this.handlers[type]; //遍历事件函数监听的程序,依次执行 for (var i=0;i<handlers.length;i++){ handlers[i](); } } }, //事件解绑 removeListener: function (type,handler){ if (!this.handlers[type]) return; var handlers = this.handlers[type]; if (handler == undefined){ handlers.length = 0;//不传某个具体函数时,解绑所有 }else if( handlers.length ){ for ( var i=0;i<handlers.length;i++ ){ if ( handlers[i] == handler ){ //解绑单个 this.handlers[type].splice(i,1); } } } } } var box = new Box(); function listenShow1(){ console.log(11); } function listenShow2(){ console.log(22); } box.addListener('show',listenShow1); box.addListener('show',listenShow2); box.show();
54.实现双向绑定
<body>
<div id= "app">
<input type = "text" id = "txt">
<p id = "show-txt">
</p>
</div>
<script>
var obj = {};
Object.defineProperty(obj, 'txt', {
get : function() {
return obj;
},
set : function(newValue) {
document.getElementById('txt').value = newValue;
documentById('show-txt').innerHTML = newValue;
}
})
document.addEventListener('keyup', function(e) {
obj.txt = e.target.value
});
</script>
</body>
-
一些代码
if ('m' in window) { var m = m && 12; } console.log(m); //undefined 因为window中没有m,所以没有赋值 let n = 10; if (!('n' in window)) { let n = n + 30; } console.log(n); //referenceError let存在临时性死区,在if循环里面 ,从右向左,n没有定义 let s = 10, m = 20; ~function (s, m) { let arg= arguments; arg[0] = s || 100; arg[1] = m || 200; console.log(s, m); }(m); console.log(s, m); //20 undefined //10 20 //立即执行函数只传了s进去,值是m所以在内部 s的值被改为20, args[1]的值是undefined,不可以对undefined赋值。 //而外部的不能取到立即执行函数内部的值,所以仍然是10 和20 let ary = [12, 23, 34, 45]; (function (ary) { ary.pop(); ary = ary.slice(0); ary.shift(); console.log(ary); })(ary); console.log(ary); //[23,34] //[12,23,34] //第一次ary.pop操作的是外面声明的let数组。 //然后ary = ary.slice(1) 实际上是,var ary = ary.slice(0) //在之后的操作都是操作这个新的变量了。。所以对外面的就没有变化了 let i = 0; let fn = function (n) { i += 2; return function (m) { i += (++n) + (m--); console.log(i); } }; let f = fn(2); f(3); fn(2)(3); f(4); console.log(i); //f(3) = 2 + (2+1) + 3 =8 //fn(2,3) = 8 + 2 + (2 + 1) + 3 = 16 //f(4) = // 这个时候要注意 f = fn(2) 之后 f实际上是。f = function(m) { // i += (++n) + (m--); // console.log(i); // } //他已经没有上面的I+=2le。 也是是f(4)执行的时候,执行的是 //16+ ++n + m 又因为n被保存在函数中(闭包)所以这个时候的n是第一次执行之后的3、 也就是 16 + 4 + 4 = 24 let n = 10, obj = {n: 20}; let fn = obj.fn = (function () { this.n++; n++; return function (m) { n += 10 + (++m); this.n += n; console.log(n); } })(obj.n); fn(10); obj.fn(10); console.log(n, obj.n); // 32 // 53 // 32 53 这个代码加几个console.log就简单了 let n = 10, obj = {n: 20}; let fn = obj.fn = (function () { this.n++; n++; console.log('123 '+this.n,n); console.log(this); return function (m) { console.log(n,m); n += 10 + (++m); this.n += n; console.log(n); console.log("n " + this.n) } })(obj.n); fn(10) obj.fn(10); 在一开始的立即执行函数中,this.n 不存在,因为this指向window,立即执行函数是单独的作用域,拿不到let n=10的值,下面n++ = 11 然后,return function(m) {}在这个过程中,他没有计算,只是返回出来这个函数。 然后fn(10) 进入function(m) {}这个函数,也就是11 + 10 +11=32、 同时32赋值给n,作为闭包保存下来,接着obj.fn执行。 32+ 10 + 11 = 53.在这中间, this.n 第一次是nan,第二次是 73 let a = {n: 4};let b = a; b.x = a = {n: 10}; console.log(a.x); console.log(b.x); //undefined //{n:10} function C1(name) { if (name) this.name = name; } function C2(name) { this.name = name; } function C3(name) { this.name = name || 'join'; } C1.prototype.name = 'Tom'; C2.prototype.name = 'Tom'; C3.prototype.name = 'Tom'; alert(new C1().name + new C2().name + new C3().name); //代码要这样看 function C1(name) { console.log(name) if (name) this.name = name; } function C2(name) { console.log(name) this.name = name; } function C3(name) { console.log(name) this.name = name || 'join'; } C1.prototype.name = 'Tom'; C2.prototype.name = 'Tom'; C3.prototype.name = 'Tom'; console.log(new C1().name + new C2().name + new C3().name); //Tomundefinedjoin 发现三个函数内部name都是undefined a函数,发现name 是undefined就不执行if操作,那么就顺着原型链向上找,找到了prototype的值 b函数直接把name也就是undefined赋值给name c函数 判断是不是undefined,是的话就用join let m = 20; let Fn = function (n, m) { this.n = n; this.aa = function () { console.log(this.n + (++m)); } }; Fn.prototype.bb = function () { console.log(this.n + m); }; let f1 = new Fn(10, 20); Fn.prototype = { cc: function () { console.log(this.n + m); } }; let f2 = new Fn(30); console.log(f1.constructor === f2.constructor); console.log(f1.constructor); console.log(f2.constructor); f1.aa(); f1.bb(); f1.cc(); f2.bb(); f2.cc(); f2.__proto__.cc(); false ƒ (n, m) { this.n = n; this.aa = function () { console.log(this.n + (++m)); } } ƒ Object() { [native code] } 31 30 VM12877:23 Uncaught TypeError: f1.cc is not a function at <anonymous>:23:4 VM12918:24 Uncaught TypeError: f2.bb is not a function at <anonymous>:24:4 50 NaN
-
扁平化n维数组
//最终版 [1,[2,3]].flat(2); [1,[2,3,[4,5]]].flat(3); [1,[2,3,[4,5,[...]]]].flat(Infinity); //初始 function flatten(arr) { while (arr.some(item=>isArray(item))) { arr = [].concat(...arr); } return arr; } flatten([1,[2,3]]); flatten(1,[2,3,[4,5]]) 实质是利用递归和数组合并的方法concat实现扁平
-
去重
//最终版 Array.from(new Set([1,2,3,3,4,4])) [...new Set([1,2,3,3,4,4])] //初始版 Array.prototype.distinct = function() { var arr = this, result = [], i, j, len = arr.length; for (i = 0; i < len; i++) { for (j = i + 1; j < len; j++) { if (arr[i] === arr[j]) { j = ++ i; } } result.push(arr[i]); } rturn result; } [1,2,3,3,4,4].distinct();
-
排序
//终极篇 [1,2,3,4].sort(); [1,2,3,4].sort((a,b)=> b-a); //初始版 见排序
-
最大值
//终极版 Math.max(...[1,2,3,4]) Math.max.apply(this,[1,2,3,4]) [1,2,3,4].reduce((prev, cur ,curIndex, arr) => { return Math.max(prev, cur) }) Math.max()是Math对象内置的方法,参数是字符串 reduce是es5的数组api,参数有函数和默认初始值 pre是上一次的返回值,cur是当前值,curindedx是当前值索引,arr是当前数组 //初始版 先排序再取值
-
求和
//终极版 [1,2,3,4].reduce(function (prev,cur){ return prev + cur; }, 0) //开始篇 function sum(arr) { var len = arr.length; if (len == 0) { return 0; } else if (len == 1) { return arr[0]; } else { return arr[0] + sum(arr.slice(1)); } } sum([1,2,3,4]);
-
合并
//终极版 [1,2,3,4].concat([4,5]); [...[1,2,3], ...[4,5,6]] [1,2,3,4].push.apply ([1,2,3,4], [5,6]); //初始版 [5,6].map(item => { [1,2,3,4].push(item); })
-
判断是否包含值
//终极版 [1,2,3].includes(4) //false [1,2,3].indexOf(4) // -1 [1, 2, 3].find((item) => item ===3) //3 [1, 2, 3].finIndex((item) => item === 3) //2 //开始篇 [1,2,3].some(item => { return item == 3 })
-
类数组转化
Array.prototype.slice.call(arguments); Array.prototype.slice.apply(arguments); Array.from(arguments); [...arguments]; //类数组对象有length属性,但是不具备数组方法, //call和apply改变slice里面的this指向arguments //Array.from 把类似数组或可迭代的对象创建为数据 //。。。把类数组对象扩展为字符串,然后再定义为数组 //开始篇 Array.prototype.slice = function(start, end) { var result = new Array(); start = start || 0; end = end || this.length; for (var i = start; i < end; i++) { result.push(this[i]); } return result; }
-
每一项设置值
//终极版 [1, 2, 3].fill(false) //初始版 [1, 2, 3].map(()=>0) var arr = [1, 2, 3]; for (var i = 0, j = arr.length; i != j; i++) { arr[i] = 0; }
-
每一项都满足
[1, 2, 3].every (item => { return item > 2 })
-
有一项满足
[1, 2, 3].some(item => { return item > 2 })
-
过滤数据
[1, 2, 3].fliter(item => { return item > 2 })
-
对象和数组转换
Object.keys({ name :'张三', age : 14 }) Object.values({ name :'张三', age : 14 }) Object.entries({ name :'张三', age : 14 })
-
有关异步
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') }) async1() new Promise(function (resolve) { console.log('promise1') resolve() }).then(function () { console.log('promise2') }) console.log('script end') //答案 script start async1 start async2 promise1 script end async1 end promise2 settimeout https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7
-
异步解决方案的发展历程和优缺点
1. 回调函数(callback)
setTimeout(() => { // callback 函数体 }, 1000)
缺点:回调地狱,不能用 try catch 捕获错误,不能 return
回调地狱的根本问题在于:
- 缺乏顺序性: 回调地狱导致的调试困难,和大脑的思维方式不符;
- 嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身,即(控制反转);
- 嵌套函数过多的多话,很难处理错误。
ajax('XXX1', () => { // callback 函数体 ajax('XXX2', () => { // callback 函数体 ajax('XXX3', () => { // callback 函数体 }) }) })
优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行)。
2. Promise
Promise 就是为了解决 callback 的问题而产生的。
Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新 Promise,如果我们在 then 中 return ,return 的结果会被 Promise.resolve() 包装。
优点:解决了回调地狱的问题。
ajax('XXX1') .then(res => { // 操作逻辑 return ajax('XXX2') }).then(res => { // 操作逻辑 return ajax('XXX3') }).then(res => { // 操作逻辑 })
缺点:无法取消 Promise ,错误需要通过回调函数来捕获。
3. Generator
特点:可以控制函数的执行,可以配合 co 函数库使用。
function *fetch() { yield ajax('XXX1', () => {}) yield ajax('XXX2', () => {}) yield ajax('XXX3', () => {}) } let it = fetch() let result1 = it.next() let result2 = it.next() let result3 = it.next()
4. Async/await
async、await 是异步的终极解决方案。
优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题;
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。
async function test() { // 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式 // 如果有依赖性的话,其实就是解决回调地狱的例子了 await fetch('XXX1') await fetch('XXX2') await fetch('XXX3') }
下面来看一个使用
await
的例子:let a = 0 let b = async () => { a = a + await 10 console.log('2', a) // -> '2' 10 } b() a++ console.log('1', a) // -> '1' 1
对于以上代码你可能会有疑惑,让我来解释下原因:
- 首先函数
b
先执行,在执行到await 10
之前变量a
还是 0,因为await
内部实现了generator
,generator 会保留堆栈中东西,所以这时候 a = 0 被保存了下来; - 因为
await
是异步操作,后来的表达式不返回Promise
的话,就会包装成Promise.reslove(返回值)
,然后会去执行函数外的同步代码; - 同步代码执行完毕后开始执行异步代码,将保存下来的值拿出来使用,这时候
a = 0 + 10
。
上述解释中提到了
await
内部实现了generator
,其实await
就是generator
加上Promise
的语法糖,且内部实现了自动执行generator
。如果你熟悉 co 的话,其实自己就可以实现这样的语法糖。本题链接:
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/11
计算机知识
-
http和https的区别
- 超文本传输协议http是一个通过互联网传输和接受消息的协议,https是基于安全套接字的超文本传输协议。 也可以说,https+http+ssl,
- http的url以http开头,https的url以https开头,
- http是不安全的,https是安全的,他对网页进行了校验,
- http的标准端口是80端口,https的标准端口是443
- osi协议中,http工作在应用层,https工作在传输层
- http不需要加密,https要进行加密。http不需要证书,https要证书
- http默认是短连接,也就是浏览器和服务器没进行一次http操作都会简历一次链接,但是任务结束就会中断链接,二https默认使用长链接,也就是在响应头中加入 connection:keep-alive
-
Web App 、hybrid App、Native App
- webapp是指采用h5语言写出来的app,不需要下载安装,类似于现在说的轻应用,生存在浏览器中的 ,基本上可以说是触屏版的网页应用,作用是向广大的用户发布一组复杂的内容和功能。
- 具有开发成本低,更新快,无需手动更新,能够跨平台终端
- 缺点是,入口临时,并且无法获取系统界别的通知提醒,用户留存率低,设计受限制诸多,比如网络环境没渲染性能,平台特性,限制于浏览器。并且体验较差。
- 详细的是,网络环境差,自身渲染性能比较弱,对复杂的图形样式多样的动效,自定义字体等支持不够强,由于html语言的技术特性,是无法调用系统级别的权限,比如地点,通讯录,语音等。
- 优化在于,简化不重要的动画特性,简化复杂的图形文字样式,少用手势,减少与浏览器的冲突,少用弹窗,减少页面内容,减少空间数量,减少页面跳转次数。
- hybrid App是指,半原生半web的混合类app。越来越火,主要原因是支持热更新,不再受上线审核等复杂流程。
- 很多电商类新闻类app都采用这张方式进行开发。
- 体验的好坏,主要取决于底层中间件的交互和跨平台的能力
- Native App是一种基于智能手机本地操作系统如ios,andriod并使用原生程序编写运行的第三方应用程序,依托于操作系统,交互性很强,是一个完整的app。。但是开发成本高,维护成本高,更新也缓慢。
- webapp是指采用h5语言写出来的app,不需要下载安装,类似于现在说的轻应用,生存在浏览器中的 ,基本上可以说是触屏版的网页应用,作用是向广大的用户发布一组复杂的内容和功能。
-
seo是一种搜索引擎优化。
- 一种是内部优化,通过meta标签进行优化,比如title,keywords。内部链接的优化包括相关性链接,锚文本链接,图片链接,然后每天对网站内容进行更新
- 外部优化是友情链接,博客,论坛,b2b,新闻
-
commonjs,amd和cmd都是js模块化的规范。
commonjs是服务器端js模块化的规范,nodejs是这种规范的实现。
amd(异步模块定义)和cmd(通用模块定义)是浏览器端js模块化的规范,requirejs遵循的amd,seajs遵循的是cmd
-
commonjs,一个单独的文件就是一个模块,加载模块使用require方法,改方法读取一个文件并且执行,最后返回文件内部的export对象。所以,定义一个模块就是写一个新的js文件,但是最后要将文件内容exports出来,下面看一下如何定义模块和加载模块。
// 定义一个module.js文件 var A = function () { console.log("我是定义的模块"); } //导出这个模块 //方法一 moudle.exports = A; //方法二 moudle.exports.test = A; //方法三 exports.test = A; //在写一个test,js文件,去调用刚才定义好的模块,这两个文件在一个目录下。 var moudle = require("./module"); //第一种方式 module(); //第二种方式 module.test(); //第三种方式 module.test(); //执行这个文件,前提是本机安装了nodejs,首先cd到当前目录下,然后执行 node test.js //输出结果是 我是定义的模块
当我们执行 node test.js的时候,根据var module = require ("./module")同一个目录下的module.js文件也会被加载,并且吧这个文件的export对象返回,赋值 给exports,然后我们调用module.test就相当于执行了module文件。
commonjs文件的加载是同步的,所以只有加载完成之后才能执行后面的操作,像nodejs主要用于服务器的编程,加载的模块文件基本都已经存在本地硬盘,所以加载起来很快,不用考虑异步加载的方式,所以commonjs比较适合,但是如果是浏览器环境,要从服务器加载模块,这就必须采用异步,模式了。
-
AMD(异步模块定义)
AND规范通过define的方法去定义模块,通过reuqire方法去加载模块,require实现了这种规范。
AMD只有一个接口,define(id?dependence?factory)它要在声明模块的时候指定所有的依赖,并且还要当形参传到factory中,要是没有什么依赖,就定义简单的模块。
//编写一个module1.js文件 //定义独立的模块 define({ methodA: function() { console.log("我是module1的methodA") }, methodB : function() { console.log("我是module2的methodB"); } }); //编写一个module2.js文件 //另一种定义独立模块的方式 define (function() { return { methodA : function() { console.log("我是module2的methondA"); }, methodB : function() { console.log("我是module2的methodB"); } } }); //编写一个module3.js文件 //定义非独立的模块,这个模块依赖其它的模块 define(['module1', 'module2'], function(m1, m2)) { return { methodC: function() { m1.methodA(); m2.methodB(); } } } //在定义一个main.js去加载这些模块 require(['module3'], function(m3)) { me.methodC(); } //我们在一个html文件中通过ruquirejs加载这个main.js。 data-main右边的main指的就是main.js <script data-main = "main" src = "require.js"></srcipt>
-
CMD(通用模块定义)
define (function(ruquire, exports, module)) { var a = require('./a'); a.dosomething(); var b = require('./b'); b.dosomethine } //AMDmoren 推荐是 define(['./a', './b'], function(a, b) { a.dosomething(); b.dosomething(); })
-
AMD和CMD的区别
- AMD是提前执行,CMD是延迟执行,CMD推崇懒加载,依赖酒就,AMD推崇前置。AMD支持CMD
- AMD的API默认是一个当多个用,CMD的API严格区分
-
commonjs中ruquire和exports 和 es5 import和export的区别
- commonjs模块重要的特征是在加载时执行,也就是脚本代码在require的时候就会全部执行,一旦出现了某个模块被循环加载,就只输出已经执行的部分,还没有执行的部分不会输出
- es6是动态引用,如果使用import从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块的的引用,需要开发者自己保证。
- import和export最终都会编译成rquire和exports来执行。
- commonjs规定,每个模块内部,module变量代表当前模块,这个变量时一个对象,它的exports属性,是对外的接口。加载某个木块,其实是加载module.exports属性,
-
-
树的深度优先遍历和广度优先遍历,如何实现
-
深度优先遍历是搜索算法的一种, 它沿着树的深度遍历树的节点,尽可能深地搜索树的分支,当节点v的所有边都已经被探寻过,将回溯到发现发现节点v的那条边的其实节点,这个过程一直进行到已探寻源节点到其它所有节点为止,如果还有没有被发现的节点, 那么就选择其中一个没有被发现的节点为源节点进行以上操作,直到所有节点都被探寻完成。
简单地说,dfs就是从图中的一个节点开始追溯,知道最后一个节点,然后开始回溯,继续追溯到下一跳路径,直到到达所有的节点,如此往复,直到没有路径为止。
dns是盲目搜索,无法保证搜索到的路径是最短路径, 也不是搜索指定路径,Graph.prototype.dfs = function() { var marked = [] for (var i=0; i<this.vertices.length; i++) { if (!marked[this.vertices[i]]) { dfsVisit(this.vertices[i]) } } function dfsVisit(u) { let edges = this.edges marked[u] = true console.log(u) var neighbors = edges.get(u) for (var i=0; i<neighbors.length; i++) { var w = neighbors[i] if (!marked[w]) { dfsVisit(w) } } } } graph.dfs()
-
广度优先遍历是从根节点开始,沿着图的宽度遍历节点,如果所有节点均被访问过,那么算法终止, bfs同样是盲目搜索,。一般用队列数据结构来辅助实现bfs。
bfs从一个节点开始,尝试访问尽可能靠近它的目标节点,本质上这种遍历是在涂上逐层移动的, 首先检查最靠近第一个节点的层,再追歼移动到离起始节点最远的层。
创建一个队列,并把开始节点放入到队列中,如果队列非空,那么久从队列中去除一个节点,并检测它是否是目标节点,如果是,那么结束搜寻,返回结果,如果不是,那么吧所有没被检测过的子节点放入队列中,
Graph.prototype.bfs = function(v) { var queue = [], marked = [] marked[v] = true queue.push(v) // 添加到队尾 while(queue.length > 0) { var s = queue.shift() // 从队首移除 if (this.edges.has(s)) { console.log('visited vertex: ', s) } let neighbors = this.edges.get(s) for(let i=0;i<neighbors.length;i++) { var w = neighbors[i] if (!marked[w]) { marked[w] = true queue.push(w) } } } }
-
浏览器的高级特性
-
浏览器内核
- 主要分为两部分,一个是渲染引擎,和js引擎
- 渲染引擎负责取得网页的内容,html,xml和图像等等,整理讯息,加入css和计算网页的显示方式。
- js引擎解释和执行js来实现网页的动态效果。
-
当一个ajax请求由于跨域问题不能顺利完成时,一般是因为浏览器不会吧存在跨域限制的服务器端响应返回给客户端处理。也就是请求发出去了,服务器端收到请求并且正常返回结果,但是被浏览器拦截了
-
xss攻击
也就是跨站脚本,是发生在目标用户的浏览器层面上的,当渲染dom的过程中,发生了不在预期内执行的js代码,就是xss攻击,
- 反射性xss,是指发生请求时,xss代码出现在请求url中,作为参数提交到服务器,服务器解析并且响应,响应的结果包含xss代码,最后浏览器解析之后执行。主要是没有对用户输入的信息进行过滤,没有剔除dom节点中存在的一些有危害的事件和有危害的dom节点
- 存储型xss,主要是吧xss代码发送到服务器,然后在下次请求时候就不用带上xss了,比如留言板xss,可以提交一条包含xss代码的留言到数据库,当目标用户查询留言时,留言的内容就会从服务器解析加载出来,浏览器发现有xss代码就执行了
- dom xss主要是由于eval。他会吧一段字符串转化js
-
xss防御
- 对cookie进行保护,比如设置cookie为http-only,防止客户端通过document.getcookie获取cookie。
- 对用户输入数据进行处理,比如编码,解码,过滤(吧输入的一些不合法的东西都过滤掉,比如style节点,iframe,script)
-
csrf攻击,也就是跨站点请求伪造, csrf攻击者在用户登录了目标网站后,诱导用户访问一个攻击网站,利用目标网站对用户的信任。以用户数很粉在攻击页面对目标网站发起伪造用户操作的请求。举个例子。如果我在购物网站买东西, 那么我的cookie已经保存了,这个时候我点击进入一个恶意网站,恶意网站发起一个在购物网站购物的请求,自动戴上了我的ciikie,那么购物网站认为是我本人发起的请求, 就真的买了。。这就是csrf攻击
-
csrf的防御方式
- 尽量使用post
- 加验证码,强制用户与应用进行操作才嫩恶搞完成最终请求, 是一个很有效的方式。
- 使用token,当用户访问某个页面的时候,服务器端生成一个token,放在用户的session中,然后在页面的表单中附带上token参数,用户提交请求的时候,服务器端验证表单中的token和用户session中的是否一致,一致就是合法的,不一致就不合法。
-
解决跨域的方式
- 由于script 和img link这种标签是没有跨域限制的,所以我们利用这一点,衍生出jsonp,通过其他来源动态产生的json数据,jsonp请求一定需要对方的服务器做支持才可以。
- jsonp的优点是兼容性好并且简单,可以解决主流浏览器的跨域数据访问的问题,缺点是仅仅支持get方法,具有局限性,不安全,榕溪遭受xss攻击
- cors 需要前后端同时支持,浏览器会自动进行cors通信,实现cors的通信的关键是后端只要后台实现了cors就实现了跨域。分为两种情况,一种是简单请求和复杂请求
- 简单请求满足两大条件
- 使用get head 和post方法之一
- content-type 是text/plain、 multipart/form-data、 application/x-www.form-urlencoded
- 不是简单请求就是复杂请求了
- node中间件代理
- 同源策略是浏览器遵循的标准,如果是服务器向服务器发起请求就无需遵循同源策略
- 代理服务器要完成的任务
- 接受客户端请求
- 把请求转发给服务器
- 把服务器的响应数据拿到
- 把响应数据转发给客户端
- nginx反向代理
- 由于script 和img link这种标签是没有跨域限制的,所以我们利用这一点,衍生出jsonp,通过其他来源动态产生的json数据,jsonp请求一定需要对方的服务器做支持才可以。
-
从输入url到返回数据发生了什么
- 浏览器开启一个线程处理这个请求,如果是http协议就按照web的方式来处理
- 调用浏览器内核中的对应方法,比如loadurl
- dns解析
- tcp连接
- http请求
- 服务器请求并返回http报文
- 浏览器解析渲染页面
- 连接结束
-
dns解析的过程就是寻找那台机器上有你需要资源的过程,当你在浏览器输入一个地址时,比如ww w.baidu.com ,他其实不是百度网站真正意义上的地址,互联网上每一台电脑的唯一标识是他的ip地址,但是ip地址不方便用户记忆,用户更喜欢简单的网址,所以互联网设计者就在ip地址和网址间做了一个转换,也就是dns解析。 dns建立在udp的基础之上。端口号是53.
dns解析是一个递归查询的过程,首先在本地域名服务器查询ip地址,如果没有找到的话,就去根域名发送一个请求,如果根域名也没有,那么久向顶级域名com发送请求,依次类推下去,知道找到ip地址并且缓存到本地。
-
tcpip协议的三次握手和四次挥手
- 简单的说就是 A对B说,我要连接啦,B回A说,好的,我知道你要连接啦。然后A开始发送数据。
- 官方的说就是,建立连接的时候,客户端发送syn=i包,进入等待状态,然后客户端收到syn包,发送一个确认标志ack= syn+1,并且发送一个自己的syn=j包,客户端收到syn和ack包就完成了,开始传送数据。
- 简单的说A对B说,我要停止发送啦,B对A说,你等下,我还没结束,然后B又对A说,我接受完成啦, 然后A就停止发送了
- 官方的说,就是A发送fin报文段给服务器端,进入等待状态,B收到了fin字段,向A发送ACK报文段,同时告诉A数据发送结束,然后B再次发送结束链接的请求,A收到之后,再去发送ack报文段,此时b关闭,a在一段时间之内没收到回复也关闭
-
http请求
发生在客户端,发送请求的过程就是构造http请求报文并通过tcp协议中发送到服务器指定端口,http请求报文由三部分组成请求行,请求报头和请求征文
- 请求行类似于方法+网址, 常用的方法有get,post,put,delete,option,head
- 请求报头允许客户端向服务器端传递请求的附加信息和客户端自己的信息。比如accept,content-type,cookie那些
- 请求正文,当使用post,put请求的时候,通常需要客户端向服务器传递数据,这些数据就储存在请求正文中,请求数据一般为json
- 浏览器缓存分为强缓存和协商缓存,当客户端请求某个资源的时候,获取缓存的流程如下
- 现根据这个资源的一些http-header判断是否命中强缓存,如果命中,那么直接从本地获取强缓存资源,不会发请求到服务器。
- 当强缓存没有命中的时候,客户端法强求到服务器,服务器通过requert header来判断是否命中协商缓存,也就是http再验证,如果命中,服务器将请求返回,但不返回资源,而是告诉科幻段直接从缓存中获取。
- 强缓存和协商缓存的共同点是,如果命中缓存,服务器都不会返回资源,
- 强缓存不会发送请求到服务器。
- 协商缓存也没有命中的时候,服务器会返回资源。
- ctrl+f5会跳过强缓存和协商缓存
- f5会跳过强缓存
-
服务器处理请求并返回http报文
返回的状态码由三位数组成,第一个数定义了响应的类别
1xx :请求已经接受,继续处理
2xx:成功,表示请求已经被成功的接受,理解接受
3xx:重定向,要完成请求必须进行进一步的操作
4xx:客户端错误:请求有语法错误或者请求无法实现
5xx: 服务器端错误,服务器不能实现合法的请求
- 301永久重定向
- 302临时重定向,新的url会从response中的location返回,浏览器会发起新的request
- 304在上次请求之后,请求的网页没有修改过,是有意服务器不会返回网页的内容,代表上次的文档已经被缓存了,客户端已经执行了GET,但文件未变化。
- 400客户端请求有错误,服务器不能理解
- 401.1 未授权登录失败
- 401.2未授权,服务器配置问题导致登录失败
- 401.3 禁止访问资源
- 403 禁止访问
- 404找不到
- 500 服务器内部错误
- 502 网关错误
- 504 超载或者 停机维护,短暂的
响应报文一般是htmlcss,js图片等
-
浏览器解析渲染页面。
部分渲染树或者整个渲染树需要重新分析并且节点尺寸需要重新计算,这个就是重排, 节点的属性发生改变或者样式发生改变,例如改变背景颜色,屏幕上的部分内容需要更新,这样子叫做重绘。
添加删除更新dom节点,display:none,添加动画,添加样式表,用户行为,visibility都可能引起重绘和重排。
当客户端收到数据的时候,他需要一边解析一边渲染,首先浏览器解析html文件构建dom树,然后解析css文件构建渲染树,当渲染树构建完成后,开始布局渲染树并重绘到屏幕上,这上面涉及两个概念,一个是重排,一个是重构。dom的各个元素都是盒模型的形式存在,这些都要浏览器计算位置和大小,这个就叫做重排。 当盒模型的大小位置以及其它属性都确定之后,浏览器就开始绘制内容,这个就是重绘,页面在第一次加载的时候,必定会经历重绘和重排的,这个过程非常的消耗性能,所以我们应该减少这两个操作。
js解析是由浏览器解析引擎完成的,js是的单线程运行,也就是一个时间只能做一件事情,所有的任务都要排队,前一个任务结束,后一个任务才能开始,,但是又存在某些任务比较耗时,比如请求数据和io读写,这就需要一种机制可以实现先执行后面的任务,也就是异步任务。本质上就是主线程加上一个任务队列,所有的同步任务都在主线程上运行,异步任务都放在任务队列中执行。 脚本先执行主线程,然后从任务队列里提取时间。运行其中的任务,这个过程是不断重复的,所以叫做事件循环。异步的原因是,js主要用途是和用户进行交互,以及操作dom,例如一个线程要增加dom,另一个要删除, 容易引发冲突。
-
web优化
-
减少http请求
-
使用cdn
-
使用缓存
-
样式表放在顶部
-
脚本放在底部
-
避免使用css表达式
-
减少dns查询
-
避免重定向
-
代码要压缩
-
删除重复的脚本
-
ajax可缓存
-
避免空图像
-
避免过滤器
-
使用雪碧图
-
最小化dom访问
-
减小cookie
-
避免404
-
使用静态资源服务器
-
使用js+模板,减少因为html标签导致的带宽浪费,前端用变量保存ajax的数据结果,每次操作本地变量,减少请求次数
-
用innerhtml代替dom操作,减少dom操作次数,优化js性能
-
需要设置的样式很多的时候,直接切换classname 而不是style
-
少用全局变量,缓存dom节点查找的结果,减少io读取操作
-
避免使用table,要等其中的内容完全下载之后才会显示出来,对比div和css要满。
-
尽量向前端优化,减少数据库操作,减少磁盘io,在不影响功能和体验的情况下,讷讷个在浏览器执行的不要在服务器端执行,能在缓存服务器直接返回的不要到应用服务器,,程序能直接去的的结果不要到外部取,本机内能取得的数据不要远程取,内存能取到的不要到磁盘,缓存中有的不要去数据库。减少数据库操作。
-
优雅降级和渐进增强。
- web站点对所有新式浏览器都能正常工作,如果用户使用的是老式浏览器,代码就会针对旧版本的ie进行降级处理了,使得在旧版本浏览器以某种形式降级体验缺不至于完全不能用。比如border-shadow
- 渐进增强是被所有浏览器支持的基本功能开始,初步增加那些只有新版本浏览器才支持的孤男寡女,向页面增加不影响基础浏览器的额外样式和功能,他们会自动的呈现出来发挥作用,比如默认使用flash上传,但是如果浏览器支持h5上传功能,那么久使用h5来实现更好的体验
-
##发散性问题
-
项目中遇到的问题,如何解决
- 自己在学习的时候,由于没有ui给图,也没有测试机,写样式都是按照电脑屏幕写固定的px,但是在实习中发现,有与设备大小不一样, 会导致在一个设备上正常显示,在别的设备上显示不正常。在兼容各个机型之间我一开始做的不太好,为了解决这个问题, 我一开始想用媒体查询来做的,后来发现,媒体查询不适合解决这样子的问题,因为媒体查询主要是针对大屏小屏那种区别很大的, 写几套样式,后来我采用了rem,rem是一种相对单位,相对于html的font-size,剩下的就是我解决页面有多少rem了, 我现在一般页面会分为10rem
-
最近在学什么,之后的规划
Need to do
-
对加班的看法
- 就像借钱一样, 救急不救穷
-
对前端的看法
- 是最贴近用户的程序员,比后台,数据库,产品,运营,安全都要近
- 实现界面交互
- 提升用户体验
- 通过nodejs可以完成服务端的一些事情
-
前端的职责
- 参与项目,快速高质量完成效果图
- 与团队成员,ui设计,产品经理,后台开发的沟通
- 做好页面接口,页面重构和用户体验
- 处理hack,兼容,写出有没的代码格式
- 针对服务器的优化,拥抱最新的前端技术
-
自我介绍
最喜欢的是专业领域前端开发。一开始学习前端是因为丰富多彩的页面设计,在不断的学习中我发现,前端是一个很广很大的概念,包含着网页开发,移动端网页开发以及各种小程序,这是离用户距离最近的一个方向。同时,随着技术的发展,前端可以完成的事情越来越多, nodejs的出现,可以让前端开发者实现后台功能,各种小程序以及native的流行,已经不需要为ios和安卓分别写一套代码,甚至前端可以完成制作游戏,制作桌面应用等任务。
我从大一暑期开始学习前端,从基础的html、css、js开始学习,制作一些基本网页,在老师的带领下,为学校护理学院实验中心设计并制作了官网。在大二的上学期时候,我学习了css3、jquery以及bootstrap,并与同学一起参与服务外包等比赛,取得一点小成绩。到了大二下学期的时候,我又学习了git版本管理以及打包工具webpack。 并开始学习模块化和面向对象prototype编程,尝试使用sass和less等去简化css的编写,并参与老师的校企合作项目,为南通市企业制作后台管理系统。大二暑假在杭州同花顺公司实习,与产品经理,后端程序员,客户端开发一起开需求会,讨论接口数据,前后端联调完成项目,并准时上线。大三上学期我认真学习前端基础,写自己的博客,并在github上写了一些demo,大三下学期我学习微信小程序,了解react的相关知识,并开始与搭档一起独立做东西,自己提需求,自己想流程,自己实现。
学习规划:打算在大三暑假前学习react,并做一个自己的demo,了解前端框架,争取理解框架的实现原理,尝试做一些自己的组件 -
排序算法
-
树的查询和深度
https://www.cnblogs.com/yexiaochai/p/4876099.html
在业务代码中 实践 继承
业务中分模块,模块之间如何通信。
性能优化。分析, 优化 瓶颈
代码如何组织