HTML5:
1. 你是如何理解HTML语义化的?(必考)
- 最开始的混沌阶段:
后台来写HTML
table标签布局 - 美工阶段:
DIV+CSS布局
但不够语义化 - 前端阶段:
使用h1 p article等标签 专业
2. meta viewport 是做什么用的,怎么写?
举例法:
meta viewport 是用于适配移动设备的,为了使不管是什么宽度的页面都能在移动端设备得到完美适配(不需要用户缩放和滚动横向滚动条并且字体图片等显示正常)。
<meta name="viewport" content="width=device-width, initial-scale=1,
maximum-scale=1, minimum-scale=1,user-scalable=no">
然后逐个解释每个单词的意思:设备宽度,初始比例,最大比例,最小比例
3. HTML5 常用标签
内容相关:header main footer aticle
功能相关:canvas video oudio
canvas如何进行绘制的呢?
var canvas = document.getElementById('canvas')
var ctx = canvas.getContext('2d')
ctx.fillstyle = 'green';
ctx.fillRect(10,10,100,100)
首先获取一个canvas,然后获取这个canvas的2d(2维)上下文,然后设置canvas笔刷颜色,设置笔刷范围。 video 那你用videodisc时候加什么属性呢? src就是视频的地址,autoPlay就是自动播放,poster就是它的封面,还可以用 track 加字幕; 兼容性:chrome: 3.0 Firefox: 3.5 IE: 9.0
4. H5是什么?
H5表示移动端页面,手机上各种能够被浏览器打开的页面都可以被笼统称作H5,H5的应用方式主要有电子邀请函、电子宣传册、企业汇报幻灯片、信息收集等。
H5相当于一个产品名词,而HTML5则是一个技术名词。
CSS:
1. CSS 两种盒模型 (必考)
1、 content-box内容盒模型,也叫W3C 标准盒子模型
2、 border-box边框盒模型,也叫IE 怪异盒子模型
content-box:width = content内容宽度
border-box: width = 内容的宽度 + padding + border
总结:
内容盒模型在定义width宽度后,后面写padding和border是会改变盒子实际大小。如果需要保持固定大小,需要进行计算。
边框盒模型在定义width宽度后,不管怎么写padding和border宽度都固定在书写时的width。但是有可能导致内容被压缩,导致布局异常。
拔高:为什么会有新的盒模型?追问两种盒模型哪种好用?
传统盒模型的margin和border会撑大盒子,这种撑大盒子的特性会在实际开发中带给我们很多困难。例如我们已经利用浮动或定位排列好了许多模块,现在只需要各个模块的间距大一点,如果用传统盒模型的border或者padding,就会破坏我们之前设置好的布局。这也是border-box更好用的原因。
2.必考: 如何实现垂直居中?
分情况讨论 (七种方式实现垂直居中)
如果 .parent 的 height 不写死,只需要 padding: 10px 0; 就能将 .child 垂直居中;
如果 .parent 的 height 写死了,就很难把 .child 居中,以下是垂直居中的方法。
忠告:能不写 height 就千万别写 height。
1、table自带功能:
<table class="parent">
<tr>
<td class="child">一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字</td>
</tr>
</table>
CSS:
.parent{
border: 1px solid red;
height: 600px;
}
.child{
border: 1px solid green;
}
2、100%高度的before after加上inline-block
<div class="parent">
<span class=before></span><div class="child">
一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字</div> <span class=after></span>
</div>
CSS:
.parent{
border: 3px solid red;
hetght: 600px;
text-align: center;
}
.child{
border: 3px solid black;
display: inline-block;
width: 300px;
vertical-align: middle;
}
.parent .before{
outline: 3px solid red;
display: inline-block;
height: 100%;
vertical-align: middle;
}
.parent .after{
outline: 3px solid red;
display: inline-block;
height: 100%;
vertical-align: middle;
}
优化版:
<div class="parent">
<div class="child">
一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字</div>
</div>
CSS:
.parent{
border: 3px solid red;
hetght: 600px;
text-align: center;
}
.child{
border: 3px solid black;
display: inline-block;
width: 300px;
vertical-align: middle;
}
.parent:before{
content: '';
outline: 3px solid red;
display: inline-block;
height: 100%;
vertical-align: middle;
}
.parent:after{
content: '';
outline: 3px solid red;
display: inline-block;
height: 100%;
vertical-align: middle;
}
3、div 伪装成 table
<div class="table">
<div class="td">
<div class="child">一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字</div>
</div>
</div>
CSS:
div.table{
display: table;
border: 1px solid red;
height: 600px;
}
div.tr{
display: table-row;
border: 1px solid green;
}
div.td{
display: table-cell;
border: 1px solid blue;
vertical-align: middle;
}
.child{
border: 10px solid black;
}
4、margin-top -50%
<div class="parent">
<div class="child"> 一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字
</div>
</div>
CSS:
.parent{
height: 600px;
border: 1px solid red;
position: relative;
}
.child{
border: 1px solid green;
position: absolute;
width: 300px;
height: 100px;
top: 50%;
left: 50%;
margin-left: -150px;
margin-top: -50px;
}
5、translate -50%
<div class="parent">
<div class="child">一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字</div>
</div>
CSS:
.parent{
height: 600px;
border: 1px solid red;
position: relative;
}
.child{
border: 1px solid green;
positon: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
6、absolute margin auto
<div class="parent">
<div class="child">一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字</div>
</div>
CSS:
.parent{
height: 600px;
border: 1px solid red;
position: relative;
}
.child{
border: 1px solid green;
position: absolute;
width: 300px;
height: 200px;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
7、flex 布局
<div class="parent">
<div class="child">一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字</div>
</div>
CSS:
.parent{
height: 600px;
border: 3px solid red;
display: flex;
justify-content: center;
align-items: center;
}
.child{
border: 3px solid green;
width: 300px;
}
拔高:为什么水平居中容易实现,垂直居中不容易实现? 涉及到CSS渲染的回溯机制
3.flex怎么用,常用属性有哪些?(必考)
1.开启flex布局
只需要在父级元素上写上 display: flex;
行内元素写 display:inline-flex;
Webkit内核的浏览器,需加上前缀:display: -webkit-flex;
2.父容器上的属性
容器具有这样的特点:父容器可以统一设置子容器的排列方式,子容器也可以单独设置自身的排列方式,如果两者同时设置,以子容器的设置为准。
设置主轴的方向:flex-direction
row: 默认值,主轴为水平方向从左到右;
row-reverse: 主轴为水平方向从右到左;
column: 主轴为垂直方向从上到下;
column-reverse: 主轴为垂直方向从下到上
设置子容器沿主轴排列:justify-content
位置排列:flex-start , flex-end , center
分布排列:space-between , space-around
设置子容器如何沿交叉轴排列:align-items
位置排列:flex-start , flex-end , center
基线排列:baseline
拉伸排列:stretch
设置换行方式:flex-wrap
不换行:nowrap
换行:wrap
反向换行:wrap-reverse
轴向与换行组合设置:flex-flow
flow 即流向,也就是子容器沿着哪个方向流动,流动到终点是否允许换行,比如 flex-flow: row wrap,flex-flow 是一个复合属性,相当于 flex-direction 与 flex-wrap 的组合,可选的取值如下:
1、row、column 等,可单独设置主轴方向
2、wrap、nowrap 等,可单独设置换行方式
3、row nowrap、column wrap 等,也可两者同时设置多行沿交叉轴对齐:align-content
当子容器多行排列时,设置行与行之间的对齐方式。
位置排列:flex-start , flex-end , center
分布排列:space-between , space-around
拉伸排列:stretch
3.子容器上的属性
在主轴上如何伸缩:flex子容器是有弹性的(flex即弹性),它们会自动填充剩余空间,子容器的伸缩比例由flex属性确定。
单独设置子容器如何沿交叉轴排列:align-self
位置排列:flex-start , flex-end , center
基线排列:baseline
拉伸排列:stretch
每个子容器也可以单独定义沿交叉轴排列的方式,此属性的可选值与父容器align-items属性完全一致,如果两者同时设置则以子容器的align-self属性为准。
设置扩展比例:flex-grow
子容器弹性伸展的比例。
设置收缩比例:flex-shrink
子容器弹性收缩的比例。
设置排列顺序:order
改变子容器的排列顺序,覆盖HTML代码中的顺序,默认值为0,可以为负值,数值越小排列越靠前。
4. BFC 是什么?举例说明什么情况下会产生BFC?(必考)
背 BFC 触发条件,MDN 写了:
BFC 全称为 块级格式化上下文(Block Fromatting Context),是Web页面的可视化CSS渲染出的一部分。它是块级盒布局出现的区域,也是浮动层元素进行交互的区域。简单的来说,它就是一种会影响元素与元素之间的位置、间距的属性。
指一个独立的渲染区域或者说是一个隔离的独立容器。如果给个div,写个overflow:hidden;那么这个div里面的浮动元素就会被它包裹起来。
具有BFC特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且BFC具有普通容器所没有的一些特性。通俗一点来讲,可以把BFC理解为一个封闭的大箱子,箱子内部的元素无论如何翻江倒海,都不会影响到外部。BFC 有以下特点:
1.内部的元素会在垂直方向,从顶部开始一个接一个地放置。
2.元素垂直方向的距离由margin决定。属于同一个BFC的两个相邻元素的margin会发生叠加。
3.都是从最左边开始的。每个元素的margin box的左边,与包含块border box的左边(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
4.BFC的区域不会与float box叠加。
5.BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然。
6.计算BFC的高度时,浮动元素也参与计算(当BFC内部有浮动时,为了不影响外部元素的布局,BFC计算高度时会包括浮动元素的高度)。
BFC 的使用场景: 1、解决边距合并 2、清除浮动的原理 3、布局
看到这里相信大家都知道怎么解决外边距重合了,方法是:将两个外边距重合的元素放在不同的 BFC 容器中 创建块级格式化上下文:(面试官一般只知道下列几个:) 1.根元素 2.浮动元素(元素的 float 除 none外的left或right) 3.绝对定位元素(元素的 position 为 absolute 或 fixed) 4.行内块元素 5.overflow 除了 visible 以外的值(hidden,auto,scroll) 的块元素 6.弹性元素(display为 flex 或 inline-flex元素的直接子元素)
块格式化上下文包含创建它的元素内部的所有内容。
块格式化上下文对浮动定位(参见 float)与清除浮动(参见 clear)都很重要。
浮动定位和清除浮动时都只会应用于同一个BFC内的元素。浮动不会影响其它BFC中元素的布局,而清除浮动只能清除同一BFC中在它前面的元素的浮动。外边距折叠(Margin collapsing)也只会发生在属于同一BFC的块级元素之间。
外边距合并
所谓外边距合并,指的是margin合并,MDN是这样定义的:
块的顶部外边距和底部外边距有时被组合(折叠)为单个外边距,其大小是组合到其中的最大外边距,这种行为称为外边距合并。
要注意的是,外边距合并只针对块级元素,而且是顶部或底部的外边距。
外边距合并有下面几种情况:
1、相邻兄弟元素
2、父子元素
给父级加了overflow:hidden,其实就是给父级元素创建一个BFC(块级格式化上下文)。
5. 清除浮动说一下
.clearfix:after{
content: '';
display: block /*或者 table*/;
clear: both;
}
.clearfix{
zoom: 1;/* IE 兼容*/
}
.clearfix 加到容器上,里面的子元素的浮动就被清除了
6. CSS选择器优先级
网上错误观点:一般分个十百千万,标签选择器权值为1,class、属性和伪类选择器的权值为10,id选择器优先级很高,权值为100
普通标签0001
class:0010
id:0100
后代选择器的定位原则:
在这里介绍一下对于后代选择器,浏览器是如何查找元素的呢?
浏览器CSS匹配不是从左到右进行查找,而是从右到左进行查找。
比如DIV#divBox p span.red{color:red;},浏览器的查找顺序如下:先查找html中所有class='red'的span元素,找到后,再查找其父辈元素中是否有p元素,再判断p的父元素中是否有id为divBox的div元素,如果都存在则匹配上。浏览器从右到左进行查找的好处是为了尽早过滤掉一些无关的样式规则和元素。
应举例说明:
1、越具体优先级越高 如 :not(.xxx):first-child{}比.xxx{}高
2、同样优先级,写在后面的会覆盖写在前面的
3、!important 优先级最高,但要少用
JavaScript
1. ES 6语法知道哪些,分别怎么用?(必考)
举例法:
let const 箭头函数 Promise 展开操作符 默认参数 import export
方方整理的ES 6 新特性列表: fangyinghang.com/es-6-tutori…
或看阮一峰 ECMAScript 6 入门: es6.ruanyifeng.com/
2. Promise、Promise.all、Promise.race 分别怎么用?(必考)
- 背代码 Promise 用法
function fn(){
return new Promise((resolve, reject)=>{
成功时调用 resolve(数据)
失败时调用 reject(错误)
})
}
fn().then(success, fail).then(success2, fail2)
- 背代码 Promise.all 用法
Promise.all([promise1, promise2]).then(success1, fail1)
promise1和promise2都成功才会调用success1
举例:
promise.all 是解决并发问题的,多个异步并发获取最终的结果(如果有一个失败则失败)
Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的promise都“完成(resolved)”或参数中不包含promise时回调完成(resolve);
如果参数中promise有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败promise的结果。它通常在启动多个异步任务并发运行并为其结果创建承诺之后使用,以便人们可以等待所有任务完成。
参数iterable表示一个可迭代对象,如 Array 或 String。
示例:
Promise.all 的使用,Promise.all 等待所有都完成(或第一个失败)。
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // [3, 1337, "foo"]
});
- 背代码 Promise.race 用法
Promise.race([promise1, promise2]).then(success1, fail1)
promise1和promise2只要有一个成功就会调用success1;
promise1和promise2只要有一个失败就会调用fail1;
总之,谁第一个成功或失败,就认为是race的成功或失败。
Promise.race 用来处理多个请求,采用最快的(谁先完成用谁的)。
Promise.race(iterable)方法返回一个promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
const promise1 = new Promise((resolve,reject) =>{
setTimeout(resolve,500,'one');
});
const promise2 = new Promise ((resolve,reject) => {
setTimeout(resolve,100,'two');
});
Promise.race([promise1,promise2]).then((value) => {
console.log(value);
// Both resolve,but promise2 is faster
}) //expected output:"two"
3. 手写函数防抖和函数节流
函数节流(throttle) 可理解为 cd 冷却时间,可用在拉动滚动条时,每隔一段时间判断是否已经滚动到底。
function fn(){}
var cd = false
button.onclick = function(){
if(cd){
//
}else {
fn()
cd = true
var timerId = setTemeout(()=>{
cd = false
},3000)
}
}
或:
// 节流(执行一次之后,在一段时间内就不再执行第二次)
function throttle(fn, delay){
let canUse = true
return function(){
if(canUse){
fn.apply(this, arguments)
canUse = false
setTimeout(()=>canUse = true, delay)
}
}
}
const throttled = throttle(()=>console.log('hi'))
throttled()
throttled()
函数防抖(debounce) 即带着一起做(如接外卖订单去送) 任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。 可用在用户名注册检查:在一定时间内检查输入框输入的用户名是否存在或合法。
var timerId = null
button.onclick = function(){
if(timerId){
window.clearTimeout(timerId)
}
timerId = setTimeout(()=>{
fn()
timerId = null
},5000)
}
或:
// 防抖(一段时间内会等,超过这段时间后会带着一起做)
function debounce(fn, delay){
let timerId = null
return function(){
const context = this
if(timerId){window.clearTimeout(timerId)}
timerId = setTimeout(()=>{
fn.apply(context, arguments)
timerId = null
},delay)
}
}
const debounced = debounce(()=>console.log('hi'))
debounced()
debounced()
4. 手写Ajax
var request = new XMLHttpRequest()
request.open('GET','/a/b/c?name=ff',true)
request.onreadystatechange =function(){
if(request.readyState ===4){
console.log('请求完成')
if(request.status >=200 && request.status <300){
console.log('请求成功') // console.log(request.responseText)
}else{
}
}
}
request.send()
简化版:
var request = new XMLHttpRequest()
request.open('GET','/xxxx')
request.onload = ()=>{console.log('请求成功')}
request.send()
AJAX 是什么?
AJAX(Asynchronous-JavaScript-and-XML),指的是通过JavaScript的异步通信,从服务器获取XML文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。
后来,AJAX这个词就成为JavaScript脚本发起HTTP通信的代名词,也就是说,只要用脚本发起通信,就可以叫做 AJAX 通信。
AJAX的步骤:
1.创建XMLHttpRequest实例对象
2.发出Http请求
3.服务器返回XML格式的字符串
4.JS解析XML,并更新局部页面
不过随着历史进程的推进,XML 已经被淘汰,取而代之的是 JSON。
JSON(JavaScript Object Notation,JavaScript对象表示法)是一种由 Douglas Crockford 构想和设计、轻量级的数据交换语言。它是 JavaScript 的一个子集,因此 JSON 在语法上保留了很多 JavaScript 的特征。区别:
JSON 没有 function、undefined,也没有 Number 中的 NaN 和 Infinity;
JSON 字符串的首尾必须是双引号,这意味着对象的键也必须加上双引号;
JSON只是一种数据格式,数据格式其实就是一种规范,格式、形式、规范是不能用来存储数据的。因此诸如 var obj={"width":100,"height":200,"name":"rose"}
这样的不能称之为 JSON 对象,而是一种 JSON 格式的 JS 对象。
XMLHttpRequest对象是AJAX的主要接口,用于浏览器与服务器之间的通信。尽管名字里面有XML和HTTP,它实际上可以使用多种协议(比如file或ftp),发送任何格式的数据(包括字符串和二进制)。
注意:
AJAX 只能向同源网址(协议、域名、端口都相同)发出 HTTP 请求,如果发出跨域请求,就会报错。
XMLHttpRequest 的实例属性
XMLHttpRequest.readyState
XMLHttpRequest.readyState 属性返回一个 XMLHttpRequest 代理当前所处的状态。
5. 这段代码里的this是什么?
看调用
1. fn()
this => window
2. obj.fn()
this => obj
3. fn.call(xx,yy)
this => xx
4. fn.apply(xx)
this=> xx
5. fn.bind(xx)
this => xx
6. new Fn()
this =>新的对象
7. fn = ()=> {}
this =>外面的 this
8. with/this?
- this 就是你 call 一个函数时,传入的第一个参数。(固定公式「this 就是 call 的第一个参数」)
2.如果你的函数调用形式不是 call 形式,请按照「转换代码」将其转换为 call 形式。
3.箭头函数里并没有 this,如果你在箭头函数里看到 this,你直接把它当作箭头函数外面的 this 即可。
外面的this是什么,箭头函数里面的this就还是什么,因为箭头函数本身不支持 this。
文章:方应杭《this的值到底是什么?一次说清楚》zhuanlan.zhihu.com/p/23804247
真伪数组:
比如很多人不懂什么是伪数组,很简单:
1.如果一个数组的__proto__直接或间接指向Array.prototye(用到了数组的共用属性),那么就是真数组。
2.如果一个数组的__proto__没有直接或间接指向Array.prototye,那么就是伪数组。
var realArr = {0: 'a', 1:'b', length: 2}
realArr.__proto__ = Array.prototype
// 这就是真数组(并不完全是)
// 基本等价于 realArr = ['a', 'b']
realArr.push !== undefined // true
var fakeArr = {0: 'a', 1:'b', length: 2}
// 这就是伪数组
realArr.push === undefined // true
6. 闭包/立即执行函数是什么?
请用自己的话简述
- 什么是「闭包」。
- 闭包」的作用是什么。
首先来简述什么是闭包:
var local = '变量'
function foo(){
console.log(local)
}
在函数foo内部可访问的local变量
假设上面三行代码在一个立即执行函数中,
三行代码中,有一个局部变量 local,有一个函数 foo,foo 里面可以访问到外部的 local 变量。
好了这就是一个闭包:
「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。
就这么简单。
有的同学就疑惑了,闭包这么简单么?
「我听说闭包是需要函数套函数,然后 return 一个函数的呀!」
比如这样:
function foo(){
var local = 1
function bar(){
local++
return local
}
return bar
}
var func = foo()
func()
这里面确实有闭包,local 变量和 bar 函数就组成了一个闭包(Closure)。
为什么要函数套函数呢?
是因为需要局部变量,所以才把local放在一个函数里,如果不把local放在一个函数里,local就是一个全局变量了,达不到使用闭包的目的——隐藏变量。
这也是为什么上面要说「运行在一个立即执行函数中」。
有些人看到「闭包」这个名字,就一定觉得要用什么包起来才行。其实这是翻译问题,闭包的原文是 Closure,跟「包」没有任何关系。
所以函数套函数只是为了造出一个局部变量,跟闭包无关。
为什么要 return bar 呢?
因为如果不 return,你就无法使用这个闭包。把 return bar 改成 window.bar = bar 也是一样的,只要让外面可以访问到这个 bar 函数就行了。
所以 return bar 只是为了 bar 能被使用,也跟闭包无关。
闭包的作用:
闭包常常用来[间接访问一个变量]。换句话说,[隐藏一个变量]。
假设我们在做一个游戏,在写其中关于「还剩几条命」的代码。
如果不用闭包,你可以直接用一个全局变量:
window.lives = 30 // 还有三十条命
这样看起来很不妥。万一不小心把这个值改成 -1 了怎么办。所以我们不能让别人「直接访问」这个变量。怎么办呢?
用局部变量。
但是用局部变量别人又访问不到,怎么办呢?
暴露一个访问器(函数),让别人可以「间接访问」。
代码如下:
!function(){
var lives = 50
window.奖励一条命 = function(){
lives += 1
}
window.死一条命 = function(){
lives -= 1
}
}()
那么在其他的 JS 文件,就可以使用 window.奖励一条命() 来涨命,使用 window.死一条命() 来让角色掉一条命。
关于闭包的谣言
闭包会造成内存泄露?
错。
说这话的人根本不知道什么是内存泄露。内存泄露是指你用不到(访问不到)的变量,依然占据着内存空间,不能被再次利用起来。
闭包里面的变量明明就是我们需要的变量(lives),凭什么说是内存泄露?
这个谣言是如何来的?
因为IE。IE有bug,IE在我们使用完闭包之后,依然回收不了闭包里面引用的变量。
这是 IE 的问题,不是闭包的问题。参见司徒正美的文章。
立即执行函数就是
- 声明一个匿名函数
- 马上调用这个匿名函数
(function(){alert('我是匿名函数')} ) ()
上面是一个典型的立即执行函数。
首先声明一个匿名函数 function(){alert('我是匿名函数')}。
然后在匿名函数后面接一对括号 (),调用这个匿名函数。
那么为什么还要用另一对括号把匿名函数包起来呢?
其实是为了兼容 JS 的语法。
如果我们不加另一对括号,直接写成
function(){alert('我是匿名函数')}()
浏览器会报语法错误。想要通过浏览器的语法检查,必须加点小东西,比如下面几种
(function(){alert('我是匿名函数')} ())
// 用括号把整个表达式包起来
(function(){alert('我是匿名函数')}) ()
//用括号把函数包起来
!function(){alert('我是匿名函数')}() //求反,我们不在意值是多少,只想通过语法检查。
+function(){alert('我是匿名函数')}()
-function(){alert('我是匿名函数')}()
~function(){alert('我是匿名函数')}()
void function(){alert('我是匿名函数')}()
new function(){alert('我是匿名函数')}()
2. 立即执行函数有什么用?
只有一个作用:创建一个独立的作用域。
这个作用域里面的变量,外面访问不到(即避免「变量污染」)。
以一个著名的面试题为例:
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
liList[i].onclick = function(){
alert(i) // 为什么 alert 出来的总是 6,而不是 0、1、2、3、4、5
}
}
为什么 alert 的总是 6 呢,因为 i 是贯穿整个作用域的,而不是给每个 li 分配了一个 i 那么怎么解决这个问题呢?用立即执行函数给每个 li 创造一个独立作用域即可(当然还有其他办法):
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
!function(ii){
liList[ii].onclick = function(){
alert(ii) // 0、1、2、3、4、5
}
}(i)
}
在立即执行函数执行的时候,i 的值被赋值给 ii,此后 ii 的值一直不变。i 的值从 0 变化到 5,对应 6 个立即执行函数,这 6 个立即执行函数里面的 ii 「分别」是 0、1、2、3、4、5。
以上,就是立即执行函数的基本概念。
7. 什么是JSONP,什么是CORS,什么是跨域?(必考)
1.什么是同源:同源策略(Same-origin-policy)是一种约定,
也是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。
同源策略是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。 所谓同源是指,域名,协议,端口相同。非同源的客户端脚本在没有明确授权的情况下,不能读写对方资源,在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
非同源受到的限制:
cookie不能读取
dom无法获得
ajax请求不能发送
2.什么是跨域
跨域是指跨域名的访问,以下情况都属于跨域:
域名不同 www.jd.com 与 www.taobao.com
域名相同,端口不同 www.jd.com:8080 与 www.jd.com:8081
二级域名不同 item.jd.com 与 miaosha.jd.com
如果域名和端口都相同,但是请求路径不同,不属于跨域,如:
www.jd.com/item
www.jd.com/goods
跨域问题是浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是于当前页同域名的路径,这能有效的阻止跨站攻击。因此,跨域问题是针对ajax的一种限制。
3.JSONP跨域
JSONP即JSON-with-Padding的缩写,动态创建script标签,利用script标签的src属性可以获取任何域下的js脚本,通过这个特性(也可以说漏洞),服务器端不在返还json格式,而是返回一段调用。
某个函数的js代码,在src中进行了调用,这样实现了跨域.为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSONP数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
由于同源策略的限制,XMLHttpRequest只允许请求当前源(域名、协议、端口)的资源。而动态添加一个<script>标签,script标签的src属性是没有跨域的限制的。这样一来,这种跨域方式就与ajax XMLHttpRequest协议无关了。
我们可以通过使用HTML的script标记来进行跨域请求,并在响应中返回要执行的script代码,其中可以直接使用JSON传递javascript对象,这种跨域的通讯方式称为JSONP。
JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行(IE跨域要用JSONP来解决),不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。
JSONP的缺点则是:它只支持 GET 请求而不支持 POST 等其它类型的 HTTP 请求;它只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 JavaScript 调用的问题。
那么为什么可以跨域使用CSS、JS和图片等?
同源策略限制的数据访问,在引用CSS、JS和图片的时候,其实并不知道其内容,只是单纯的引用,如并不知道CSS的第一个字符是什么
限制:
需要服务器的支持
只能发起GET请求
4.CORS跨域
CORS:全称"跨域资源共享"(Cross Origin Resource Sharing)。
CORS需要浏览器和服务器同时支持,才可以实现跨域请求,目前几乎所有浏览器都支持CORS,IE则不能低于IE10。CORS的整个过程都由浏览器自动完成,前端无需做任何设置,跟平时发送ajax请求并无差异。所以,实现CORS的关键在于服务器,只要服务器实现CORS接口,就可以实现跨域通信。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
对于简单请求,浏览器会直接发送CORS请求,具体说来就是在header中加入origin请求头字段。同样,在响应头中,返回服务器设置的相关CORS头部字段,Access-Control-Allow-Origin字段为允许跨域请求的源。 请求时浏览器在请求头的Origin中说明请求的源,服务器收到后发现允许该源跨域请求,则会成功返回。
简单请求:
(1) 请求方法是以下三种方法之一:HEAD、GET、POST
(2)HTTP的头信息不超出以下几种字段:
Accept、Accept-Language、Content-Language、Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
当浏览器发现发出的ajax请求是简单请求时,会在请求头中携带一个字段:Origin.
Origin中会指出当前请求属于哪个域(协议+域名+端口)。服务器会根据这个值决定是否允许其跨域。
如果服务器允许跨域,需要在返回的响应头中携带下面信息:
Access-Control-Allow-Origin:可接受的域,是一个具体域名或者*,代表任意。
CORS和JSONP两者优点与缺点大致互补:
- JSONP的主要优势在于对浏览器的支持较好;虽然目前主流浏览器支持CORS,但IE10以下不支持CORS。
- JSONP只能用于获取资源(即只读,类似于GET请求);CORS支持所有类型的HTTP请求,功能完善。(这点JSONP被完虐,但大部分情况下GET已经能满足需求了)
- JSONP的错误处理机制并不完善,我们没办法进行错误处理;而CORS可以通过onerror事件监听错误,并且浏览器控制台会看到报错信息,利于排查。
- JSONP只会发一次请求;而对于复杂请求,CORS会发两次请求。
- 始终觉得安全性这个东西是相对的,没有绝对的安全,也做不到绝对的安全。毕竟JSONP并不是跨域规范,它存在很明显的安全问题:callback参数注入和资源访问授权设置。CORS好歹也算是个跨域规范,在资源访问授权方面进行了限制(Access-Control-Allow-Origin),而且标准浏览器都做了安全限制,比如拒绝手动设置origin字段,相对来说是安全了一点。
但是回过头来看一下,就算是不安全的JSONP,我们依然可以在服务端端进行一些权限的限制,服务端和客户端也都依然可以做一些注入的安全处理,哪怕被攻克,它也只能读一些东西。就算是比较安全的CORS,同样可以在服务端设置出现漏洞或者不在浏览器的跨域限制环境下进行攻击,而且它不仅可以读,还可以写。
8. 常考:async/await怎么用,如何捕获异常?
async函数是Generator函数的语法糖。使用关键字async来表示,在函数内部使用await来表示异步。 函数返回的是一个Promise对象,如果函数中有返回值,则通过Promise.resolve()封装成Promise对象,当然我们使用then()就可以取出这个值。async只能配套和await使用,单独使用就会报错。 例:
async function foo(){
let bar = await test()
}
await 后面接受一个 Promise 对象。await 表达式会暂停当前 async function的执行,等待Promise处理完成。若Promise正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function。
若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。
另外,如果 await 操作符后的表达式的值不是一个 Promise,则返回该值本身。
async 函数内部 return 返回的值,会成为 then 方法回调函数的参数。
async 函数返回的 Promise 对象,必须等到内部所有的 await 命令的 Promise 对象执行完,才会发生状态改变。
也就是说,只有当 async 函数内部的异步操作都执行完,才会执行 then 方法的回调。
正常情况下,await 命令后面跟着的是Promise,如果不是的话,也会被转换成一个 立即 resolve 的 Promise。
错误处理:
如果await后面的异步操作出错,那么等同于async函数返回的Promise对象被reject。
防止出错的方法就是我们将其放在try/catch代码块中,捕获异常就是try catch。
async function fn(){
try{
let a = await Promise.reject('error')
}catch(error){
console.log(error)
}
}
fn()
Async 与其他异步操作的对比
Promise 方式:
Promise 的方式虽然解决了 callback hell,但是这种方式充满了 Promise的 then() 方法,如果处理流程复杂的话,整段代码将充满 then。语义化不明显,代码流程不能很好的表示执行流程。
Generator 方式:
Generator 的方式解决了 Promise 的一些问题,流程更加直观、语义化。但是 Generator 的问题在于,函数的执行需要依靠执行器,每次都需要通过 g.next() 的方式去执行。
async 方式:
async 函数完美的解决了上面两种方式的问题。流程清晰,直观、语义明显。操作异步流程就如同操作同步流程。同时 async 函数自带执行器,执行的时候无需手动加载。
阮一峰的ES6入门:es6.ruanyifeng.com/?search=asy…
9. 如何实现深拷贝?
代码及关键字:
1.递归
2.判断类型 不同类型有不同拷贝方法
3.检查循环引用(环),如果某个对象引用了自己,在递归时就可能出不来,就需检查
4.不能拷贝__Proto__,拷贝原型是十分浪费内存的
浅拷贝:将内存中的某个对象复制一份,在内存中开辟一块新的空间,如果复制的这个对象的属性为基本数据类型,则拷贝的便为这个值本身;
如果为复杂数据类型,则拷贝复制的为地址,因此,修改新对象会对原对象产生影响。Object.assign和{...obj}都属于浅拷贝。
深拷贝:开辟一块新的空间,完整的复制一份,包括复杂数据类型,拷贝的这个对象和原对象无任何关系,修改什么的都互不影响。
浅拷贝 简单思路:
1.创建一个新对象
2.遍历要克隆的对象
3.给新对象添加上对应的成员
function qianClone(obj){
var newObj={};
for (var key in obj){
newObj[key]=obj[key]
}
return newObj;
}
var xm={
name:"xm",
age:30,
cars:{
bmw:"30w",
bench:"60w"
}
}
var newXm=qianClone(xm);
console.log(newXm)
显然实现了拷贝功能. 如果对拷贝后的对象进行修改呢?
newXm.name="Dm";
newXm.cars.bmw="40w";
console.log(newXm)
打印一下xm对象就会发现,xm对象的cars属性也会被改掉。
于是我们可以想到如何可以进行深层拷贝,将对象内部的对象也能完整的拷贝一份;
深拷贝:思考:如何简单修改浅拷贝的代码即可实现深拷贝?
function deepClone(obj) {
// 如果不是复杂数据类型,就直接返回一个一样的对象
if(typeof obj !="object"){
return obj
}
// 如果是,就递归调用
var newObj = {};
for (var key in obj) {
newObj[key] = deepClone(obj[key])
}
return newObj;
}
var newXm = deepClone(xm);
newXm.name = "Dm";
newXm.cars.bmw = "40w";
console.log(newXm);
console.log(xm)
优化版深拷贝:
function deepClone(obj) {
// 如果不是复杂数据类型,就直接返回一个一样的对象
if(typeof obj !="object"){
return obj
}
// 如果是,就递归调用
var newObj = {};
for (var key in obj) {
newObj[key] = deepClone(obj[key])
}
return newObj;
}
用几个常用数据测试一下上面函数
1.null
var o=deepClone(null)
console.log(o)
打印结果:
Object:
proto:Object
2.正则表达式
var RegExp = /[\u4e00-\u9fa5]/gm;
var o = deepClone(RegExp)
console.log(o)
打印结果:
Object:
proto:Object
3.日期对象
var RegExp=/[\u4e00-\u9fa5]/gm;
var o=deepClone(RegExp)
console.log(o)
打印结果:
{}:
=> Object
显然结果均是错误的,因此,如何解决deepClone的bug呢?
function deepClone(obj) {
if (obj === null) return null; //null 的情况
if (obj instanceof RegExp) return new RegExp(obj); //正则表达式的情况
if (obj instanceof Date) return new Date(obj); //日期对象的情况
if (typeof obj == 'Function') return new function(obj){}; //函数的情况
if (typeof obj != "object") {
// 非复杂类型,直接返回 也是结束递归的条件
return obj
}
// [].__proto__.constructor=Array()
// {}.__proto__.constructor=Object()
// 因此处理数组的情况时,可以取巧用这个办法来new新对象
var newObj = new obj.__proto__.constructor;
for (var key in obj) {
newObj[key] = deepClone(obj[key])
}
return newObj;
}
测试代码:
var obj={
name: 'xm',
birth: new Date,
desc: null,
reg: /^123$/,
ss: [1,2,3],
fn: function(){
console.log('123')
}
}
var obj2 = deepClone(obj);
console.log(obj,obj2)
obj.fn()
obj2.fn()
另一组代码:
function deepCopy(target){
let copyed_objs = [];
// 此数组解决了循环引用和相同引用的问题,它存放已经递归到的目标对象
function _deepCopy(target){
if((typeof target !== 'object')|| !target){return target;}
for(let i = 0;i < copyed_objs.length;i++){
if(copyed_objs[i].target === target){
return copyed_objs[i].copyTarget;
}
}
let obj = {};
if(Array.isArray(target)){
obj = [];
// 处理target是数组的情况
}
copyed_objs.push({target:target,copyTarget:obj})
Object.keys(target).forEach(key=>{
if(obj[key]){ return;}
obj[key] = _deepCopy(target[key]);
});
return obj;
}
return _deepCopy(target);
}
copyed_objs 这个数组存放的是已经递归过的目标对象。在递归一个目标对象之前,我们应该检查这个数组; 如果当前目标对象和copyed_objs中的某个对象相等,那么不对其递归。这样就解决了循环引用和相同引用的问题。 测试代码:
var a = {
arr:[1,2,3,{key:'123'}],//数组测试
};
a.self = a;//循环引用测试
a.common1 = {name:'ccc'};
a.common2 = a.common1;//相同引用测试
var c = deepCopy(a);
c.common1.name = 'changed';
console.log(c);
结果:
▲{arr:Array(4),self:{..},common1:{..},common2:{..}}
▲arr:(4)[1,2,3,{..}]
▲common1:{name:"changed"}
▲common2:{name:"changed"}
▲self:{arr:Array(4),self:{..},common1:{..},common2:{..}}
▲__proto__:Object
普通业务需求用JSON法和for...in加递归的方法即可实现。
1.JSON.parse(JSON.stringify(obj))
2.function isObj(obj) {
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj) {
let tempObj = Array.isArray(obj) ? [] : {}
for(let key in obj) {
tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]
}
return tempObj
}
10. 常考:如何用正则实现trim()?
代码:
String.prototype.trim = function(){
return this.replace(/^s+|\s+$/g,'')
}
//或者
function trim(string){
return string.replace(/^\s+|\s+$/g,'')
}
11. 常考:不用class如何实现继承?用class又如何实现?
代码:不用class这样实现:
function Animal(color){
this.color = color
}
Animal.prototype.move = function(){} //动物可以动
function Dog(color,name){
Animal.call(this,name)
this.name = name
}
//下面三行实现Dog.prototype.__proto__ = Animal.prototype
function temp(){}
temp.prototype = Animal.prototype
Dog.prototype = new temp()
//让 dog 的原型继承 animal 的原型
Dog.prototype.constructor = Dog
Dog.prototype.say = function(){console.log('汪')}
var dog =new Dog('黄色','阿黄')
这样就实现了一个animal的类,然后实现了一个狗的类,狗的类继承animal的类,继承有三个得分点,
第一个:是否在一个构造函数里调用另一个构造函数,在dog里调用animal,同时把this和所有参数返回去; 第二个:实现原型的继承;
第三个:把constructor变成Dog。
用class则相对较简单:
class Animal{
constructor(color){
this.color = color
}
move(){}
}
class Dog extends Animal{
constructor(color,name){
super(color)
this.name = name
}
say(){}
}
用class的得分点有两个,一个是用到了extends,另一个是否调用了super
12. 如何实现数组去重?
代码 3种方案
1.hash
2.[...new Set(array)] //就是把数组放在Set里面,就会变成没有重复元素的集合,然后把集合变成数组,就变成没有重复元素的数组
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
return [...new Set(arr)]
}
3.WeakMap //支持所有类型的去重
13. ==相关题目?(反着答)
性价比
1 == ‘ ’?但是 0 == ‘ ’ 一般都不用两个等于号,因为太难记了,只用三个等于号
== 会自动进行类型转换 如:
var a = 1;
var b = true;
console.log(a == b);//true
文章 方应杭 ===
复杂类型 []、{}、NAN不相等,其他都相等
14. 手写一个Promise(送命题)
说思路?Promise有三种状态:Pending 初始态;Fulfilled 成功态;Rejected 失败态。
function Promise(executor) {
let self = this;
self.status = 'pending'; //等待态
self.value = undefined; //成功的返回值
self.reason = undefined; //失败的原因
function resolve(value){
if(self.status === 'pending'){
self.status = 'resolved';
self.value = value;
}
}
function reject(reason) {
if(self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
}
}
try{
executor(resolve, reject);
}catch(e){
reject(e);// 捕获时发生异常,就直接失败
}
}
//onFufiled 成功的回调
//onRejected 失败的回调
Promise.prototype.then = function (onFufiled, onRejected) {
let self = this;
if(self.status === 'resolved'){
onFufiled(self.value);
}
if(self.status === 'rejected'){
onRejected(self.reason);
}
}
module.exports = Promise;
15. 什么是回调函数?
按照MDN的描述:回调函数是作为参数传给另一个函数的函数,然后通过在外部函数内部调用该回调函数以完成某种操作。
简单来说就是,回调函数是一个函数,将会在另一个函数完成执行后立即执行。回调函数是一个作为参数传给另一个JS函数的函数。这个回调函数会在传给的函数内部执行。
在JavaScript中函数被看作是一类对象。对于一类对象,是指数字、函数或变量可以与语言中的其他实体相同。同时也可以将函数作为变量传给其他函数,也可以从其他函数中返回这些函数。
可以执行这种操作的函数被称为高阶函数。回调函数实际上是一种模式。 “模式”一词表示解决软件开发中常见问题的某种行之有效的方法。最好将回调函数作为回调模式去使用。
为什么我们需要回调?
客户端JavaScript在浏览器中运行,并且浏览器的主进程是单线程事件循环。如果我们尝试在单线程事件循环中执行长时间运行的操作,则会阻止该过程。从技术上讲这是不好的,因为过程在等待操作完成时会停止处理其他事件。
例如,alert 语句被视为浏览器中 javascript 中的阻止代码之一。如果运行 alert,则在关闭alert对话框窗口之前,你将无法在浏览器中进行任何交互。为了防止阻塞长时间运行的操作,我们使用了回调。
16. typeof 和 instanceof的区别?
typeof可以判断出基本数据类型(当然除了null的数据类型为object的bug),还可以正确判断出某个对象是否为function,其余的Date,Array等无法判断。
instanceof相反,可以准确判断出复杂数据类型,但是无法判断简单数据类型.
2.实例对象的原型链?
篇外题
1.判断字符串是否是这样组成的,第一个必须是字母,后面可以是字母、数字、下划线,总长度为5-20(程序)。
var c = /^[a-z|A-Z]{1}[/w|_]{4,19}/;
c.test(‘str’);
2.截取字符串abcdefg的efg(程序)
var str='abcdefg';
alert(str.substring(4));
或用正则:用起来还显麻烦
var str = 'abcdefg';
var re= /efg/;
if(re.test(str)){
var efg=str.substring(str.indexOf('efg'));
alert(efg)
}
// indexOf():方法可返回某个指定的字符串值在字符串中首次出现的位置。
// substring:如果只有一个参数,则是从什么位置开始,到结束
或:
<script type="text/javascript">
window.onload=function test() {
var str = "abcdefg";
if (/efg/.test(str)){ //判断是否存在efg
var efg = str.substr(str.indexOf("efg"), 3);
alert(efg);
}
}
</script>
3.判断一个字符串中出现次数最多的字符,统计这个次数(程序)
思路:首先将str每一项存到json中,然后遍历json数据,找到最大次数以及对应的键。
var str = 'qweqrtyuiqqqwrtyudfgerqtywer';
var result = maxN(str);
function maxN(str) {
//定义一个json对象用于保存str的每一项以及出现次数。
var json = {}; //遍历str,循环其中的每一个字符,将某个字符的值及出现的个数拿出来作为json的key和value
for(var i=0;i<str.length;i++){
//判断json中是否有当前str的值
if(!json[str.charAt(i)]){
//如果不存在 就将当前值添加到json中去,并设置1
json[str.charAt(i)] = 1;
} else {
//如果存在的话就让数组中已有的当前值的value值++;
json[str.charAt(i)] ++;
}
}
//存储出现次数最多的值和次数
var number = '';
var num = 0;
//遍历 json 使用打擂算法统计需要的值
for(var j in json){
//如果当前项大于下一项
if (json[j]>num) {
//就让当前值更改为出现最多次数的值
num = json[j];
number = j;
}
}
return {
number:number,
num:num
}
}
document.write('该字符串出现'+ result.num +'次的'+ result.number);
方法二、利用数组reduce()方法;同时应用一个函数针对数组的两个值(从左到右)
var str = 'qweqrtyuiqqqwrtyudfgerqtywer';
var result = maxN(str);
function maxN(str) {
// 定义一个json对象用于保存str的每一项以及出现次数。
var json = str.split('').reduce((m, n) => (m[n]++ || (m[n] = 1), m), {});
// 存储出现次数最多的值和次数
var number = '';
var num = 0;
// 遍历 json 使用打擂算法统计需要的值
for(var j in json){
// 如果当前项大于下一项
if (json[j]>num) {
// 就让当前值更改为出现最多次数的值
num = json[j];
number = j;
}
}
return {
number:number,
num:num
}
}
document.write('该字符串出现'+ result.num +'次的'+ result.number);
方法三、利用正则表达式的replace对str的每一项进行检测:
var str = 'qweqrtyuiqqqwrtyudfgerqtywer';
var result = maxN(str);
function maxN(str){
var json = {};
str.replace(/(\w{1})/g,function($1){
json[$1] ? json[$1]+=1 : json[$1] = 1;
});
// 存储出现次数最多的值和次数
var number = '';
var num = 0;
// 遍历json 使用打擂算法统计需要的值
for(var j in json){
// 如果当前项大于下一项
if (json[j]>num) {
// 就让当前值更改为出现最多次数的值
num = json[j];
number = j;
}
}
return {
number:number,
num:num
}
}
document.write('该字符串出现'+ result.num +'次的'+ result.number);
4.编写一个方法去掉一个数组的重复元素
1.遍历数组法
最简单的去重方法, 实现思路:新建一新数组,遍历传入数组,值不在新数组就加入该新数组中;
注意点:判断值是否在数组的方法“indexOf”是ECMAScript5 方法,IE8以下不支持,需多写一些兼容低版本浏览器代码,源码如下:
// 最简单数组去重法:
function unique1(array) {
var n = [];
//一个新的临时数组
//遍历当前数组
for (var i = 0; i < array.length; i++) {
//如果当前数组的第i已经保存进了临时数组,那么跳过,
//否则把当前项push到临时数组里面
if (n.indexOf(array[i]) == -1) n.push(array[i]);
}
return n;
}
// 判断浏览器是否支持indexOf ,indexOf 为ecmaScript5新方法
//IE8以下(包括IE8, IE8只支持部分ecma5)不支持
if (!Array.prototype.indexOf) {
// 新增indexOf方法
Array.prototype.indexOf = function(item) {
var result = -1,
a_item = null;
if (this.length == 0) {
return result;
}
for (var i = 0, len = this.length; i < len; i++) {
a_item = this[i];
if (a_item === item) {
result = i;
break;
}
}
return result;
}
}
2.对象键值对法 该方法执行的速度比其他任何方法都快, 就是占用的内存大一些;实现思路:新建一js对象以及新数组,遍历传入数组时,判断值是否为js对象的键,不是的话给对象新增该键并放入新数组。 注意点: 判断是否为js对象键时,会自动对传入的键执行“toString()”,不同的键可能会被误认为一样;例如: a[1]、a["1"] 。解决上述问题还是得调用“indexOf”。
// 速度最快, 占空间最多(空间换时间)
function unique2(array) {
var n = {},
r = [],
len = array.length,
val, type;
for (var i = 0; i < array.length; i++) {
val = array[i];
type = typeof val;
if (!n[val]) {
n[val] = [type];
r.push(val);
} else if (n[val].indexOf(type) < 0) {
n[val].push(type);
r.push(val);
}
}
return r;
}
3.数组下标判断法 还是得调用“indexOf”性能跟方法1差不多,实现思路:如果当前数组的第i项在当前数组中第一次出现的位置不是i,那么表示第i项是重复的,忽略掉。否则存入结果数组。
function unique3(array) {
var n = [array[0]]; //结果数组
//从第二项开始遍历
for (var i = 1; i < array.length; i++) {
//如果当前数组的第i项在当前数组中第一次出现的位置不是i,
//那么表示第i项是重复的,忽略掉。否则存入结果数组
if (array.indexOf(array[i]) == i) {
n.push(array[i]);
}
}
return n;
}
4.排序后相邻去除法 虽然原生数组的”sort”方法排序结果不怎么靠谱,但在不注重顺序的去重里该缺点毫无影响。实现思路:给传入数组排序,排序后相同值相邻,然后遍历时新数组只加入不与前一值重复的值。
// 将相同的值相邻,然后遍历去除重复值
function unique4(array) {
array.sort();
var re = [array[0]];
for(var i = 1; i < array.length; i++) {
if(array[i] !== re[re.length - 1]) {
re.push(array[i]);
}
}
return re;
}
5.优化遍历数组法 实现思路:获取没重复的最右一值放入新数组。(检测到有重复值时终止当前循环同时进入顶层循环的下一轮判断)
// 思路:获取没重复的最右一值放入新数组
function unique5(array) {
var r = [];
for (var i = 0, l = array.length; i < l; i++) {
for (var j = i + 1; j < l; j++) {
if (array[i] === array[j]){
j = ++i;
r.push(array[i]);
}
}
}
return r;
}
5.JavaScript中如何检测一个变量是一个String类型?请写出函数实现 方法1:
function isString(obj){
return typeof(obj) ==="string"?true:false;
}
alert(a(123));
alert(a("abc"));
方法2:
function isString(obj){
return obj.constructor === String?true:false;
}
alert(a(123));
alert(a("abc"));
方法3:
function isString(obj){
return Object.prototype.toString.call(obj) ===
"[object String]"?true:false;
}
方法4:
function type(data){
return Object.prototype.toString.call(data).slice(8,-1).toLowerCase();
}
alert(type(123));
alert(type("abc"));
如:varisstring = isString('xiaoming');
console.log(isstring); // true
6.如何显示/隐藏一个DOM元素?
1.style属性的display 被隐藏的控件不再占用显示时占用的位置:
this.getElementByXid(‘domXid’).style.display=”none”
this.getElementByXid(‘domXid’).style.display=”block”
2.visibility隐藏的控件仅仅是将控件设置成不可见了,控件仍然占据原来的位置:
this.getElementByXid(‘domXid’).style.visibility=”hidden”
this.getElementByXid(‘domXid’).style.visibility=”visible”
3.通过 jQuery 进行控制显示隐藏:
(this.getElementByXid(‘domXid’)).show()
4.通过bind-visible 绑定KO对象或data组件字段控制隐藏,隐藏后dom节点实际还在
详情连接:doc.wex5.com/data-bind-v…
5.通过bind-if 绑定KO对象或data组件字段控制隐藏,隐藏后dom节点已经不存在了,用法和bind-visible是一样的。
需要注意的是:bind-if控制的是当前dom节点下的子节点的存在与否(不包含当前节点),而bind-visible 是控制当前节点下的所有元素的隐藏的(包含当前节点)
7.前端开发的优化问题?
(1)减少 http 请求次数:CSS Sprites, JS、CSS 源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存,图片服务器。
(2)前端模板 JS+数据,减少由于 HTML 标签导致的带宽浪费,前端用变量保存 AJAX
请求结果,每次操作本地变量,不用请求,减少请求次数。
(3)用 innerHTML 代替 DOM 操作,减少 DOM 操作次数,优化 javascript 性能。
(4)当需要设置的样式很多时设置 className, 而不是直接操作 style。
(5)少用全局变量、缓存 DOM 节点查找的结果。减少 IO 读取操作。
(6)避免使用 CSS Expression(css 表达式)又称 Dynamic-properties(动态属性)。
(7)图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳。
(8)避免在页面的主体布局中使用table,table要等其中的内容完全下载之后才会显
示出来,显示比 div+css 布局慢。
总结:
1.减少 css,js 文件数量及大小(减少重复性代码,代码重复利用),压缩 CSS 和 Js 代码
2.图片的大小
3.把 css 样式表放置顶部,把 js 放置页面底部
4.减少 http 请求数
5.使用外部 Js 和 CSS
8.用JS递归的方式写1到100求和?
假设让你来,你是否会这样写:
var sum = 0;
for(var i=1; i<=100; i++){
sum += i;
}
console.log(sum); // 5050
那么JavaScript用递归如何计算求1-100的和了?,分析: 假设递归函数已经写好,即sum(100),就是求1-100的和。寻找递推关系: 就是 n 与 n-1 ,或 n-2 之间的关系:
sum(n) == sum(n-1) + n
var resulst = sum(100);
var resulst = sum(99) + 100;
...
1、将递归结构转换成递归体
function sum(n){
return sum(n-1) + n;
}
这时候我们差一个重要的步骤,也就是临界值,来阻止程序死循环
2、将临界条件加入到递归中
求100 转换为 求99
求99 转换为 求98
求98 转换为 求97
...
求2 转换为 求1
求1 转换为 求1
即 sum(1) = 1
递归函数:
function sum(n){
if(n==1) return 1;
return sum(n-1) + n;
}
var amount = sum(100);
console.log(amount); // 5050
9.请给出异步加载JS方案,不少于两种。
默认情况javascript是同步加载的,也就是javascript的加载是单线程的,后面的元素要等待javascript加载完毕后才能进行再加载,对于一些意义不是很大的javascript,如果放在页头会导致加载很慢的话,将会严重影响用户体验的。
异步加载方式:
(1) defer,只支持IE
(2) async:
(3) 创建script,插入到DOM中,加载完毕后callBack,见代码:
function loadScript(url, callback){
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others: Firefox, Safari, Chrome, and Opera
script.onload = function(){
callback();
};
}
script.src = url;
document.body.appendChild(script);
}
10.请写出jQuery绑定事件的方法,不少于两种
简写事件:click、hover、mousemove、mouseup、mousedown……
jQuery中提供了四种事件监听方式,分别是bind、live、delegate、on,对应的解除监听的函数分别是unbind、die、undelegate、off。在开始之前,先来声明一个例子,各函数的用法将围绕这个例子进行,
同时再声明一个函数,用来作为监听函数,JS代码如下:
function getHtml(){
alert(this.innerHTML);
}
1、bind(type,[data],function(eventObject))
bind是使用频率较高的一种,作用就是在选择到的元素上绑定特定事件类型的监听函数,参数的含义如下:
type:事件类型,如click、change、mouseover等;
data:传入监听函数的参数,通过event.data取到。可选;
function:监听函数,可传入event对象,这里的event是jQuery封装的event对象,与原生的event对象有区别,使用时需要注意。
我们来瞄一眼bind的源码:
bind: function( types, data, fn ) {
return this.on( types, null, data, fn );
}
可以看到内部是调用了on方法,这个on是什么样的呢?稍后我们再看。先用我们上面的例子来试试:
$('#myol li').bind('click',getHtml);
bind的特点就是会把监听器绑定到目标元素上,有一个绑一个,在页面上的元素不会动态添加的时候使用它没什么问题。但如果列表中动态增加一个“列表元素5”,点击它是没有反应的,必须再bind一次才行。要想不这么麻烦,我们可以使用live。
jQuery还有一种事件绑定的简写方式如a.click(function(){});、a.change(function(){});等,它们的作用与bind一样,仅仅是简写而已。
2、live(type, [data], fn)
live的参数和bind一样,它又有什么蹊跷呢,我们还是先瞄一眼源码:
live: function( types, data, fn ) {
jQuery( this.context ).on( types, this.selector, data, fn );
return this;
}
可以看到live方法并没有将监听器绑定到自己(this)身上,而是绑定到了this.context上了。这个context是什么东西呢?其实就是元素的限定范围,看了下面的代码就清楚了:
$('#myol li').context; //document
$('#myol li','#myol').context; //document
$('#myol li',$('#myol')[0]); //ol
通常情况下,我们都不会像第三种方式那样使用选择器,所以也就认为这个context通常就是document了,即live方法把监听器绑定到了document上了。不把监听器直接绑定在元素上,你是不是想起事件委托机制来了呢?若没有,可以点击这里回忆一下。live正是利用了事件委托机制来完成事件的监听处理,把节点的处理委托给了document。在监听函数中,我们可以用event.currentTarget来获取到当前捕捉到事件的节点。下面的例子来揭晓:
$('#myol li').live('click',getHtml);
使用事件委托的优点一目了然,新添加的元素不必再绑定一次监听器。看来live这货还真不错,以后抛弃bind就用它了!可以吗?答案是否定的,而且是大大的否定。因为将监听器绑定到了document上,所以事件的处理得等待层层冒泡,直到冒泡到根节点才开始处理,在DOM树较深或者节点的嵌套关系很复杂时,会有意想不到的结果,根节点的负担太重了。就像四世同堂、五世同堂,甚至八世同堂(现实中不太可能,但在HTML中层级关系可能远比这还多),老爷子肯定记不清哪个孙子是哪个儿子的,哪个重孙又是哪个儿子的儿子的,老爷子脑子一乱,糊涂了,事情就办错了。为此,jQuery官方已宣布在1.7版本开始废弃live,改用其他方式代替。所以我们也顺应号召,罢用此方法。
正因为live存在那样的缺点,所以我们就思考,既然老爷子负担那么重,可不可以别把监听器绑定在document上呢,绑定在就近的父级元素上不就好了。顺应正常逻辑,delegate诞生了。
3、delegate(selector,type,[data],fn)
参数多了一个selector,用来指定触发事件的目标元素,监听器将被绑定在调用此方法的元素上。看看源码:
delegate: function( selector, types, data, fn ) {
return this.on( types, selector, data, fn );
}
又是调用了on,并且把selector传给了on。看来这个on真的是举足轻重的东西。照样先不管它。看看示例先:
$('#myol').delegate('li','click',getHtml);
我们在例子中将监听器绑定到ol上,event.currentTarget显示当前捕获到事件的元素是ol。这下,我们的选择又多了一些灵活性,不单可以利用事件委托,还可以选择委托的对象。毕竟老麻烦同一个人帮忙很不好嘛。对于如何选择委托对象,还是需要一定的策略的,毕竟父级元素可以有很多。我觉得原则应该是选择最近的“稳定”元素,选择最近是因为事件可以更快的冒泡上去,能够在第一时间进行处理。所谓“稳定”是指该父级元素是一开始就在页面上的,不是动态添加上来的,而且将来也不会消失掉,这样可以保证它可以时时监控着自己的孩子。
看了这么多,你是不是迫不及待想看看这个on的真实面目了呢,这就来:
4.on(type,[selector],[data],fn)
参数与delegate差不多但还是有细微的差别,首先type与selector换位置了,其次selector变为了可选项。交换位置的原因不好查证,应该是为了让视觉上更舒服一些吧。
我们先不传selector来看一个例子:
$('#myol li').on('click',getHtml);
可以看到event.currentTarget是li自己,与bind的效果一样。至于传selector进去,就是跟delegate一样的意义了,除了参数顺序不同,其他完全一样。
终于看到on的真实作用了,那么,这么多的事件绑定方式,我们该如何进行选择呢?
其实这个问题是完全不必纠结的,因为你已经知道他们之间的区别了不是么?根据实际情况斟酌使用就行。不过官方有一个推荐就是尽量使用on,因为其他方法都是内部调用on来完成的,直接使用on可以提高效率,而且你完全可以用on来代替其他三种写法。至于如何代替我想就不必这么直白的写出来了,真正理解它们的区别之后自然而然也就不是难事了。
11.说下CORS
1.CORS是Cross Origin Resource Sharing的缩写,中文意思是跨域资源共享
2.该技术通过在目标域名后返回CORS响应头来达到获取该域名的数据的目的
3.该技术核心就是设置response header,分为简单请求和复杂请求
4.简单请求只需要设置Access-Control-Allow-Origin:目标源即可,复杂请求则分两步走,第一步是浏览器发起OPTIONS请求,第二步才是真实请求。OPTIONS请求需要把服务器支持的操作通过响应头来表明,如Access-Control-Allow-Methods:POST,GET,OPTIONS,另外一个重要的响应头是Access-Control-Allow-Credentials:true用来表明是否接受请求中Cookie
5.优点是通过简单的配置就能实现跨域
6.缺点是某些古老浏览器不支持CORS或不支持Credentials
7.解决办法是用JSONP或P3P技术
12.div的高度等于浏览器可见区域的高度,浏览器滚动,div始终覆盖浏览器的整个可见区域(写出思路)
window.onscroll = function(){
var divObj = document.getElementByIdx_x_x("floatDiv");
var topDistance = parseInt(document.documentElement.scrollTop + document.body.scrollTop);
divObj.style.top = topDistance + "px";
}
代码说明:window.onscroll 屏幕滚动事件
document.documentElement.scrollTop 或者 document.body.scrollTop为:网页被减去的高。
代码中 document.documentElement.scrollTop + document.body.scrollTop,
之所以这么写,是因为:IE,Firefox识别document.documentElement.scrollTop,而在chrome里面,这个则为0,chrome识别document.body.scrollTop。
13.1rem、1em、1vh、1px各自代表的含义
1rem表示全部的长度都相对于根元素,通常做法是给html元素设置一个字体大小,然后其他元素的长度单位就为rem;
1em一个字节的长度,表示子元素字体大小的相对于父元素字体大小,元素的width/height/padding/margin用em的话是相对于该元素的font-size;
1vh表示屏幕(浏览器)的高度1%,
1px相对长度单位;像素px是相对于显示器屏幕分辨率而言的。
14.一个定宽元素在浏览器(IE6,IE7,Firefox)中横向居中对齐的局。
(写出主要的HTML标签及CSS思路)
15.水平居中和垂直居中的方式(至少四种)
1.
标签, 2.margin-top:-50%; 3.position:absolute margin:auto; 4.flex:justify-content:center; align-items:center 5.transform: translateX -50%16.CSS引入的方式有哪些?link和@import的区别?
在html设计制作中,css有四种引入方式。
方式一: 内联样式(内联引用CSS)
内联样式,也叫行内样式,将STYLE属性直接加在个别的元件标签里,<元件(标签) STYLE="性质(属性)1: 设定值1; 性质(属性)2: 设定值2; ...}
示例:
<td style="color:#c00; font-size:15px; line-height:18px;>
模板无忧 - www.mb5u.com
</td>
这通常是个很糟糕的书写方式,它只能改变当前标签的样式,如果想要多个
如果想要修改一种样式,又不得不修改所有的 style 中的代码。很显然,内联方式引入 CSS 代码会导致 HTML 代码变得冗长,且使得网页难以维护。
这种方法优点:可灵巧应用样式於各标签中。方便于编写代码时的使用。
这种方法缺点:没有整篇文件的“统一性”,在需要修改某样式的时候也变的比较困难。
方式二: 嵌入样式(内部引用CSS)
嵌入方式指的是在 HTML 头部中的 标签下书写 CSS 代码。 示例:
<head>
<style>
.content {
background: red;
}
</style>
</head>
通常是将整个的 <STYLE>...</STYLE>结构写在网页的<HEAD> </HEAD>部份之中。
这种方法的优点:整篇文章有了统一性,只要是有声明的的元件即会套用该样式规则。
这种方法的缺点:个别元件的灵活度不足,整站的功能性较弱。
嵌入方式的CSS只对当前的网页有效。因为CSS代码是在HTML文件中,所以会使得代码比较集中,当我们写模板网页时这通常比较有利,查看模板代码的人可以一目了然地查看 HTML 结构和 CSS 样式。
因为嵌入的 CSS 只对当前页面有效,所以当多个页面需要引入相同的 CSS 代码时,这样写会导致代码冗余,也不利于维护。
方式三:链接样式(外部引用 link 标签引用CSS)
链接方式指的是使用 HTML 头部的标签引入外部的 CSS 文件。
示例:
将样式规则写在.css的样式文件中,再以标签引入。
假设我们把样式规则存成一个example.css的档案,我们只要在网页中加入:
<link rel=stylesheet type="text/css" href="example.css">
这样引入该css样式表文件以后,就可以直接套用该样式档案中所制定的样式了。 通常是将link标签写在网页的部份之中。 这种方法的优点:可以把要套用相同样式规则的数篇文件都指定到同一个样式文件中,可以进行统一的修改,也便于整站的设置有统一的风格。 一般css网页布局都使用此种方法。 这种方法的缺点:在个别文件或元素的灵活度不足。 方式四:导入样式(外部引用 @import 引用CSS) 导入方式指的是使用 CSS 规则引入外部 CSS 文件。 示例:
<style type="text/css">
@import url(css/example.css引入的样式表的位址、路径与文档名);
</style>
或者写在css样式中
@charset "utf-8";
@import url(style.css);
*{ margin:0; padding:0;}
.notice-link a{ color:#999;}
link和@import的区别?
区别1:link是XHTML标签,除了加载CSS外,还可以定义RSS,定义rel连接属性等等其他事务;@import属于CSS范畴,只能加载CSS。
区别2:link引用CSS时,在页面载入时同时加载;@import需要页面网页完全载入以后加载。
所以会出现一开始没有css样式,闪烁一下出现样式后的页面(网速慢的情况下)
区别3:兼容性的差别。link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持,IE5以上可用。
区别4:link支持使用JavaScript控制DOM去改变样式;而@import不支持。
17.HTML5和CSS3的了解情况
CSS3 和 HTML5 新特性一览:juejin.cn/post/684490… HTML5 & CSS3面试题:juejin.cn/post/684490…
18.web标准网站有哪些优点?
一、Web标准化可以增强浏览器的兼容性
二、Web标准化可以提高开发团队代码的规范性,高效率的开发与简单的维护
三、Web标准化对SEO有利
四、信息跨平台的可用性
五、降低服务器成本 通过样式的重用、整个网站的文件量可以成倍的减小,使得降低服务器带宽成本成为可能。特别是对大型门户网站,网页数量越大,意味着重用的代码数量越多,从而使得同一时间服务器的数据访问量降低,降低带宽使用。
HTML 负责构建网页的基本结构;
CSS 负责设计网页的表现效果;
JavaScript 负责开发网页的交互效果。
网页结构:
网页标准在网页中主要是对页面信息进行组织和分类,结构化标准语言主要包括 XML、HTML、XHTML。
- XML
XML 是 The Extensible Markup Language 的缩写,中文译为“可扩展标识语言”,是一种能定义其他语言的语言。
XML 的最初设计目标是弥补 HTML 的不足,以强大的扩展性满足网络信息发布的需求。现在 XML 主要作为一种数据格式,用于网络数据交换和书写配置文件。
XML 推荐标准是 W3C 于 2000 年 10 月 6 日发布的 XML 1.0,参考地址是:www.w3.org/TR/2000/REC… - HTML
HTML 是 HyperText Markup Language 的缩写,中文译为“超文本标识语言”,而 XHTML 1.0 是在 HTML 4.0 基础上,用 XML 的规则对其进行扩展。
发布XHTML的最初目的就是实现HTML向XML的过渡。在一般语境中,人们习惯使用HTML代替HTML和XHTML。 HTML推荐标准是W3C于2000年1月26日发布的XHTML1.0,参考地址是:www.w3.org/TR/xhtml1/。
网页行为:
行为标准在网页中主要对网页信息的结构和显示进行逻辑控制,实现网页的智能交互。行为标准语言主要包括文档对象模型(如 W3C DOM)和 EMAScript 等。 - DOM
DOM 是 Document Object Model 的缩写,中文译为“文档对象模型”,根据 W3C DOM 规范(参考地址:www.w3.org/DOM/),DOM 是一种让浏览器与网页内容沟通的语言,使得用户可以访问页面元素和组件。 - ECMAScript
ECMAScript 是由 EMA(European Computer Manufactures Association)组织制定的标准脚本语言。
目前使用最广泛的是 ECMAScript 262,也即 JavaScript 5.0 版本,参考地址为:www.ecma-international.org/publication…