面試題庫1

本文最后更新于:2023年3月21日 下午

面试题库(@花夏 liubiao@itoxs.com

1.初级:

1、position四个属性

1
2
3
4
absolute
relative
fixed
static

2、移动端:CSS盒模型

1
2
3
4
5
6
7
8
display: -webkit-box; /* Chrome 4+, Safari 3.1, iOS Safari 3.2+ */
display: -moz-box; /* Firefox 17- */
display: -webkit-flex; /* Chrome 21+, Safari 6.1+, iOS Safari 7+, Opera 15/16 */
display: -moz-flex; /* Firefox 18+ */
display: -ms-flexbox; /* IE 10 */
display: flex; /* Chrome 29+, Firefox 22+, IE 11+, Opera 12.1/17/18, Android 4.4+ */

// 父元素设置display:flex后,子元素宽度会随父元素宽度的改变而改变,display:box不会

3、递归实现斐波那契数列:1 1 2 3 5 8

1
2
3
4
5
6
7
8
9
function Fibonacci (n) {
if( n == 1 || n == 2) {
// 递归结束的条件,求前两项
return 1;
} else {
// 如果是求其它项,先要求出它前面两项,然后做和。
return Fibonacci(n-1)+Fibonacci(n-2);
}
}

4、解释css sprites,如何使用
​ css sprites就是将网页中所有的图片放到一张里面,通过background-position使用

5、call和apply的区别

call 方法调用一个对象的一个方法,以另一个对象替换当前对象
对于apply和call两者在作用上是相同的,但两者在参数上有区别的。
对于第一个参数意义都一样,但对第二个参数:
apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。
func.call(func1,var1,var2,var3)对应的apply写法为:func.apply(func1,[var1,var2,var3])

同时使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入

6、CSS选择符有哪些?优先级算法如何计算?内联和important哪个优先级高?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
*
id
class
标签名
:visited :link :hover :active :checked :after :befor :focus
x+y 相邻选择符
x>y后代直接选择符
x~y兄弟选择符
x[title]属性选择器
x[href*="nettuts"]包含字符
x[href^="http"]以什么开始
x[href$=".jpg"]以什么结束
x:not(selector) 非选择器
x:nth-child(n) 从一开始的子元素
x:nth-last-child(n) 倒序
x:nth-of-type(n) 从一开始的子元素
x:nth-last-of-type(n) 倒序
x:first-child 第一个
x:last-child 最后一个

7、闭包是什么,有什么特性,对页面有什么影响

闭包的实质是一个函数,是一个用于返回局部变量值的函数,因为在全局中,受JavaScript链式作用域结构的影响,父级变量中无法访问到子级的变量值,为了解决这个问题,才使用闭包这个概念。由于闭包时,变量的值都保存到内存中,会导致页面加载时内存消耗很大,IE会导致内在泄露,因此尽量少用或用时要及时删除变量。

8、解释jsonp的原理

就是利用<script>标签没有跨域限制的“漏洞”(历史遗迹啊)来达到与第三方通讯的目的。当需要通讯时,本站脚本创建一个<script>元素,地址指向第三方的API网址,形如:
<script src="http://www.example.net/api?param1=1&param2=2"></script>
并提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)。
第三方产生的响应为json数据的包装(故称之为jsonp,即json padding),形如:
callback({"name":"hax","gender":"Male"})
这样浏览器会调用callback函数,并传递解析后json对象作为参数。本站脚本可在callback函数里处理所传入的数据。
补充:“历史遗迹”的意思就是,如果在今天重新设计的话,也许就不会允许这样简单的跨域了嘿,比如可能像XHR一样按照CORS规范要求服务器发送特定的http头。

9、数组的前后增加、删除

1
2
3
4
5
6
7
8
9
10
unshift // 将参数添加到原数组开头,并返回数组的长度
shift // 删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefined
push // 将新元素添加到一个数组中,并返回数组的新长度值。
pop // 删除原数组最后一项,并返回删除元素的值;如果数组为空则返回undefined
concat // 返回一个新数组,是将参数添加到原数组中构成的
splice(start,deleteCount,val1,val2,...) // 从start位置开始删除deleteCount项,并从该位置起插入val1,val2,... 返回刪除的元素組成的新數組,會改變原數組
reverse // 将数组反序
sort(orderfunction) // 按指定的参数对数组进行排序
slice(start,end) // 返回从原数组中指定开始下标到结束下标之间的项组成的新数组,不會改變原數組
join(separator) // 将数组的元素组起一个字符串,以separator为分隔符,省略的话则用默认用逗号为分隔符

10、字符串方法

1
2
3
4
5
6
7
str.charAt() // charAt() 方法可返回指定位置的字符
str.toLowerCase() // 用于把字符串转换为小写
str.toUpperCase() // 用于把字符串转换为大写
str.indexOf() // 可返回某个指定的字符串值在字符串中首次出现的位置
str.laseIndexOf() // 返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索
str.substring() // 用于提取字符串中介于两个指定下标之间的字符
str.split() // 用于把一个字符串分割成字符串数组,参数是用什么字符分隔

11、数学方法

1
2
3
4
5
6
7
8
9
Math.random() // 可返回介于 0 ~ 1 之间的一个随机数 [0,1)
Math.pow() // 可返回 x 的 y 次幂的值
Math.sqrt() // 可返回一个数的算术平方根
Math.abs() // 可返回数的绝对值
Math.floor() // 可对一个数进行下舍入
Math.ceil() // 可对一个数进行上舍入
Math.round() // 可把一个数字舍入为最接近的整数
Math.max() // 可返回两个指定的数中带有较大的值的那个数
Math.min()// 可返回两个指定的数中带有较小的值的那个数

12、数组的合并

1
2
concat方法
a.push.apply(a, b);

13、=====的区别

===叫做严格运算符,==叫做相等运算符。

严格运算符的运算规则如下,

(1)不同类型值
如果两个值的类型不同,直接返回false。

(2)同一类的原始类型值
同一类型的原始类型的值(数值、字符串、布尔值)比较时,值相同就返回true,值不同就返回false。

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

(4)undefined和null
undefined 和 null 与自身严格相等。

14、编程排序

插入排序、冒泡排序、选择排序、Shell排序、快速排序、归并排序

插入排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var insertSort = function (arr) {
var len = arr.length, key;
for (var i = 1; i < len; i++) {
var j = i;
key = arr[j];
while (--j > -1) {
if (arr[j] > key) {
arr[j + 1] = arr[j];
} else {
break;
}
}
arr[j + 1] = key;
}
};

冒泡排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var bubbleSort = function (arr) {
var len = array.length,
i, j, tmp, result,exchange;
result = array.slice(0);
for (i = 0; i < len; i++) {
exchange = 0;
for (j = len - 1; j > i; j--) {
if (result[j] < result[j - 1]) {
tmp = result[j - 1];
result[j - 1] = result[j];
result[j] = tmp;
exchange = 1;
}
}
// 如果在某次的排序中没有出现交换的情况,
// 那么说明在无序的元素现在已经是有序了,就可以直接返回了。
if (!exchange) return result;
}
return result;
};

选择排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 在无序区中选出最小的元素,然后将它和无序区的第一个元素交换位置。
// 原理跟冒泡排序一样,算是冒泡的衍生版本
var selectSort = function (arr) {
var min;
for (var i = 0; i < arr.length; i++) {
min = i;
for (var j = i + 1; j < arr.length; j++) {
if (arr[min] > arr[j]) {
min = j;
}
}
if (i != min) {
swap(arr, i, min);
}
}
};
function swap(arr, index1, index2) {
var temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
};

Shell排序:

1
2
3
4
5
6
7
8
9
10
11
12
var shellSort = function (arr) {
var gaps = [5, 3, 1];
for (var g = 0; g < gaps.length; ++g) {
for (var i = gaps[g]; i < arr.length; ++i) {
var temp = arr[i];
for (var j = i; j >= gaps[g] && arr[j - gaps[g]] > temp; j -= gaps[g]){
arr[j] = arr[j - gaps[g]];
}
arr[j] = temp;
}
}
};

快速排序:

“快速排序”的思想很简单,整个排序过程只需要三步:

1)在数据集之中,选择一个元素作为”基准”(pivot)。

2)所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边。

3)对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。

举例来说,现在有一个数据集{85, 24, 63, 45, 17, 31, 96, 50},怎么对其排序呢?

第一步,选择中间的元素45作为”基准”。(基准值可以任意选择,但是选择中间的值比较容易理解。)

第二步,按照顺序,将每个元素与”基准”进行比较,形成两个子集,一个”小于45”,另一个”大于等于45”。

第三步,对两个子集不断重复第一步和第二步,直到所有子集只剩下一个元素为止。

源碼:

首先,定义一个quickSort函数,它的参数是一个数组。

1
var quickSort = function(arr) {};

然后,检查数组的元素个数,如果小于等于1,就返回。

1
2
3
var quickSort = function(arr) {
if (arr.length <= 1) { return arr; }
};

接着,选择”基准”(pivot),并将其与原数组分离,再定义两个空数组,用来存放一左一右的两个子集。

1
2
3
4
5
6
7
var quickSort = function(arr) {
if (arr.length <= 1) { return arr; }
var pivotIndex = Math.floor(arr.length / 2) ;
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [];
var right = [];
};

然后,开始遍历数组,小于”基准”的元素放入左边的子集,大于基准的元素放入右边的子集。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var quickSort = function(arr) {
if (arr.length <= 1) { return arr; }
var pivotIndex = Math.floor(arr.length / 2) ;
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] < pivot) {
  left.push(arr[i]);
} else {
  right.push(arr[i]);
}
}
};

最后,使用递归不断重复这个过程,就可以得到排序后的数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var quickSort = function(arr) {
if (arr.length <= 1) { return arr; }
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [];
var 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], quickSort(right));
};

归并排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function mergeSort(arr) {
if (arr.length < 2) {
return;
}
var step = 1;
var left, right;
while (step < arr.length) {
left = 0;
right = step;
while (right + step <= arr.length) {
mergeArrays(arr, left, left + step, right, right + step);
left = right + step;
right = left + step;
}
if (right < arr.length) {
mergeArrays(arr, left, left + step, right, arr.length);
}
step *= 2;
}
}
function mergeArrays(arr, startLeft, stopLeft, startRight, stopRight) {
var rightArr = new Array(stopRight - startRight + 1);
var leftArr = new Array(stopLeft - startLeft + 1);
k = startRight;
for (var i = 0; i < (rightArr.length - 1); ++i) {
rightArr[i] = arr[k];
++k;
}
k = startLeft;
for (var i = 0; i < (leftArr.length - 1); ++i) {
leftArr[i] = arr[k];
++k;
}
rightArr[rightArr.length - 1] = Infinity; // 哨兵值
leftArr[leftArr.length - 1] = Infinity; // 哨兵值
var m = 0;
var n = 0;
for (var k = startLeft; k < stopRight; ++k) {
if (leftArr[m] <= rightArr[n]) {
arr[k] = leftArr[m];
m++;
} else {
arr[k] = rightArr[n];
n++;
}
}
}

2.中级:

1、多DOM节点事件触发时,如何事件代理委托

有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。

这里其实还有2层意思的:

第一,现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;

第二,新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的。

事件委托的原理:

事件委托是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,然后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比如给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么我们给最外面的div加点击事件,那么里面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。

1
2
3
4
5
6
7
8
9
10
11
window.onload = function(){
  var oUl = document.getElementById("ul1");
  oUl.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    if(target.nodeName.toLowerCase() == 'li'){
         alert(123);
        alert(target.innerHTML);
    }
  }
}

总结:

那什么样的事件可以用事件委托,什么样的事件不可以用呢?

适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。

值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。

不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

2、Css媒体查询

媒体查询

从 CSS 版本 2 开始,就可以通过媒体类型在 CSS 中获得媒体支持。如果您曾经使用过打印样式表,那么您可能已经使用过媒体类型。清单 1 展示了一个示例。

清单 1. 使用媒体类型

1
2
<link rel="stylesheet" type="text/css" href="site.css" media="screen" />
<link rel="stylesheet" type="text/css" href="print.css" media="print" />

在清单 1 中,media 属性定义了应该用于指定每种媒体类型的样式表:
screen 适用于计算机彩色屏幕。
print 适用于打印预览模式下查看的内容或者打印机打印的内容。

作为 CSS v3 规范的一部分,可以扩展媒体类型函数,并允许在样式表中使用更精确的显示规则。媒体查询 是评估 True 或 False 的一种表达。如果为 True,则继续使用样式表。如果为 False,则不能使用样式表。这种简单逻辑通过表达式变得更加强大,使您能够更灵活地对特定的设计场景使用自定义的显示规则。

媒体查询包含一个媒体类型,后跟一个或多个检查特定条件(如最小的屏幕宽度)的表达式。样式表中的媒体查询看起来如清单 2 中的示例所示。

清单 2. 媒体查询规则

1
@media all and (min-width: 800px) { ... }

根据清单 2 中的标记,所有最小水平屏幕宽度为 800 像素的屏幕(屏幕和打印等)都应使用如下 CSS 规则。该规则在示例中省略号所在的地方。对于该媒体查询:
@media all 是媒体类型,也就是说,将此 CSS 应用于所有媒体类型。
(min-width:800px) 是包含媒体查询的表达式,如果浏览器的最小宽度为 800 像素,则会告诉浏览器只运用下列 CSS。

请注意,在清单 2 中,可以省略关键词 all 和 and。在将某个媒体查询应用于所有媒体类型时,会省略 all。后面的 and 也是可选的。使用简写语法重新编写媒体查询,如清单 3 所示。

清单 3. 简写语法

1
@media (min-width:800px) { ... }

媒体查询也可以包含复杂表达式。例如,如果您想要创建一个仅在最小宽度为 800 像素且最大宽度为 1200 像素时应用的样式,则需要按照清单 4 中的规则来做。

清单 4. 复杂表达式

1
@media (min-width:800px) and (max-width:1200px) { ... }

在您的表达式中,您可以根据自己的喜好使用任意数量的 and 条件。如果您想要增加其他条件来检查特定的屏幕方向,只需添加另一个 and 关键词,后跟 orientation 媒体查询,如清单 5 所示。

清单 5. and 条件

1
@media (min-width:800px) and (max-width:1200px) and (orientation:portrait) { ... }

清单 5 中的媒体查询仅在宽度为 800 到 1200 像素且方向是纵向时才能激活。(通常,方向仅对能够轻易转换纵横模式的智能手机和平板电脑上是有意义的。)如果其中一个条件为 False,则无法应用媒体查询规则。

and 关键词的反义词是 or 关键词。和 and 一样,这些条件组合在一起会构成复杂表达式。如果其中有一个条件为 True,那么整个表达式或分离的两个条件都会为 True,如清单 6 所示。

清单 6. or 关键词

1
@media (min-width:800px) or (orientation:portrait) { ... }

如果宽度至少是 800 像素或方向是纵向的,则会应用该规则。

另一个保存在词库中的媒体查询关键词是 not。位于媒体查询的开始处,not 会忽略结果。换句话说,如果该查询本来在没有 not 关键词的情况下为 true,那么现在它将为 false。清单 7 展示一个示例。

清单 7. 使用 not

1
@media (not min-width:800px) { ... }

仅从英文意思上理解,清单 7 中代码的表示:当最小宽度不是 800 像素时,会应用下列 CSS 规则。这些示例只是将像素作为媒体查询中的测量单位,但是测量单位并不仅限于像素。您可以使用任何有效的 CSS 测量单位,比如厘米 (cm)、英寸 (in)、毫米 (mm) 等。

有用的媒体特性

媒体很多特性,比如宽度、颜色和网格,您可以在媒体查询中使用它们。对每个可能的媒体特性进行描述不在本文讨论范围内。关于媒体查询的文档 The World Wide Web Consortium’s (W3C) 提供了这方面的一个完整清单。(请参阅 参考资料)。

要设计响应式网站,只需要了解一些媒体特性:方向、宽度和高度。作为一个简单媒体特性,方向的值可以是 portrait 或 landscape。这些值适用于持有手机或平板电脑的用户,使您可以根据两个形状因素来优化内容。当高度大于长度时,则认为屏幕是纵向模式,当宽度大于高度时,则认为屏幕是横向模式。清单 8 显示了一个使用 orientation 媒体模式查询的示例。

清单 8. orientation 媒体查询

1
@media (orientation:portrait) { ... }

高度和宽度行为十分相似,都支持以 min- 和 max- 为前缀。清单 9 展示了一个有效的媒体查询。

清单 9. 高度和宽度媒体查询

1
@media (min-width:800px) and (min-height:400px) { ... }

如果没有 min- 或 max- 前缀,您还可以使用 width 和 height 媒体特性,如清单 10 所示。

清单 10. 不带 min- 和 max- 前缀

1
@media (width:800px) and (height:400px) { ... }

当屏幕正好是 800 像素宽、400 像素高时,可以使用清单 10 中的媒体查询。现实中,像这样的媒体查询可能过于具体而不太有用。检测精确维度是大多数网站访问者都不可能遇到的一个场景。通常情况下,响应式设计会使用范围来执行屏幕检测。

作为媒体查询大小范围的后续内容,下一节将讨论一些常见的媒体查询,在设计一个响应式网站时,您可能会发现它们非常有用。

见媒体查询

因为 Apple 首次向市场推出了用户智能手机和平板电脑产品,所以下列大多数媒体查询都是基于这些型号的设备。

如果目标是横向模式智能手机,则使用: @media (min-width: 321px) { … }

如果目标是纵向模式智能手机,则使用: @media (max-width: 320px) { … }

如果目标是横向模式 Apple iPad,则使用: @media (orientation: landscape) { … }

如果目标是纵向模式 iPad,则使用: @media (orientation: portrait) { … }

您可能已经注意到了,iPad 上使用的是 orientation 媒体特性,而 width 用于 Apple iPhone 之上。主要是因为 iPhone 不支持 orientation 媒体特性。您必须使用 width 模拟这些方向断点。请参阅 参考资料,获取有关常见媒体特性的更多信息。

浏览器支持

到现在为止,您可能已经相信 CSS 媒体查询是一个强大的工具,而且想知道哪些浏览器支持 CSS 媒体查询。以下是这方面的好消息和坏消息。
好消息是:大多数现代浏览器(包括智能手机上的浏览器)都支持 CSS 媒体查询。
坏消息是:最近,来自 Redmond 的 Windows® Internet Explorer® 8 浏览器不支持媒体查询。
对于大多数专业 Web 开发人员来说,这意味着您需要提供一个解决方案,以便在 Internet Explorer 中支持媒体查询。

下列解决方案是一个名为 respond.js 的 JavaScript polyfill。

带有 respond.js 的 Polyfill

Respond.js 是一个极小的增强 Web 浏览器的 JavaScript 库,使得原本不支持 CSS 媒体查询的浏览器能够支持它们。该脚本循环遍历页面上的所有 CSS 引用,并使用媒体查询分析 CSS 规则。然后,该脚本会监控浏览器宽度变化,添加或删除与 CSS 中媒体查询匹配的样式。最终结果是,能够在原本不支持的浏览器上运行媒体查询。

由于这是一个基于 JavaScript 的解决方案,所以浏览器需要支持 JavaScript,以便运行脚本。该脚本只支持创建响应式设计所需的最小和最大 width 媒体查询。这并不是适用于所有可能 CSS 媒体查询的一个替代。在 参考资料 部分,可以阅读关于该脚本特性和局限性的更多信息。

Respond.js 是您可以选择的诸多可用开源媒体查询 polyfills 之一。如果您觉得 respond.js 无法满足您的需求,在进行一个小小的研究之后,您就会发现几个替代方案。

3、正则去掉首尾空格

replace(/(^\s*)|(\s*$)/g, “”);表示将匹配的空格或控制符替换成””

4、页面性能优化

1.提倡前端开发工程师在书写xhtml的时候做到结构语义化。
结构中主要包括了head和body两个部分,但是我们经常说的是结构语义化主要是body中的标签,但是我在这里还是简单的说一下head,head中其实包括了一些对于我们seo很有用的一些东西,比如title,Description,Keywords,这些东西在蜘蛛抓取的时候都是有帮助的,当然,还有其他的一些,我在此就不一一说明了,比如设置缓存等一些其他的信息。

那么body中的话,包括的标签就很多了,我觉得作为一个合格的前端开发人员你应该去熟悉他们,比如div,span,h,ul,ol,dl,p等等这类的标签的使用。应该非常合理,还有就是注意h标签的断层,及h1标签的使用,这些都是非常重要的。

同时在我们的结构中不要出现style和onclick这样的内联的样式和事件。希望大家能够注意结构与表现、行为的分离。

2.css,js文件数量及大小的优化
那么关于css、js的优化的话,一般情况下建议css和js采用外联式。但是如果你的页面内容比较多,设计师把整个效果做得比较花的话,恐怕css就非常多了,那么这种情况下,你一定要把你的css规划好,尽量的采用缩写,这样可以减少css文件的大小,那么对css做相应的规划也可以减少css的个数,减少http请求数,js同理。
(PS:减少重复性代码,代码重复利用,在这里显得特别重要)

3.背景图片数量及大小的优化 这里建议使用PNG8格式的图片结合css sprite,同样的图片,PNG8格式会相对来比GIF小

4.内容图片的大小的优化

5、跨域解决方案

1.document.domain + iframe (只有在主域相同的时候才能使用该方法)

2.动态创建script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function loadScript(url, func) {
var head = document.head || document.getElementByTagName('head')[0];
var script = document.createElement('script');
script.src = url;

script.onload = script.onreadystatechange = function() {
if(!this.readyState || this.readyState === 'loaded' || this.readyState ==='complete'){
func();
script.onload = script.onreadystatechange = null;
}
};
head.insertBefore(script, 0);
}
window.baidu = {
sug: function(data){
console.log(data);
}
}
loadScript('http://suggestion.baidu.com/su?wd=w', function() {console.log('loaded')});
// 我们请求的内容在哪里?
// 我们可以在chorme调试面板的source中看到script引入的内容

3.location.hash + iframe
原理是利用location.hash来进行传值。
假设域名a.com下的文件cs1.html要和cnblogs.com域名下的cs2.html传递信息。

  1. cs1.html首先创建自动创建一个隐藏的iframe,iframe的src指向cnblogs.com域名下的cs2.html页面

  2. cs2.html响应请求后再将通过修改cs1.html的hash值来传递数据

  3. 同时在cs1.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一旦有变化则获取获取hash值

注:由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 代码如下: 先是a.com下的文件cs1.html文件:
function startRequest(){
var ifr = document.createElement('iframe');
ifr.style.display = 'none';
ifr.src = 'http://www.cnblogs.com/lab/cscript/cs2.html#paramdo';
document.body.appendChild(ifr);
}

function checkHash() {
try {
var data = location.hash ? location.hash.substring(1) : '';
if (console.log) {
console.log('Now the data is '+data);
}
} catch(e) {};
}
setInterval(checkHash, 2000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 另一個域名下的cs2.html:
// 模拟一个简单的参数处理操作
switch(location.hash) {
case '#paramdo':
callBack();
break;
case '#paramset':
//do something……
break;
}

function callBack() {
try {
parent.location.hash = 'somedata';
} catch (e) {
// ie、chrome的安全机制无法修改parent.location.hash,
// 所以要利用一个中间的cnblogs域下的代理iframe
var ifrproxy = document.createElement('iframe');
ifrproxy.style.display = 'none';
ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata'; // 注意该文件在"a.com"域下
document.body.appendChild(ifrproxy);
}
}
1
2
3
// a.com下的域名cs3.html
// 因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);

4.window.name + iframe
window.name 的美妙之处:name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 1) 创建a.com/cs1.html
// 2) 创建a.com/proxy.html,并加入如下代码
<head>
<script>
function proxy(url, func) {
var isFirst = true,
ifr = document.createElement('iframe'),
loadFunc = function() {
if(isFirst) {
ifr.contentWindow.location = 'http://a.com/cs1.html';
isFirst = false;
} else {
func(ifr.contentWindow.name);
ifr.contentWindow.close();
document.body.removeChild(ifr);
ifr.src = '';
ifr = null;
}
};

ifr.src = url;
ifr.style.display = 'none';
if(ifr.attachEvent) ifr.attachEvent('onload', loadFunc);
else ifr.onload = loadFunc;
document.body.appendChild(iframe);
}
</script>
</head>
<body>
<script>
proxy('http://www.baidu.com/', function(data){
console.log(data);
});
</script>
</body>
````

```js
// 3 在b.com/cs1.html中包含:
<script>
window.name = '要传送的内容';
</script>

5.postMessage(HTML5中的XMLHttpRequest Level 2中的API)

1
2
3
4
5
6
7
8
9
10
// 1) a.com/index.html中的代码:
<iframe id="ifr" src="b.com/index.html"></iframe>
<script type="text/javascript">
window.onload = function() {
var ifr = document.getElementById('ifr');
var targetOrigin = 'http://b.com'; // 若写成'http://b.com/c/proxy.html'效果一样
// 若写成'http://c.com'就不会执行postMessage了
ifr.contentWindow.postMessage('I was there!', targetOrigin);
};
</script>
1
2
3
4
5
6
7
8
9
10
11
// 2) b.com/index.html中的代码:
<script type="text/javascript">
window.addEventListener('message', function(event){
// 通过origin属性判断消息来源地址
if (event.origin == 'http://a.com') {
alert(event.data); // 弹出"I was there!"
alert(event.source); // 对a.com、index.html中window对象的引用
// 但由于同源策略,这里event.source不可以访问window对象
}
}, false);
</script>

6.CORS
CORS背后的思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// IE中对CORS的实现是xdr
var xdr = new XDomainRequest();
xdr.onload = function(){
console.log(xdr.responseText);
}
xdr.open('get', 'http://www.baidu.com');
......
xdr.send(null);
```

```js
// 其它浏览器中的实现就在xhr中
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 304 || xhr.status == 304){
console.log(xhr.responseText);
}
}
}
xhr.open('get', 'http://www.baidu.com');
......
xhr.send(null);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 实现跨浏览器的CORS
function createCORS(method, url){
var xhr = new XMLHttpRequest();
if('withCredentials' in xhr){
xhr.open(method, url, true);
}else if(typeof XDomainRequest != 'undefined'){
var xhr = new XDomainRequest();
xhr.open(method, url);
}else{
xhr = null;
}
return xhr;
}
var request = createCORS('get', 'http://www.baidu.com');
if(request){
request.onload = function(){
......
};
request.send();
}

7.JSONP
JSONP包含两部分:回调函数和数据。

回调函数是当响应到来时要放在当前页面被调用的函数。
数据就是传入回调函数中的json数据,也就是回调函数的参数了。

1
2
3
4
5
6
7
8
9
10
11
12
13
function handleResponse(response){
console.log('The responsed data is: '+response.data);
}
var script = document.createElement('script');
script.src = 'http://www.baidu.com/json/?callback=handleResponse';
document.body.insertBefore(script, document.body.firstChild);
/*handleResonse({"data": "zhe"})*/
// 原理如下:
// 当我们通过script标签请求时
// 后台就会根据相应的参数(json,handleResponse)
// 来生成相应的json数据(handleResponse({"data": "zhe"}))
// 最后这个返回的json数据(代码)就会被放在当前js文件中被执行
// 至此跨域通信完成

jsonp虽然很简单,但是有如下缺点:

1)安全问题(请求代码中可能存在安全隐患)

2)要确定jsonp请求是否失败并不容易

8.web sockets

web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用)

web sockets原理:在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。

只有在支持web socket协议的服务器上才能正常工作。

1
2
3
4
5
var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wss
socket.send('hello WebSockt');
socket.onmessage = function(event){
var data = event.data;
}

6、编程:删除重复元素(重点:二重循环要倒序,map方案)

1.遍历数组法

最简单的去重方法, 实现思路:新建一新数组,遍历传入数组,值不在新数组就加入该新数组中;注意点:判断值是否在数组的方法“indexOf”是ECMAScript5 方法,IE8以下不支持,需多写一些兼容低版本浏览器代码,源码如下:

1
2
3
4
5
6
7
8
9
10
11
// 最简单数组去重法
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 判断浏览器是否支持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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 速度最快, 占空间最多(空间换时间)
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项是重复的,忽略掉。否则存入结果数组。

1
2
3
4
5
6
7
8
9
10
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”方法排序结果不怎么靠谱,但在不注重顺序的去重里该缺点毫无影响。实现思路:给传入数组排序,排序后相同值相邻,然后遍历时新数组只加入不与前一值重复的值。

1
2
3
4
5
6
7
8
9
10
11
// 将相同的值相邻,然后遍历去除重复值
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.优化遍历数组法

该方法的实现代码相当酷炫;实现思路:获取没重复的最右一值放入新数组。(检测到有重复值时终止当前循环同时进入顶层循环的下一轮判断)

1
2
3
4
5
6
7
8
9
10
11
// 思路:获取没重复的最右一值放入新数组
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;
}

7、编程:取URL的参数

1.正则分析法

1
2
3
4
5
6
function getQueryString(name) {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}

2.location.search

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Script language="javascript">
function GetRequest() {
var url = location.search; // 获取url中"?"符后的字串
var theRequest = new Object();
if (url.indexOf('?') != -1) {
var str = url.substr(1);
strs = str.split('&');
for(var i = 0; i < strs.length; i ++) {
theRequest[strs[i].split('=')[0]]=unescape(strs[i].split('=')[1]);
}
}
return theRequest;
}
</script>

8、移动端:rem与px、em的区别及优势

1.PX:

PX实际上就是像素,用PX设置字体大小时,比较稳定和精确。但是这种方法存在一个问题,当用户在浏览器中浏览我们制作的Web页面时,如果改变了浏览器的缩放,这时会使用我们的Web页面布局被打破。这样对于那些关心自己网站可用性的用户来说,就是一个大问题了。因此,这时就提出了使用“em”来定义Web页面的字体。

2.EM:

EM就是根据基准来缩放字体的大小。EM实质是一个相对值,而非具体的数值。这种技术需要一个参考点,一般都是以的“font-size”为基准。如WordPress官方主题Twenntytwelve的基准就是14px=1em。
另外,em是相对于父元素的属性而计算的,如果想计算px和em之间的换算,这个网站不错,输入数据就可以px和em相互计算。狠击这里:px和em换算

3.Rem:

EM是相对于其父元素来设置字体大小的,这样就会存在一个问题,进行任何元素设置,都有可能需要知道他父元素的大小。而Rem是相对于根元素,这样就意味着,我们只需要在根元素确定一个参考值。

浏览器的兼容性
除了IE6-IE8r,其它的浏览器都支持em和rem属性,px是所有浏览器都支持。
因此为了浏览器的兼容性,可“px”和“rem”一起使用,用”px”来实现IE6-8下的效果,然后使用“Rem”来实现代浏览器的效果。

9、移动端:转屏事件:onorientationchange;全屏方法:requestFullScreen

移动端的onorientationchange事件是用来监听手机横竖屏事件的,通过window.orientation方法可以获得屏幕方向以判定横竖。

值为180或0 时,是竖屏状态,值为90或-90是横屏状态(其中两个值的原因是竖屏或者横屏也有上下两个方向)

这里要注意下,chrome pc版的浏览器是不兼容window.orientation方法的,需要通过screen.orientation.angle方法。

移动版本兼容,但是在chrome pc 上面的模拟调试手机界面也是不兼容window.orientation的,只能在手机上进行测试,这也是一个大坑所在,一开始我以为 chrome 就不兼容,造成一些误会和 编码的复杂性。

onorientationchange 事件的触发后,无论用何种方法获取屏幕的宽度,都不是屏幕完成旋转后的宽度,而是旋转之前的宽度。而 旋转后的宽度会在onorientationchange触发后的一些时间后才能获取到 。

js下的此类坑是我最厌烦的,而移动端chrome 则 活生生就存在这个bug

要解决这个问题,目前我所知道的就只能是通过在onorientationchange 用setTimeout 延迟一定时间去获得屏幕旋转后的宽度,并且实测这个延迟不能小于300毫秒,这特么是要多么的蛋疼!

全屏方法requestFullScreen和退出全屏cancelFullScreen/exitFullscreen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// mozilla草案
element.requestFullScreen();
document.cancelFullScreen();

// Webkit (works in Safari and Chrome)
element.webkitRequestFullScreen();
document.webkitCancelFullScreen();

// Firefox (works in nightly)
element.mozRequestFullScreen();
document.mozCancelFullScreen();

// W3C
element.requestFullscreen();
document.exitFullscreen();

10、ES6箭头函数

1
2
3
4
<!-- 	单行注释
--> “趋向于”操作符
<= 小于等于
=> 这又是什么?

定义一个箭头函数很简单,基本语法是:

1
2
3
4
5
([param] [, param]) => {
statements
}

param => expression

param 是参数,根据参数个数不同,分这几种情况:

() => { ... } // 零个参数用 () 表示;
x => { ... } // 一个参数可以省略 ();
(x, y) => { ... } // 多参数不能省略 ();

当然,和普通函数一样,箭头函数也可以使用 ES6 新增的「默认参数」和「剩余参数」( Firefox15+ 开始支持):

1
2
3
4
5
var func1 = (x = 1, y = 2) => x + y;
func1(); // 得到 3

var func2 = (x, ...args) => { console.log(args) };
func2(1, 2, 3); // 输出 [2, 3]

箭头函数允许多行语句或者单行表达式作为函数体。多行语句要用 {} 括起来;单行表达式不需要 {},并且会作为函数返回值:

1
2
3
4
x => { return x * x }; // 函数返回 x * x
x => x * x; // 同上一行
x => return x * x; // SyntaxError 报错,不能省略 {}
x => { x * x }; // 合法,没有定义返回值,返回 undefined

箭头函数也是 JS 函数的一种,所以之前的 instanceof 和 typeof 依然可用:

1
2
3
4
5
var func1 = () => {};
func1 instanceof Function; // true

var func2 = () => {};
typeof func2; // 'function'

11、编程:实现获取jsonp函数
12、数组的排序方法有哪些

快速排序、插入排序、冒泡排序、选择排序

13、编程:删除指定元素

然后使用通过得到这个元素的索引,使用js数组自己固有的函数去删除这个元素:

1
2
3
4
5
6
7
8
Array.prototype.remove = function(val) {
var index = this.indexOf(val);
if (index > -1) {
this.splice(index, 1);
}
};
var emp = ['abs','dsf','sdf','fd']
emp.remove('fd');

3.高级:

1、正则去掉重复字符串

1
2
3
4
5
6
7
8
9
10
function delReiteration(str) {
var reg = /(.).*?\1/g;
while (str.match(reg) != null) {
str = str.replace(RegExp.$1, '');
}
return str;
}
var str = 'google';
console.log(delReiteration(str));
// ogle

2、实现模块框架的define函数
3、模块框架:browerify、webpack、requirejs、seajs
4、node的web服务器:express、koa
5、ES6语法的语法及用法
6、移动端:video标签哪些属性方法,兼容性问题有哪些

Video标签含有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
src // 视频地址
poster // 视频地址错误
preload
// 这个属性也能通过名字了解用处,此属性用于定义视频是否预加载。
// 属性有三个可选择的值:none、metadata、auto。如果不使用此属
// 性,默认为auto
// None:不进行预加载。使用此属性值,可能是页面制作者认为用户
// 不期望此视频,或者减少HTTP请求。

// Metadata:部分预加载。使用此属性值,代表页面制作者认为用户
// 不期望此视频,但为用户提供一些元数据(包括尺寸,第一帧,
// 曲目列表,持续时间等等)。

// Auto:全部预加载。
autoplay // 属性用于设置视频是否自动播放
loop // 指定视频是否循环播放
controls
// Controls属性用于向浏览器指明页面制作者没有使用脚本生成播放
// 控制器,需要浏览器启用本身的播放控制栏。

// 控制栏须包括播放暂停控制,播放进度控制,音量控制等等。

// 每个浏览器默认的播放控制栏在界面上不一样。由于我浏览器的诡异
// 问题,Firefox和Safari的Video标签不正常,所以这两个只能在网
// 上找截图了
width // 寬度
height // 高度

等几个属性, 以及一个内部使用的标签。Video标签内除了可以包含标签外,还可以包含当指定的视频都不能 播放时,返回的内容。

兼容:

1.提前准备好多格式视频文件
由于编码器的版权问题,导致不同浏览器对视频格式的兼容性不同。目前没有一个视频格式兼容所有浏览器,唯一的解决方法就是把视频转换成多种格式。
首先要准备一个 mp4 格式的视频,可以在苹果设备中使用;其次要准备 ogv 格式的视频,用在火狐浏览器中;最后要准备一下 webm 格式的视频,这个可以用在谷歌浏览器等。webm 是谷歌提出的,开源、免费,很有可能成为兼容所有浏览器的视频格式。

2.编写对应 HTML5 video 代码
HTML5 中的 video 实际上就是一个简单的标签,包含了一些视频相关信息等。下面我直接给出具体代码,然后简单解释一下:

1
2
3
4
5
6
<video width="800" height="374">
<source src="my_video.mp4" type="video/mp4" />
<source src="my_video.ogv" type="video/ogg" />
<source src="my_video.webm" type="video/webm" />
你浏览器不支持 video 功能,点击这里下载视频: <a href="video.webm">下载视频</a>
</video>

video 标签表示这里是一个视频,width、height 属性分别表示这个视频内容的宽高(单位像素)。video 标签中可以包含 source 标签,source 标签用来表示引用的视频和视频的格式、类型。为了保证向下的兼容性,我们还在 video 标签中加了一句提示,这句话在支持 video 标签的浏览器中是不会显示的,如果不支持就会显示出来。这里,还增加了一个视频的下载地址,如果浏览器不支持,可以让用户选择下载下来看。

3.为旧版浏览器做兼容

前面说过,如果浏览器不支持 video ,将会把 video 中的提示内容显示出来。那么对付老旧浏览器,我们可以用传统的 flash 来替换这个提示内容。这样,当浏览器不兼容 video 标签的时候,就会显示出 flash 版本的视频。例如:

1
2
3
4
5
6
7
8
<video width="800" height="374">
<source src="my_video.mp4" type="video/mp4" />
<source src="my_video.ogv" type="video/ogg" />
<object width="800" height="374" type="application/x-shockwave-flash" data="fallback.swf">
<param name="movie" value="fallback.swf" />
<param name="flashvars" value="autostart=true&amp;file=video.flv" />
</object>
</video>

直接按照原来正常的 flash 引入方法写进 video 标签中即可。这样,我们就实现了跨浏览器兼容的 video 功能使用。

7、移动端:开发工具和技术:adb、weinre、代理设置、抓包
8、MVVM意思,及框架介绍:angular、vue、avalon
9、移动端:android、ios与H5交互原理及实现
10、如何做前后端分离
11、开发模式及工具
12、对开发框架的认识和理解,有自己的见解
13、如何避免回调地狱
14、哪些工具可以用来保证一致性的代码风格
15、什么是异步编程?为什么在 JavaScript 中这么重要?
16、gulp顶层方法有哪些?
17、模块解析并动态加载


面試題庫1
https://seven3.site/js/面試題庫1/
作者
Seven3s
发布于
2016年11月15日
许可协议