`元素,并遍历它的父元素直到根元素以找到``元素。对于特定的``,只要找到一个``,就知道'`已经匹配并停止继续匹配。
320 |
321 | ###### 参考
322 |
323 | * https://stackoverflow.com/questions/5797014/why-do-browsers-match-css-selectors-from-right-to-left
324 |
325 | [[↑] 回到顶部](#css-问题)
326 |
327 | ### 描述伪元素及其用途。
328 |
329 | CSS 伪元素是添加到选择器的关键字,去选择元素的特定部分。它们可以用于装饰(`:first-line`,`:first-letter`)或将元素添加到标记中(与 content:...组合),而不必修改标记(`:before`,`:after`)。
330 |
331 | * `:first-line`和`:first-letter`可以用来修饰文字。
332 | * 上面提到的`.clearfix`方法中,使用`clear: both`来添加不占空间的元素。
333 | * 使用`:before`和`after`展示提示中的三角箭头。鼓励关注点分离,因为三角被视为样式的一部分,而不是真正的 DOM。如果不使用额外的 HTML 元素,只用 CSS 样式绘制三角形是不太可能的。
334 |
335 | ###### 参考
336 |
337 | * https://css-tricks.com/almanac/selectors/a/after-and-before/
338 |
339 | [[↑] 回到顶部](#css-问题)
340 |
341 | ### 说说你对盒模型的理解,以及如何告知浏览器使用不同的盒模型渲染布局。
342 |
343 | CSS 盒模型描述了以文档树中的元素而生成的矩形框,并根据排版模式进行布局。每个盒子都有一个内容区域(例如文本,图像等)以及周围可选的`padding`、`border`和`margin`区域。
344 |
345 | CSS 盒模型负责计算:
346 |
347 | * 块级元素占用多少空间。
348 | * 边框是否重叠,边距是否合并。
349 | * 盒子的尺寸。
350 |
351 | 盒模型有以下规则:
352 |
353 | * 块级元素的大小由`width`、`height`、`padding`、`border`和`margin`决定。
354 | * 如果没有指定`height`,则块级元素的高度等于其包含子元素的内容高度加上`padding`(除非有浮动元素,请参阅下文)。
355 | * 如果没有指定`width`,则非浮动块级元素的宽度等于其父元素的宽度减去父元素的`padding`。
356 | * 元素的`height`是由内容的`height`来计算的。
357 | * 元素的`width`是由内容的`width`来计算的。
358 | * 默认情况下,`padding`和`border`不是元素`width`和`height`的组成部分。
359 |
360 | ###### 参考
361 |
362 | * https://www.smashingmagazine.com/2010/06/the-principles-of-cross-browser-css-coding/#understand-the-css-box-model
363 |
364 | [[↑] 回到顶部](#css-问题)
365 |
366 | ### `* { box-sizing: border-box; }`会产生怎样的效果?
367 |
368 | * 元素默认应用了`box-sizing: content-box`,元素的宽高只会决定内容(content)的大小。
369 | * `box-sizing: border-box`改变计算元素`width`和`height`的方式,`border`和`padding`的大小也将计算在内。
370 | * 元素的`height` = 内容(content)的高度 + 垂直方向的`padding` + 垂直方向`border`的宽度
371 | * 元素的`width` = 内容(content)的宽度 + 水平方向的`padding` + 水平方向`border`的宽度
372 |
373 | [[↑] 回到顶部](#css-问题)
374 |
375 | ### `display`的属性值都有哪些?
376 |
377 | * `none`, `block`, `inline`, `inline-block`, `table`, `table-row`, `table-cell`, `list-item`.
378 |
379 | TODO
380 |
381 | [[↑] 回到顶部](#css-问题)
382 |
383 | ### `inline`和`inline-block`有什么区别?
384 |
385 | 我把`block`也加入其中,为了获得更好的比较。
386 |
387 | | | `block` | `inline-block` | `inline` |
388 | | ------------------------------- | ----------------------------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
389 | | 大小 | 填充其父容器的宽度。 | 取决于内容。 | 取决于内容。 |
390 | | 定位 | 从新的一行开始,并且不允许旁边有 HTML 元素(除非是`float`) | 与其他内容一起流动,并允许旁边有其他元素。 | 与其他内容一起流动,并允许旁边有其他元素。 |
391 | | 能否设置`width`和`height` | 能 | 能 | 不能。 设置会被忽略。 |
392 | | 可以使用`vertical-align`对齐 | 不可以 | 可以 | 可以 |
393 | | 边距(margin)和填充(padding) | 各个方向都存在 | 各个方向都存在 | 只有水平方向存在。垂直方向会被忽略。 尽管`border`和`padding`在`content`周围,但垂直方向上的空间取决于'line-height' |
394 | | 浮动(float) | - | - | 就像一个`block`元素,可以设置垂直边距和填充。 |
395 |
396 | [[↑] 回到顶部](#css-问题)
397 |
398 | ### `relative`、`fixed`、`absolute`和`static`四种定位有什么区别?
399 |
400 | 经过定位的元素,其`position`属性值必然是`relative`、`absolute`、`fixed`或`sticky`。
401 |
402 | * `static`:默认定位属性值。该关键字指定元素使用正常的布局行为,即元素在文档常规流中当前的布局位置。此时 top, right, bottom, left 和 z-index 属性无效。
403 | * `relative`:该关键字下,元素先放置在未添加定位时的位置,再在不改变页面布局的前提下调整元素位置(因此会在此元素未添加定位时所在位置留下空白)。
404 | * `absolute`:不为元素预留空间,通过指定元素相对于最近的非 static 定位祖先元素的偏移,来确定元素位置。绝对定位的元素可以设置外边距(margins),且不会与其他边距合并。
405 | * `fixed`:不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed 属性会创建新的层叠上下文。当元素祖先的 transform 属性非 none 时,容器由视口改为该祖先。
406 | * `sticky`:盒位置根据正常流计算(这称为正常流动中的位置),然后相对于该元素在流中的 flow root(BFC)和 containing block(最近的块级祖先元素)定位。在所有情况下(即便被定位元素为 `table` 时),该元素定位均不对后续元素造成影响。当元素 B 被粘性定位时,后续元素的位置仍按照 B 未定位时的位置来确定。`position: sticky` 对 `table` 元素的效果与 `position: relative` 相同。
407 |
408 | ###### 参考
409 |
410 | * https://developer.mozilla.org/en/docs/Web/CSS/position
411 |
412 | [[↑] 回到顶部](#css-问题)
413 |
414 | ### 你使用过哪些现有的 CSS 框架?你是如何改进它们的?
415 |
416 | * **Bootstrap**: 更新周期缓慢。Bootstrap 4 已经处于 alpha 版本将近两年了。添加了在页面中广泛使用的微调按钮组件。
417 | * **Semantic UI**:源代码结构使得自定义主题很难理解。非常规主题系统的使用体验很差。外部库的路径需要硬编码(hard code)配置。变量重新赋值没有 Bootstrap 设计得好。
418 | * **Bulma**: 需要很多非语义的类和标记,显得很多余。不向后兼容,以至于升级版本后,会破坏应用的正常运行。
419 |
420 | [[↑] 回到顶部](#css-问题)
421 |
422 | ### 你了解 CSS Flexbox 和 Grid 吗?
423 |
424 | 了解。Flexbox 主要用于一维布局,而 Grid 则用于二维布局。
425 |
426 | Flexbox 解决了 CSS 中的许多常见问题,例如容器中元素的垂直居中,粘性定位(sticky)的页脚等。Bootstrap 和 Bulma 基于 Flexbox,这是创建布局的推荐方式。我之前曾使用过 Flexbox,但在使用`flex-grow`时遇到了一些浏览器不兼容问题(Safari),我必须使用`inline-blocks`和手动计算百分比宽度,来重写我的代码,这种体验不是很好。
427 |
428 | Grid 创建基于栅格的布局,是迄今为止最直观的方法(最好是!),但目前浏览器支持并不广泛。
429 |
430 | ###### 参考
431 |
432 | * https://philipwalton.github.io/solved-by-flexbox/
433 |
434 | [[↑] 回到顶部](#css-问题)
435 |
436 | ### 请解释在编写网站时,响应式与移动优先的区别。
437 |
438 | TODO
439 |
440 | [[↑] 回到顶部](#css-问题)
441 |
442 | ### 响应式设计与自适应设计有何不同?
443 |
444 | 响应式设计和自适应设计都以提高不同设备间的用户体验为目标,根据视窗大小、分辨率、使用环境和控制方式等参数进行优化调整。
445 |
446 | 响应式设计的适应性原则:网站应该凭借一份代码,在各种设备上都有良好的显示和使用效果。响应式网站通过使用媒体查询,自适应栅格和响应式图片,基于多种因素进行变化,创造出优良的用户体验。就像一个球通过膨胀和收缩,来适应不同大小的篮圈。
447 |
448 | 自适应设计更像是渐进式增强的现代解释。与响应式设计单一地去适配不同,自适应设计通过检测设备和其他特征,从早已定义好的一系列视窗大小和其他特性中,选出最恰当的功能和布局。与使用一个球去穿过各种的篮筐不同,自适应设计允许使用多个球,然后根据不同的篮筐大小,去选择最合适的一个。
449 |
450 | ###### 参考
451 |
452 | * https://developer.mozilla.org/en-US/docs/Archive/Apps/Design/UI_layout_basics/Responsive_design_versus_adaptive_design
453 | * http://mediumwell.com/responsive-adaptive-mobile/
454 | * https://css-tricks.com/the-difference-between-responsive-and-adaptive-design/
455 |
456 | [[↑] 回到顶部](#css-问题)
457 |
458 | ### 你有没有使用过视网膜分辨率的图形?当中使用什么技术?
459 |
460 | 我倾向于使用更高分辨率的图形(显示尺寸的两倍)来处理视网膜显示。更好的方法是使用媒体查询,像`@media only screen and (min-device-pixel-ratio: 2) { ... }`,然后改变`background-image`。
461 |
462 | 对于图标类的图形,我会尽可能使用 svg 和图标字体,因为它们在任何分辨率下,都能被渲染得十分清晰。
463 |
464 | 还有一种方法是,在检查了`window.devicePixelRatio`的值后,利用 JavaScript 将`
`的`src`属性修改,用更高分辨率的版本进行替换。
465 |
466 | ###### 参考
467 |
468 | * https://www.sitepoint.com/css-techniques-for-retina-displays/
469 |
470 | [[↑] 回到顶部](#css-问题)
471 |
472 | ### 什么情况下,用`translate()`而不用绝对定位?什么时候,情况相反。
473 |
474 | `translate()`是`transform`的一个值。改变`transform`或`opacity`不会触发浏览器重新布局(reflow)或重绘(repaint),只会触发复合(compositions)。而改变绝对定位会触发重新布局,进而触发重绘和复合。`transform`使浏览器为元素创建一个 GPU 图层,但改变绝对定位会使用到 CPU。 因此`translate()`更高效,可以缩短平滑动画的绘制时间。
475 |
476 | 当使用`translate()`时,元素仍然占据其原始空间(有点像`position:relative`),这与改变绝对定位不同。
477 |
478 | ###### 参考
479 |
480 | * https://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/
481 |
482 | [[↑] 回到顶部](#css-问题)
483 |
484 | ### 其他答案
485 |
486 | * https://neal.codes/blog/front-end-interview-css-questions
487 | * https://quizlet.com/28293152/front-end-interview-questions-css-flash-cards/
488 | * http://peterdoes.it/2015/12/03/a-personal-exercise-front-end-job-interview-questions-and-my-answers-all/
489 |
490 | ### CSS选择器有哪些
491 |
492 | 1. ***通用选择器**:选择所有元素,**不参与计算优先级**,兼容性IE6+
493 | 2. **#X id选择器**:选择id值为X的元素,兼容性:IE6+
494 | 3. **.X 类选择器**: 选择class包含X的元素,兼容性:IE6+
495 | 4. **X Y后代选择器**: 选择满足X选择器的后代节点中满足Y选择器的元素,兼容性:IE6+
496 | 5. **X 元素选择器**: 选择标所有签为X的元素,兼容性:IE6+
497 | 6. **:link,:visited,:focus,:hover,:active链接状态**: 选择特定状态的链接元素,顺序LoVe HAte,兼容性: IE4+
498 | 7. **X + Y直接兄弟选择器**:在**X之后第一个兄弟节点**中选择满足Y选择器的元素,兼容性: IE7+
499 | 8. **X > Y子选择器**: 选择X的子元素中满足Y选择器的元素,兼容性: IE7+
500 | 9. **X ~ Y兄弟**: 选择**X之后所有兄弟节点**中满足Y选择器的元素,兼容性: IE7+
501 | 10. **[attr]**:选择所有设置了attr属性的元素,兼容性IE7+
502 | 11. **[attr=value]**:选择属性值刚好为value的元素
503 | 12. **[attr~=value]**:选择属性值为空白符分隔,其中一个的值刚好是value的元素
504 | 13. **[attr|=value]**:选择属性值刚好为value或者value-开头的元素
505 | 14. **[attr^=value]**:选择属性值以value开头的元素
506 | 15. **[attr$=value]**:选择属性值以value结尾的元素
507 | 16. **[attr*=value]**:选择属性值中包含value的元素
508 | 17. **[:checked]**:选择单选框,复选框,下拉框中选中状态下的元素,兼容性:IE9+
509 | 18. **X:after, X::after**:after伪元素,选择元素虚拟子元素(元素的最后一个子元素),CSS3中::表示伪元素。兼容性:after为IE8+,::after为IE9+
510 | 18. **:hover**:鼠标移入状态的元素,兼容性a标签IE4+, 所有元素IE7+
511 | 19. **:not(selector)**:选择不符合selector的元素。**不参与计算优先级**,兼容性:IE9+
512 | 20. **::first-letter**:伪元素,选择块元素第一行的第一个字母,兼容性IE5.5+
513 | 21. **::first-line**:伪元素,选择块元素的第一行,兼容性IE5.5+
514 | 22. **:nth-child(an + b)**:伪类,选择前面有an + b - 1个兄弟节点的元素,其中n
515 | >= 0, 兼容性IE9+
516 | 23. **:nth-last-child(an + b)**:伪类,选择后面有an + b - 1个兄弟节点的元素
517 | 其中n >= 0,兼容性IE9+
518 | 24. **X:nth-of-type(an+b)**:伪类,X为选择器,**解析得到元素标签**,选择**前面**有an + b - 1个**相同标签**兄弟节点的元素。兼容性IE9+
519 | 25. **X:nth-last-of-type(an+b)**:伪类,X为选择器,解析得到元素标签,选择**后面**有an+b-1个相同**标签**兄弟节点的元素。兼容性IE9+
520 | 26. **X:first-child**:伪类,选择满足X选择器的元素,且这个元素是其父节点的第一个子元素。兼容性IE7+
521 | 27. **X:last-child**:伪类,选择满足X选择器的元素,且这个元素是其父节点的最后一个子元素。兼容性IE9+
522 | 28. **X:only-child**:伪类,选择满足X选择器的元素,且这个元素是其父元素的唯一子元素。兼容性IE9+
523 | 29. **X:only-of-type**:伪类,选择X选择的元素,**解析得到元素标签**,如果该元素没有相同类型的兄弟节点时选中它。兼容性IE9+
524 | 30. **X:first-of-type**:伪类,选择X选择的元素,**解析得到元素标签**,如果该元素
525 | 是此此类型元素的第一个兄弟。选中它。兼容性IE9+
526 |
527 |
528 | ### css sprite是什么,有什么优缺点
529 |
530 | 概念:将多个小图片拼接到一个图片中。通过background-position和元素尺寸调节需要显示的背景图案。
531 |
532 | 优点:
533 |
534 | 1. 减少HTTP请求数,极大地提高页面加载速度
535 | 2. 增加图片信息重复度,提高压缩比,减少图片大小
536 | 3. 更换风格方便,只需在一张或几张图片上修改颜色或样式即可实现
537 |
538 | 缺点:
539 |
540 | 1. 图片合并麻烦
541 | 2. 维护麻烦,修改一个图片可能需要从新布局整个图片,样式
542 |
543 |
544 | ### `display: none;`与`visibility: hidden;`的区别
545 | 联系:它们都能让元素不可见
546 |
547 | 区别:
548 |
549 | 1. display:none;会让元素完全从渲染树中消失,渲染的时候不占据任何空间;visibility: hidden;不会让元素从渲染树消失,渲染师元素继续占据空间,只是内容不可见
550 | 2. display: none;是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示;visibility: hidden;是继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显式
551 | 3. 修改常规流中元素的display通常会造成文档重排。修改visibility属性只会造成本元素的重绘。
552 | 4. 读屏器不会读取display: none;元素内容;会读取visibility: hidden;元素内容
553 |
554 | ### css hack原理及常用hack
555 |
556 | 原理:利用**不同浏览器对CSS的支持和解析结果不一样**编写针对特定浏览器样式。常见的hack有1)属性hack。2)选择器hack。3)IE条件注释
557 |
558 | - IE条件注释:适用于[IE5, IE9]常见格式如下
559 |
560 | ```
561 |
564 | ```
565 |
566 | - 选择器hack:不同浏览器对选择器的支持不一样
567 |
568 | ```
569 | /***** Selector Hacks ******/
570 |
571 | /* IE6 and below */
572 | * html #uno { color: red }
573 |
574 | /* IE7 */
575 | *:first-child+html #dos { color: red }
576 |
577 | /* IE7, FF, Saf, Opera */
578 | html>body #tres { color: red }
579 |
580 | /* IE8, FF, Saf, Opera (Everything but IE 6,7) */
581 | html>/**/body #cuatro { color: red }
582 |
583 | /* Opera 9.27 and below, safari 2 */
584 | html:first-child #cinco { color: red }
585 |
586 | /* Safari 2-3 */
587 | html[xmlns*=""] body:last-child #seis { color: red }
588 |
589 | /* safari 3+, chrome 1+, opera9+, ff 3.5+ */
590 | body:nth-of-type(1) #siete { color: red }
591 |
592 | /* safari 3+, chrome 1+, opera9+, ff 3.5+ */
593 | body:first-of-type #ocho { color: red }
594 |
595 | /* saf3+, chrome1+ */
596 | @media screen and (-webkit-min-device-pixel-ratio:0) {
597 | #diez { color: red }
598 | }
599 |
600 | /* iPhone / mobile webkit */
601 | @media screen and (max-device-width: 480px) {
602 | #veintiseis { color: red }
603 | }
604 |
605 | /* Safari 2 - 3.1 */
606 | html[xmlns*=""]:root #trece { color: red }
607 |
608 | /* Safari 2 - 3.1, Opera 9.25 */
609 | *|html[xmlns*=""] #catorce { color: red }
610 |
611 | /* Everything but IE6-8 */
612 | :root *> #quince { color: red }
613 |
614 | /* IE7 */
615 | *+html #dieciocho { color: red }
616 |
617 | /* Firefox only. 1+ */
618 | #veinticuatro, x:-moz-any-link { color: red }
619 |
620 | /* Firefox 3.0+ */
621 | #veinticinco, x:-moz-any-link, x:default { color: red }
622 | ```
623 |
624 | - 属性hack:不同浏览器解析bug或方法
625 |
626 | ```
627 | /* IE6 */
628 | #once { _color: blue }
629 |
630 | /* IE6, IE7 */
631 | #doce { *color: blue; /* or #color: blue */ }
632 |
633 | /* Everything but IE6 */
634 | #diecisiete { color/**/: blue }
635 |
636 | /* IE6, IE7, IE8 */
637 | #diecinueve { color: blue\9; }
638 |
639 | /* IE7, IE8 */
640 | #veinte { color/*\**/: blue\9; }
641 |
642 | /* IE6, IE7 -- acts as an !important */
643 | #veintesiete { color: blue !ie; } /* string after ! can be anything */
644 | ```
645 |
646 | ### specified value,computed value,used value计算方法
647 |
648 | - specified value: 计算方法如下:
649 | 1. 如果样式表设置了一个值,使用这个值
650 | 2. 如果没有设置值,这个属性是继承属性,从父元素继承
651 | 3. 如果没设置,并且不是继承属性,使用css规范指定的初始值
652 |
653 | - computed value: 以specified value根据规范定义的行为进行计算,通常将相对值计算为绝对值,例如em根据font-size进行计算。一些使用百分数并且需要布局来决定最终值的属性,如width,margin。百分数就直接作为computed value。line-height的无单位值也直接作为computed value。这些值将在计算used value时得到绝对值。**computed value的主要作用是用于继承**
654 |
655 | - used value:属性计算后的最终值,对于大多数属性可以通过window.getComputedStyle获得,尺寸值单位为像素。以下属性依赖于布局,
656 | - background-position
657 | - bottom, left, right, top
658 | - height, width
659 | - margin-bottom, margin-left, margin-right, margin-top
660 | - min-height, min-width
661 | - padding-bottom, padding-left, padding-right, padding-top
662 | - text-indent
663 |
664 | ### `link`与`@import`的区别
665 |
666 | 1. ``link``是HTML方式, ``@import``是CSS方式
667 | 2. ``link``最大限度支持并行下载,``@import``过多嵌套导致串行下载,出现[FOUC](http://www.bluerobot.com/web/css/fouc.asp/)
668 | 4. ``link``可以通过``rel="alternate stylesheet"``指定候选样式
669 | 5. 浏览器对``link``支持早于``@import``,可以使用``@import``对老浏览器隐藏样式
670 | 6. ``@import``必须在样式规则之前,可以在css文件中引用其他文件
671 | 6. 总体来说:**[link优于@import](http://www.stevesouders.com/blog/2009/04/09/dont-use-import/)**
672 |
673 | ### ``display: block;``和``display: inline;``的区别
674 |
675 | ``block``元素特点:
676 |
677 | 1.处于常规流中时,如果``width``没有设置,会自动填充满父容器
678 | 2.可以应用``margin/padding``
679 | 3.在没有设置高度的情况下会扩展高度以包含常规流中的子元素
680 | 4.处于常规流中时布局时在前后元素位置之间(独占一个水平空间)
681 | 5.忽略``vertical-align``
682 |
683 | ``inline``元素特点
684 |
685 | 1.水平方向上根据``direction``依次布局
686 | 2.不会在元素前后进行换行
687 | 3.受``white-space``控制
688 | 4.``margin/padding``在竖直方向上无效,水平方向上有效
689 | 5.``width/height``属性对非替换行内元素无效,宽度由元素内容决定
690 | 6.非替换行内元素的行框高由``line-height``确定,替换行内元素的行框高由``height``,``margin``,``padding``,``border``决定
691 | 6.浮动或绝对定位时会转换为``block``
692 | 7.``vertical-align``属性生效
693 |
694 |
695 |
696 | ### PNG,GIF,JPG的区别及如何选
697 | 参考资料: [选择正确的图片格式](http://www.yuiblog.com/blog/2008/11/04/imageopt-2/)
698 | **GIF**:
699 |
700 | 1. 8位像素,256色
701 | 2. 无损压缩
702 | 3. 支持简单动画
703 | 4. 支持boolean透明
704 | 5. 适合简单动画
705 |
706 | **JPEG**:
707 |
708 | 1. 颜色限于256
709 | 2. 有损压缩
710 | 3. 可控制压缩质量
711 | 4. 不支持透明
712 | 5. 适合照片
713 |
714 | **PNG**:
715 |
716 | 1. 有PNG8和truecolor PNG
717 | 2. PNG8类似GIF颜色上限为256,文件小,支持alpha透明度,无动画
718 | 3. 适合图标、背景、按钮
719 |
720 | ### CSS有哪些继承属性
721 |
722 | - 关于文字排版的属性如:
723 | + [font](https://developer.mozilla.org/en-US/docs/Web/CSS/font)
724 | + [word-break](https://developer.mozilla.org/en-US/docs/Web/CSS/word-break)
725 | + [letter-spacing](https://developer.mozilla.org/en-US/docs/Web/CSS/letter-spacing)
726 | + [text-align](https://developer.mozilla.org/en-US/docs/Web/CSS/text-align)
727 | + [text-rendering](https://developer.mozilla.org/en-US/docs/Web/CSS/text-rendering)
728 | + [word-spacing](https://developer.mozilla.org/en-US/docs/Web/CSS/word-spacing)
729 | + [white-space](https://developer.mozilla.org/en-US/docs/Web/CSS/white-space)
730 | + [text-indent](https://developer.mozilla.org/en-US/docs/Web/CSS/text-indent)
731 | + [text-transform](https://developer.mozilla.org/en-US/docs/Web/CSS/text-transform)
732 | + [text-shadow](https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow)
733 | - [line-height](https://developer.mozilla.org/en-US/docs/Web/CSS/line-height)
734 | - [color](https://developer.mozilla.org/en-US/docs/Web/CSS/color)
735 | - [visibility](https://developer.mozilla.org/en-US/docs/Web/CSS/visibility)
736 | - [cursor](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor)
737 |
738 |
739 |
740 | ### IE6浏览器有哪些常见的bug,缺陷或者与标准不一致的地方,如何解决
741 |
742 | - IE6不支持min-height,解决办法使用css hack:
743 |
744 | ```
745 | .target {
746 | min-height: 100px;
747 | height: auto !important;
748 | height: 100px; // IE6下内容高度超过会自动扩展高度
749 | }
750 | ```
751 |
752 | - ``ol``内``li``的序号全为1,不递增。解决方法:为li设置样式``display: list-item;``
753 |
754 | - 未定位父元素``overflow: auto;``,包含``position: relative;``子元素,子元素高于父元素时会溢出。解决办法:1)子元素去掉``position: relative;``; 2)不能为子元素去掉定位时,父元素``position: relative;``
755 |
756 | ```
757 |
772 |
773 |
776 | ```
777 |
778 | - IE6只支持``a``标签的``:hover``伪类,解决方法:使用js为元素监听mouseenter,mouseleave事件,添加类实现效果:
779 |
780 | ```
781 |
787 |
788 | aaaa bbbbbDDDDDDDDDDDd aaaa lkjlkjdf j
789 |
790 |
814 | ```
815 |
816 | - IE5-8不支持``opacity``,解决办法:
817 |
818 | ```
819 | .opacity {
820 | opacity: 0.4
821 | filter: alpha(opacity=60); /* for IE5-7 */
822 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; /* for IE 8*/
823 | }
824 | ```
825 |
826 | - IE6在设置``height``小于``font-size``时高度值为``font-size``,解决办法:``font-size: 0;``
827 | - IE6不支持PNG透明背景,解决办法: **IE6下使用gif图片**
828 | - IE6-7不支持``display: inline-block``解决办法:设置inline并触发hasLayout
829 |
830 | ```
831 | display: inline-block;
832 | *display: inline;
833 | *zoom: 1;
834 | ```
835 |
836 | - IE6下浮动元素在浮动方向上与父元素边界接触元素的外边距会加倍。解决办法:
837 | 1)使用padding控制间距。
838 | 2)浮动元素``display: inline;``这样解决问题且无任何副作用:css标准规定浮动元素display:inline会自动调整为block
839 | - 通过为块级元素设置宽度和左右margin为auto时,IE6不能实现水平居中,解决方法:为父元素设置``text-align: center;``
840 |
841 | ### 容器包含若干浮动元素时如何清理(包含)浮动
842 |
843 | 1. 容器元素闭合标签前添加额外元素并设置``clear: both``
844 | 2. 父元素触发块级格式化上下文(见块级可视化上下文部分)
845 | 3. 设置容器元素伪元素进行清理[推荐的清理浮动方法](http://nicolasgallagher.com/micro-clearfix-hack/)
846 |
847 | ```
848 | /**
849 | * 在标准浏览器下使用
850 | * 1 content内容为空格用于修复opera下文档中出现
851 | * contenteditable属性时在清理浮动元素上下的空白
852 | * 2 使用display使用table而不是block:可以防止容器和
853 | * 子元素top-margin折叠,这样能使清理效果与BFC,IE6/7
854 | * zoom: 1;一致
855 | **/
856 |
857 | .clearfix:before,
858 | .clearfix:after {
859 | content: " "; /* 1 */
860 | display: table; /* 2 */
861 | }
862 |
863 | .clearfix:after {
864 | clear: both;
865 | }
866 |
867 | /**
868 | * IE 6/7下使用
869 | * 通过触发hasLayout实现包含浮动
870 | **/
871 | .clearfix {
872 | *zoom: 1;
873 | }
874 | ```
875 |
876 | ### 什么是FOUC?如何避免
877 | Flash Of Unstyled Content:用户定义样式表加载之前浏览器使用默认样式显示文档,用户样式加载渲染之后再从新显示文档,造成页面闪烁。**解决方法**:把样式表放到文档的`head`
878 |
879 | ### 如何创建块级格式化上下文(block formatting context),BFC有什么用
880 | 创建规则:
881 |
882 | 1. 根元素
883 | 2. 浮动元素(``float``不是``none``)
884 | 3. 绝对定位元素(``position``取值为``absolute``或``fixed``)
885 | 4. ``display``取值为``inline-block``,``table-cell``, ``table-caption``,``flex``, ``inline-flex``之一的元素
886 | 5. ``overflow``不是``visible``的元素
887 |
888 |
889 | 作用:
890 |
891 | 1. 可以包含浮动元素
892 | 2. 不被浮动元素覆盖
893 | 3. 阻止父子元素的margin折叠
894 |
895 | ### display,float,position的关系
896 |
897 | 1. 如果``display``为none,那么position和float都不起作用,这种情况下元素不产生框
898 | 2. 否则,如果position值为absolute或者fixed,框就是绝对定位的,float的计算值为none,display根据下面的表格进行调整。
899 | 3. 否则,如果float不是none,框是浮动的,display根据下表进行调整
900 | 4. 否则,如果元素是根元素,display根据下表进行调整
901 | 5. 其他情况下display的值为指定值
902 | 总结起来:**绝对定位、浮动、根元素都需要调整``display``**
903 | 
904 |
905 | ### 外边距折叠(collapsing margins)
906 | 毗邻的两个或多个``margin``会合并成一个margin,叫做外边距折叠。规则如下:
907 |
908 | 1. 两个或多个毗邻的普通流中的块元素垂直方向上的margin会折叠
909 | 2. 浮动元素/inline-block元素/绝对定位元素的margin不会和垂直方向上的其他元素的margin折叠
910 | 3. 创建了块级格式化上下文的元素,不会和它的子元素发生margin折叠
911 | 4. 元素自身的margin-bottom和margin-top相邻时也会折叠
912 |
913 | ### 如何确定一个元素的包含块(containing block)
914 |
915 | 1. 根元素的包含块叫做初始包含块,在连续媒体中他的尺寸与viewport相同并且anchored at the canvas origin;对于paged media,它的尺寸等于page area。初始包含块的direction属性与根元素相同。
916 | 2. ``position``为``relative``或者``static``的元素,它的包含块由最近的块级(``display``为``block``,``list-item``, ``table``)祖先元素的**内容框**组成
917 | 3. 如果元素``position``为``fixed``。对于连续媒体,它的包含块为viewport;对于paged media,包含块为page area
918 | 4. 如果元素``position``为``absolute``,它的包含块由祖先元素中最近一个``position``为``relative``,``absolute``或者``fixed``的元素产生,规则如下:
919 | - 如果祖先元素为行内元素,the containing block is the bounding box around the **padding boxes** of the first and the last inline boxes generated for that element.
920 | - 其他情况下包含块由祖先节点的**padding edge**组成
921 |
922 | 如果找不到定位的祖先元素,包含块为**初始包含块**
923 |
924 | ### stacking context,布局规则
925 | z轴上的默认层叠顺序如下(从下到上):
926 |
927 | 1. 根元素的边界和背景
928 | 2. 常规流中的元素按照html中顺序
929 | 3. 浮动块
930 | 4. positioned元素按照html中出现顺序
931 |
932 | 如何创建stacking context:
933 |
934 | 1. 根元素
935 | 2. z-index不为auto的定位元素
936 | 3. a flex item with a z-index value other than 'auto'
937 | 4. opacity小于1的元素
938 | 5. 在移动端webkit和chrome22+,z-index为auto,position: fixed也将创建新的stacking context
939 |
940 | ### 如何水平居中一个元素
941 | - 如果需要居中的元素为**常规流中inline元素**,为父元素设置`text-align: center;`即可实现
942 | - 如果需要居中的元素为**常规流中block元素**,1)为元素设置宽度,2)设置左右margin为auto。3)IE6下需在父元素上设置`text-align: center;`,再给子元素恢复需要的值
943 |
944 | ```
945 |
946 |
947 | aaaaaa aaaaaa a a a a a a a a
948 |
949 |
950 |
951 |
964 | ```
965 |
966 | - 如果需要居中的元素为**浮动元素**,1)为元素设置宽度,2)`position: relative;`,3)浮动方向偏移量(left或者right)设置为50%,4)浮动方向上的margin设置为元素宽度一半乘以-1
967 |
968 | ```
969 |
970 |
971 | aaaaaa aaaaaa a a a a a a a a
972 |
973 |
974 |
975 |
990 | ```
991 |
992 | - 如果需要居中的元素为**绝对定位元素**,1)为元素设置宽度,2)偏移量设置为50%,3)偏移方向外边距设置为元素宽度一半乘以-1
993 |
994 | ```
995 |
996 |
997 | aaaaaa aaaaaa a a a a a a a a
998 |
999 |
1000 |
1001 |
1016 | ```
1017 |
1018 | - 如果需要居中的元素为**绝对定位元素**,1)为元素设置宽度,2)设置左右偏移量都为0,3)设置左右外边距都为auto
1019 |
1020 | ```
1021 |
1022 |
1023 | aaaaaa aaaaaa a a a a a a a a
1024 |
1025 |
1026 |
1027 |
1043 | ```
1044 |
1045 | ### 如何竖直居中一个元素
1046 | 参考资料:[6 Methods For Vertical Centering With CSS](http://www.vanseodesign.com/css/vertical-centering/)。 [盘点8种CSS实现垂直居中](http://blog.csdn.net/freshlover/article/details/11579669)
1047 |
1048 | - 需要居中元素为**单行文本**,为包含文本的元素设置大于`font-size`的`line-height`:
1049 |
1050 | ```
1051 | center text
1052 |
1053 |
1058 | ```
1059 |
1060 |
--------------------------------------------------------------------------------
/html/html-questions.md:
--------------------------------------------------------------------------------
1 | # HTML 问题
2 |
3 | 欢迎提出 PR 进行建议和指正!
4 |
5 | * [`DOCTYPE`有什么用?](#doctype有什么用)
6 | * [如何提供包含多种语言内容的页面?](#如何提供包含多种语言内容的页面)
7 | * [在设计开发多语言网站时,需要留心哪些事情?](#在设计开发多语言网站时需要留心哪些事情)
8 | * [什么是`data-`属性?](#什么是data-属性)
9 | * [将 HTML5 看作成开放的网络平台,什么是 HTML5 的基本构件(building block)?](#将-html5-看作成开放的网络平台什么是-html5-的基本构件building-block)
10 | * [请描述`cookie`、`sessionStorage`和`localStorage`的区别。](#请描述cookiesessionstorage和localstorage的区别)
11 | * [请描述`
484 |
485 |
486 | ```
487 |
488 | ```js
489 | // 文件加载自 https://example.com?callback=printData
490 | printData({ name: 'Yang Shun' });
491 | ```
492 |
493 | 客户端必须在其全局范围内具有`printData`函数,并且在收到来自跨域的响应时,该函数将由客户端执行。
494 |
495 | JSONP 可能具有一些安全隐患。由于 JSONP 是纯 JavaScript 实现,它可以完成 JavaScript 所能做的一切,因此需要信任 JSONP 数据的提供者。
496 |
497 | 现如今,[跨来源资源共享(CORS)](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) 是推荐的主流方式,JSONP 已被视为一种比较 hack 的方式。
498 |
499 | ###### 参考
500 |
501 | * https://stackoverflow.com/a/2067584/1751946
502 |
503 | [[↑] 回到顶部](#js-问题)
504 |
505 | ### 你使用过 JavaScript 模板吗?用过什么相关的库?
506 |
507 | 使用过。Handlebars、Underscore、Lodash、AngularJS 和 JSX。我不喜欢 AngularJS 中的模板,因为它在指令中大量使用了字符串,并且书写错误会被忽略。JSX 是我的新宠,因为它更接近 JavaScript,几乎没有什么学习成本。现在,可以使用 ES2015 模板字符串快速创建模板,而不需依赖第三方代码。
508 |
509 | ```js
510 | const template = `My name is: ${name}
`;
511 | ```
512 |
513 | 但是,请注意上述方法中可能存在的 XSS,因为内容不会被转义,与模板库不同。
514 |
515 | [[↑] 回到顶部](#js-问题)
516 |
517 | ### 请解释变量提升(hoisting)。
518 |
519 | 变量提升(hoisting)是用于解释代码中变量声明行为的术语。使用`var`关键字声明或初始化的变量,会将声明语句“提升”到当前作用域的顶部。 但是,只有声明才会触发提升,赋值语句(如果有的话)将保持原样。我们用几个例子来解释一下。
520 |
521 | ```js
522 | // 用 var 声明得到提升
523 | console.log(foo); // undefined
524 | var foo = 1;
525 | console.log(foo); // 1
526 |
527 | // 用 let/const 声明不会提升
528 | console.log(bar); // ReferenceError: bar is not defined
529 | let bar = 2;
530 | console.log(bar); // 2
531 | ```
532 |
533 | 函数声明会使函数体提升,但函数表达式(以声明变量的形式书写)只有变量声明会被提升。
534 |
535 | ```js
536 | // 函数声明
537 | console.log(foo); // [Function: foo]
538 | foo(); // 'FOOOOO'
539 | function foo() {
540 | console.log('FOOOOO');
541 | }
542 | console.log(foo); // [Function: foo]
543 |
544 | // 函数表达式
545 | console.log(bar); // undefined
546 | bar(); // Uncaught TypeError: bar is not a function
547 | var bar = function() {
548 | console.log('BARRRR');
549 | };
550 | console.log(bar); // [Function: bar]
551 | ```
552 |
553 | [[↑] 回到顶部](#js-问题)
554 |
555 | ### 请描述事件冒泡。
556 |
557 | 当一个事件在 DOM 元素上触发时,如果有事件监听器,它将尝试处理该事件,然后事件冒泡到其父级元素,并发生同样的事情。最后直到事件到达祖先元素。事件冒泡是实现事件委托的原理(event delegation)。
558 |
559 | [[↑] 回到顶部](#js-问题)
560 |
561 | ### “attribute” 和 “property” 之间有什么区别?
562 |
563 | “Attribute” 是在 HTML 中定义的,而 “property” 是在 DOM 上定义的。为了说明区别,假设我们在 HTML 中有一个文本框:``。
564 |
565 | ```js
566 | const input = document.querySelector('input');
567 | console.log(input.getAttribute('value')); // Hello
568 | console.log(input.value); // Hello
569 | ```
570 |
571 | 但是在文本框中键入“ World!”后:
572 |
573 | ```js
574 | console.log(input.getAttribute('value')); // Hello
575 | console.log(input.value); // Hello World!
576 | ```
577 |
578 | ###### 参考
579 |
580 | * https://stackoverflow.com/questions/6003819/properties-and-attributes-in-html
581 |
582 | [[↑] 回到顶部](#js-问题)
583 |
584 | ### 为什么扩展 JavaScript 内置对象是不好的做法?
585 |
586 | 扩展 JavaScript 内置(原生)对象意味着将属性或方法添加到其`prototype`中。虽然听起来很不错,但事实上这样做很危险。想象一下,你的代码使用了一些库,它们通过添加相同的 contains 方法来扩展`Array.prototype`,如果这两个方法的行为不相同,那么这些实现将会相互覆盖,你的代码将不能正常运行。
587 |
588 | 扩展内置对象的唯一使用场景是创建 polyfill,本质上为老版本浏览器缺失的方法提供自己的实现,该方法是由 JavaScript 规范定义的。
589 |
590 | ###### 参考
591 |
592 | * http://lucybain.com/blog/2014/js-extending-built-in-objects/
593 |
594 | [[↑] 回到顶部](#js-问题)
595 |
596 | ### document 中的`load`事件和`DOMContentLoaded`事件之间的区别是什么?
597 |
598 | 当初始的 HTML 文档被完全加载和解析完成之后,`DOMContentLoaded`事件被触发,而无需等待样式表、图像和子框架的完成加载。
599 |
600 | `window`的`load`事件仅在 DOM 和所有相关资源全部完成加载后才会触发。
601 |
602 | ###### 参考
603 |
604 | * https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded
605 | * https://developer.mozilla.org/en-US/docs/Web/Events/load
606 |
607 | [[↑] 回到顶部](#js-问题)
608 |
609 | ### `==`和`===`的区别是什么?
610 |
611 | `==`是抽象相等运算符,而`===`是严格相等运算符。`==`运算符是在进行必要的类型转换后,再比较。`===`运算符不会进行类型转换,所以如果两个值不是相同的类型,会直接返回`false`。使用`==`时,可能发生一些特别的事情,例如:
612 |
613 | ```js
614 | 1 == '1'; // true
615 | 1 == [1]; // true
616 | 1 == true; // true
617 | 0 == ''; // true
618 | 0 == '0'; // true
619 | 0 == false; // true
620 | ```
621 |
622 | 我的建议是从不使用`==`运算符,除了方便与`null`或`undefined`比较时,`a == null`如果`a`为`null`或`undefined`将返回`true`。
623 |
624 | ```js
625 | var a = null;
626 | console.log(a == null); // true
627 | console.log(a == undefined); // true
628 | ```
629 |
630 | ###### 参考
631 |
632 | * https://stackoverflow.com/questions/359494/which-equals-operator-vs-should-be-used-in-javascript-comparisons
633 |
634 | [[↑] 回到顶部](#js-问题)
635 |
636 | ### 请解释关于 JavaScript 的同源策略。
637 |
638 | 同源策略可防止 JavaScript 发起跨域请求。源被定义为 URI、主机名和端口号的组合。此策略可防止页面上的恶意脚本通过该页面的文档对象模型,访问另一个网页上的敏感数据。
639 |
640 | ###### 参考
641 |
642 | * https://en.wikipedia.org/wiki/Same-origin_policy
643 |
644 | [[↑] 回到顶部](#js-问题)
645 |
646 | ### 请使下面的语句生效:
647 |
648 | ```js
649 | duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]
650 | ```
651 |
652 | ```js
653 | function duplicate(arr) {
654 | return arr.concat(arr);
655 | }
656 |
657 | duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]
658 | ```
659 |
660 | [[↑] 回到顶部](#js-问题)
661 |
662 | ### 请说明三元表达式中“三元”这个词代表什么?
663 |
664 | “三元”表示接受三个操作数:判断条件,`then`表达式和`else`表达式。三元表达式不是 JavaScript 特有的,我不知道这个问题为什么会出现在这里。
665 |
666 | ###### 参考
667 |
668 | * https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Conditional_Operator
669 |
670 | [[↑] 回到顶部](#js-问题)
671 |
672 | ### 什么是`"use strict";`?使用它有什么优缺点?
673 |
674 | 'use strict' 是用于对整个脚本或单个函数启用严格模式的语句。严格模式是可选择的一个限制 JavaScript 的变体一种方式 。
675 |
676 | **优点:**
677 |
678 | * 无法再意外创建全局变量。
679 | * 会使引起静默失败(silently fail,即:不报错也没有任何效果)的赋值操抛出异常。
680 | * 试图删除不可删除的属性时会抛出异常(之前这种操作不会产生任何效果)。
681 | * 要求函数的参数名唯一。
682 | * 全局作用域下,`this`的值为`undefined`。
683 | * 捕获了一些常见的编码错误,并抛出异常。
684 | * 禁用令人困惑或欠佳的功能。
685 |
686 | **缺点:**
687 |
688 | * 缺失许多开发人员已经习惯的功能。
689 | * 无法访问`function.caller`和`function.arguments`。
690 | * 以不同严格模式编写的脚本合并后可能导致问题。
691 |
692 | 总的来说,我认为利大于弊,我从来不使用严格模式禁用的功能,因此我推荐使用严格模式。
693 |
694 | ###### 参考
695 |
696 | * http://2ality.com/2011/10/strict-mode-hatred.html
697 | * http://lucybain.com/blog/2014/js-use-strict/
698 |
699 | [[↑] 回到顶部](#js-问题)
700 |
701 | ### 创建一个循环,从 1 迭代到 100,`3`的倍数时输出 "fizz",`5`的倍数时输出 "buzz",同时为`3`和`5`的倍数时输出 "fizzbuzz"。
702 |
703 | 来自 [Paul Irish](https://gist.github.com/jaysonrowe/1592432#gistcomment-790724)的 FizzBuzz。
704 |
705 | ```js
706 | for (let i = 1; i <= 100; i++) {
707 | let f = i % 3 == 0,
708 | b = i % 5 == 0;
709 | console.log(f ? (b ? 'FizzBuzz' : 'Fizz') : b ? 'Buzz' : i);
710 | }
711 | ```
712 |
713 | 我不建议你在面试时写上面的代码。只要写得清晰即可。关于更多千奇百怪的 FizzBuzz 实现,请查看下面的参考链接。
714 |
715 | ###### 参考
716 |
717 | * https://gist.github.com/jaysonrowe/1592432
718 |
719 | [[↑] 回到顶部](#js-问题)
720 |
721 | ### 为什么不要使用全局作用域?
722 |
723 | 每个脚本都可以访问全局作用域,如果人人都使用全局命名空间来定义自己的变量,肯定会发生冲突。使用模块模式(IIFE)将变量封装在本地命名空间中。
724 |
725 | [[↑] 回到顶部](#js-问题)
726 |
727 | ### 为什么要使用`load`事件?这个事件有什么缺点吗?你知道一些代替方案吗,为什么使用它们?
728 |
729 | 在文档装载完成后会触发`load`事件。此时,在文档中的所有对象都在 DOM 中,所有图像、脚本、链接和子框架都完成了加载。
730 |
731 | DOM 事件`DOMContentLoaded`将在页面的 DOM 构建完成后触发,但不要等待其他资源完成加载。如果在初始化之前不需要装入整个页面,这个事件是使用首选。
732 |
733 | TODO.
734 |
735 | ###### 参考
736 |
737 | * https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onload
738 |
739 | [[↑] 回到顶部](#js-问题)
740 |
741 | ### 请解释单页应用是什么,如何使其对 SEO 友好。
742 |
743 | 以下摘自 [Grab Front End Guide](https://github.com/grab/front-end-guide),碰巧的是,这正是我自己写的!
744 |
745 | 现如今,Web 开发人员将他们构建的产品称为 Web 应用,而不是网站。虽然这两个术语之间没有严格的区别,但网络应用往往具有高度的交互性和动态性,允许用户执行操作并接收他们的操作响应。在过去,浏览器从服务器接收 HTML 并渲染。当用户导航到其它 URL 时,需要整页刷新,服务器会为新页面发送新的 HTML。这被称为服务器端渲染。
746 |
747 | 然而,在现代的 SPA 中,客户端渲染取而代之。浏览器从服务器加载初始页面、整个应用程序所需的脚本(框架、库、应用代码)和样式表。当用户导航到其他页面时,不会触发页面刷新。该页面的 URL 通过 [HTML5 History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) 进行更新。浏览器通过 [AJAX](https://developer.mozilla.org/en-US/docs/AJAX/Getting_Started) 请求向服务器检索新页面所需的数据(通常采用 JSON 格式)。然后,SPA 通过 JavaScript 来动态更新页面,这些 JavaScript 在初始页面加载时已经下载。这种模式类似于原生移动应用的工作方式。
748 |
749 | **好处:**
750 |
751 | * 用户感知响应更快,用户切换页面时,不再看到因页面刷新而导致的白屏。
752 | * 对服务器进行的 HTTP 请求减少,因为对于每个页面加载,不必再次下载相同的资源。
753 | * 客户端和服务器之间的关注点分离。可以为不同平台(例如手机、聊天机器人、智能手表)建立新的客户端,而无需修改服务器代码。只要 API 没有修改,可以单独修改客户端和服务器上的代码。
754 |
755 | **坏处:**
756 |
757 | * 由于加载了多个页面所需的框架、应用代码和资源,导致初始页面加载时间较长。
758 | * 服务器还需要进行额外的工作,需要将所有请求路由配置到单个入口点,然后由客户端接管路由。
759 | * SPA 依赖于 JavaScript 来呈现内容,但并非所有搜索引擎都在抓取过程中执行 JavaScript,他们可能会在你的页面上看到空的内容。这无意中损害了应用的搜索引擎优化(SEO)。然而,当你构建应用时,大多数情况下,搜索引擎优化并不是最重要的因素,因为并非所有内容都需要通过搜索引擎进行索引。为了解决这个问题,可以在服务器端渲染你的应用,或者使用诸如 [Prerender](https://prerender.io/) 的服务来“在浏览器中呈现你的 javascript,保存静态 HTML,并将其返回给爬虫”。
760 |
761 | ###### 参考
762 |
763 | * https://github.com/grab/front-end-guide#single-page-apps-spas
764 | * http://stackoverflow.com/questions/21862054/single-page-app-advantages-and-disadvantages
765 | * http://blog.isquaredsoftware.com/presentations/2016-10-revolution-of-web-dev/
766 | * https://medium.freecodecamp.com/heres-why-client-side-rendering-won-46a349fadb52
767 |
768 | [[↑] 回到顶部](#js-问题)
769 |
770 | ### 你对 Promises 及其 polyfill 的掌握程度如何?
771 |
772 | 掌握它的工作原理。`Promise`是一个可能在未来某个时间产生结果的对象:操作成功的结果或失败的原因(例如发生网络错误)。 `Promise`可能处于以下三种状态之一:fulfilled、rejected 或 pending。 用户可以对`Promise`添加回调函数来处理操作成功的结果或失败的原因。
773 |
774 | 一些常见的 polyfill 是`$.deferred`、Q 和 Bluebird,但不是所有的 polyfill 都符合规范。ES2015 支持 Promises,现在通常不需要使用 polyfills。
775 |
776 | ###### 参考
777 |
778 | * https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261
779 |
780 | [[↑] 回到顶部](#js-问题)
781 |
782 | ### `Promise`代替回调函数有什么优缺点?
783 |
784 | **优点:**
785 |
786 | * 避免可读性极差的回调地狱。
787 | * 使用`.then()`编写的顺序异步代码,既简单又易读。
788 | * 使用`Promise.all()`编写并行异步代码变得很容易。
789 |
790 | **缺点:**
791 |
792 | * 轻微地增加了代码的复杂度(这点存在争议)。
793 | * 在不支持 ES2015 的旧版浏览器中,需要引入 polyfill 才能使用。
794 |
795 | [[↑] 回到顶部](#js-问题)
796 |
797 | ### 用转译成 JavaScript 的语言写 JavaScript 有什么优缺点?
798 |
799 | Some examples of languages that compile to JavaScript include CoffeeScript, Elm, ClojureScript, PureScript and TypeScript.
800 | 这些是转译成 JavaScript 的语言,包括 CoffeeScript、Elm、ClojureScript、PureScript 和 TypeScript。
801 |
802 | **优点:**
803 |
804 | * 修复了 JavaScript 中的一些长期问题,并摒弃了 JavaScript 不好的做法。
805 | * 在 JavaScript 的基础上提供一些语法糖,使我们能够编写更短的代码,我认为 ES5 缺乏语法糖的支持,但 ES2015 非常好。
806 | * 对于需要长时间维护的大型项目,静态类型非常好用(针对 TypeScript)。
807 |
808 | **缺点:**
809 |
810 | * 由于浏览器只运行 JavaScript,所以需要构建、编译过程,在将代码提供给浏览器之前,需要将代码转译为 JavaScript。
811 | * 如果 source map 不能很好地映射到预编译的源代码,调试会很痛苦。
812 | * 大多数开发人员不熟悉这些语言,需要学习它。如果将其用于项目,会增加团队成本。
813 | * 社区比较小(取决于语言),这意味着资源、教程、图书和工具难以找到。
814 | * 可能缺乏 IDE(编辑器)的支持。
815 | * 这些语言将始终落后于最新的 JavaScript 标准。
816 | * 开发人员应该清楚代码正在被编译到什么地方——因为这是实际运行的内容,是最重要的。
817 |
818 | 实际上,ES2015 已经大大改进了 JavaScript,编写体验很好。我现在还没有真正看到对 CoffeeScript 的需求。
819 |
820 | ###### 参考
821 |
822 | * https://softwareengineering.stackexchange.com/questions/72569/what-are-the-pros-and-cons-of-coffeescript
823 |
824 | [[↑] 回到顶部](#js-问题)
825 |
826 | ### 你使用什么工具和技巧调试 JavaScript 代码?
827 |
828 | * React 和 Redux
829 | * [React Devtools](https://github.com/facebook/react-devtools)
830 | * [Redux Devtools](https://github.com/gaearon/redux-devtools)
831 | * Vue
832 | * [Vue Devtools](https://github.com/vuejs/vue-devtools)
833 | * JavaScript
834 | * [Chrome Devtools](https://hackernoon.com/twelve-fancy-chrome-devtools-tips-dc1e39d10d9d)
835 | * `debugger`声明
836 | * 使用万金油`console.log`进行调试
837 |
838 | ###### 参考
839 |
840 | * https://hackernoon.com/twelve-fancy-chrome-devtools-tips-dc1e39d10d9d
841 | * https://raygun.com/blog/javascript-debugging/
842 |
843 | [[↑] 回到顶部](#js-问题)
844 |
845 | ### 你使用什么语句遍历对象的属性和数组的元素?
846 |
847 | **对象:**
848 |
849 | * `for`循环:`for (var property in obj) { console.log(property); }`。但是,这还会遍历到它的继承属性,在使用之前,你需要加入`obj.hasOwnProperty(property)`检查。
850 | * `Object.keys()`:`Object.keys(obj).forEach(function (property) { ... })`。`Object.keys()`方法会返回一个由一个给定对象的自身可枚举属性组成的数组。
851 | * `Object.getOwnPropertyNames()`:`Object.getOwnPropertyNames(obj).forEach(function (property) { ... })`。`Object.getOwnPropertyNames()`方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。
852 |
853 | **数组:**
854 |
855 | * `for` loops:`for (var i = 0; i < arr.length; i++)`。这里的常见错误是`var`是函数作用域而不是块级作用域,大多数时候你想要迭代变量在块级作用域中。ES2015 引入了具有块级作用域的`let`,建议使用它。所以就变成了:`for (let i = 0; i < arr.length; i++)`。
856 | * `forEach`:`arr.forEach(function (el, index) { ... })`。这个语句结构有时会更精简,因为如果你所需要的只是数组元素,你不必使用`index`。还有`every`和`some`方法可以让你提前终止遍历。
857 |
858 | 大多数情况下,我更喜欢`.forEach`方法,但这取决于你想要做什么。`for`循环有更强的灵活性,比如使用`break`提前终止循环,或者递增步数大于一。
859 |
860 | [[↑] 回到顶部](#js-问题)
861 |
862 | ### 请解释可变对象和不可变对象之间的区别。
863 |
864 | * 什么是 JavaScript 中的不可变对象的例子?
865 | * 不变性有什么优点和缺点?
866 | * 你如何在自己的代码中实现不变性?
867 |
868 | **_可变对象_** 在创建之后是可以被改变的。
869 |
870 | **_不可变对象_** 在创建之后是不可以被改变的。
871 |
872 | 1. 在 `JavaScript` 中,`string` 和 `number` 从设计之初就是不可变(Immutable)。
873 | 2. **_不可变_** 其实是保持一个对象状态不变,这样做的好处是使得开发更加简单,可回溯,测试友好,减少了任何可能的副作用。但是,每当你想添加点东西到一个不可变(Immutable)对象里时,它一定是先拷贝已存在的值到新实例里,然后再给新实例添加内容,最后返回新实例。相比可变对象,这势必会有更多内存、计算量消耗。
874 | 3. 比如:构造一个纯函数
875 |
876 | ```js
877 | const student1 = {
878 | school: 'Baidu',
879 | name: 'HOU Ce',
880 | birthdate: '1995-12-15',
881 | };
882 |
883 | const changeStudent = (student, newName, newBday) => {
884 | return {
885 | ...student, // 使用解构
886 | name: newName, // 覆盖name属性
887 | birthdate: newBday, // 覆盖birthdate属性
888 | };
889 | };
890 |
891 | const student2 = changeStudent(student1, 'YAN Haijing', '1990-11-10');
892 |
893 | // both students will have the name properties
894 | console.log(student1, student2);
895 | // Object {school: "Baidu", name: "HOU Ce", birthdate: "1995-12-15"}
896 | // Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10"}
897 | ```
898 |
899 | ###### 参考
900 |
901 | * https://juejin.im/post/58d0ff6f1b69e6006b8fd4e9
902 | * https://www.interviewcake.com/concept/java/mutable
903 | * https://www.sitepoint.com/immutability-javascript/
904 |
905 | [[↑] 回到顶部](#js-问题)
906 |
907 | ### 请解释同步和异步函数之间的区别。
908 |
909 | 同步函数阻塞,而异步函数不阻塞。在同步函数中,语句完成后,下一句才执行。在这种情况下,程序可以按照语句的顺序进行精确评估,如果其中一个语句需要很长时间,程序的执行会停滞很长时间。
910 |
911 | 异步函数通常接受回调作为参数,在调用异步函数后立即继续执行下一行。回调函数仅在异步操作完成且调用堆栈为空时调用。诸如从 Web 服务器加载数据或查询数据库等重负载操作应该异步完成,以便主线程可以继续执行其他操作,而不会出现一直阻塞,直到费时操作完成的情况(在浏览器中,界面会卡住)。
912 |
913 | [[↑] 回到顶部](#js-问题)
914 |
915 | ### 什么是事件循环?调用堆栈和任务队列之间有什么区别?
916 |
917 | 事件循环是一个单线程循环,用于监视调用堆栈并检查是否有工作即将在任务队列中完成。如果调用堆栈为空并且任务队列中有回调函数,则将回调函数出队并推送到调用堆栈中执行。
918 |
919 | 如果你没有看过 Philip Robert [关于事件循环的演讲](https://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html),你应该看一下。这是观看次数最多的 JavaScript 相关视频之一。
920 |
921 | ###### 参考
922 |
923 | * https://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html
924 | * http://theproactiveprogrammer.com/javascript/the-javascript-event-loop-a-stack-and-a-queue/
925 |
926 | [[↑] 回到顶部](#js-问题)
927 |
928 | ### 请解释`function foo() {}`和`var foo = function() {}`之间`foo`的用法上的区别。
929 |
930 | 前者是函数声明,后者是函数表达式。关键的区别在于函数声明会使函数体提升(具有与变量相同的提升行为),但函数表达式的函数体不能。有关变量提升的更多解释,请参阅上面关于变量提升的问题。如果你试图在定义函数表达式之前调用它,你会得到一个`Uncaught TypeError: XXX is not a function`的错误。
931 |
932 | **函数声明**
933 |
934 | ```js
935 | foo(); // 'FOOOOO'
936 | function foo() {
937 | console.log('FOOOOO');
938 | }
939 | ```
940 |
941 | **函数表达式**
942 |
943 | ```js
944 | foo(); // Uncaught TypeError: foo is not a function
945 | var foo = function() {
946 | console.log('FOOOOO');
947 | };
948 | ```
949 |
950 | ###### 参考
951 |
952 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
953 |
954 | [[↑] 回到顶部](#js-问题)
955 |
956 | ### 使用`let`、`var`和`const`创建变量有什么区别?
957 |
958 | 用`var`声明的变量的作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。`let`和`const`是块级作用域,意味着它们只能在最近的一组花括号(function、if-else 代码块或 for 循环中)中访问。
959 |
960 | ```js
961 | function foo() {
962 | // 所有变量在函数中都可访问
963 | var bar = 'bar';
964 | let baz = 'baz';
965 | const qux = 'qux';
966 |
967 | console.log(bar); // bar
968 | console.log(baz); // baz
969 | console.log(qux); // qux
970 | }
971 |
972 | console.log(bar); // ReferenceError: bar is not defined
973 | console.log(baz); // ReferenceError: baz is not defined
974 | console.log(qux); // ReferenceError: qux is not defined
975 | ```
976 |
977 | ```js
978 | if (true) {
979 | var bar = 'bar';
980 | let baz = 'baz';
981 | const qux = 'qux';
982 | }
983 |
984 | // 用 var 声明的变量在函数作用域上都可访问
985 | console.log(bar); // bar
986 | // let 和 const 定义的变量在它们被定义的语句块之外不可访问
987 | console.log(baz); // ReferenceError: baz is not defined
988 | console.log(qux); // ReferenceError: qux is not defined
989 | ```
990 |
991 | `var`会使变量提升,这意味着变量可以在声明之前使用。`let`和`const`不会使变量提升,提前使用会报错。
992 |
993 | ```js
994 | console.log(foo); // undefined
995 |
996 | var foo = 'foo';
997 |
998 | console.log(baz); // ReferenceError: can't access lexical declaration 'baz' before initialization
999 |
1000 | let baz = 'baz';
1001 |
1002 | console.log(bar); // ReferenceError: can't access lexical declaration 'bar' before initialization
1003 |
1004 | const bar = 'bar';
1005 | ```
1006 |
1007 | 用`var`重复声明不会报错,但`let`和`const`会。
1008 |
1009 | ```js
1010 | var foo = 'foo';
1011 | var foo = 'bar';
1012 | console.log(foo); // "bar"
1013 |
1014 | let baz = 'baz';
1015 | let baz = 'qux'; // Uncaught SyntaxError: Identifier 'baz' has already been declared
1016 | ```
1017 |
1018 | `let`和`const`的区别在于:`let`允许多次赋值,而`const`只允许一次。
1019 |
1020 | ```js
1021 | // 这样不会报错。
1022 | let foo = 'foo';
1023 | foo = 'bar';
1024 |
1025 | // 这样会报错。
1026 | const baz = 'baz';
1027 | baz = 'qux';
1028 | ```
1029 |
1030 | ###### 参考
1031 |
1032 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
1033 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
1034 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
1035 |
1036 | [[↑] 回到顶部](#js-问题)
1037 |
1038 | ### ES6 的类和 ES5 的构造函数有什么区别?
1039 |
1040 | TODO
1041 |
1042 | [[↑] 回到顶部](#js-问题)
1043 |
1044 | ### 你能给出一个使用箭头函数的例子吗,箭头函数与其他函数有什么不同?
1045 |
1046 | TODO
1047 |
1048 | [[↑] 回到顶部](#js-问题)
1049 |
1050 | ### 在构造函数中使用箭头函数有什么好处?
1051 |
1052 | TODO
1053 |
1054 | [[↑] 回到顶部](#js-问题)
1055 |
1056 | ### 高阶函数(higher-order)的定义是什么?
1057 |
1058 | 高阶函数是将一个或多个函数作为参数的函数,它用于数据处理,也可能将函数作为返回结果。高阶函数是为了抽象一些重复执行的操作。一个典型的例子是`map`,它将一个数组和一个函数作为参数。`map`使用这个函数来转换数组中的每个元素,并返回一个包含转换后元素的新数组。JavaScript 中的其他常见示例是`forEach`、`filter`和`reduce`。高阶函数不仅需要操作数组的时候会用到,还有许多函数返回新函数的用例。`Function.prototype.bind`就是一个例子。
1059 |
1060 | **Map 示例:**
1061 |
1062 | 假设我们有一个由名字组成的数组,我们需要将每个字符转换为大写字母。
1063 |
1064 | ```js
1065 | const names = ['irish', 'daisy', 'anna'];
1066 | ```
1067 |
1068 | 不使用高阶函数的方法是这样:
1069 |
1070 | ```js
1071 | const transformNamesToUppercase = function(names) {
1072 | const results = [];
1073 | for (let i = 0; i < names.length; i++) {
1074 | results.push(names[i].toUpperCase());
1075 | }
1076 | return results;
1077 | };
1078 | transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']
1079 | ```
1080 |
1081 | 使用`.map(transformerFn)`使代码更简明
1082 |
1083 | ```js
1084 | const transformNamesToUppercase = function(names) {
1085 | return names.map(name => name.toUpperCase());
1086 | };
1087 | transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']
1088 | ```
1089 |
1090 | ###### 参考
1091 |
1092 | * https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99
1093 | * https://hackernoon.com/effective-functional-javascript-first-class-and-higher-order-functions-713fde8df50a
1094 | * https://eloquentjavascript.net/05_higher_order.html
1095 |
1096 | [[↑] 回到顶部](#js-问题)
1097 |
1098 | ### 请给出一个解构(destructuring)对象或数组的例子。
1099 |
1100 | 解构是 ES6 中新功能,它提供了一种简洁方便的方法来提取对象或数组的值,并将它们放入不同的变量中。
1101 |
1102 | **数组解构**
1103 |
1104 | ```js
1105 | // 变量赋值
1106 | const foo = ['one', 'two', 'three'];
1107 |
1108 | const [one, two, three] = foo;
1109 | console.log(one); // "one"
1110 | console.log(two); // "two"
1111 | console.log(three); // "three"
1112 | ```
1113 |
1114 | ```js
1115 | // 变量交换
1116 | let a = 1;
1117 | let b = 3;
1118 |
1119 | [a, b] = [b, a];
1120 | console.log(a); // 3
1121 | console.log(b); // 1
1122 | ```
1123 |
1124 | **对象解构**
1125 |
1126 | ```js
1127 | // 变量赋值
1128 | const o = { p: 42, q: true };
1129 | const { p, q } = o;
1130 |
1131 | console.log(p); // 42
1132 | console.log(q); // true
1133 | ```
1134 |
1135 | ###### 参考
1136 |
1137 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
1138 | * https://ponyfoo.com/articles/es6-destructuring-in-depth
1139 |
1140 | [[↑] 回到顶部](#js-问题)
1141 |
1142 | ### ES6 的模板字符串为生成字符串提供了很大的灵活性,你可以举个例子吗?
1143 |
1144 | **_模板字面量_**(Template literals) 是允许嵌入表达式的字符串字面量。你可以使用多行字符串和字符串插值功能。
1145 |
1146 | **语法**
1147 |
1148 | ```js
1149 | `string text``string text line 1
1150 | string text line 2``string text ${expression} string text`;
1151 |
1152 | tag`string text ${expression} string text`;
1153 | ```
1154 |
1155 | **示例**
1156 |
1157 | ```js
1158 | console.log(`string text line 1
1159 | string text line 2`);
1160 | // "string text line 1
1161 | // string text line 2"
1162 |
1163 | var a = 5;
1164 | var b = 10;
1165 | console.log(`Fifteen is ${a + b} and\nnot ${2 * a + b}.`);
1166 | // "Fifteen is 15 and
1167 | // not 20."
1168 | ```
1169 |
1170 | ```js
1171 | //show函数采用rest参数的写法如下:
1172 |
1173 | let name = '张三',
1174 | age = 20,
1175 | message = show`我来给大家介绍:${name}的年龄是${age}.`;
1176 |
1177 | function show(stringArr, ...values) {
1178 | let output = '';
1179 |
1180 | let index = 0;
1181 |
1182 | for (; index < values.length; index++) {
1183 | output += stringArr[index] + values[index];
1184 | }
1185 |
1186 | output += stringArr[index];
1187 |
1188 | return output;
1189 | }
1190 |
1191 | message; //"我来给大家介绍:张三的年龄是20."
1192 | ```
1193 |
1194 | ###### 参考
1195 |
1196 | * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/template_strings
1197 |
1198 | [[↑] 回到顶部](#js-问题)
1199 |
1200 | ### 你能举出一个柯里化函数(curry function)的例子吗?它有哪些好处?
1201 |
1202 | 柯里化(currying)是一种模式,其中具有多个参数的函数被分解为多个函数,当被串联调用时,将一次一个地累积所有需要的参数。这种技术帮助编写函数式风格的代码,使代码更易读、紧凑。值得注意的是,对于需要被 curry 的函数,它需要从一个函数开始,然后分解成一系列函数,每个函数都需要一个参数。
1203 |
1204 | ```js
1205 | function curry(fn) {
1206 | if (fn.length === 0) {
1207 | return fn;
1208 | }
1209 |
1210 | function _curried(depth, args) {
1211 | return function(newArgument) {
1212 | if (depth - 1 === 0) {
1213 | return fn(...args, newArgument);
1214 | }
1215 | return _curried(depth - 1, [...args, newArgument]);
1216 | };
1217 | }
1218 |
1219 | return _curried(fn.length, []);
1220 | }
1221 |
1222 | function add(a, b) {
1223 | return a + b;
1224 | }
1225 |
1226 | var curriedAdd = curry(add);
1227 | var addFive = curriedAdd(5);
1228 |
1229 | var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10]
1230 | ```
1231 |
1232 | ###### 参考
1233 |
1234 | * https://hackernoon.com/currying-in-js-d9ddc64f162e
1235 |
1236 | [[↑] 回到顶部](#js-问题)
1237 |
1238 | ### 使用扩展运算符(spread)的好处是什么,它与使用剩余参数语句(rest)有什么区别?
1239 |
1240 | 在函数泛型编码时,ES6 的扩展运算符非常有用,因为我们可以轻松创建数组和对象的拷贝,而无需使用`Object.create`、`slice`或其他函数库。这个语言特性在 Redux 和 rx.js 的项目中经常用到。
1241 |
1242 | ```js
1243 | function putDookieInAnyArray(arr) {
1244 | return [...arr, 'dookie'];
1245 | }
1246 |
1247 | const result = putDookieInAnyArray(['I', 'really', "don't", 'like']); // ["I", "really", "don't", "like", "dookie"]
1248 |
1249 | const person = {
1250 | name: 'Todd',
1251 | age: 29,
1252 | };
1253 |
1254 | const copyOfTodd = { ...person };
1255 | ```
1256 |
1257 | ES6 的剩余参数语句提供了一个简写,允许我们将不定数量的参数表示为一个数组。它就像是扩展运算符语法的反面,将数据收集到数组中,而不是解构数组。剩余参数语句在函数参数、数组和对象的解构赋值中有很大作用。
1258 |
1259 | ```js
1260 | function addFiveToABunchOfNumbers(...numbers) {
1261 | return numbers.map(x => x + 5);
1262 | }
1263 |
1264 | const result = addFiveToABunchOfNumbers(4, 5, 6, 7, 8, 9, 10); // [9, 10, 11, 12, 13, 14, 15]
1265 |
1266 | const [a, b, ...rest] = [1, 2, 3, 4]; // a: 1, b: 2, rest: [3, 4]
1267 |
1268 | const { e, f, ...others } = {
1269 | e: 1,
1270 | f: 2,
1271 | g: 3,
1272 | h: 4,
1273 | }; // e: 1, f: 2, others: { g: 3, h: 4 }
1274 | ```
1275 |
1276 | ###### 参考
1277 |
1278 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
1279 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
1280 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
1281 |
1282 | [[↑] 回到顶部](#js-问题)
1283 |
1284 | ### 如何在文件之间共用代码?
1285 |
1286 | 这取决于执行 JavaScript 的环境。
1287 |
1288 | 在客户端(浏览器环境)上,只要变量或函数在全局作用域(`window`)中声明,所有脚本都可以引用它们。或者,通过 RequireJS 采用异步模块定义(AMD)以获得更多模块化方法。
1289 |
1290 | 在服务器(Node.js)上,常用的方法是使用 CommonJS。每个文件都被视为一个模块,可以通过将它们附加到`module.exports`对象来导出变量和函数。
1291 |
1292 | ES2015 定义了一个模块语法,旨在替换 AMD 和 CommonJS。 这最终将在浏览器和 Node 环境中得到支持。
1293 |
1294 | [[↑] 回到顶部](#js-问题)
1295 |
1296 | ###### 参考
1297 |
1298 | * http://requirejs.org/docs/whyamd.html
1299 | * https://nodejs.org/docs/latest/api/modules.html
1300 | * http://2ality.com/2014/09/es6-modules-final.html
1301 |
1302 | ### 什么情况下会用到静态类成员?
1303 |
1304 | 静态类成员(属性或方法)不绑定到某个类的特定实例,不管哪个实例引用它,都具有相同的值。静态属性通常是配置变量,而静态方法通常是纯粹的实用函数,不依赖于实例的状态。
1305 |
1306 | ###### 参考
1307 |
1308 | * https://stackoverflow.com/questions/21155438/when-to-use-static-variables-methods-and-when-to-use-instance-variables-methods
1309 |
1310 | [[↑] 回到顶部](#js-问题)
1311 |
1312 | ### 其他答案
1313 |
1314 | * http://flowerszhong.github.io/2013/11/20/javascript-questions.html
1315 |
1316 | ### 请用原生js实现一个函数,给页面制定的任意一个元素添加一个透明遮罩(透明度可变,默认0.2),使这个区域点击无效,要求兼容IE8+及各主流浏览器,遮罩层效果如下图所示:
1317 | 
1318 |
1319 | ```
1320 |
1328 |
1329 |
1330 |
1331 |
1363 | ```
1364 |
1365 | ### 请用代码写出(今天是星期x)其中x表示当天是星期几,如果当天是星期一,输出应该是"今天是星期一"
1366 |
1367 | ```
1368 | var days = ['日','一','二','三','四','五','六'];
1369 | var date = new Date();
1370 |
1371 | console.log('今天是星期' + days[date.getDay()]);
1372 | ```
1373 |
1374 | ### 下面这段代码想要循环延时输出结果0 1 2 3 4,请问输出结果是否正确,如果不正确,请说明为什么,并修改循环内的代码使其输出正确结果
1375 |
1376 | ```
1377 | for (var i = 0; i < 5; ++i) {
1378 | setTimeout(function () {
1379 | console.log(i + ' ');
1380 | }, 100);
1381 | }
1382 | ```
1383 |
1384 | 不能输出正确结果,因为循环中setTimeout接受的参数函数通过闭包访问变量i。javascript运行环境为单线程,setTimeout注册的函数需要等待线程空闲才能执行,此时for循环已经结束,i值为5.五个定时输出都是5
1385 | 修改方法:将setTimeout放在函数立即调用表达式中,将i值作为参数传递给包裹函数,创建新闭包
1386 |
1387 | ```
1388 | for (var i = 0; i < 5; ++i) {
1389 | (function (i) {
1390 | setTimeout(function () {
1391 | console.log(i + ' ');
1392 | }, 100);
1393 | }(i));
1394 | }
1395 | ```
1396 |
1397 |
1398 |
1399 | ### 现有一个Page类,其原型对象上有许多以post开头的方法(如postMsg);另有一拦截函数chekc,只返回ture或false.请设计一个函数,该函数应批量改造原Page的postXXX方法,在保留其原有功能的同时,为每个postXXX方法增加拦截验证功能,当chekc返回true时继续执行原postXXX方法,返回false时不再执行原postXXX方法
1400 |
1401 | ```
1402 | function Page() {}
1403 |
1404 | Page.prototype = {
1405 | constructor: Page,
1406 |
1407 | postA: function (a) {
1408 | console.log('a:' + a);
1409 | },
1410 | postB: function (b) {
1411 | console.log('b:' + b);
1412 | },
1413 | postC: function (c) {
1414 | console.log('c:' + c);
1415 | },
1416 | check: function () {
1417 | return Math.random() > 0.5;
1418 | }
1419 | }
1420 |
1421 | function checkfy(obj) {
1422 | for (var key in obj) {
1423 | if (key.indexOf('post') === 0 && typeof obj[key] === 'function') {
1424 | (function (key) {
1425 | var fn = obj[key];
1426 | obj[key] = function () {
1427 | if (obj.check()) {
1428 | fn.apply(obj, arguments);
1429 | }
1430 | };
1431 | }(key));
1432 | }
1433 | }
1434 | } // end checkfy()
1435 |
1436 | checkfy(Page.prototype);
1437 |
1438 | var obj = new Page();
1439 |
1440 | obj.postA('checkfy');
1441 | obj.postB('checkfy');
1442 | obj.postC('checkfy');
1443 | ```
1444 |
1445 | ### 完成下面的tool-tip
1446 | 
1447 |
1448 | ### 编写javascript深度克隆函数deepClone
1449 |
1450 | function deepClone(obj) {
1451 | var _toString = Object.prototype.toString;
1452 |
1453 | // null, undefined, non-object, function
1454 | if (!obj || typeof obj !== 'object') {
1455 | return obj;
1456 | }
1457 |
1458 | // DOM Node
1459 | if (obj.nodeType && 'cloneNode' in obj) {
1460 | return obj.cloneNode(true);
1461 | }
1462 |
1463 | // Date
1464 | if (_toString.call(obj) === '[object Date]') {
1465 | return new Date(obj.getTime());
1466 | }
1467 |
1468 | // RegExp
1469 | if (_toString.call(obj) === '[object RegExp]') {
1470 | var flags = [];
1471 | if (obj.global) { flags.push('g'); }
1472 | if (obj.multiline) { flags.push('m'); }
1473 | if (obj.ignoreCase) { flags.push('i'); }
1474 |
1475 | return new RegExp(obj.source, flags.join(''));
1476 | }
1477 |
1478 | var result = Array.isArray(obj) ? [] :
1479 | obj.constructor ? new obj.constructor() : {};
1480 |
1481 | for (var key in obj ) {
1482 | result[key] = deepClone(obj[key]);
1483 | }
1484 |
1485 | return result;
1486 | }
1487 |
1488 | function A() {
1489 | this.a = a;
1490 | }
1491 |
1492 | var a = {
1493 | name: 'qiu',
1494 | birth: new Date(),
1495 | pattern: /qiu/gim,
1496 | container: document.body,
1497 | hobbys: ['book', new Date(), /aaa/gim, 111]
1498 | };
1499 |
1500 | var c = new A();
1501 | var b = deepClone(c);
1502 | console.log(c.a === b.a);
1503 | console.log(c, b);
1504 |
1505 | ### 补充代码,鼠标单击Button1后将Button1移动到Button2的后面
1506 |
1507 |
1508 |
1509 |
1510 | TEst
1511 |
1512 |
1513 |
1514 |
1515 |
1516 |
1517 |
1518 |
1519 |
1543 |
1544 |
1545 |
1546 | ### 网页中实现一个计算当年还剩多少时间的倒数计时程序,要求网页上实时动态显示"××年还剩××天××时××分××秒"
1547 |
1548 |
1549 |
1550 |
1551 |
1552 | TEst
1553 |
1554 |
1555 |
1556 |
1557 |
1558 |
1559 |
1602 |
1603 |
1604 |
1605 |
1606 | ### 完成一个函数,接受数组作为参数,数组元素为整数或者数组,数组元素包含整数或数组,函数返回扁平化后的数组
1607 | 如:[1, [2, [ [3, 4], 5], 6]] => [1, 2, 3, 4, 5, 6]
1608 |
1609 | ```
1610 | var data = [1, [2, [ [3, 4], 5], 6]];
1611 |
1612 | function flat(data, result) {
1613 | var i, d, len;
1614 | for (i = 0, len = data.length; i < len; ++i) {
1615 | d = data[i];
1616 | if (typeof d === 'number') {
1617 | result.push(d);
1618 | } else {
1619 | flat(d, result);
1620 | }
1621 | }
1622 | }
1623 |
1624 | var result = [];
1625 | flat(data, result);
1626 |
1627 | console.log(result);
1628 | ```
1629 |
1630 | ### 如何判断一个对象是否为数组
1631 | 如果浏览器支持Array.isArray()可以直接判断否则需进行必要判断
1632 |
1633 | ```
1634 | /**
1635 | * 判断一个对象是否是数组,参数不是对象或者不是数组,返回false
1636 | *
1637 | * @param {Object} arg 需要测试是否为数组的对象
1638 | * @return {Boolean} 传入参数是数组返回true,否则返回false
1639 | */
1640 | function isArray(arg) {
1641 | if (typeof arg === 'object') {
1642 | return Object.prototype.toString.call(arg) === '[object Array]';
1643 | }
1644 | return false;
1645 | }
1646 | ```
1647 |
1648 | ### 请评价以下事件监听器代码并给出改进意见
1649 |
1650 | ```
1651 | if (window.addEventListener) {
1652 | var addListener = function (el, type, listener, useCapture) {
1653 | el.addEventListener(type, listener, useCapture);
1654 | };
1655 | }
1656 | else if (document.all) {
1657 | addListener = function (el, type, listener) {
1658 | el.attachEvent('on' + type, function () {
1659 | listener.apply(el);
1660 | });
1661 | };
1662 | }
1663 | ```
1664 |
1665 | 作用:浏览器功能检测实现跨浏览器DOM事件绑定
1666 |
1667 | 优点:
1668 |
1669 | 1. 测试代码只运行一次,根据浏览器确定绑定方法
1670 | 2. 通过``listener.apply(el)``解决IE下监听器this与标准不一致的地方
1671 | 3. 在浏览器不支持的情况下提供简单的功能,在标准浏览器中提供捕获功能
1672 |
1673 | 缺点:
1674 |
1675 | 1. document.all作为IE检测不可靠,应该使用if(el.attachEvent)
1676 | 2. addListener在不同浏览器下API不一样
1677 | 3. ``listener.apply``使this与标准一致但监听器无法移除
1678 | 4. 未解决IE下listener参数event。 target问题
1679 |
1680 | 改进:
1681 |
1682 | ```
1683 | var addListener;
1684 |
1685 | if (window.addEventListener) {
1686 | addListener = function (el, type, listener, useCapture) {
1687 | el.addEventListener(type, listener, useCapture);
1688 | return listener;
1689 | };
1690 | }
1691 | else if (window.attachEvent) {
1692 | addListener = function (el, type, listener) {
1693 | // 标准化this,event,target
1694 | var wrapper = function () {
1695 | var event = window.event;
1696 | event.target = event.srcElement;
1697 | listener.call(el, event);
1698 | };
1699 |
1700 | el.attachEvent('on' + type, wrapper);
1701 | return wrapper;
1702 | // 返回wrapper。调用者可以保存,以后remove
1703 | };
1704 | }
1705 | ```
1706 |
1707 | ### 如何判断一个对象是否为函数
1708 |
1709 | ```
1710 | /**
1711 | * 判断对象是否为函数,如果当前运行环境对可调用对象(如正则表达式)
1712 | * 的typeof返回'function',采用通用方法,否则采用优化方法
1713 | *
1714 | * @param {Any} arg 需要检测是否为函数的对象
1715 | * @return {boolean} 如果参数是函数,返回true,否则false
1716 | */
1717 | function isFunction(arg) {
1718 | if (arg) {
1719 | if (typeof (/./) !== 'function') {
1720 | return typeof arg === 'function';
1721 | } else {
1722 | return Object.prototype.toString.call(arg) === '[object Function]';
1723 | }
1724 | } // end if
1725 | return false;
1726 | }
1727 | ```
1728 |
1729 | ### 编写一个函数接受url中query string为参数,返回解析后的Object,query string使用application/x-www-form-urlencoded编码
1730 |
1731 | ```
1732 | /**
1733 | * 解析query string转换为对象,一个key有多个值时生成数组
1734 | *
1735 | * @param {String} query 需要解析的query字符串,开头可以是?,
1736 | * 按照application/x-www-form-urlencoded编码
1737 | * @return {Object} 参数解析后的对象
1738 | */
1739 | function parseQuery(query) {
1740 | var result = {};
1741 |
1742 | // 如果不是字符串返回空对象
1743 | if (typeof query !== 'string') {
1744 | return result;
1745 | }
1746 |
1747 | // 去掉字符串开头可能带的?
1748 | if (query.charAt(0) === '?') {
1749 | query = query.substring(1);
1750 | }
1751 |
1752 | var pairs = query.split('&');
1753 | var pair;
1754 | var key, value;
1755 | var i, len;
1756 |
1757 | for (i = 0, len = pairs.length; i < len; ++i) {
1758 | pair = pairs[i].split('=');
1759 | // application/x-www-form-urlencoded编码会将' '转换为+
1760 | key = decodeURIComponent(pair[0]).replace(/\+/g, ' ');
1761 | value = decodeURIComponent(pair[1]).replace(/\+/g, ' ');
1762 |
1763 | // 如果是新key,直接添加
1764 | if (!(key in result)) {
1765 | result[key] = value;
1766 | }
1767 | // 如果key已经出现一次以上,直接向数组添加value
1768 | else if (isArray(result[key])) {
1769 | result[key].push(value);
1770 | }
1771 | // key第二次出现,将结果改为数组
1772 | else {
1773 | var arr = [result[key]];
1774 | arr.push(value);
1775 | result[key] = arr;
1776 | } // end if-else
1777 | } // end for
1778 |
1779 | return result;
1780 | }
1781 |
1782 | function isArray(arg) {
1783 | if (arg && typeof arg === 'object') {
1784 | return Object.prototype.toString.call(arg) === '[object Array]';
1785 | }
1786 | return false;
1787 | }
1788 | /**
1789 | console.log(parseQuery('sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8'));
1790 | */
1791 | ```
1792 |
1793 | ### 解析一个完整的url,返回Object包含域与window.location相同
1794 |
1795 | ```
1796 | /**
1797 | * 解析一个url并生成window.location对象中包含的域
1798 | * location:
1799 | * {
1800 | * href: '包含完整的url',
1801 | * origin: '包含协议到pathname之前的内容',
1802 | * protocol: 'url使用的协议,包含末尾的:',
1803 | * username: '用户名', // 暂时不支持
1804 | * password: '密码', // 暂时不支持
1805 | * host: '完整主机名,包含:和端口',
1806 | * hostname: '主机名,不包含端口'
1807 | * port: '端口号',
1808 | * pathname: '服务器上访问资源的路径/开头',
1809 | * search: 'query string,?开头',
1810 | * hash: '#开头的fragment identifier'
1811 | * }
1812 | *
1813 | * @param {string} url 需要解析的url
1814 | * @return {Object} 包含url信息的对象
1815 | */
1816 | function parseUrl(url) {
1817 | var result = {};
1818 | var keys = ['href', 'origin', 'protocol', 'host',
1819 | 'hostname', 'port', 'pathname', 'search', 'hash'];
1820 | var i, len;
1821 | var regexp = /(([^:]+:)\/\/(([^:\/\?#]+)(:\d+)?))(\/[^?#]*)?(\?[^#]*)?(#.*)?/;
1822 |
1823 | var match = regexp.exec(url);
1824 |
1825 | if (match) {
1826 | for (i = keys.length - 1; i >= 0; --i) {
1827 | result[keys[i]] = match[i] ? match[i] : '';
1828 | }
1829 | }
1830 |
1831 | return result;
1832 | }
1833 | ```
1834 |
1835 | ### 完成函数getViewportSize返回指定窗口的视口尺寸
1836 |
1837 | ```
1838 | /**
1839 | * 查询指定窗口的视口尺寸,如果不指定窗口,查询当前窗口尺寸
1840 | **/
1841 | function getViewportSize(w) {
1842 | w = w || window;
1843 |
1844 | // IE9及标准浏览器中可使用此标准方法
1845 | if ('innerHeight' in w) {
1846 | return {
1847 | width: w.innerWidth,
1848 | height: w.innerHeight
1849 | };
1850 | }
1851 |
1852 | var d = w.document;
1853 | // IE 8及以下浏览器在标准模式下
1854 | if (document.compatMode === 'CSS1Compat') {
1855 | return {
1856 | width: d.documentElement.clientWidth,
1857 | height: d.documentElement.clientHeight
1858 | };
1859 | }
1860 |
1861 | // IE8及以下浏览器在怪癖模式下
1862 | return {
1863 | width: d.body.clientWidth,
1864 | height: d.body.clientHeight
1865 | };
1866 | }
1867 | ```
1868 |
1869 | ### 完成函数getScrollOffset返回窗口滚动条偏移量
1870 |
1871 | /**
1872 | * 获取指定window中滚动条的偏移量,如未指定则获取当前window
1873 | * 滚动条偏移量
1874 | *
1875 | * @param {window} w 需要获取滚动条偏移量的窗口
1876 | * @return {Object} obj.x为水平滚动条偏移量,obj.y为竖直滚动条偏移量
1877 | */
1878 | function getScrollOffset(w) {
1879 | w = w || window;
1880 | // 如果是标准浏览器
1881 | if (w.pageXOffset != null) {
1882 | return {
1883 | x: w.pageXOffset,
1884 | y: w.pageYOffset
1885 | };
1886 | }
1887 |
1888 | // 老版本IE,根据兼容性不同访问不同元素
1889 | var d = w.document;
1890 | if (d.compatMode === 'CSS1Compat') {
1891 | return {
1892 | x: d.documentElement.scrollLeft,
1893 | y: d.documentElement.scrollTop
1894 | }
1895 | }
1896 |
1897 | return {
1898 | x: d.body.scrollLeft,
1899 | y: d.body.scrollTop
1900 | };
1901 | }
1902 |
1903 |
1904 | ### 现有一个字符串richText,是一段富文本,需要显示在页面上.有个要求,需要给其中只包含一个img元素的p标签增加一个叫pic的class.请编写代码实现.可以使用jQuery或KISSY.
1905 |
1906 | function richText(text) {
1907 | var div = document.createElement('div');
1908 | div.innerHTML = text;
1909 | var p = div.getElementsByTagName('p');
1910 | var i, len;
1911 |
1912 | for (i = 0, len = p.length; i < len; ++i) {
1913 | if (p[i].getElementsByTagName('img').length === 1) {
1914 | p[i].classList.add('pic');
1915 | }
1916 | }
1917 |
1918 | return div.innerHTML;
1919 | }
1920 |
1921 | ### 请实现一个Event类,继承自此类的对象都会拥有两个方法on,off,once和trigger
1922 |
1923 |
1924 | function Event() {
1925 | if (!(this instanceof Event)) {
1926 | return new Event();
1927 | }
1928 | this._callbacks = {};
1929 | }
1930 | Event.prototype.on = function (type, handler) {
1931 | this_callbacks = this._callbacks || {};
1932 | this._callbacks[type] = this.callbacks[type] || [];
1933 | this._callbacks[type].push(handler);
1934 |
1935 | return this;
1936 | };
1937 |
1938 | Event.prototype.off = function (type, handler) {
1939 | var list = this._callbacks[type];
1940 |
1941 | if (list) {
1942 | for (var i = list.length; i >= 0; --i) {
1943 | if (list[i] === handler) {
1944 | list.splice(i, 1);
1945 | }
1946 | }
1947 | }
1948 |
1949 | return this;
1950 | };
1951 |
1952 | Event.prototype.trigger = function (type, data) {
1953 | var list = this._callbacks[type];
1954 |
1955 | if (list) {
1956 | for (var i = 0, len = list.length; i < len; ++i) {
1957 | list[i].call(this, data);
1958 | }
1959 | }
1960 | };
1961 |
1962 | Event.prototype.once = function (type, handler) {
1963 | var self = this;
1964 |
1965 | function wrapper() {
1966 | handler.apply(self, arguments);
1967 | self.off(type, wrapper);
1968 | }
1969 | this.on(type, wrapper);
1970 | return this;
1971 | };
1972 |
1973 | ### 编写一个函数将列表子元素顺序反转
1974 |
1975 | ```
1976 |
1977 | - 1
1978 | - 2
1979 | - 3
1980 | - 4
1981 |
1982 |
1983 |
1993 | ```
1994 |
1995 | ### 以下函数的作用是?空白区域应该填写什么
1996 |
1997 | ```
1998 | // define
1999 | (function (window) {
2000 | function fn(str) {
2001 | this.str = str;
2002 | }
2003 |
2004 | fn.prototype.format = function () {
2005 | var arg = __1__;
2006 | return this.str.replace(__2__, function (a, b) {
2007 | return arg[b] || '';
2008 | });
2009 | };
2010 |
2011 | window.fn = fn;
2012 | })(window);
2013 |
2014 | // use
2015 | (function () {
2016 | var t = new fn('{1}{2}
');
2017 | console.log(t.format('http://www.alibaba.com', 'Alibaba', 'Welcome'));
2018 | })();
2019 | ```
2020 |
2021 | define部分定义一个简单的模板类,使用{}作为转义标记,中间的数字表示替换目标,format实参用来替换模板内标记
2022 | 横线处填:
2023 |
2024 | 1. ``Array.prototype.slice.call(arguments, 0)``
2025 | 2. ``/\{\s*(\d+)\s*\}/g``
2026 |
2027 | ### 编写一个函数实现form的序列化(即将一个表单中的键值序列化为可提交的字符串)
2028 |
2029 |
2030 |
2049 |
2050 |
2051 |
2120 |
2121 | ### 使用原生javascript给下面列表中的li节点绑定点击事件,点击时创建一个Object对象,兼容IE和标准浏览器
2122 |
2123 | ```
2124 |
2130 |
2131 | Object:
2132 | {
2133 | "index": 1,
2134 | "name": "111",
2135 | "link": "http://1111"
2136 | }
2137 | ```
2138 |
2139 | script:
2140 |
2141 | ```
2142 | var EventUtil = {
2143 | getEvent: function (event) {
2144 | return event || window.event;
2145 | },
2146 | getTarget: function (event) {
2147 | return event.target || event.srcElement;
2148 | },
2149 | // 返回注册成功的监听器,IE中需要使用返回值来移除监听器
2150 | on: function (elem, type, handler) {
2151 | if (elem.addEventListener) {
2152 | elem.addEventListener(type, handler, false);
2153 | return handler;
2154 | } else if (elem.attachEvent) {
2155 | function wrapper(event) {
2156 | return handler.call(elem, event);
2157 | };
2158 | elem.attachEvent('on' + type, wrapper);
2159 | return wrapper;
2160 | }
2161 | },
2162 | off: function (elem, type, handler) {
2163 | if (elem.removeEventListener) {
2164 | elem.removeEventListener(type, handler, false);
2165 | } else if (elem.detachEvent) {
2166 | elem.detachEvent('on' + type, handler);
2167 | }
2168 | },
2169 | preventDefault: function (event) {
2170 | if (event.preventDefault) {
2171 | event.preventDefault();
2172 | } else if ('returnValue' in event) {
2173 | event.returnValue = false;
2174 | }
2175 | },
2176 | stopPropagation: function (event) {
2177 | if (event.stopPropagation) {
2178 | event.stopPropagation();
2179 | } else if ('cancelBubble' in event) {
2180 | event.cancelBubble = true;
2181 | }
2182 | }
2183 | };
2184 | var DOMUtil = {
2185 | text: function (elem) {
2186 | if ('textContent' in elem) {
2187 | return elem.textContent;
2188 | } else if ('innerText' in elem) {
2189 | return elem.innerText;
2190 | }
2191 | },
2192 | prop: function (elem, propName) {
2193 | return elem.getAttribute(propName);
2194 | }
2195 | };
2196 |
2197 | var nav = document.getElementById('nav');
2198 |
2199 | EventUtil.on(nav, 'click', function (event) {
2200 | var event = EventUtil.getEvent(event);
2201 | var target = EventUtil.getTarget(event);
2202 |
2203 | var children = this.children;
2204 | var i, len;
2205 | var anchor;
2206 | var obj = {};
2207 |
2208 | for (i = 0, len = children.length; i < len; ++i) {
2209 | if (children[i] === target) {
2210 | obj.index = i + 1;
2211 | anchor = target.getElementsByTagName('a')[0];
2212 | obj.name = DOMUtil.text(anchor);
2213 | obj.link = DOMUtil.prop(anchor, 'href');
2214 | }
2215 | }
2216 |
2217 | alert('index: ' + obj.index + ' name: ' + obj.name +
2218 | ' link: ' + obj.link);
2219 | });
2220 | ```
2221 |
2222 | ### 有一个大数组,var a = ['1', '2', '3', ...];a的长度是100,内容填充随机整数的字符串.请先构造此数组a,然后设计一个算法将其内容去重
2223 |
2224 | ```
2225 | /**
2226 | * 数组去重
2227 | **/
2228 | function normalize(arr) {
2229 | if (arr && Array.isArray(arr)) {
2230 | var i, len, map = {};
2231 | for (i = arr.length; i >= 0; --i) {
2232 | if (arr[i] in map) {
2233 | arr.splice(i, 1);
2234 | } else {
2235 | map[arr[i]] = true;
2236 | }
2237 | }
2238 | }
2239 | return arr;
2240 | }
2241 |
2242 | /**
2243 | * 用100个随机整数对应的字符串填充数组。
2244 | **/
2245 | function fillArray(arr, start, end) {
2246 | start = start == undefined ? 1 : start;
2247 | end = end == undefined ? 100 : end;
2248 |
2249 | if (end <= start) {
2250 | end = start + 100;
2251 | }
2252 |
2253 | var width = end - start;
2254 | var i;
2255 | for (i = 100; i >= 1; --i) {
2256 | arr.push('' + (Math.floor(Math.random() * width) + start));
2257 | }
2258 | return arr;
2259 | }
2260 |
2261 | var input = [];
2262 | fillArray(input, 1, 100);
2263 | input.sort(function (a, b) {
2264 | return a - b;
2265 | });
2266 | console.log(input);
2267 |
2268 | normalize(input);
2269 | console.log(input);
2270 | ```
--------------------------------------------------------------------------------