前端常见面试题整理

来源:互联网 发布:php怎么调用存储过程 编辑:程序博客网 时间:2024/05/29 19:54

前端常见面试题整理

关于BFC

  • BFC(Block Formatting Context),块级格式化上下文,指一个独立的渲染区域。它决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。在进行盒子元素布局的时候,BFC提供了一个环境,在这个环境中按照一定规则进行布局不会影响到其它环境中的布局。也就是说,如果一个元素符合了成为BFC的条件,该元素内部元素的布局和定位就和外部元素互不影响(除非内部的盒子建立了新的 BFC)。

  • 形成 BFC 的条件

    • 浮动元素,float的值不为none;( left || right || inherit )

    • 定位元素, position的值为 absolute || fixed;

    • display的值为 inline-block || table-cell ;

    • overflow的值不为visible( hidden || auto || scroll || inherit )
  • BFC常见作用

    • 清浮动

      • 对子元素设置浮动后,父元素会发生高度塌陷,也就是父元素的高度变为0。解决这个问题,只需要把父元素变成一个BFC就行了。

      • 常见的解决方法:为父元素设置 overflow:hidden 或浮动父元素。

      • 根本原因:创建BFC的元素,子浮动元素也会参与其高度计算,即不会产生高度塌陷问题。

    • 解决上下margin叠加问题

      • 在标准文档流中,块级标签之间竖直方向的margin会以大的为准,产生margin的塌陷现象。

      • 可以通过触发BFC来解决。

  • BFC布局规则:

    • 内部的盒子从顶端开始垂直地一个接一个地排列。

    • 两个盒子之间的垂直的间隙是由他们的margin 值所决定的。在一个BFC中,两个相邻的块级盒子的垂直外边距会产生折叠。

    • 每一个盒子的左外边缘(margin-left)会触碰到容器的左边缘(border-left)(对于从右到左的格式来说,则触碰到右边缘)。即使存在浮动也是如此。

    • BFC的区域不会与float box重叠。

    • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

    • 计算BFC的高度时,浮动元素也参与计算。

清浮动的方法

  • 在浮动元素下添加 <div class="clear"></div>(clear属性只能加在块元素)

    .clear{ height:0px;         font-size:0;         clear:both;}
    • 缺点:增加无意义标签
  • 给父元素设置 overflow:auto属性

  • 浮动元素父级应用after伪元素

    .clear:after{ content:'';              display:block;              clear:both;}

    IE6、7还要增加.clear{zoom:1;}


语义化

  • 页面结构清晰。

  • 有利于SEO,和搜索引擎建立良好沟通,有助于爬虫抓取更多的有效信息:爬虫依赖于标签来确定上下文和各个关键字的权重。

  • 方便其他设备解析(如屏幕阅读器、盲人阅读器、移动设备),以有意义的方式来渲染网页。

  • 便于团队开发和维护,语义化更具可读性,可以减少差异化。


Doctype作用

声明位于文档中的最前面,处于 <htm> 标签之前。告知浏览器以何种模式来渲染文档。

严格模式与混杂模式的区别及意义

  • 严格模式的排版和 JS 运作模式是以该浏览器支持的最高标准运行。

  • 在混杂模式中,页面以宽松的向后兼容的方式显示。模拟老式浏览器的行为以防止站点无法工作。

  • DOCTYPE不存在或格式不正确会导致文档以混杂模式呈现。

Doctype文档类型

该标签可声明三种 DTD 类型,分别表示严格版本、过渡版本以及基于框架的 HTML 文档。


HTML5新特性

  • 为了更好的实践语义化,增加了 <header><footer><nav><article>语义化标签

  • 在存储方面,提供了 sessionStoragelocalStorage 和离线缓存等,通过这些存储方式方便数据在 客户端的存储 和获取。

  • 在多媒体方面,规定了音频和视频元素 <audio><vedio>

  • 画布(Canvas) API、地理(Geolocation) API。

  • Websocket 是 Web 应用程序的传输协议,基于TCP,是一个HTML5协议。

    • 是一个持久化的协议,服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。

    • WebSocket提供了 双向的,按序到达的数据流服务器和客户端可以彼此相互推送信息,通过在客户端和服务器之间保持双工连接,服务器的更新可以被及时推送给客户端,而不需要客户端以一定时间间隔去轮询。( WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求。)

    • WebSocket允许跨域通信

    • 在 WebSocket API,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

  • web worker运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能

    (当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成,而 web worker 会在后台运行。)

    • worker主线程
      • 通过 worker = new Worker( url ) 加载一个JS文件来创建一个worker,同时返回一个worker实例。
      • 通过 worker.postMessage(data) 方法来向worker发送数据。
      • 绑定 worker.onmessage 方法来接收worker发送过来的数据。
      • 可以使用 worker.terminate() 来终止一个worker的执行。

localStorage

  • localStorage存储的值都是字符串类型(在处理 json 数据时,需要借助 JSON 类(JSON.parse() & JSON.stringify())实现字符串与 json 转换。)

  • 没有时间限制

//创建和访问 localStorage<script type="text/javascript">// Check browser supportif (typeof(Storage) !== "undefined") {    localStorage.setItem("key", "value");      // 将value存储到key字段    document.getElementById("result").innerHTML = localStorage.getItem("key");                //获取指定key本地存储的值} //还可以用点(.)操作符,及[]的方式进行数据存储//localStorage.removeItem(key)    删除指定key本地存储的值//localStorage.clear();           清除所有的key & value</script>

sessionStorage

针对一个 session 进行数据存储。当用户关闭浏览器窗口后,数据会被删除。

sessionStorage 存储的值也是字符串类型,方法同 localStorage。


  • 定义:

    • Cookie 就是浏览器储存在用户电脑上的一小段文本文件

    • Cookie 是纯文本格式,不包含任何可执行的代码

    • Cookie 由键值对构成,由分号和空格隔开

    • Cookie 虽然是存储在浏览器,但是 通常由服务器端进行设置

    • Cookie 的大小限制在 4kb 左右(4096字节)

  • 属性

    • expires / max-age

      • 控制 Cookie 失效时刻的选项。如果没有设置这两个选项,则默认有效期为 session,即会话 Cookie。这种 Cookie 在浏览器关闭后就没有了。

      • expires 是 http/1.0 协议中的选项,必须是 GMT 格式 的时间(可以通过 new Date().toGMTString() 或者 new Date().toUTCString() 来获得)

      • 在新的 http/1.1 协议中 expires 已经由 max-age 选项代替。如果同时设置了 max-age 和 expires,以 max-age 的时间为准。

      • 如果max-age为负数,则表示该cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该cookie即失效。max-age为负数的Cookie,为临时性cookie,不会被持久化,不会被写到cookie文件中。cookie信息保存在浏览器内存中,因此关闭浏览器该cookie就消失了。cookie默认的max-age值为-1。

      • 如果max-age为0,则表示删除该cookie。cookie机制没有提供删除cookie的方法,因此通过设置该cookie即时失效实现删除cookie的效果。失效的Cookie会被浏览器从cookie文件或者内存中删除。

      • Cookie对象的Expires属性设置为MinValue表示永不过期。

      • 注意:从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name与value属性。maxAge属性只被浏览器用来判断Cookie是否过期。

    • domainpath

      • namedomainpath 可以标识一个唯一的 Cookie

      • domainpath 两个选项共同决定了 Cookie 何时被浏览器自动添加到请求头部中发送出去。

      • domain 的默认值为设置该 Cookie 的网页所在的域名,path 默认值为设置该 Cookie 的网页所在的目录。

      • path 设置为“/”时允许所有路径使用Cookie。path属性需要使用符号“/”结尾。

      • 注意:修改、删除Cookie时,新建的Cookie除 valuemaxAge 之外的所有属性,例如 namepathdomain 等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。

      • 浏览器判断一个网站是否能操作另一个网站 Cookie 的依据是 域名

    • secure属性

      • 当设置为 true 时,表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息,所以不会被窃取到Cookie 的具体内容。默认为 false,通过一个普通的HTTP连接传输。

      • 目的:防止信息在传递的过程中被监听捕获后信息泄漏

      • secure属性并不能对Cookie内容加密,因而不能保证绝对的安全性。如果需要高安全性,需要在程序中对Cookie内容加密、解密,以防泄密。

    • HttpOnly属性

      • 如果在Cookie中设置了”HttpOnly”属性,那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息,(不能通过document.cookie获取。)这样能有效的防止XSS攻击。默认情况下,cookie不会带 HttpOnly 选项(即为空)。

      • 目的:防止程序获取cookie后进行攻击(XSS)

      • 在客户端是不能通过 js 代码去设置一个HttpOnly 类型的cookie的,这种类型的cookie只能通过服务端来设置。

      • 注意:HttpOnly 属性和 Secure 属性相互独立:一个 cookie 既可以是 HttpOnly 的也可以有 Secure 属性。

  • 创建和存储cookie

// 函数中的参数分别为 cookie 的名称、值以及过期天数function setCookie( c_name, value, expiresday){    var exdate = new Date();    exdate.setDate( exdate.getDate() + expiresday );    document.cookie = c_name + "=" + escape(value) +    (( expiredays == null ) ? "" : ";expires=" + exdate.toGMTString());}setCookie('name','hhh',1); // cookie过期时间为1天。// 如果要设置过期时间以秒为单位function setCookie(c_name,value,expiressecond){    var exdate=new Date();    exdate.setTime(exdate.getTime()+expiressecond * 1000);    document.cookie = c_name + "=" + escape(value) +    (( expireseconds== null ) ? "" : ";expires=" + exdate.toGMTString());}setCookie('name','hhh',3600);  //cookie过期时间为一个小时
  • 读取cookie值
// 函数中的参数为 要获取的cookie键的名称。function getCookie(c_name){    if (document.cookie.length>0){        c_start=document.cookie.indexOf(c_name + "=");        if (c_start!=-1){            c_start = c_start + c_name.length+1;            c_end=document.cookie.indexOf(";",c_start);            if (c_end==-1){                 c_end=document.cookie.length;            }            return unescape(document.cookie.substring(c_start,c_end));        }     }    return "";}var username= getCookie('name');console.log(username);
  • 判断cookie是否存在
// 函数中的参数为,要判断的cookie名称 function checkCookie(c_name){    username=getCookie(c_name);    if (username!=null && username!=""){        // 如果cookie值存在,执行下面的操作。        alert('Welcome again '+username+'!');    }else{        username=prompt('Please enter your name:',"");        if (username!=null && username!=""){            //如果cookie不存在,执行下面的操作。            setCookie('username',username,365)        }       }}
  • 删除cookie
function removeCookie(key) {    setCookie(key, '', -1);//这里只需要把Cookie保质期退回一天便可以删除}
  • Cookie满足同源策略

    网站 images.google.com 与 www.google.com 域名不一样,二者同样不能互相操作彼此的Cookie。

    访问完 zhidao.baidu.com 再访问 wenku.baidu.com 还需要重新登陆百度账号

    解决办法:设置 document.domain = ‘baidu.com’; //跨子域

参考自:Cookie/Session机制详解


Session

  • Session是指一个终端用户与交互系统进行通信的时间间隔,通常指从注册进入系统到注销退出系统之间所经过的时间。

  • Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。

    • 为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简。
  • 当多个客户端执行程序时,服务器会保存多个客户端的Session。Session机制决定了当前客户只会获取到自己的Session,而不会获取到别人的Session。各客户的Session也彼此独立,互不可见。

  • Session的使用比Cookie方便,但是过多的Session存储在服务器内存中,会对服务器造成压力。

    • 为防止内存溢出,服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。如果超过了超时时间没访问过服务器,Session就自动失效了。
  • Session在用户第一次访问服务器的时候自动创建。需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。

  • Session保存在服务器,对客户端是透明的,但它的正常运行仍然需要客户端浏览器的支持。这是因为 Session 使用 Cookie 作为识别标志

    • HTTP协议是无状态的,Session不能依据HTTP连接来判断是否为同一客户,因此服务器向客户端浏览器发送一个名为 SESSIONID 的Cookie,它的值为该Session的id(也就是HttpSession.getId()的返回值)。Session依据该Cookie来识别是否为同一用户。

    • 该Cookie为服务器自动生成的,它的maxAge属性一般为–1,表示仅当前浏览器内有效,并且各浏览器窗口间不共享,关闭浏览器就会失效。

    • 因此同一机器的两个浏览器窗口访问服务器时,会生成两个不同的Session。但是由浏览器窗口内的链接、脚本等打开的新窗口(也就是说不是双击桌面浏览器图标等打开的窗口)除外,这类子窗口会共享父窗口的Cookie,因此会共享一个Session。

  • URL地址重写
    URL地址重写是对客户端不支持Cookie的解决方案。URL地址重写的原理是将该用户Session的id信息重写到URL地址中。服务器能够解析重写后的URL获取Session的id。这样即使客户端不支持Cookie,也可以使用Session来记录用户状态。


Cache

  • Cache用于在Http请求期间保存页面或者数据,存储于服务器的内存中,允许您自定义如何缓存项以及将它们缓存多长时间。

  • Cache允许将频繁访问的服务器资源存储在内存中,当用户发出相同的请求后,服务器不是再次处理而是将Cache中保存的数据直接返回给用户。不发生服务器-客户端数据传输。

  • 当缺乏系统内存时,缓存会自动移除很少使用的或优先级较低的项以释放内存。该技术也称为清理,这是缓存确保过期数据不使用宝贵的服务器资源的方式之一。

  • Cache节省的是服务器处理时间。

  • Cache实例是每一个应用程序专有的,其生命周期==该应用程序周期。应用程序重启将重新创建其实例。

  • Cache不与会话相关,所以它是多会话共享的,因此使用它可以提高网站性能,但是可能泄露用户的安全信息,还由于在服务器缺乏内存时可能会自动移除,Cache因此需要在每次获取数据时检测该Cache项是否还存在。

Cache["ID"]="cc";   //或者Cache.Insert("ID","test");String ID =Cache["ID"].ToString();

几种存储方式的区别

通常使用最频繁的是Session。

Session缓存和Cache缓存的区别:

  • 最大的区别是Cache提供缓存依赖来更新数据,而Session只能依靠定义的缓存时间来判断缓存数据是否有效。

  • 即使应用程序终止,只要Cache.Add方法中定义的缓存时间未过期,下次开启应用程序时,缓存的数据依然存在。而Session缓存只是存在于一次会话中,会话结束后,数据也就失效了。

  • Session容易丢失,导致数据的不确定性,而Cache不会出现这种情况。

  • 由于Session是每次会话就被加载,所以不适宜存放大量信息,否则会导致服务器的性能降低。而Cache则主要用来保存大容量信息,如数据库中的多个表。

  • 用户停止使用应用程序之后,Session信息仍在内存中存留一段时间

Session 和 Cookie的区别:

  • cookie数据存放在客户的浏览器上,session数据放在服务器上。

  • session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。考虑到减轻服务器性能方面,应当使用COOKIE。

  • sessionStorage是当前对话的缓存,浏览器窗口关闭即消失,localStorage持久存在,除非清除浏览器缓存。

  • cookie在浏览器与服务器之间来回传递;sessionStorage和localStorage不会把数据发给服务器,仅在本地保存。

  • cookie数据还有路径的概念,可以限制cookie只属于某个路径下。

特性 Cookie localStorage sessionStorage session 数据有效期 一般由服务器生成,可设置失效时间。如果在浏览器生成,默认是关闭浏览器之后失效 除非被清除,否则永久保存 仅在当前会话有效,关闭页面或浏览器后被清除 服务器会把长时间没有活动的Session从服务器内存中清除,此时Session便失效。Tomcat中Session的默认失效时间为20分钟。 存储数据大小 4KB 一般 5MB 一般 5MB 20条Cookie 作用域 在所有同源窗口共享 在所有同源窗口共享 不在不同的浏览器窗口中共享 20条Cookie

display:none 和 visibility:hidden 的区别

  • display:none :隐藏对应的元素,在文档布局中不再给它分配空间

  • visibility:hidden :隐藏对应的元素,但是在文档布局中仍保留原来的空间


  • link 属于HTML标签,而 @import 是CSS提供的;

  • 页面被加载的时,link 会同时被加载,而 @import 被引用的CSS会等到引用它的CSS文件被加载完再加载;

  • @import 只在IE5以上才能识别,而 link 是HTML标签,无兼容问题;

  • link 方式的样式的权重高于 @import 的权重。


关于box-sizing属性

  • box-sizing属性主要用来控制元素的盒模型的解析模式。默认值是content-box。

    • content-box:让元素维持 W3C 的标准盒模型。元素的宽度/高度由border + padding + content的宽度/高度决定,设置width/height属性指的是content部分的宽/高
    • border-box:让元素维持 IE 传统盒模型(IE6以下版本和IE6~7的怪异模式)。设置width/height属性指的是border + padding + content
  • 标准浏览器下,按照W3C规范对盒模型解析,一旦修改了元素的边框或内距,就会影响元素的盒子尺寸,就不得不重新计算元素的盒子尺寸,从而影响整个页面的布局。

  • 应用场景:统一表单元素的风格


CSS 选择符

  • id选择器( # myid)

  • 类选择器(.myclassname)

  • 标签选择器(div, h1, p)

  • 相邻选择器(h1 + p)

  • 子选择器(ul > li)

  • 后代选择器(li a)

  • 通配符选择器( * )

  • 属性选择器(a[rel = “external”])

  • 伪类选择器(a: hover, li:nth-child)

优先级

  • 内联样式表(标签内部)> 嵌入样式表(当前文件中)> 外部样式表(外部文件中)。

  • !important > id > class = 伪类选择器 > tag(标签)

  • !important 比 内联优先级高

不可继承的样式

displayborderpaddingmarginwidthheightbackgroundoverflowpositionz-indexfloatclearvertical-align……

所有元素可继承的样式

visibilitycursor

内联元素可继承的样式

fontfont-sizefont-familycolorletter-spacingword-spacingwhite-spaceline-heightfont-weighttext-transform……

块状元素可继承的样式

text-indenttext-align

列表元素可继承的样式

list-stylelist-style-typelist-style-positionlist-style-image

表格元素可继承的样式

border-collapse


position:absolute 和 float 属性的异同

  • 共同点:对内联元素设置float和absolute属性,可以让元素脱离文档流,并且可以设置其宽高

  • 不同点:float仍会占据位置,absolute会覆盖文档流中的其他元素。


position的值, relative和absolute分别是相对于谁进行定位的?

  • absolute:生成绝对定位的元素, 相对于最近一级的定位不是 static 的父元素来进行定位。

  • fixed(老IE不支持):生成绝对定位的元素,通常相对于浏览器窗口或 frame 进行定位。

  • relative:生成相对定位的元素,相对于其在普通流中的位置进行定位。

  • static:默认值。没有定位,元素出现在正常的流中

  • sticky:生成粘性定位的元素,容器的位置根据正常文档流计算得出


CSS绘图

  • 原理:元素的每条 boder 都是一个等腰梯形

    • 当元素宽高皆为0时,元素的每条 boder 都是一个等腰直角三角形

    • IE6以下的兼容性问题

    • 解决方法:line-height:0;

    这里写图片描述

  • 梯形

  • 平行四边形

  • 三角形

    • 等腰直角三角形

  • 椭圆

  • 六角星

  • 对话框

实现思想:div:before 制作一个与边框颜色相同的向下三角形,div:after 制作一个与背景颜色相同的向下三角形,覆盖 div:before 的三角形,利用定位使div:before 三角形的边界显示出来。

具体实现:

div{  width:200px;  height:100px;  border:1px solid blue;  position:relative;  background:#fff;}div:before{  width:0;  height:0;  content:'';  position:absolute;  top:100px;  left:150px;  border-top:10px solid blue;  border-left:10px solid transparent;  border-right:10px solid transparent;}div:after{  width:0;  height:0;  content:'';  position:absolute;  top:99px;  left:150px;  border-top:10px solid #fff;  border-left:10px solid transparent;  border-right:10px solid transparent;}

  • 线性渐变

  • 径向渐变


  • 重复线性渐变


  • 重复径向渐变


  • 图形的平面旋转动画
//过渡div {  transition:transform 1s linear;  /* transition: property duration timing-function delay; */  /*              属性名称  过渡用时      过渡速度    开始时间 */}div:hover{  transform:rotate(90deg);  /*    rotate(90deg)           二维旋转90度      */  /*    scale(10deg,20deg)      沿x轴方向倾斜10度(y轴变),沿y轴方向倾斜20度(x轴变)      */  /*    scale(2,2)             二维缩放转换      */  /*    translate(10px,20px)    沿x轴平移10像素,沿y轴平移20像素      */}
//动画:一直旋转的时针div{  animation:round 2s linear infinite;  transform-origin:0 0 0;               //旋转基点  /*      animation: name duration timing-function delay iteration-count direction;    */  /*         keyframe名称  动画用时      动画速度    开始时间   播放速度    是否轮流反向播放 */}@keyframes round{  25% {transform:rotate(90deg);}  50% {transform:rotate(180deg);}  75% {transform:rotate(270deg);}  100% {transform:rotate(360deg);}}

  • 立体旋转的圆
div{  width:200px;  height:200px;  border-radius:100px;  background:black;  animation:round 2s linear infinite;  transform:perspective(600px);}@keyframes round{  0% {transform:rotateY(0deg);}  25% {transform:rotateY(45deg);}  50% {transform:rotateY(90deg);}  75% {transform:rotateY(135deg);}  100% {transform:rotateY(180deg);}}

CSS 实现自适应浏览器宽度的正方形

  • CSS3 vw 单位
{      width:30%;      height:30vw;      // CSS3:1vw = 1% viewport width    background:red;  }  
  • 设置垂直方向的padding撑开容器(容器内无内容)
div{      width:30%;      height:0;      padding-bottom: 30%;      background:red;  }  

margin, padding 的百分比数值是相对父元素的宽度计算的。

  • 利用 after伪元素的 padding-top 撑开容器
{        width:30%;        background:red;        max-width:200px;  }    div:after{        content: '';        display: block;        padding-top:100%;   }    

js继承方式及其优缺点

  • 原型链继承

    • 核心:拿父类实例来充当子类原型对象

    • 优点:简单,易于实现

    • 缺点:
      • 来自原型对象的引用属性是所有实例共享的,造成实例间的属性会相互影响。
      • 创建子类实例时,无法向父类构造函数传参。
    • 具体实现:

      function Parent() {  this.name = ['super'];  this.reName = function () {    this.name.push('super111');  }}function Child() {}Child.prototype = new Parent();     //核心var child1 = new Child();var child2 = new Child();child1.reName();console.log(child1.name, child2.name);// [ 'super', 'super111' ] [ 'super', 'super111' ], 可以看到子类的实例属性皆来自于父类的一个实例,即子类共享了同一个实例console.log(child1.reName === child2.reName) // true, 共享了父类的方法

  • 借用构造函数

    • 核心:借父类的构造函数来增强子类实例,相当于把父类的实例属性复制了一份给子类实例(完全没有用到原型)

    • 优点:

      • 解决了子类实例共享父类引用属性的问题
      • 创建子类实例时,可以向父类构造函数传参
    • 缺点:父类的方法没有被共享,无法实现函数复用,造成内存浪费

    • 具体实现:

      function Parent() {  this.name = ['super'];  this.reName = function () {    this.name.push('super111');  }}function Child() {    Parent.call(this);     //核心}var child1 = new Child();var child2 = new Child();child1.reName();console.log(child1.name, child2.name) // [ 'super', 'super111' ] [ 'super1' ], 子实例的属性都是相互独立的console.log(child1.reName === child2.reName) // false, 实例方法也是独立的,没有共享同一个方法

  • 组合式继承(最常用)

    • 思路:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。

    • 核心:把实例函数都放在原型对象上,以实现函数复用。同时还要保留借用构造函数方式的优点,通过 Parent.call(this),继承父类的基本属性和引用属性并保留能传参的优点;通过 Child.prototype = new Parent(),继承父类函数,实现函数复用。

    • 优点:

      • 不存在引用属性共享问题
      • 可传参
      • 函数可复用
    • 缺点:父类构造函数被调用两次,子类实例的属性存在两份,造成内存浪费。

    • 具体实现:

      function Parent() {    this.name = ['super'];}Parent.prototype.reName = function() {  this.name.push('super111');}function Child() {  Parent.call(this);    // 生成子类的实例属性(不包括父对象的方法)}Child.prototype = new Parent();     // 继承父类的属性和方法var child1 = new Child();var child2 = new Child();child1.reName();console.log(child1.name, child2.name); // [ 'super', 'super111' ] [ 'super' ], 子类实例不会相互影响console.log(child1.reName === child2.reName); //true, 共享了父类的方法

  • 寄生继承

    • 优点:子类都有各自的实例不会相互影响,且共享了父类的方法。

    • 具体实现:

      function Parent() {    this.name = ['super'];}Parent.prototype.reName = function() {  this.name.push('super111');}function Child() {  Parent.call(this);      // 生成子类的实例属性(不包括父对象的方法)}Child.prototype = Object.create(Parent.prototype) // 该方法会使用指定的原型对象及其属性去创建一个新的对象var child1 = new Child();var child2 = new Child();console.log(child1.name, child2.name); // [ 'super', 'super111' ] [ 'super' ], 子类实例不会相互影响console.log(child1.reName === child2.reName);    //true, 共享了父类的方法

  • ES6 class

    • 和寄生继承实现的效果一致

    • 具体实现:

      class Parent {    constructor() {        this.name = ['super'];    }    reName() {        this.name.push('super111');    }}class Child extends Parent {    constructor() {        Parent();    }}var child1 = new Child();var child2 = new Child();child1.reName();console.log(child1.name, child2.name); // [ 'super', 'super111' ] [ 'super' ], 子类实例不会相互影响console.log(child1.reName === child2.reName);     //true, 共享了父类的方法


get 和 post 的区别

特性 get post 后退按钮 / 刷新 无害 数据会被重新提交 书签 可收藏为书签 不可收藏为书签 缓存 能被缓存 不能缓存 编码类型 application/x-www-form-urlencoded application/x-www-form-urlencoded (默认)或 multipart/form-data(使用表单上传文件时)。为二进制数据使用多重编码。 历史 参数保留在浏览器历史中 参数不会保留在浏览器历史中 对数据长度的限制 当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符) 理论上无限制 对数据类型的限制 只允许 ASCII 字符 没有限制,也允许二进制数据 安全性 与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。在发送密码或其他敏感信息时不要使用 GET POST 比 GET 更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中 可见性 数据在 URL 中对所有人都是可见的 数据不会显示在 URL 中

其他一些 HTTP 请求方法

方法 描述 HEAD 与 GET 相同,但只返回 HTTP 报头,不返回文档主体。 PUT 上传指定的 URI 表示。 DELETE 删除指定资源。 OPTIONS 返回服务器支持的 HTTP 方法。 CONNECT 把请求连接转换到透明的 TCP/IP 通道。

fetch和ajax的区别

  • Ajax的本质是使用XMLHttpRequest对象来请求数据。

  • fetch 是全局量 window 的一个方法,它的主要特点有:

    • 第一个参数是URL;
    • 第二个是可选参数,可以控制不同配置的 init 对象;
    • 使用了 JavaScript Promises 来处理结果/回调。
      // 链式处理,将异步变为类似单线程的写法: fetch('/some/url').then( function(response) {    return . //... 执行成功, 第1...}).then( function(returnedValue) {    // ... 执行成功, 第2...}).catch( function(err) {    // 中途任何地方出错...在此处理 });
  • 从 fetch()返回的 Promise 将不会拒绝HTTP错误状态, 即使响应是一个 HTTP 404 或 500。相反,它会正常解决 (其中ok状态设置为false), 并且仅在网络故障时或任何阻止请求完成时,它才会拒绝。

  • 默认情况下, fetch在服务端不会发送或接收任何 cookies, 如果站点依赖于维护一个用户会话,则导致未经认证的请求(要发送 cookies,必须发送凭据头)。

  • fetch采用了Promise的异步处理机制,使用比ajax更加简单


闭包

  • 函数嵌套函数

  • 函数内部可以引用外部的参数和变量

  • 参数和变量不会被垃圾回收机制回收

  • 缺点:常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。


跨域

只要协议域名端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。

如何解决跨域问题

  • JSONP

    • 由于同源策略的限制,XmlHttpRequest 只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过 script 标签实现跨域请求,然后在服务端输出 JSON 数据并执行回调函数,从而解决了跨域的数据请求。

    • 原理:

      • 首先在客户端注册一个 callback,然后把 callback 的名字传给服务器。

      • 服务器先生成 json 数据,然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp。最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。

      • 客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里。(动态执行回调函数)
    • 优点:兼容性好,简单易用,支持浏览器与服务器双向通信。
    • 缺点:
      • 只支持GET请求。
      • 没有关于 JSONP 调用的错误处理。
    • 具体实现:
      <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />  <script type="text/javascript">      function jsonpCallback(result) {          //alert(result);          for(var i in result) {              alert(i+":"+result[i]);    //循环输出a:1,b:2,etc.          }      }      var JSONP=document.createElement("script");      JSONP.type="text/javascript";      JSONP.src="http://crossdomain.com/services.php?callback=jsonpCallback";      document.getElementsByTagName("head")[0].appendChild(JSONP);    // 等同于 <script type="text/javascript" src="http://crossdomain.com/services.php?callback=jsonpCallback"></script>  </script>  

    注意:JavaScript 的链接,必须在 function 的下面。


  • CORS: 服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。

  • 通过修改document.domain来跨子域。

    • 将子域和主域的document.domain设为同一个主域。前提条件:这两个域名必须属于同一个基础域名,而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域。
  • 使用window.name来进行跨域。

    • window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中
  • 使用HTML5中新引进的window.postMessage方法来跨域传送数据。


关于深浅拷贝

  • 浅拷贝:浅拷贝是拷贝引用,拷贝后的引用都是指向同一个对象的实例,彼此之间的操作会互相影响。

    浅拷贝分两种情况,拷贝源对象的引用 和 源对象拷贝实例。

    • 拷贝原对象的引用

      var a = {c:1};var b = a;console.log(a === b); // 输出true。a.c = 2;console.log(b.c); // 输出 2

    • 源对象拷贝实例

      外层源对象是拷贝实例,如果其属性元素为复杂数据类型(类型为Object,Array的属性)时,内层元素拷贝引用。

      对源对象直接操作,不影响另外一个对象,但是对其属性操作时候,会改变两外一个对象的属性的值。

      常用方法为:Array.prototype.slice()Array.prototype.concat()
      jQury的 $.extend({},obj)

      var a = [{c:1}, {d:2}];var b = a.slice();console.log(a === b); // 输出false,说明外层数组拷贝的是实例a[0].c = 3;console.log(b[0].c); // 输出 3,说明其元素拷贝的是引用

  • 深拷贝:在堆中重新分配内存,并且把源对象所有属性都进行新建拷贝,以保证深拷贝的对象的引用图不包含任何原有对象或对象图上的任何对象,拷贝后的对象与原来的对象是完全隔离,互不影响,包括其内部的元素互不干扰。

    常见方法有JSON.parse()JSON.stringify(),jQury的$.extend(true,{},obj),lodash的_.cloneDeep_.clone(value, true)

    var a = {c: {d: 1}};var b = $.extend(true, {}, a);console.log(a === b);    // 输出falsea.c.d = 3;console.log(b.c.d);      // 输出 1,没有改变。
    • $.extend(true,{},obj) 函数用于将一个或多个对象的内容合并到目标对象。
    /* 数组的深拷贝 *//* 方法一:for循环 */var arr = [1,2,3,4,5];var arr2 = copyArr(arr)function copyArr(arr) {    let res = [];    for (let i = 0; i < arr.length; i++) {     res.push(arr[i]);    }return res;}/* 方法二:slice() */var arr = [1,2,3,4,5]var arr2 = arr.slice(0);arr[2] = 5;console.log(arr);             //[1,2,5,4,5] console.log(arr2);            //[1,2,3,4,5]/* 方法三:concat() */var arr = [1,2,3,4,5];var arr2 = arr.concat();arr[2] = 5;console.log(arr);             //[1,2,5,4,5] console.log(arr2);            //[1,2,3,4,5]

说说你对作用域链的理解

作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。


创建ajax过程

  • 创建XMLHttpRequest对象

  • 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息

  • 设置响应HTTP请求状态变化的函数

  • 发送HTTP请求

  • 获取异步调用返回的数据

  • 使用JavaScript和DOM实现局部刷新


事件模型

DOM事件流:

  • 事件捕捉阶段:事件开始由顶层对象触发,然后逐级向下传播,直到目标的元素;

  • 处于目标阶段:处在绑定事件的元素上;

  • 事件冒泡阶段:事件由具体的元素先接收,然后逐级向上传播,直到不具体的元素;

    • 阻止 冒泡/捕获 event.stopPropagation() 和 IE 的event.cancelBubble=true
  • DOM事件绑定

    • 绑定事件监听函数:addEventListenerattchEvent

    • 在JavaScript代码中绑定:获取DOM元素 dom.onlick = fn

    • 在DOM元素中直接绑定:<div onclick = 'fn()'>


事件委托

因为事件具有冒泡机制,因此我们可以利用冒泡的原理,把事件加到父级上,触发执行效果。这样做的好处是提高性能。

最重要的是通过 event.target.nodeName 判断子元素。


事件绑定

  • 传统方式

      element.onclick = function(e){        // ...    };
    • 优点:

      • 非常简单和稳定,可以确保它在你使用的不同浏览器中运作一致
      • 处理事件时,this关键字引用的是当前元素
    • 缺点:

      • 传统方法只会在事件冒泡中运行,而非捕获和冒泡。

      • 一个元素一次只能绑定一个事件处理函数。新绑定的事件处理函数会覆盖旧的事件处理函数。

      • 事件对象参数(e)仅非IE浏览器可用
  • W3C方式

     element.addEventListener('click', function(e){    // ...}, false); 
    • 优点:

      • 该方法同时支持事件处理的捕获和冒泡阶段。事件阶段取决于addEventListener最后的参数设置:false (冒泡) 或 true (捕获)。

      • 在事件处理函数内部,this关键字引用当前元素

      • 事件对象总是可以通过处理函数的第一个参数(e)捕获。

      • 可以为同一个元素绑定你所希望的多个事件,同时并不会覆盖先前绑定的事件。

    • 缺点:

      • IE不支持,你必须使用IE的attachEvent函数替代。
  • IE方式

    element.attachEvent('onclick', function(){    // ...});
    • 优点:

      • 可以为同一个元素绑定你所希望的多个事件,同时并不会覆盖先前绑定的事件。
    • 缺点:

      • IE仅支持事件捕获的 冒泡 阶段。

      • 事件监听函数内的this关键字指向了window对象,而不是当前元素。

      • 事件对象仅存在与window.event参数中

      • 事件必须以ontype的形式命名,比如,onclick而非click

      • 仅IE可用。你必须在非IE浏览器中使用W3C的addEventListener 。

JS事件:target与currentTarget区别

  • target在事件流的目标阶段。
  • currentTarget在事件流的捕获,目标及冒泡阶段。
  • 只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的对象,currentTarget指向当前事件活动的对象(一般为父级)。

关于数据类型

  • 简单(基本)数据类型:Number、String、Boolean、Undefined、Null

  • 复杂数据类型:Object

  • 引用类型:Object、Array、Date、RegExp、Function等
特性 简单数据类型 复杂数据类型 存储方式 把数据名和值直接存储在栈当中 在栈中存储数据名和一个堆的地址,在堆中存储属性及值。访问时先从栈获取地址,再到堆中拿出相应的值 函数内部对参数的修改 简单数据类型作为参数时,函数内部对参数值的修改不会改变外部变量的值 复杂数据类型作为参数时,函数内部对参数值的修改会改变外部变量的值

ES6的了解

  • 箭头函数

    • 箭头函数不属于普通的 function,所以没有独立的上下文。箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

      var f = () => "hhh";f();                      // "hhh"typeof f;                 // truef instanceof Function;    // true
    • 箭头函数没有[[Construct]] 属性和 prototype 属性,和 new一起用会抛出错误。(因此也没有 super 和 new.target 属性)

    • 没有arguments对象,更不能通过arguments对象访问传入参数。只能使用显式命名或其他ES6新特性来完成。

    • 由于箭头函数没有自己的this,不能用call()、apply()、bind()这些方法去改变this的指向。

    • 箭头函数不能当作 generators 使用,使用 yield 会产生错误。

  • 新增模板字符串(为JavaScript提供了简单的字符串插值功能)、for-of(用来遍历数据,例如数组中的值。)arguments对象可被不定参数和默认参数完美代替。

  • ES6将promise对象纳入规范,提供了原生的Promise对象。

  • 增加了letconst命令,用来声明变量。增加了块级作用域。let命令实际上就增加了块级作用域。ES6规定,var命令和function命令声明的全局变量,属于全局对象的属性;let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。

  • 引入module模块的概念。


关于Promise对象

  • ES6 原生提供了 Promise 对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,各种异步操作都可以用同样的方法进行处理。

  • 特点:

    • 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

    • pending 状态的 Promise 对象最终只能有两种状态,一种是fulfilled,一种是rejected。一旦异步任务操作成功,pending状态立马变成fulfilled。反之操作失败,Promise由pending状态变成rejected。而且这种最终的状态都不会再改变。

  • 语法:

    var promise = new Promise(    /* executor */    function(resolve, reject) {        // ... some code        if (/* 异步操作成功 */){            resolve(value);        } else {            reject(error);        }});

    executor是一个带有resolve和reject两个参数的函数 。executor 函数在Promise的创建时就立即执行。而异步任务一旦完成,就调用resolve函数来解决promise,并将异步操作的结果,作为参数传递出去。反之操作失败,就调用reject函数,并同样将异步操作报出的错误,作为参数传递出去。注意的是,在executor函数运行时抛出任何一个错误,都会导致promise状态变为rejected,这也意味着异步操作失败。

    Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。

    promise.then(function(value) {  // success}, function(error) {  // failure});

    then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为Reject时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

    /* 例子一 */var myFirstPromise = new Promise(function(resolve, reject){    //当异步代码执行成功时,调用resolve(...), 当异步代码失败时就会调用reject(...)    setTimeout(function(){        resolve("成功!");    //代码正常执行    }, 250);});myFirstPromise.then(function(successMessage){    //successMessage的值是上面调用resolve(...)方法传入的值"成功!"    console.log("Yay! " + successMessage);    // Yay! 成功!});
    • Promise.prototype.catch().then(null, rejection) 的别名,用于指定发生错误时的回调函数。

      promise.then(function(data) {     // success}).catch(function(err) {// 处理 Promise对象 和 前一个then方法指定的回调函数 运行时发生的错误});

      如果没有使用catch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。

      建议总是使用catch方法,而不使用then方法的第二个参数。

    function promise(){    var a = new Promise((resolve,reject)=>{        setTimeout(()=>resolve({obj: 3}), 3000)    })    return a;}var pro = promise();pro. then(re=>{   console.log("success:", re);   x = x + 1;}).catch(error => console.log("error: ", error));//success: Object {obj: 3}//error:  ReferenceError: x is not defined//上面.then()方法中异步操作失败也可以换成catch方法function promise(){    var a = new Promise((resolve,reject)=>{        setTimeout(()=>reject({obj: 3}), 3000)   })   return a;}promise().then(result=>console.log("success",result)).catch(result=>console.log("failed",result))//failed Object {obj: 3}

    因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回 promise 对象自身, 所以它们可以被链式调用

    • Promise.resolve() 将现有对象转为Promise对象。

    • Promise.reject(reason)返回的一定是一个rejected状态的Promise对象

    • Promise.all() 方法用于将多个Promise实例,包装成一个新的Promise实例。

      var p = Promise.all([p1, p2, p3]);

      p1、p2、p3都是Promise对象的实例,如果不是,就会调用Promise.resolve方法将参数转为Promise。p的状态由p1、p2、p3决定,分成两种情况:当p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数;只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

    • Promise.race()

      var p = Promise.race([p1, p2, p3]);

    只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

  • 优点:代码层次清晰,便于理解,更加容易维护。

  • 缺点:

    • 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。

    • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。

    • 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

function promise(){    var a = new Promise((resolve,reject)=>{        console.log("carry on");       //Promise实例一经创建便立即执行匿名函数        var timer1 = setTimeout(()=>resolve({obj: 3}), 3000)           //当执行到timer1意味着异步操作成功,使用resolve函数来解决        var timer2 = setTimeout(()=>{console.log("going on");  reject({obj: 3})}, 3000)        //当执行完timer1,Promise的状态已经是fulfilled,不管后续发生什么,都不会再改变状态,因此,timer2即使执行也改变不了Promise的状态    })    return a;}setTimeout(() => console.log("timeout"), 4000);promise ().then(result=>console.log("success",result), result=>console.log("failed",result));console.log("origin");// carry on//origin//success Object {obj: 3}//going on//timeout

await async

var sleep = function (time) {    return new Promise(function (resolve, reject) {        setTimeout(function () {            resolve();        }, time);    })};var start = async function () {    // 在这里使用起来就像同步代码那样直观    console.log('start');    await sleep(3000);    console.log('end');};start();//控制台先输出start,稍等3秒后,输出了end。
  • async 表示这是一个async函数,await只能用在这个函数里面。

  • await 表示在这里等待promise返回结果了,再继续执行。(不必写.then(..),直接可以得到返回值。)

  • await 后面跟着的应该是一个promise对象(其他非promise对象会立即执行)

  • 可以直接用标准的try catch语法捕捉错误。

    var sleep = function (time) {    return new Promise(function (resolve, reject) {        setTimeout(function () {            // 模拟出错了,返回 ‘error’            reject('error');        }, time);    })};var start = async function () {    try {        console.log('start');        await sleep(3000); // 这里得到了一个返回错误        // 所以以下代码不会被执行了        console.log('end');    } catch (err) {        console.log(err); // 这里捕捉到错误 `error`    }};
  • await写在for循环里,不必担心以往需要闭包才能解决的问题。

    ..省略以上代码let arr = [1,2,3,4,5,6,7,8,9,10];var start = async function () {    for (var i of arr ) {        console.log(`当前是第${i}次等待..`);        await sleep(1000);      //await必须在async函数的上下文中的    }};

参考自:体验异步的终极解决方案-ES7的Async/Await


你觉得jQuery源码有哪些写的好的地方

  • jquery源码封装在一个匿名函数的自执行环境中,有助于防止变量的全局污染,然后通过传入window对象参数,可以使window对象作为局部变量使用,好处是当jquery中访问window对象的时候,就不用将作用域链退回到顶层作用域了,从而可以更快的访问window对象。同样,传入undefined参数,可以缩短查找undefined时的作用域链。

  • jquery将一些原型属性和方法封装在了jquery.prototype中,为了缩短名称,又赋值给了jquery.fn,这是很形象的写法。

  • 有一些数组或对象的方法经常能使用到,jQuery将其保存为局部变量以提高访问速度。

  • jquery实现的链式调用可以节约代码,所返回的都是同一个对象,可以提高代码效率。


关于DOM操作

  • 创建新节点

    • createDocumentFragment() //创建一个DOM片段

    • createElement() //创建一个具体的元素

    • createTextNode() //创建一个文本节点

  • 添加、移除、替换、插入

    • appendChild()

    • removeChild()

    • replaceChild()

    • insertBefore() //并没有insertAfter()

  • 查找

    • getElementsByTagName() //通过标签名称

    • getElementsByName() //通过元素的Name属性的值(IE容错能力较强,

    • 会得到一个数组,其中包括id等于name值的)

    • getElementById() //通过元素Id,唯一性


深度优先遍历

function DFS(node) {    if(!node) {return;}    console.log(node.nodeName);    Array.from(node.children).forEach( child => {DFS(child);});    //Array.from() 方法从一个类似数组或可迭代对象中创建一个新的数组实例。    //forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。}


广度优先遍历

function BFS(node) {    var queue = [node];    while(queue.length) {        var current = queue.shift();        console.log(current.nodeName);        Array.from(current.children).forEach(child => queue.push(child));    }}

== 和 === 的区别

JavaScript 提供两种相等运算符:==(相等运算符)和===(严格相等运算符)。

如果两个值不是同一类型,严格相等运算符(===)直接返回false,而相等运算符(==)会将它们转化成同一个类型,再用严格相等运算符进行比较。

严格相等运算符的算法如下:

  • 如果两个值的类型不同,直接返回false。
1 === "1" // falsetrue === "true" // false
  • 同一类型的原始类型的值(数值、字符串、布尔值)比较时,值相同就返回true,值不同就返回false。
1 === 0x1       // trueNaN === NaN     // false         NaN与任何值都不相等(包括自身)+0 === -0       // true             正0等于负0
  • 两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个对象。
{} === {} // false[] === [] // false(function (){} === function (){})    // false//如果两个变量引用同一个对象,则它们相等var v1 = {};var v2 = v1;v1 === v2     // true

PS:注意,对于两个对象的比较,严格相等运算符比较的是地址,而大于或小于运算符比较的是值。

new Date() > new Date()     // falsenew Date() < new Date()     // falsenew Date() === new Date()   // false
  • undefined和null与自身严格相等。
undefined === undefined     // truenull === null               // truevar v1;var v2;v1 === v2                   // true

相等运算符的算法如下:

  • 原始类型(字符串、布尔值)的数据会转换成数值类型再进行比较。
'true' == true      // false        等同于 NaN === 1false == 'false'    // false        等同于 0 === NaNfalse == '0'        // true'' == '0'           // false0 == ''             // true'\n  123  \t' == 123    // true' \t\r\n ' == 0         // true// 因为字符串转为数字时,省略前置和后置的空格
  • 对象(这里指广义的对象,包括数组和函数)与原始类型的值比较时,对象转化成原始类型的值,再进行比较。

  • 两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个对象。

[1] == [1]          //false{a:1} == {a:1}      //false
  • undefined和null与其他类型的值比较时,结果都为false,它们互相比较时结果为true。
false == null // falsefalse == undefined // false0 == null // false0 == undefined // falseundefined == null // true

参考自:http://javascript.ruanyifeng.com/grammar/operator.html#toc6


null和undefined的区别

  • null表示尚未存在的对象,转为数值时为0,常用来表示函数企图返回一个不存在的对象。

    • 作为函数的参数,表示该函数的参数不是对象。
    • 作为对象原型链的终点。
  • undefined是一个表示”无”的原始值,转为数值时为NaN

    • 变量被声明了,但没有赋值时,就等于undefined。
    • 调用函数时,应该提供的参数没有提供,该参数等于undefined。
    • 对象没有赋值的属性,该属性的值为undefined。
    • 函数没有返回值时,默认返回undefined。

关于内存泄漏

  • 内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。

  • 引起内存泄漏的操作

    • 全局变量

    • 被遗忘的计时器或回调

    • setTimeout 的第一个参数使用字符串而非函数

    • 闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)

    • 当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露。

js延迟加载的方式

  • defer和async

    • defer并行加载js文件,会按照页面上script标签的顺序执行。
    • async并行加载js文件,下载完成立即执行,不会按照页面上script标签的顺序执行。
  • 动态创建DOM方式(创建script,插入到DOM中,加载完毕后callBack)

  • 按需异步载入js


关于new操作符

  • 创建一个空对象
var obj=new Object();  
  • 设置原型链
obj.__proto__= Func.prototype;  
  • 让Func中的this指向obj,并执行Func的函数体。
var result =Func.call(obj);  
  • 判断Func的返回值类型:如果是值类型,返回obj。如果是引用类型,就返回这个引用类型的对象。
if (typeof(result) == "object"){    func=result;  }  else{      func=obj;;  }

获取一个实例的原型链

  • obj.__proto__

  • Object.getPrototypeOf()


常见网站漏洞的原理及解决方法

  • XSS 跨站脚本攻击

    • 原理:本质是html注入,攻击者往Web页面里插入恶意 html标签或者javascript代码,攻击者的输入没有经过严格的控制进入了数据库,最终显示给来访的用户,导致可以在来访用户的浏览器里以浏览用户的身份执行html代码。
    • 解决方法:
      • 对用户表单输入的数据进行过滤,对javascript代码进行转义,然后再存入数据库;
      • 在信息的展示页面,也要进行转义,防止javascript在页面上执行。
      • 尽量采用POST 而非GET 提交表单
  • CSRF 跨站请求伪造

    • 原理:受害者登录了受信任网站A,并在本地生成Cookie,在不登出A的情况下,访问危险网站B,危险网站B跨站伪造用户的请求,模拟用户的操作。
    • 解决方法:

      • 使用验证码:每一个重要的post提交页面,使用一个验证码,因为第三方网站是无法获得验证码的。

      • 使用token:服务器随机产生tooken,然后以tooken为秘钥产生一段密文,把token和密文都随cookie交给前端,前端发起请求时把密文和token交给后端,后端对token和密文进行验证,看token能不能生成同样的密文,这样即使黑客拿到了token也无法拿到密文。

      • 检测 http 的头信息 refer 。Referer记录了请求的来源地址,服务器要做的是验证这个来源地址是否合法。

      • 涉及敏感操作的请求改为POST请求。
  • XSS与CSRF的区别

    • XSS是获取信息,不需要提前知道其他用户页面的代码和数据包。
    • CSRF是代替用户完成指定的动作,需要知道其他用户页面的代码和数据包。
  • SQL注入

    • 原理:通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
    • 防护原理:
      • 对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双”-“进行转换等。
      • 永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
      • 不要把机密信息明文存放,加密或者hash掉密码和敏感的信息。
  • 点击劫持
  • DDOS攻击
  • DNS劫持

Javascript垃圾回收方法

  • 标记清除

    • 这是JavaScript最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。
    • 垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了
  • 引用计数

    • 在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个 变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时 候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。
    • 在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收,但BOM与DOM对象却是通过引用计数回收垃圾的,也就是说只要涉及BOM及DOM就会出现循环引用问题。

页面呈现的具体过程

  • 浏览器把获取到的HTML代码解析成一个DOM树,HTML中的每个tag都是DOM树中的一个节点,根节点就是我们常用的document对象。DOM树里包含了所有HTML标签,包括display:none,还有用JS动态添加的元素等。

  • 浏览器把所有样式(用户定义的CSS和用户代理)解析成样式结构体,在解析的过程中会去掉浏览器不能识别的样式,比如IE会去掉-moz开头的样式,而FF会去掉 _ 开头的样式。

  • DOM Tree 和样式结构体组合后构建 render tree , render tree能识别样式,render tree中每个NODE都有自己的style,而且 render tree不包含隐藏的节点 (比如display:none的节点,还有head节点),因为这些节点不会用于呈现,而且不会影响呈现的,所以就不会包含到 render tree中。注意 visibility:hidden隐藏的元素还是会包含到 render tree中的,因为visibility:hidden 会影响布局(layout),会占有空间。根据CSS2的标准,render tree中的每个节点都称为Box (Box dimensions),理解页面元素为一个具有填充、边距、边框和位置的盒子。

  • 一旦render tree构建完毕后,浏览器就可以根据render tree来绘制页面了。


前端渲染的优势

  • 局部刷新。无需每次都进行完整页面请求。

  • 懒加载。如在页面初始时只加载可视区域内的数据,滚动后rp加载其它数据,可以通过 react-lazyload 实现。

  • 交互。使用 JS 实现各种酷炫效果。

  • 节约服务器成本。省电省钱,JS 支持 CDN 部署,且部署极其简单,只需要服务器支持静态文件即可。

  • 天生的关注分离设计。服务器来访问数据库提供接口,JS 只关注数据获取和展现。


服务端渲染的优势

  • 更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。

  • 服务端渲染不需要先下载一堆 js 和 css 后才能看到页面(首屏性能)。

  • 服务端渲染不用关心浏览器兼容性问题(随意浏览器发展,这个优点逐渐消失)。


回流与重绘

  • 当render tree中的一部分(或全部)因为元素的规模尺寸布局隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。

  • 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

    注意:回流必将引起重绘,而重绘不一定会引起回流。页面若发生回流则需要付出很高的代价。

  • 回流何时发生:

    • 添加或者删除可见的DOM元素;

    • 元素位置改变;

    • 元素尺寸改变——边距、填充、边框、宽度和高度;

    • 内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;

    • 页面渲染初始化;

    • 浏览器窗口尺寸改变——resize事件发生时。

如何减少回流、重绘

  • 减少回流、重绘其实就是需要减少对render tree的操作(合并多次多DOM和样式的修改),并减少对一些style信息的请求,尽量利用好浏览器的优化策略。具体方法有:

    • 直接改变className,如果动态改变样式,则使用cssText

    // 比较好的写法一el.className += " className1";// 比较好的写法二el.style.cssText += ";left: " + left + "px;top: " + top + "px;";

    • 让要操作的元素进行”离线处理”,处理完后一起更新
      • 使用DocumentFragment进行缓存操作,引发一次回流和重绘;

        //不好的写法var p, t;p = document.creatElement('p');t = document.creatTextNode('fist paragraph');p.appendChild(t);document.body.appendChild(p);  //将引起一次回流p = document.creatElement('p');t = document.creatTextNode('second paragraph');p.appendChild(t);document.body.appendChild(p);  //将再引起一次回流//好的写法var p, t, frag;frag = document.creatDocumentFragment();p = document.creatElement('p');t = document.creatTextNode('fist paragraph');p.appendChild(t);farg.appendChild(p);p = document.creatElement('p');t = document.creatTextNode('second paragraph');p.appendChild(t);farg.appendChild(p);document.body.appendChild(frag);    //相比前面的方法,这里仅仅引起一次回流,倘若页面里有很多这样的操作,利用文档随便将会提升很多

    • 使用display:none技术,只引发两次回流和重绘; ( 只是减少重绘和回流的次数,display:none 是会引起重绘并回流,相对来说,visibility: hidden只会引起重绘 ) //不懂

    • 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。

此题待续……


计算机网络

  • HTTP状态码分类

    分类 分类描述 1XX 服务器收到请求,需要请求者继续执行操作 2XX 操作被成功接收并处理 3XX 重定向,需要进一步的操作以完成请求 4XX 客户端错误,请求包含语法错误或无法完成请求 5XX 服务器错误,服务器在处理请求的过程中发生了错误


  • 常见HTTP状态码

    状态码 状态码英文描述 中文描述 200 OK 请求成功。一般用于GET与POST请求。 301 Moved Permanently 永久重定向。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替。 302 Found 临时重定向。与301类似,但资源只是临时被移动,客户端应继续使用原有URI。 304 Not Modified 未修改。自从上次请求后,所请求的资源未修改过,服务器返回此状态码时,不会返回任何资源。 400 Bad Request 请求出现语法错误,服务器无法理解。 401 (未授权) 请求要求身份验证。 403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求。 404 Not Found 请求失败,请求所希望得到的资源未被在服务器上发现。 405 (方法禁用)禁用请求中指定的方法。 500 Internal Server Error 服务器内部错误,无法完成请求。 502 (错误网关)服务器作为网关或代理,从上游服务器收到无效响应。 503 (服务不可用) 服务器目前无法使用。 通常这只是暂时状态。 504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。

  • HTTP和HTTPS的区别

    • 默认端口号不一样,HTTP的默认端口号为80,HTTPS的默认端口号为443。

    • HTTP在传输过程中使用的是明文传输,内容可能被窃取,而且无法验证通信方的身份,还有可能遭遇身份伪装;而HTTPS在应用层和传输层之间增加了ssl协议用来加密内容,因此通过证书验证来验证身份,即使数据被窃取也无法解密,数据的传输更加安全。


HTTP报头

  • 请求头
    • Accept
    • Cache-control
    • Host
    • User-agent
    • Accenp-Language
  • 响应头:
    • Cache-Control:max-age 避免了服务端和客户端时间不一致的问题。
    • content-type
    • Date
    • Expires
    • Last-Modified 标记此文件在服务期端最后被修改的时间

关于HTTP/2

与 HTTP 1.1 相比:

  • HTTP/2 采用 二进制格式 而非 文本格式。

  • HTTP/2是完全多路复用的,而非有序并阻塞的——客户端和服务器之间只需要建立一个 TCP 连接,即可同时收发多个文件,而且,该连接在相当长的时间周期内保持打开(持久化),以便复用,大幅降低了多个请求的开销。

  • HTTP/2 引入了“服务端推(server push)”的概念,它允许服务端在客户端需要数据之前就主动地将数据发送到客户端缓存中,从而提高性能。

  • HTTP/2提供更多的加密支持。

  • 使用报头压缩,HTTP/2降低了开销


一个url输入浏览器到页面渲染的过程

  • 域名解析,查找缓存
    • 查找浏览器缓存(DNS缓存)
    • 查找操作系统缓存(如果浏览器缓存没有,浏览器会从hosts文件查找是否有DNS信息)
    • 查找路由器缓存
    • 查找ISP缓存
  • 浏览器获得对应的ip地址后,浏览器与远程 Web 服务器通过 TCP 三次握手协商来建立一个 TCP/IP 连接

  • TCP/IP 连接建立起来后,浏览器就可以向服务器发送HTTP请求

  • 服务器处理请求,返回资源(MVC设计模式)

  • 浏览器处理(加载,解析,渲染)

    • HTML页面加载顺序从上而下。
    • 解析文档为有意义的结构,DOM树;解析css文件为样式表对象。
    • 渲染。将DOM树进行可视化表示。
  • 绘制网页

    • 浏览器根据HTML和CSS计算得到渲染数,最终绘制到屏幕上。

    一个完整HTTP请求的过程为:
    DNS Resolving -> TCP handshake -> HTTP Request -> Server -> HTTP Response -> TCP shutdown


网络模型(七层)

  • 应用层(HTTP、SMTP、DNS):允许访问OSI环境的手段(应用协议数据单元APDU)

  • 表示层(FTP):对数据进行翻译、加密和压缩(表示协议数据单元PPDU)

  • 会话层:建立、管理和终止会话(会话协议数据单元SPDU)

  • 传输层(TCP、UDP):提供端到端的可靠报文传递和错误恢复(段Segment)

  • 网络层(IP):负责数据包从源到宿的传递和网际互连(包PackeT)

  • 数据链路层:将比特组装成帧和点到点的传递(帧Frame)

  • 物理层:通过媒介传输比特,确定机械及电气规范(比特Bit)


TCP和UDP的区别

  • TCP 是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来。

  • UDP 是与TCP相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去。


页面缓存原理

页面缓存状态是由http header决定的,一个浏览器请求信息,一个是服务器响应信息。主要包括Pragma: no-cache、Cache-Control、 Expires、 Last-Modified、If-Modified-Since。


性能优化

  • 页面内容

    • 减少 HTTP 请求数

      • 合并 JavaScript、CSS 等文件;
      • 使用CSS Sprite:将背景图片合并成一个文件,通过 background-imagebackground-position 控制显示;
    • 延迟加载

      • 延迟渲染
        将首屏以外的 HTML 放在不渲染的元素中,如隐藏的 < textarea >,或者 type 属性为非执行脚本的 < script > 标签中,减少初始渲染的 DOM 元素数量,提高速度。等首屏加载完成或者用户操作时,再去渲染剩余的页面内容。
    • 预先加载。利用浏览器空闲时间请求将来要使用的资源,以便用户访问下一页面时更快地响应。

    • 减少 DOM 元素数量。移除不必要的标记。

      • 能通过伪元素实现的功能,就没必要添加额外元素,如清除浮动。
      • 默认的表格布局算法会产生大量重绘。
    • 缓存 Ajax 请求

    • 尽量减少 iframe 使用

      • iframe 优点:
        • 可以用来加载速度较慢的第三方资源,如广告;
        • 可用作安全沙箱;
        • 可以并行下载脚本。
      • iframe 缺点:
        • 加载代价昂贵,即使是空的页面;
        • 阻塞页面 load 事件触发。iframe 完全加载以后,父页面才会触发 load 事件。
        • 缺乏语义。
    • 减少 DNS 查询

      用户输入 URL 以后,浏览器首先要查询域名(hostname)对应服务器的 IP 地址,一般需要耗费 20-120 毫秒 时间。DNS 查询完成之前,浏览器无法从服务器下载任何数据。

      首次访问、没有相应的 DNS 缓存时,域名越多,查询时间越长。

      • 应尽量减少域名数量。但基于并行下载考虑,把资源分布到 2 个域名上(最多不超过 4 个)。这是减少 DNS 查询同时保证并行下载的折衷方案。
  • 服务器

    • 使用 CDN(内容分发网络)

      • CDN做了两件事,一是让用户访问最近的节点,二是从缓存或者源站获取资源。
      • CDN的工作原理:通过dns服务器来实现优质节点的选择,通过缓存来减少源站的压力。
    • 添加 Expires 或 Cache-Control 响应头

      • 静态内容:将 Expires 响应头设置为将来很远的时间,实现「永不过期」策略;
      • 动态内容:设置合适的 Cache-Control 响应头,让浏览器有条件地发起请求。
    • 启用 Gzip

      • 应该对 HTML、CSS、JS、XML、JSON 等文本类型的内容启用压缩。
      • 图片和 PDF 文件不要使用 gzip。它们本身已经压缩过,再使用 gzip 压缩不仅浪费 CPU 资源,而且还可能增加文件体积。
    • 配置 Etag。

      Etag 通过文件版本标识,方便服务器判断请求的内容是否有更新,如果没有就响应 304,避免重新下载。

    • Ajax 请求使用 GET 方法

      浏览器执行 XMLHttpRequest POST 请求时分成两步,先发送 Header,再发送数据。而 GET 只使用一个 TCP 数据包发送数据,所以首选 GET 方法。

    • 避免图片 src 为空。虽然 src 属性为空字符串,但浏览器仍然会向服务器发起一个 HTTP 请求

    • 尽早输出(flush)缓冲

      用户请求页面时,服务器通常需要花费 200 ~ 500 毫秒来组合 HTML 页面。在此期间,浏览器处于空闲、等待数据状态。使用PHP 中的 flush() 函数,可以发送部分已经准备好的 HTML 到浏览器,以便服务器还在忙于处理剩余页面时,浏览器可以提前开始获取资源。
      可以考虑在 </head> 之后输出一次缓冲,HTML head 一般比较容易生成,先发送以便浏览器开始获取 <head> 里引用的 CSS 等资源。

  • 减少,压缩 Cookie 大小, 去除不必要的 Cookie,设置合适的过期时间。

  • CSS

    • 把样式表放在 <head> 中,让页面渐进渲染,尽早呈现视觉反馈,用户可以先查看已经下载渲染的内容。
    • 避免使用css表达式,避免使用高级选择器,通配选择器。
    • 使用 <link> 替代 @import
  • JavaScript

    • 把脚本放在页面底部

      浏览器下载脚本时,会阻塞其他资源并行下载,即使是来自不同域名的资源。因此,最好将脚本放在底部,以提高页面加载速度。

    • 使用外部 JavaScript 和 CSS。外部 JavaScript 和 CSS 文件可以被浏览器缓存,在不同页面间重用,也能降低页面大小。

    • 减少 DOM 操作

      • 缓存已经访问过的元素;
      • 使用 DocumentFragment 暂存 DOM,整理好以后再插入 DOM 树;
      • 操作 className,而不是多次读写 style
      • 避免使用 JavaScript 修复布局。
    • 使用高效的事件处理
      • 减少绑定事件监听的节点,如通过事件委托;
      • 尽早处理事件,在 DOMContentLoaded 即可进行,不用等到 load 以后。
    • 压缩 JavaScript 和 CSS

      压缩代码可以移除非功能性的字符(注释、空格、空行等),减少文件大小,提高载入速度。

  • 图片

    • 不要使用 <img>widthheight 缩放图片,使用相应大小的图片。

    • 设置图片的宽和高,以免浏览器按照「猜」的宽高给图片保留的区域和实际宽高差异,产生重绘。

      如果浏览器没有找到这两个参数,它需要一边下载图片一边计算大小,如果图片很多,浏览器需要不断地调整页面。
      当浏览器知道了图片的宽高,即使图片暂时无法显示,页面上也会腾出图片的空位,然后继续加载后面的内容。

  • 代码
    • 多个变量声明合并,少用全局变量;避免全局查询
    • 缓存DOM节点查找的结果
    • 避免使用 with(with会创建自己的作用域,会增加作用域链长度)
    • 尽量避免写在HTML标签中写Style属性
    • 用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能
    • 用setTimeout来避免页面失去响应
    • 网址后加斜杠(如www.campr.com/目录,会判断这个目录是什么文件类型,或者是目录。)

前端SEO技巧

  • 网站结构布局优化:尽量简单、开门见山,提倡扁平化结构。

    一般而言,建立的网站结构层次越少,越容易被“蜘蛛”抓取,也就容易被收录。一般中小型网站目录结构超过三级,“蜘蛛”便不愿意往下爬。

    • 控制首页链接数量
      • 对于中小型企业网站,建议首页链接在100个以内,链接的性质可以包含页面导航、底部导航、锚文字链接等等,注意链接要建立在用户的良好体验和引导用户获取信息的基础之上。
    • 扁平化的目录层次,尽量让“蜘蛛”只要跳转3次,就能到达网站内的任何一个内页。

    • 导航优化

      • 导航应该尽量采用文字方式,也可以搭配图片导航,但是图片代码一定要进行优化,标签必须添加“alt”和“title”属性,告诉搜索引擎导航的定位,做到即使图片未能正常显示时,用户也能看到提示文字。

      • 在每一个网页上应该加上面包屑导航,好处:从用户体验方面来说,可以让用户了解当前所处的位置以及当前页面在整个网站中的位置,帮助用户很快了解网站组织形式,从而形成更好的位置感,同时提供了返回各个页面的接口,方便用户操作;对“蜘蛛”而言,能够清楚的了解网站结构,同时还增加了大量的内部链接,方便抓取,降低跳出率。

    • 网站的结构布局

      • 页面头部:logo及主导航,以及用户的信息。
      • 页面主体:左边正文,包括面包屑导航及正文;右边放热门文章及相关文章,好处:留住访客,让访客多停留,对“蜘蛛”而言,这些文章属于相关链接,增强了页面相关性,也能增强页面的权重。
      • 页面底部:版权信息和友情链接。

      特别注意:分页导航写法,推荐写法:“首页 1 2 3 4 5 6 7 8 9 下拉框”,这样“蜘蛛”能够根据相应页码直接跳转,下拉框直接选择页面跳转。而下面的写法是不推荐的,“首页 下一页 尾页”,特别是当分页数量特别多时,“蜘蛛”需要经过很多次往下爬,才能抓取,会很累、会容易放弃。

    • 控制页面的大小,减少http请求,提高网站的加载速度。
  • 网页代码优化

    • < title > 标题:只强调重点即可,尽量把重要的关键词放在前面,关键词不要重复出现,尽量做到每个页面的< title >标题中不要设置相同的内容。

    • < meta keywords > 标签:关键词,列举出几个页面的重要关键字即可,切忌过分堆砌。

    • < body >中的标签:尽量让代码语义化,在适当的位置使用适当的标签,用正确的标签做正确的事。让阅读源码者和“蜘蛛”都一目了然。比如:h1-h6 是用于标题类的,< nav >标签是用来设置页面主导航的等。

    • < a >标签:页内链接,要加 “title” 属性加以说明,让访客和 “蜘蛛” 知道。而外部链接,链接到其他网站的,则需要加上 el=”nofollow” 属性, 告诉 “蜘蛛” 不要爬,因为一旦“蜘蛛”爬了外部链接之后,就不会再回来了。

    • 正文标题要用< h1 >标签:“蜘蛛” 认为它最重要,若不喜欢< h1 >的默认样式可以通过CSS设置。尽量做到正文标题用< h1 >标签,副标题用< h2 >标签, 而其它地方不应该随便乱用 h 标题标签。

    • < br >标签:只用于文本内容的换行。

    • 表格应该使用< caption >表格标题标签。

    • < img >应使用 “alt” 属性加以说明。
    • < strong >、< em >标签 : 需要强调时使用。
      • < strong >标签在搜索引擎中能够得到高度的重视,它能突出关键词,表现重要的内容,< em >标签强调效果仅次于< strong >标签。
      • < b >、< i >标签: 只是用于显示效果时使用,在SEO中不会起任何效果。
    • 文本缩进不要使用特殊符号   应当使用CSS进行设置。版权符号不要使用特殊符号 © 可以直接使用输入法,拼“banquan”,选择序号5就能打出版权符号©。

    • 谨慎使用 display:none :对于不想显示的文字内容,应当设置z-index或设置到浏览器显示器之外。因为搜索引擎会过滤掉display:none其中的内容。

    • 重要内容不要用JS输出,因为“蜘蛛”不认识。

    • 尽量少使用iframe框架,因为“蜘蛛”一般不会读取其中的内容。

    • js代码如果是操作DOM操作,应尽量放在body结束标签之前,html代码之后。


渐进增强和优雅降级

  • 渐进增强 :针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。

  • 优雅降级 :一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。


前端模块化

  • 解决命名冲突文件依赖问题

  • 模块化进程一:script标签:最原始的 JavaScript 文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在window 对象中,不同模块的接口调用都是一个作用域中。

    • 缺点:污染全局作用域;文件只能按照script标签的书写顺序进行加载。
  • 模块化进程二:CommonJS规范:允许模块通过require方法来同步加载所要依赖的其他模块,然后通过 exports 或 module.exports 来导出需要暴露的接口。

    • 缺点:同步的模块加载方式不适合在浏览器环境中;不能非阻塞的并行加载多个模块。
  • 模块化进程三:AMD规范:定义了两个API:require([module], callback);define(id, [depends], callback);require接口用来加载一系列模块,define接口用来定义并暴露一个模块。可以并行加载多个模块。

  • 模块化进程四:CMD规范:一个模块就是一个文件。与CommonJS和Node.js的 Modules 规范保持了很大的兼容性。

  • 模块化进程五:ES6模块化:使用export关键字来导出模块,使用import关键字引用模块。


前端工程化的理解

  • 规范。

    • 团队开发规范 => 模块化开发,组件化开发,git工作流,团队协作。
    • 开发流程 => 系统测试,日志统计,上线部署,性能优化,基础框架。
  • 自动化:开发工具。


前端工程的价值

  • 为简化用户使用提供技术支持(交互部分)

  • 为多个浏览器兼容性提供支持

  • 为提高用户浏览速度浏览器性能)提供支持

  • 跨平台或者其他基于webkit或其他渲染引擎的应用提供支持

  • 展示数据提供支持(数据接口)


谈谈你对组件的看法

一个组件应该有以下特征:

  • 可组合:一个组件易于和其它组件一起使用,或者嵌套在另一个组件内部。

  • 可重用:每个组件都是具有独立功能的,它可以被使用在多个 UI 场景。

  • 可维护:每个小的组件仅仅包含自身的逻辑,更容易被理解和维护。

  • 可测试:因为每个组件都是独立的,那么对于各个组件分别测试显然要比对于整个 UI 进行测试容易的多。


XML和JSON的区别

  • 数据体积方面:JSON相对于XML来讲,数据的体积小,传递的速度更快些。
  • 数据交互方面:JSON与JavaScript的交互更加方便,更容易解析处理,更好的数据交互。
  • 数据描述方面:JSON对数据的描述性比XML较差。
  • 传输速度方面:JSON的速度要远远快于XML。

单页面应用

  • 优点
    • 用户体验好,速度快,内容的改变不需要重新加载整个页面,SPA相对服务器压力小。
    • 没有页面切换,就没有白屏阻塞 。
  • 缺点:
    • 不利于SEO
    • 初次加载耗时增多
    • 导航不可用
    • 容易造成css命名冲突等
    • 页面复杂度提高很多,复杂逻辑难度成倍

关于Vue.js

Vue.js双向绑定的实现原理

vue.js 采用数据劫持的方式,结合发布者-订阅者模式,通过Object.defineProperty() 来劫持各个属性的 setter,getter 以监听属性的变动,在数据变动时发布消息给订阅者,触发相应的监听回调:

var obj = { };// 为obj定义一个名为 say 的访问器属性Object.defineProperty(obj, "say", {         get: function () {console.log('get方法被调用了')},         set: function (val) {console.log('set方法被调用了,参数是'+val)}})obj.say // 可以像普通属性一样读取访问器属性,get方法被调用了obj.say = 'hello vue';//set方法被调用了,参数是hello vue

实现一个极简的MVVM:

<body>    <input type="text" name="" value="" id='J_vueInput'>    <span id="J_span"></span></body><script type="text/javascript">    var obj = {};    Object.defineProperty(obj,'say',{        set: function (val) {            document.getElementById('J_vueInput').value = val;            document.getElementById('J_span').innerHTML = val;        }    })    document.addEventListener('keyup',function (e) {        obj.say = e.target.value;    })</script>

观察者模式

  • 定义:观察者模式(发布-订阅模式):其定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

  • JS里对观察者模式的实现是通过回调来实现的。

  • 作用:实现松散耦合的代码,主体和订阅者之间是相互独立的,其二者可以独立运行。


关于MVC

  • View 传送指令到 Controller

  • Controller 完成业务逻辑后,要求 Model 改变状态

  • Model 将新的数据发送到 View,用户得到反馈

  • 所有通信都是单向的。


关于MVVM

  • Model 代表数据模型,可以在Model中 定义数据修改操作业务逻辑

  • view    代表 UI组件 。负责将数据模型转换成UI展现出来

  • ViewModel 是一个同步View和Model的对象。

用户操作view层,view数据变化会同步到Model,Model数据变化会立即反应到view中。viewModel通过 双向数据绑定 把view层和Model层连接起来。


vue的核心

  • 数据绑定

    数据改变 驱动了视图的自动更新,这就是MVVM思想的实现。(传统的做法需要手动改变DOM来改变视图。)

  • 视图组件化

    把整一个网页的拆分成一个个区块,每个区块我们可以看作成一个组件。网页由多个组件拼接或者嵌套组成。


双向数据绑定和单向数据绑定

  • 双向绑定

    • 优点:在一些需要实时反应用户输入的场合会非常方便,用户在视图上的修改会自动同步到数据模型中去,数据模型中值的变化也会立刻同步到视图中去;

    • 缺点:

      • 无法追踪局部状态的变化
      • 双向数据流,值和 UI 绑定,但由于各种数据相互依赖相互绑定,导致数据问题的源头难以被跟踪到。
  • 单向绑定

    • 优点:可以带来单向数据流,流动方向可以跟踪,流动单一,没有状态,能够避免状态管理在复杂度上升时产生的各种问题,程序的调试会变得相对容易。单向数据流更利于状态的维护及优化,更利于组件之间的通信,更利于组件的复用。
  • 双绑跟单向绑定之间的差异只在于,双向绑定把数据变更的操作隐藏在框架内部,调用者并不会直接感知。


Vue 的生命周期

  • beforecreate(创建前) : 可以在这加个loading事件,在加载实例时触发
  • created (创建后): 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
  • mounted(载入后) : 挂载元素,获取到DOM节点
  • updated(更新后) : 如果对数据统一处理,在这里写上相应函数
  • beforeDestroy(销毁前) : 可以做一个确认停止事件的确认框
  • nextTick : 更新数据后立即操作dom

编程题

if( [] == false )       //trueif( {} == false )       //falseif( {} )                //trueif( [] )                //true
function foo(){    this.name = '小明';    console.log(this.name);}foo();        //输出小明window.name   //小明

js 封装 bind()

if (!Function.prototype.bind) {  Function.prototype.bind = function(oThis) {    if (typeof this !== 'function') {      throw new TypeError('Error');    }    var aArgs   = Array.prototype.slice.call(arguments, 1),        fToBind = this,        fNOP    = function() {},        fBound  = function() {          return fToBind.apply(this instanceof fNOP                 ? this                 : oThis,                 // 获取调用时(fBound)的传参                 aArgs.concat(Array.prototype.slice.call(arguments)));        };    // 维护原型关系    if (this.prototype) {      fNOP.prototype = this.prototype;     }    fBound.prototype = new fNOP();    return fBound;  };}

去掉一组整型数组中重复的值

/* 方法一:定义一个临时数组,判断元素是否已保存到新数组里。indexOf()+push() */Array.prototype.unique = function(){      var arr[];    //定义一个临时数组      for(var i = 0; i < this.length; i++){    //循环遍历当前数组      //判断当前数组下标为i的元素是否已经保存到临时数组,如果已保存,则跳过,否则将此元素保存到临时数组中          if( this.indexOf(this[i]) == -1 ){              arr.push(this[i]);          }      }      return arr;  }  /* 方法二:直接定义结果数组,判断元素在新数组中第一次出现的位置是否为i。indexOf()+push() */Array.prototype.unique = function(){      var arr[this[0]];  //直接定义结果数组     for(var i = 1; i < this.length; i++){    //从数组第二项开始循环遍历此数组      //对元素进行判断:如果数组当前元素在此数组中第一次出现的位置不是i,那么我们可以判断第i项元素是重复的,否则直接存入结果数组。          if( this.indexOf(this[i]) == i ){              arr.push(this[i]);          }      }      return arr;  }  /*方法三:hash表 */Array.prototype.unique = function(){      var h{};    //定义一个hash表      var arr[];  //定义一个临时数组      for(var i = 0; i < this.length; i++){    //循环遍历当前数组      //对元素进行判断,看是否已经存在表中,如果存在则跳过,否则存入临时数组          if(!h[this[i]]){              h[this[i]] = true;  //存入hash表             arr.push(this[i]);  //把当前数组元素存入到临时数组中        }       }       return arr;  }  /* 方法四 */Array.prototype.method4 = function(){      this.sort();             //将数组进行排序      var arr[this[0]];        //定义结果数组    for(var i = 1; i < this.length; i++){    //从数组第二项开始循环遍历数组      //判断相邻两个元素是否相等,如果相等说明数据重复,否则将元素写入结果数组          if(this[i] !== this[i+1]){              arr.push(this[i]);          }                   }       return arr;  }  

快速排序

时间复杂度:O(nlogn)

function quickSort(array) {    if (arr.length <= 1) { return arr; }  var pivotIndex = Math.floor(arr.length / 2);  var pivot = arr.splice(pivotIndex, 1)[0];  var left = [], right = [];  for (var i = 0; i < arr.length; i++){    if (arr[i] < pivot) {      left.push(arr[i]);    } else {      right.push(arr[i]);    }  }  return quickSort(left).concat([pivot], quickSort2(right));};

归并排序(Merge Sort)

时间复杂度:O(nlogn)

function merge(left, right){    var result=[];    while(left.length>0 && right.length>0){        if(left[0]<right[0]){        /* shift()方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。 */            result.push(left.shift());        }else{            result.push(right.shift());        }    }    return result.concat(left).concat(right);}function mergeSort(items){    if(items.length == 1){        return items;    }var middle = Math.floor(items.length/2),    left = items.slice(0, middle),    right = items.slice(middle);    return merge(mergeSort(left), mergeSort(right));}

洗牌算法

生成长度为 length 的随机数组

function randomArray(length) {    var i, index, temp, arr = [length];    for (i = 1; i <= length; i++) {        arr[i - 1] = i;    }    // 打乱数组    for (i = 1; i <= length; i++) {        // 产生从 i 到 length 之间的随机数        index = parseInt(Math.random() * (length - i)) + i;        if (index != i) {            temp = arr[i];            arr[i] = arr[index];            arr[index] = temp;        }    }    return arr;}

大整数加法

function sumStrings(a,b){    var res='', c=0;    a = a.split('');    b = b.split('');    while (a.length || b.length || c){        c += ~~a.pop() + ~~b.pop();        //~~作用:转换为数字类型        res = c % 10 + res;        c = c>9;    }    return res.replace(/^0+/,'');}

url转置 www.toutiao.com => com.toutiao.www

var url = 'www.toutiao.com';var array = url.split("");          //把一个字符串分割成字符串数组array = array.reverse();url = array.join("");               //数组中的所有元素放入一个字符串 

trim() JS封装

/* 正则表达式 */String.prototype.trim=function( x ){    x += '';    return x.replace( /^\s+|\s+$/gm,'' );}/* 方法二 */String.prototype.trim = function (array) {    if ( array == '') { return array; }    while (true) {        if ( array.indexOf (' ') == 0 ) {            array = array.substring( 1, parseInt(array.length) );        } else if ( (parseInt(array.length) != 0) && ( array.lastIndexOf (' ') == parseInt( array.length ) - 1)) {           array = array.substring(0, parseInt(array.length) - 1);        } else {            return array;        }    }}

数据结构

栈和队列的区别

  • 栈的插入和删除操作都在一端进行,只允许在表尾一端进行插入和删除,先进后出。
  • 队列的操作在两端进行,只允许在表尾一端进行插入,在表头一端进行删除,先进先出。

栈和堆的区别

  • 栈区(stack)—— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。
  • 堆区(heap)—— 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。
  • 堆(数据结构):堆可以被看成是一棵树,如:堆排序。
  • 栈(数据结构):一种先进后出的数据结构。

Git

  • git fetch 和 git pull的区别

    • git pull 相当于是从远程获取最新版本并merge到本地。

    • git fetch:相当于是从远程获取最新版本到本地,不会自动merge。

  • git merge 和 git rebase的区别

    • git merge 操作会生成一个新的节点,之前的提交分开显示。

    • git rebase 操作不会生成新的节点,是将两个分支融合成一个线性的提交。

  • git 命令

git checkout -b branchName         //创建并切换分支

陆续更新中,待完善……

原创粉丝点击