` HTML5 属性,效果和 `display:none;` 相同,但这个属性用于记录一个元素的状态
397 | * `height: 0;` 将元素高度设为 0 ,并消除边框
398 | * `filter: blur(0);` CSS3 属性,将一个元素的模糊度设置为 0,从而使这个元素“消失”在页面中
399 |
400 | ### 说明 relative 和 absolute 定位原点?
401 |
402 | * absolute 绝对定位,相对于最近定位父级元素进行定位。如果没有定位父级,则相对于浏览器窗口(不是body)
403 | * relative 相对定位,相对于自身正常位置进行定位
404 | * fixed 固定定位,相对于浏览器窗口进行定位(不是body)
405 | * static 消除定位,元素位于正常的流(忽略 top、bottom、left、right、z-index 值)
406 | * sticky 生成粘性定位的元素,容器的位置根据正常文档流计算得出
407 | * inherit 继承父级元素的 position 属性的值
408 |
409 |
410 | ### rgba() 和 opacity 的透明效果有什么不同?
411 |
412 | * opacity 作用于元素以及元素内的所有内容(包括文字)的透明度
413 | * rgba() 只作用于元素自身的颜色或其背景色,子元素不会继承透明效果
414 |
415 | ### css 属性 content 有什么作用?
416 |
417 | * content 属性专门应用在 before/after 伪元素上,用于插入额外内容或样式
418 |
419 | ### CSS3 有哪些新特性?
420 |
421 | - 新增选择器 `p:nth-child(n) {color: rgba(255, 0, 0, 0.75)}`
422 | - 弹性盒模型 `display: flex;`
423 | - 多列布局 `column-count: 5;`
424 | - 媒体查询 `@media (max-width: 480px) {.box: {column-count: 1;}}`
425 | - 个性化字体 `@font-face {font-family: BorderWeb; src:url(BORDERW0.eot);}`
426 | - 颜色透明度 `color: rgba(255, 0, 0, 0.75);`
427 | - 圆角 `border-radius: 5px;`
428 | - 渐变 `background: linear-gradient(red, green, blue);`
429 | - 阴影 `box-shadow: 3px 3px 3px rgba(0, 64, 128, 0.3);`
430 | - 倒影 `box-reflect: below 2px;`
431 | - 文字装饰 `text-stroke-color: red;`
432 | - 文字溢出 `text-overflow: ellipsis;`
433 | - 背景效果 `background-size: 100px 100px;`
434 | - 边框效果 `border-image: url(bt_blue.png) 0 10;`
435 | - 转换
436 | - 旋转 `transform: rotate(20deg);`
437 | - 倾斜 `transform: skew(150deg, -10deg);`
438 | - 位移 `transform: translate(20px, 20px);`
439 | - 缩放 `transform: scale(.5);`
440 | - 平滑过渡 `transition: all .3s ease-in .1s;`
441 | - 动画 `@keyframes my-anim {50% {border-radius: 10px;}} animation: my-anim 1s;`
442 |
443 | ### 请解释一下 CSS3 的 Flexbox(弹性盒布局模型)以及适用场景?
444 |
445 | Flexbox 用于不同尺寸屏幕中,创建可自动扩展和收缩的弹性布局
446 | 使用场景包括:居中布局、等高布局、多列平均分布布局等
447 |
448 | ### 用 CSS 创建一个三角形的原理是什么?
449 |
450 | * 把盒子的宽高设为 0,设置一个较粗的边框,把上、左、右三条边隐藏(颜色设为 transparent)
451 |
452 | ```Css
453 | .triangle {
454 | width: 0;
455 | height: 0;
456 | border-width: 20px;
457 | border-style: solid;
458 | border-color: transparent transparent #ccc transparent;
459 | }
460 | ```
461 |
462 | ### 一个满屏“品”字布局如何设计?
463 |
464 | 1. 上面的 div 宽 100%
465 | 2. 下面的两个 div 分别宽 50%
466 | 3. 用 float 或者 inline 使其不换行
467 |
468 | ### 经常遇到的浏览器的 CSS 兼容性有哪些?解决方法是什么?
469 |
470 | * 在 IE6/7下,li 的内容有浮动,li 下就会产生间隙
471 | - 解决方法:给 li 加 vertical-align 或 加浮动
472 | * 在 IE6 下,元素的高度的小于 19px 的时候,会被当做 19px 来处理
473 | - 解决方法:`overflow: hidden`
474 | * IE6 双边距 bug
475 | - 解决方法:在 float 的标签样式中加入 _display: inline
476 | * 在 IE6 下,浮动元素和绝对定位元素并列时,绝对定位元素会消失
477 | - 解决方法:给定位元素外面包个 div
478 | * 在 IE6/7 下,子元素有相对定位的话,父级的 overflow 包不住子元素
479 | - 解决方法:给父级加相对定位
480 | * 在 IE6/7 下,输入类型的表单控件上下各有 1px 的间隙
481 | - 解决方法:给 input 加浮动
482 | * 在 IE6 下,父级有边框的时候,子元素的 margin 值消失
483 | - 解决方法:触发父级的 haslayout
484 | * 在 IE6 下,不兼容 opacity:0.3; 透明度设置
485 | - 解决方法:`filter:alpha(opacity=30);`
486 |
487 | ### 经常遇到的浏览器的 JS 兼容性有哪些?
488 |
489 | * 获取当前样式:getComputedStyle(el, null) VS el.currentStyle
490 | * 获取事件对象:e VS window.event
491 | * 获取鼠标坐标:e.pageX, e.pageY VS window.event.x, window.event.y
492 | * 获取按键码:e.which VS event.keyCode
493 | * 获取文本节点:el.textContent VS el.innerText
494 |
495 | ### li 与 li 之间有看不见的空白间隔是什么原因引起的?有什么解决办法?
496 |
497 | * li 排列受到中间空白(回车/空格)等的影响,因为空白也属于字符,会被应用样式占据空间,产生间隔
498 | * 解决办法:在 ul 设置设置 `font-size: 0`, 再设置 li 的 `font-size`
499 |
500 | ### 什么是外边距重叠(margin-collapse)?重叠的结果是什么?
501 |
502 | * 相邻的两个盒子(可能是兄弟关系也可能是祖先关系)的 margin 会合并成一个单独的 margin,这种合并的 maring 称为折叠外边距。
503 |
504 | * 折叠结果遵循下列计算规则:
505 | - 两个相邻的外边距都是正数时,折叠结果是它们两者之间较大的值
506 | - 两个相邻的外边距都是负数时,折叠结果是两者绝对值的较大值
507 | - 两个外边距一正一负时,折叠结果是两者的相加的和
508 |
509 | ### 请写出多种等高布局
510 |
511 | * 在列的父元素上使用背景图进行 Y 轴的铺放,从而实现一种等高列的假像
512 | * 模仿表格布局等高列效果:兼容性不好,在 IE6/7 无法正常显示
513 | * 利用 css3 flexbox 布局:`.container{display: flex; align-items: stretch;}`
514 |
515 | ### 设置 css 垂直居中的方法有哪些?
516 |
517 | * 如果是单行文本,设置 line-height 与 height 相等
518 |
519 | ```Css
520 | .vertical {
521 | height: 100px;
522 | line-height: 100px;
523 | }
524 | ```
525 |
526 | * 已知高度的块级子元素,可采用 绝对定位 + 负边距
527 |
528 | ```Css
529 | .container {
530 | position: relative;
531 | }
532 | .vertical {
533 | height: 300px; /*子元素高度*/
534 | position: absolute;
535 | top:50%; /*父元素高度50%*/
536 | margin-top: -150px; /*自身高度一半*/
537 | }
538 | ```
539 |
540 | * 未知高度的块级父子元素,可模拟 表格布局(IE6/7不兼容,父级 overflow:hidden 失效)
541 |
542 | ```Css
543 | .container {
544 | display: table;
545 | }
546 | .content {
547 | display: table-cell;
548 | vertical-align: middle;
549 | }
550 | ```
551 |
552 | * 通过新增 inline-block 兄弟元素,并设置 vertical-align 实现(需要增加额外标签,IE6/7不兼容)
553 |
554 | ```Css
555 | .container {
556 | height: 100%;/*定义父级高度,作为参考*/
557 | }
558 | .extra .vertical{
559 | display: inline-block; /*行内块显示*/
560 | vertical-align: middle; /*垂直居中*/
561 | }
562 | .extra {
563 | height: 100%; /*设置新增元素高度为100%*/
564 | }
565 | ```
566 |
567 | * 通过 绝对定位 + CSS3 位移 实现
568 |
569 | ```Css
570 | .vertical {
571 | position: absolute;
572 | top:50%; /*父元素高度50%*/
573 | transform: translateY(-50%);
574 | }
575 | ```
576 |
577 | * 通过 CSS3 弹性盒模型 实现
578 |
579 | ```Css
580 | .container {
581 | display:flex;
582 | justify-content: center; /*子元素水平居中*/
583 | align-items: center; /*子元素垂直居中*/
584 | }
585 | ```
586 |
587 | ### 圣杯布局的实现原理?
588 |
589 | * 要求:三列布局;中间主体内容前置,且宽度自适应;两边内容定宽
590 | * 好处:重要的内容放在文档流前面可以优先渲染
591 | * 原理:利用相对定位、浮动、负边距布局,而不添加额外标签
592 |
593 | ```Css
594 | .container {
595 | padding-left: 150px;
596 | padding-right: 200px;
597 | }
598 | .main {
599 | float: left;
600 | width: 100%;
601 | }
602 | .left {
603 | float: left;
604 | width: 200px;
605 | margin-left: -100%;
606 | position: relative;
607 | left: -150px;
608 | }
609 | .right {
610 | float: left;
611 | width: 200px;
612 | margin-left: -200px;
613 | position: relative;
614 | right: -200px;
615 | }
616 | ```
617 |
618 | ### 什么是双飞翼布局?实现原理?
619 |
620 | * 双飞翼布局:对圣杯布局(使用了相对定位,对以后布局有局限性)的改进,消除相对定位布局
621 |
622 | * 原理:主体元素上设置左右边距,预留两翼位置。左右两栏使用浮动和负边距归位,消除相对定位。
623 |
624 | ```Css
625 | .container {
626 | /*padding-left: 150px;*/
627 | /*padding-right: 200px;*/
628 | }
629 | .main-wrap {
630 | width: 100%;
631 | float: left;
632 | }
633 | .main {
634 | margin-left: 150px;
635 | margin-right: 200px;
636 | }
637 | .left {
638 | float: left;
639 | width: 150px;
640 | margin-left: -100%;
641 | /*position: relative;*/
642 | /*left: -150px;*/
643 | }
644 | .right {
645 | float: left;
646 | width: 200px;
647 | margin-left: -200px;
648 | /*position: relative;*/
649 | /*right: -200px;*/
650 | }
651 | ```
652 |
653 | ### 在 CSS 样式中单位 px、em 在表现上有什么区别?
654 |
655 | * px 相对于显示器屏幕分辨率,无法用浏览器字体放大功能
656 | * em 值并不是固定的,会继承父级的字体大小:em = 像素值 / 父级 font-size
657 |
658 | ### 为什么要初始化 CSS 样式?
659 |
660 | * 不同浏览器对有些标签样式的默认值解析不同
661 | * 不初始化 CSS 会造成各现浏览器之间的页面显示差异
662 | * 可以使用 reset.css 或 Normalize.css 做 CSS 初始化
663 |
664 | ### 解释下什么是浮动(溢出)?它的工作原理?
665 |
666 | * 非 IE 浏览器下,容器不设高度且子元素都浮动时,容器高度不能被内容撑开。此时,内容会溢出到容器外面而影响布局。这种现象被称为浮动(溢出)
667 |
668 | * 工作原理:
669 | - 浮动元素脱离文档流,不占据空间(引起“高度塌陷”现象)
670 | - 浮动元素碰到包含它的边框,或者其他浮动元素的边框停留
671 |
672 | ### 浮动元素引起的问题?
673 |
674 | * 父元素的高度无法被撑开,影响与父元素同级的元素
675 | * 与浮动元素同级的非浮动元素会跟随其后
676 |
677 | ### 列举几种清除浮动的方式?
678 |
679 | * 添加额外标签,例如 `
`
680 | * 使用 br 标签及 html 属性,例如 `
`
681 | * 父元素设置 `overflow:hidden;` 在 IE6 中还需要触发 hasLayout,例如 zoom:1;
682 | * 父元素也设置浮动
683 | * 使用 :after 伪元素。IE6/7 不支持 :after,额外加 zoom:1 触发 hasLayout
684 |
685 | ### 清除浮动最佳实践(利用 :after 伪元素闭合浮动):
686 |
687 | - 方案一:
688 |
689 | ```Css
690 | .clearfix:after {
691 | /* content: "."; */
692 | /* visibility: hidden; */
693 | content: "\200B";
694 |
695 | /* \200B 是 Unicode 中的'零宽度空格' */
696 | /* \200B 本身不可见,因此可以省略 visibility: hidden; */
697 | /* 注意:Unicode 字符不适合内嵌 CSS 的 GB2312编码的页面 */
698 |
699 | display: block;
700 | height: 0;
701 | clear: both;
702 | }
703 | .clearfix {
704 | *zoom: 1; /* 兼容 IE6/7 */
705 | }
706 | ```
707 |
708 | - 方案二:
709 |
710 | ```Css
711 | /* For modern browsers */
712 | .clearfix:before, /* contain the top-margins of child elements */
713 | .clearfix:after {
714 | content: " ";
715 | display: table; /* trigger BFC */
716 | }
717 | .clearfix:after {
718 | clear: both;
719 | }
720 | /* For IE 6/7 (trigger hasLayout) */
721 | .clearfix {
722 | zoom: 1;
723 | }
724 | ```
725 |
726 | ### DOM 文档加载流程
727 |
728 | 1. 解析 HTML 结构
729 | 2. 加载外部脚本和样式表文件
730 | 3. 解析并执行脚本代码
731 | 4. 构造 HTML DOM 模型 // DOMContentLoaded
732 | 5. 加载图片等外部文件
733 | 6. 页面加载完毕 // load
734 |
735 |
736 | ### 什么是 BFC(Block formatting context)"块级格式化上下文"?
737 |
738 | * BFC(块级格式化上下文)是一个独立的渲染区域,只有 Block-level box 参与,它规定了内部的 Block-level Box 如何布局,并且与这个区域外部毫不相干
739 |
740 | * BFC 的渲染规则
741 | - BFC 元素的垂直方向的边距会发生重叠
742 | - BFC 的区域不会与浮动元素的 box 重叠(清除浮动原理)
743 | - BFC 在页面上是一个独立的容器,外面的元素不会影响它里面的元素。反之,里面的元素也不会影响外面的元素
744 | - 计算 BFC 的高度的时候,浮动元素也会参与计算
745 |
746 | * 如何创建 BFC
747 | - overflow 属性不为 visible
748 | - float 属性不为 none
749 | - position 属性为 absolute 或f ixed
750 | - display 属性为 inline-block、table-cell、table-caption、flex、inline-flex
751 |
752 | * BFC 的使用场景
753 | - 很常用的一个应用场景就是解决边距重叠的问题
754 |
755 | ### 什么是 FOUC? 如何来避免 FOUC?
756 |
757 | * 页面开始只显示出 HTML 内容,没有成功加载样式。但是几秒后又看到页面渲染后的样子。这期间短暂闪烁现象称为“文档样式闪烁(Flash of Unstyled Content) -- FOUC”
758 |
759 | * FOUC 现象的原理
760 | - 当一个`样式表`晚于其要渲染的 HTML 元素加载,等加载到此`样式表`时,页面将停止之前的渲染。等到此`样式表`加载完成后,页面将会再次重新渲染,从而出现 FOUC 现象
761 |
762 | * FOUC 产生的情况
763 | - 使用 `@import` 导入样式表时
764 | - 将`样式表`放在页面底部而不是 `` 内时
765 | - 将多个`样式表`置于 HTML 文档的不同位置时
766 |
767 | * 解决方案
768 | - 方案1:在 `` 标签内用 JS 隐藏相关元素,在触发 DOMContentLoaded 事件时,显示出相关元素
769 | - 方案2(推荐):将 css 样式使用 `
` 置于 `` 标签内
770 |
771 | ### 介绍使用过的 CSS 预处理器?
772 |
773 | * CSS 预处理器基本思想:为 CSS 增加了一些编程的特性(变量、逻辑判断、函数等)
774 | * 开发者使用这种语言进行进行 Web 页面样式设计,再编译成正常的 CSS 文件使用
775 | * 使用 CSS 预处理器可以使 CSS 更加简洁、适应性更强、可读性更佳,无需考虑兼容性
776 | * 最常用的 CSS 预处理器语言包括:LESS、Sass(SCSS)、Stylus
777 |
778 | ### CSS 相关的优化有哪些?
779 |
780 | * 并 css 多个,尽量减少 HTTP 请求
781 | * 将 css 文件放在页面最上面
782 | * 移除空的 css 规则
783 | * 避免使用 css 表达式
784 | * 选择器优化嵌套,尽量避免层级过深
785 | * 充分利用 css 继承属性,减少代码量
786 | * 抽象提取公共样式,减少代码量
787 | * 属性值为 0 时,不加单位
788 | * 属性值为小于 1 的小数时,省略小数点前面的 0
789 | * 使用 css 雪碧图
790 |
791 | ### 浏览器怎样解析 CSS 选择器(从左到右 or 从右到左)?
792 |
793 | * 按照从右到左的顺序解析。这样可以尽在筛选掉了大量的不匹配的叶子节点,提高解析性能。
794 |
795 | ### 在网页中的应该使用奇数还是偶数的字体?
796 |
797 | * 在网页中的应该使用“偶数”字体:
798 | * 偶数字号相对更容易和 web 设计的其他部分构成比例关系
799 | * 使用奇数号字体时文本段落无法对齐
800 | * 宋体的中文网页排布中使用最多的就是 12 和 14
801 |
802 | ### margin 和 padding 分别适合什么场景使用?
803 |
804 | * 需要在 border 外侧添加空白,且空白处不需要背景(色)时,使用 margin
805 | * 需要在 border 内测添加空白,且空白处需要背景(色)时,使用 padding
806 |
807 | ### 抽离样式模块怎么写?
808 |
809 | * CSS 可以拆分成两部分:公共 CSS 和 业务 CSS
810 | 1. 网站的配色、字体、交互提取出为公共 CSS。这部分 CSS 命名不应涉及具体的业务
811 | 2. 对于业务 CSS,建议有统一的命名,使用公用的前缀。可以参考面向对象的 CSS
812 |
813 | ### 元素竖向的百分比设定是相对于容器的高度吗?
814 |
815 | * 元素竖向的百分比设定是相对于容器的宽度,而不是高度
816 |
817 | ### 全屏滚动的原理是什么? 用到了 CSS 的那些属性?
818 |
819 | * 原理类似图片轮播原理,超出隐藏部分,滚动时显示
820 | * 可能用到的 CSS 属性:
821 |
822 | ```
823 | overflow: hidden;
824 | transform: translate(100%, 100%);
825 | display: none;
826 | ```
827 |
828 | ### 什么是响应式设计?响应式设计的基本原理是什么?如何兼容低版本的 IE?
829 |
830 | * 响应式设计就是网站能够兼容多个终端,而不是为每个终端做一个特定的版本
831 | * 基本原理是利用 CSS3 媒体查询,为不同尺寸的设备适配不同样式
832 | * 对于低版本的IE,可采用JS获取屏幕宽度,然后通过resize方法来实现兼容:
833 |
834 | ```Javascript
835 | $(window).resize(function () {
836 | screenRespond();
837 | });
838 | screenRespond();
839 |
840 | function screenRespond(){
841 | var screenWidth = $(window).width();
842 | if(screenWidth <= 1800){
843 | $("body").attr("class", "w1800");
844 | }
845 | if(screenWidth <= 1400){
846 | $("body").attr("class", "w1400");
847 | }
848 | if(screenWidth > 1800){
849 | $("body").attr("class", "");
850 | }
851 | }
852 | ```
853 |
854 | ### 什么是视差滚动效果,如何给每页做不同的动画?
855 |
856 | * 视差滚动是指多层背景以不同的速度移动,形成立体的运动效果,具有非常出色的视觉体验。
857 | 一般把网页解剖为:背景层、内容层和悬浮层。当滚动鼠标滚轮时,各图层以不同速度移动,形成视差的。
858 |
859 | * 实现原理:
860 | 1. 以 “页面滚动条” 作为 “视差动画进度条”
861 | 2. 以 “滚轮刻度” 当作 “动画帧度” 去播放动画的
862 | 3. 监听 mousewheel 事件,事件被触发即播放动画,实现“翻页”效果
863 |
864 | ### `
` 标签上四个伪类的执行顺序是怎么样的?
865 |
866 | * link > visited > hover > active
867 | - L-V-H-A love hate 用"喜欢"和"讨厌"两个词来方便记忆
868 |
869 | ### 伪元素和伪类的区别和作用?
870 |
871 | * 伪元素 -- 在内容元素的前后插入额外的元素或样式,但只在外部显示可见,不会在文档的源代码中找到它们。因此,称为“伪”元素。例如:
872 |
873 | ```Css
874 | p::before {content:"第一章:";}
875 | p::after {content:"Hot!";}
876 | p::first-line {background:red;}
877 | p::first-letter {font-size:30px;}
878 | ```
879 |
880 | * 伪类 -- 将特殊的效果添加到特定选择器上。它是已有元素上添加样式,不会产生新的元素。例如:
881 |
882 | ```Css
883 | a:hover {color: #FF00FF}
884 | p:first-child {color: red}
885 | ```
886 |
887 | ### `::before` 和 `:after` 中双冒号和单冒号有什么区别?
888 |
889 | * 在 CSS 中伪类一直用`:`表示,如 `:hover`, `:active` 等
890 | * 伪元素在 CSS1 中已存在,当时语法是用`:`表示,如 `:before` 和 `:after`
891 | * 后来在 CSS3 中修订,伪元素用 `::` 表示,如 `::before` 和 `::after`,以此区分伪元素和伪类
892 | * 由于低版本 IE 对双冒号不兼容,开发者为了兼容性各浏览器,继续使用 `:after` 这种老语法表示伪元素
893 | * 综上所述:`::before` 是 CSS3 中表示伪元素的新语法; `:after` 是 CSS1 中存在的、兼容 IE 的老语法
894 |
895 | ### 如何修改 Chrome 记住密码后自动填充表单的黄色背景?
896 |
897 | * 产生原因:由于 Chrome 默认会给自动填充的 input 表单加上 `input:-webkit-autofill` 私有属性造成的
898 |
899 | - 方案1 - 通过 form 标签 html 属性直接关闭了表单的自动填充:
900 |
901 | ```
902 | autocomplete="off"
903 | ```
904 |
905 | - 方案2 - 设置 css 样式:
906 |
907 | ```
908 | input:-webkit-autofill {
909 | background-color: transparent;
910 | }
911 | ```
912 |
913 | ### `input [type=search]` 搜索框右侧小图标如何美化?
914 |
915 |
916 | ```Css
917 | input[type="search"]::-webkit-search-cancel-button {
918 | -webkit-appearance: none;
919 | height: 15px;
920 | width: 15px;
921 | border-radius: 8px;
922 | background:url("images/searchicon.png") no-repeat 0 0;
923 | background-size: 15px 15px;
924 | }
925 | ```
926 |
927 | ### 如何设置网站图片文件点击时执行下载而非预览?
928 |
929 | `下载`
930 | `
下载`
931 |
932 | ### iOS safari 如何阻止“橡皮筋效果”?
933 |
934 | ```Javascript
935 | $(document).ready(function(){
936 | var stopScrolling = function(event) {
937 | event.preventDefault();
938 | }
939 | document.addEventListener('touchstart', stopScrolling, false);
940 | document.addEventListener('touchmove', stopScrolling, false);
941 | });
942 | ```
943 |
944 | ### 你对 line-height 是如何理解的?
945 |
946 | * line-height 指一行字的高度,包含了字间距,实际上是下一行基线到上一行基线距离
947 | * 如果一个标签没有定义 height 属性,那么其最终表现的高度是由 line-height 决定的
948 | * 一个容器没有设置高度,那么撑开容器高度的是 line-height 而不是容器内的文字内容
949 | * 把 line-height 值设置为 height 一样大小的值可以实现单行文字的垂直居中
950 | * line-height 和 height 都能撑开一个高度,height 会触发 hasLayout,而 line-height 不会
951 |
952 | ### line-height 三种赋值方式有何区别?(带单位、纯数字、百分比)
953 |
954 | * 带单位:px 是固定值,而 em 会参考父元素 font-size 值计算自身的行高
955 | * 纯数字:会把比例传递给后代。例如,父级行高为 1.5,子元素字体为 18px,则子元素行高为 1.5 * 18 = 27px
956 | * 百分比:将计算后的值传递给后代
957 |
958 | ### 怎么让 Chrome 支持小于 12px 的文字?
959 |
960 | ```Css
961 | .shrink{
962 | -webkit-transform: scale(0.8);
963 | -o-transform: scale(1);
964 | display:inline-block;
965 | }
966 | ```
967 |
968 | ### 让页面里的字体变清晰,变细用 CSS 怎么做?(IOS手机浏览器字体齿轮设置)
969 |
970 | ```Css
971 | .font1{
972 | -webkit-font-smoothing: antialiased;
973 | }
974 | ```
975 |
976 | ### font-style 属性 oblique 是什么意思?
977 |
978 | * `font-style: oblique;` 使没有 italic 属性的文字实现倾斜
979 |
980 | ### `position:fixed;` 在 android 下无效怎么处理?
981 |
982 | ```Html
983 |
985 | ```
986 |
987 | ### 如果需要手动写动画,你认为最小时间间隔是多久?
988 |
989 | * `16.7ms` 多数显示器默认频率是 `60Hz`,即 1 秒刷新 60 次,所以理论上最小间隔: `1s / 60 * 1000 = 16.7ms`
990 |
991 | ### `display: inline-block` 什么时候会显示间隙?
992 |
993 | * 相邻的 `inline-block` 元素之间有换行或空格分隔的情况下会产生间距
994 | * 非 `inline-block` 水平元素设置为 `inline-block` 也会有水平间距
995 | * 可以借助 `vertical-align:top;` 消除垂直间隙
996 | * 可以在父级加 `font-size:0;` 在子元素里设置需要的字体大小,消除垂直间隙
997 | * 把 li 标签写到同一行可以消除垂直间隙,但代码可读性差
998 |
999 | ### `overflow: scroll` 不能平滑滚动的问题怎么处理?
1000 |
1001 | * 监听滚轮事件,然后滚动到一定距离时用 jquery 的 animate 实现平滑效果。
1002 |
1003 | ### 一个高度自适应的 div,里面有两个 div,一个高度 100px,希望另一个填满剩下的高度
1004 |
1005 | * 方案1 -- calc 计算剩余高度:
1006 |
1007 | ```Css
1008 | .sub { height: calc(100%-100px); }
1009 | ```
1010 |
1011 | * 方案2 -- 绝对定位设置 `top` 和 `bottom `:
1012 |
1013 | ```Css
1014 | .container { position: relative; }
1015 | .sub { position: absolute; top: 100px; bottom: 0; }
1016 | ```
1017 |
1018 | * 方案3 -- Flexbox 设置元素`flex: 1;`:
1019 |
1020 | ```Css
1021 | .container { display: flex; flex-direction: column; }
1022 | .sub { flex: 1; }
1023 | ```
1024 |
1025 | ### png、jpg、gif 这些图片格式解释一下,分别什么时候用?有没有了解过 webp?
1026 |
1027 | ```
1028 | png 支持完全透明;不支持动画
1029 | jpg 支持上百万种颜色,不支持背景透明、动画
1030 | gif 支持背景透明、动画,只有 256 种颜色
1031 | webp 压缩体积小,加载速度快,但兼容性差
1032 |
1033 | gif 适用范围:动图、小图标
1034 | png 适用范围:小图标、平铺背景、屏幕截图
1035 | jpg 适用范围:自然风景照片、高清海报等照片
1036 | webp 目前只在 Chrome、Opera 和 Android Browser 可用
1037 | ```
1038 |
1039 | ### style 标签写在 body 结束标签后与 body 结束标签前有什么区别?
1040 |
1041 | * 标准做法是放在 head 标签区间,好处是保证网页主体加载时,样式已提交加载生效
1042 |
1043 | ### 什么是 CSS 预处理器 / 后处理器?
1044 |
1045 | * 预处理器用于将 Sass、less、Stylus 编译为静态 CSS
1046 | - 使 css 具备层级、mixin、变量、循环、函数等,极大方便了 UI 组件模块化开发
1047 |
1048 | * 后处理器(PostCSS),在完成的 CSS 样式表中,根据 CSS 规范进行处理
1049 | - 目前最常用于给 CSS 属性添加浏览器私有前缀,实现跨浏览器兼容性的问题
1050 |
--------------------------------------------------------------------------------
/JS 模块化编程.md:
--------------------------------------------------------------------------------
1 |
2 | # 模块导出函数 -- 不暴露私有成员
3 | --------------------------------------------------------------------------------
4 | var module1 = (function(){
5 | var _p = 0; // 私有属性
6 |
7 | function _method() { // 私有方法
8 | // ...
9 | };
10 |
11 | var mod = {}; // 模块
12 |
13 | mod.p1 = 1; // 共有属性
14 |
15 | mod.m1 = function(){ // 共有方法
16 | //...
17 | };
18 |
19 | mod.m2 = function(){ // 共有方法
20 | //...
21 | };
22 |
23 | return mod;
24 | })();
25 |
26 |
27 |
28 | # 放大模式 -- 继承并放大模块
29 | --------------------------------------------------------------------------------
30 | var module1 = (function (mod){
31 | mod.m3 = function () {
32 | //...
33 | };
34 | return mod;
35 | })(module1);
36 |
37 |
38 |
39 | # 宽放大模式 -- 异步执行脚本
40 | 在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。
41 | 如果采用“放大模式”写法,先执行的部分有可能加载一个不存在空对象。
42 | 这时就要采用"宽放大模式"。
43 | --------------------------------------------------------------------------------
44 | var module1 = (function(mod){
45 | //...
46 | return mod;
47 | })(window.module1 || {});
48 |
49 |
50 |
51 | # 子模块 -- 子模块拥有一切一般模块的进阶优势,包括了放大模式和私有化状态
52 | --------------------------------------------------------------------------------
53 | module1.sub = (function(){
54 | var mod = {};
55 | // ...
56 | return mod;
57 | }());
58 |
59 |
60 |
61 | # 全局变量导入 -- 显式地将其他变量输入模块
62 | 保证模块的独立性,使得模块之间的依赖关系清晰明显。
63 | --------------------------------------------------------------------------------
64 | var module1 = (function ($, YAHOO){
65 | //...
66 | })(jQuery, YAHOO);
67 |
68 |
69 |
70 | # 综合案例 -- 一个子模块动态地把自身加载到它的父模块(如果父模块不存在则创建它)
71 | 这种编程模式允许一整个复杂层级结构代码库通过子模块并行地完成加载。
72 | --------------------------------------------------------------------------------
73 | var util = (function (parent, $){
74 | var mod = parent.ajax = parent.ajax || {};
75 | mod.get = function (url, params, callback){
76 | // ok, so I'm cheating a bit :)
77 | return $.getJSON(url, params, callback);
78 | };
79 | // etc...
80 | return parent;
81 | }(util || {}, jQuery));
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/JS&&PHP常用函数对比.doc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JS&&PHP常用函数对比.doc
--------------------------------------------------------------------------------
/JavaScript 权威指南/1 数据类型/JavaScript 数据类型.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/1 数据类型/JavaScript 数据类型.pptx
--------------------------------------------------------------------------------
/JavaScript 权威指南/1 数据类型/判断两个数组是否相似.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/1 数据类型/判断两个数组是否相似.docx
--------------------------------------------------------------------------------
/JavaScript 权威指南/2 表达式和运算符/Boolean.js:
--------------------------------------------------------------------------------
1 | [] == false; // true
2 | !![] == true; // true
3 | ![] == []; // true
4 | [0] == false; // true
5 | !![0] == true; // true
6 | [1] == 1; // true
7 | [1] == 2; // false
8 | /**
9 | * 核心:== 的比较规则,最后都是转换成 Number 来比较
10 | * 以 [] == false 为例:
11 | * 1. 当 == 一边是布尔值,先把该布尔值转换为数字:
12 | * false --> 0
13 | * 2. 当 == 一边是字符串或数字,另一边是对象的时候(数组也是对象),先把对象值转换为原始值:
14 | * 2.1 对象调用 valueOf() 方法,如果返回的是原始值,则对象转为该值
15 | * [].valueOf() --> []
16 | * 2.2 如果 valueOf() 返回的不是原始值,则调用 toString() 方法,如果返回的是原始值,则对象转为该值
17 | * [].toString() --> ''
18 | * 2.3 如果 valueOf() 和 toString() 方法均没有返回原始值,则抛出 TypeError 异常
19 | * [] == 0 --> '' == 0
20 | * 3. 当 == 一边是字符串,一边是数字时,先把字符串转为数字,再进行比较:
21 | * '' == 0 --> 0 == 0 --> true
22 | *
23 | * 同理,[0] == false 转化如下:
24 | * 1. false --> 0
25 | * 2. [0] --> [0].valueOf() --> [0] --> [0].toString() --> '0'
26 | * 3. '0' == 0 --> 0 == 0 --> true
27 | */
28 |
29 |
30 | null == 0; // false
31 | null < 0; // false
32 | null >= 0; // true
33 |
34 | undefined > 0; // false
35 | undefined == 0; // false
36 | undefined < 0; // false
37 |
38 | 0 == undefined; //false
39 | 0 == null; //false
40 | false == null; //false
41 | false == undefined; //false
42 | '' == null; //false
43 | '' == undefined; //false
44 |
45 | null == undefined // true
46 | 10 + null; // 10
47 | 10 + undefined; // NaN
48 | null >= 0; // true
49 | undefined >= 0; // false
50 |
51 | /**
52 | * 比较相等性(==)时,不能将 null 和 undefined 转换成其他任何值
53 | * null < 0 操作,null 会转化为 number 类型,值为 0
54 | * >= 结果根据 < 结果推出(undefined 除外,会转化为 NaN)
55 | * >= 结果与 == 结果没有任何关系,>= 结果不是 > 和 == 的累和
56 | * undefined 只与 undefined 、null 具有 == 相等性,与其他任何值不等
57 | */
58 |
--------------------------------------------------------------------------------
/JavaScript 权威指南/2 表达式和运算符/JavaScript 表达式和运算符.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/2 表达式和运算符/JavaScript 表达式和运算符.pptx
--------------------------------------------------------------------------------
/JavaScript 权威指南/3 语句、严格模式/JavaScript 语句与严格模式.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/3 语句、严格模式/JavaScript 语句与严格模式.pptx
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/1 对象的概念与继承/prototype 属性与原型.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/1 对象的概念与继承/prototype 属性与原型.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/1 对象的概念与继承/prototype 属性与原型.txt:
--------------------------------------------------------------------------------
1 | prototype 属性与原型
2 |
3 | function Foo(){}
4 | 声明构造器 Foo 时, Foo 构造器默认会有 Foo.prototype 属性,Foo.prototype 属性本身也是对象,因此可以叫做“对象属性”
5 | 同时,Foo.prototype 默认又会有两个属性 Foo.prototype.constructor 和 Foo.prototype.__proto__
6 | 其中,Foo.prototype.__proto__ 就是 Foo.prototype 的原型
7 | Foo.prototype.__proto__ 又指向 Object 的 prototype 属性,即 Object.prototype
8 |
9 | Foo.prototype 的作用:
10 | 当使用 var foo = new Foo() 创建实例 foo 时,Foo.prototype 对象属性会作为实例 foo 的原型 obj.__proto__
11 |
12 | prototype 属性与原型 __proto__ 的关系:
13 | prototype 是构造器(函数对象)上面预置的对象属性,原型 __proto__ 是对象(实例对象)上的原型
14 | 对象的原型 __proto__ 指向其构造器的 prototype 属性
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/1 对象的概念与继承/基于原型的继承.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/1 对象的概念与继承/基于原型的继承.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/1 对象的概念与继承/对象的概念.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/1 对象的概念与继承/对象的概念.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/2 再谈原型链/再谈原型链.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/2 再谈原型链/再谈原型链.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/2 再谈原型链/原型链.txt:
--------------------------------------------------------------------------------
1 |
2 | var Foo = function(){
3 | this.x = 1;
4 | this.y = 2;
5 | };
6 | Foo.prototype.z = 3;
7 | var foo = new Foo();
8 |
9 |
10 | # 查看对象的原型:
11 | 1. 通过 foo.__proto__ // 非标准
12 | 2. 通过 Object.getPrototypeOf(foo) // ECMA5
13 |
14 |
15 | # 判断一个对象自身是否存在指定的属性(不去原型链上查找)
16 | foo.hasOwnProperty('x'); // true
17 | foo.hasOwnProperty('z'); // false
18 | 'z' in foo; // true
19 |
20 |
21 | # 判断一个该对象是否为另一个对象的实例
22 | foo instanceof Foo; // true
23 | foo instanceof Object; // true
24 |
25 |
26 | # 判断一个对象是否存在于另一个对象的原型链中
27 | Foo.prototype.isPrototypeOf(foo); // true
28 | Object.prototype.isPrototypeOf(foo); // true
29 | Array.prototype.isPrototypeOf(foo); // false
30 |
31 |
32 | # 判断给定的属性是否可以用 for...in 语句进行枚举
33 | foo.propertyIsEnumerable("x"); // true
34 | foo.propertyIsEnumerable("z"); // false
35 |
36 |
37 | # 原型链上的特殊情况:
38 | 1. Object.create(null); 创建的对象的原型 __proto__ 为 undefined
39 | 2. var foo = Foo.bind(null); 操作后,foo.prototype 为 undefined
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/3 修改 prototype/修改 prototype.txt:
--------------------------------------------------------------------------------
1 | 修改内置构造器的 prototype
2 |
3 | Object.prototype.x = 1; 在 for in 遍历时,会遍历到 x
4 | Object.defindProperty(Object.prototype, 'x', {value: 1, writable: true}); 在 for in 遍历时,则不会遍历到 x
5 |
6 | 原型链上的 prototype 属性值:
7 | 可配置(configuarable)、可枚举(enumerable)、可写(writable)
8 | 这些属性都默认都为 false
9 |
10 | object.hasOwnProperty(x) 可以判断某个属性是否属于对象本身的属性(用来排除原型链上的属性干扰)
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/3 修改 prototype/修改内置构造器的 prototype.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/3 修改 prototype/修改内置构造器的 prototype.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/3 修改 prototype/动态改变 prototype.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/3 修改 prototype/动态改变 prototype.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/4 实现继承的方法/实现继承的方式.txt:
--------------------------------------------------------------------------------
1 |
2 | 实现继承的方式1:
3 | if(! Object.create){
4 | Object.create = function(Person){
5 | function F(){}
6 | F.prototype = Person.prototype;
7 | return new F();
8 | };
9 | }
10 | Student.prototype = Object.create(Person.prototype);
11 | Student.prototype.constructor = Student;
12 |
13 | 实现继承的方式2:
14 | function extend(Child, Parent) {
15 | var F = function(){};
16 | F.prototype = Parent.prototype;
17 | Child.prototype = new F();
18 | Child.prototype.constructor = Child;
19 | Child.uber = Parent.prototype;
20 | }
21 |
22 | http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
23 | http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance_continued.html
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/defineProperty(ES5).jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/defineProperty(ES5).jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/抽象类.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/抽象类.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/模块化.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/模块化.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/模拟重载.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/模拟重载.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/调用子类方法.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/调用子类方法.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/链式调用.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/5 重载 模块化 链式调用/链式调用.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/6 探测器/Detector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/6 探测器/Detector.png
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/6 探测器/detector.js:
--------------------------------------------------------------------------------
1 | (function(global) {
2 | function DetectorBase(configs) {
3 | if (!this instanceof DetectorBase) {
4 | throw new Error('Do not invoke without new.');
5 | }
6 | this.configs = configs;
7 | this.analyze();
8 | }
9 |
10 | DetectorBase.prototype.detect = function() {
11 | throw new Error('Not implemented');
12 | };
13 |
14 | DetectorBase.prototype.analyze = function() {
15 | console.log('analyze...');
16 | this.data = "###data###";
17 | };
18 |
19 | function LinkDetector(links) {
20 | DetectorBase.apply(this, arguments);
21 | if (!this instanceof LinkDetector) {
22 | throw new Error('Do not invoke without new.');
23 | }
24 | this.links = links;
25 | }
26 |
27 | function ContainerDetector(containers) {
28 | DetectorBase.apply(this, arguments);
29 | if (!this instanceof ContainerDetector) {
30 | throw new Error('Do not invoke without new.');
31 | }
32 | this.containers = containers;
33 | }
34 |
35 | // inherit first
36 | inherit(LinkDetector, DetectorBase);
37 | inherit(ContainerDetector, DetectorBase);
38 |
39 | // expand later
40 | LinkDetector.prototype.detect = function() {
41 | console.log('Loading data:' + this.data);
42 | console.log('link detection started.');
43 | console.log('Scaning links:' + this.links);
44 | };
45 |
46 | ContainerDetector.prototype.detect = function() {
47 | console.log('Loading data:' + this.data);
48 | console.log('link detection started.');
49 | console.log('Scaning containers:' + this.containers);
50 | };
51 |
52 | // prevent from being altered
53 | Object.freeze(DetectorBase);
54 | Object.freeze(DetectorBase.prototype);
55 | Object.freeze(LinkDetector);
56 | Object.freeze(LinkDetector.prototype);
57 | Object.freeze(ContainerDetector);
58 | Object.freeze(ContainerDetector.prototype);
59 |
60 | // export to global object
61 | Object.defineProperties(global, {
62 | LinkDetector: { value: LinkDetector },
63 | ContainerDetector: { value: ContainerDetector },
64 | DetectorBase: { value: DetectorBase }
65 | });
66 |
67 | function inherit(subClass, superClass) {
68 | subClass.prototype = Object.create(superClass.prototype);
69 | subClass.prototype.constructor = subClass;
70 | }
71 | }(this));
72 |
73 |
74 | var containerDetector = new ContainerDetector('#nav #banner #sidebar');
75 | containerDetector.detect();
76 |
77 | var linkDetector = new LinkDetector('https://www.alipay.com https://www.aliyun.com');
78 | linkDetector.detect();
79 |
--------------------------------------------------------------------------------
/JavaScript 权威指南/4 对象/JavaScript 对象.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/4 对象/JavaScript 对象.pptx
--------------------------------------------------------------------------------
/JavaScript 权威指南/5 数组/JavaScript 数组.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/5 数组/JavaScript 数组.pptx
--------------------------------------------------------------------------------
/JavaScript 权威指南/6 函数(闭包)与作用域/JavaScript 函数.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/6 函数(闭包)与作用域/JavaScript 函数.pdf
--------------------------------------------------------------------------------
/JavaScript 权威指南/7 正则表达式/RegExp对象方法.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/7 正则表达式/RegExp对象方法.jpg
--------------------------------------------------------------------------------
/JavaScript 权威指南/7 正则表达式/字符串与正则相关的方法.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript 权威指南/7 正则表达式/字符串与正则相关的方法.jpg
--------------------------------------------------------------------------------
/JavaScript-Answers(2).md:
--------------------------------------------------------------------------------
1 |
2 | ## 前端框架相关
3 |
4 | ### 介绍单页面应用(SPA)?
5 |
6 | * 单页面应用(SPA) -- 核心是“无刷新”,整个 web 应用只在初始化时加载一个导航页及相关静态资源,之后的操作不再刷新页面
7 |
8 | * 目标:旨在用为用户提供了更接近原生移动 APP 或桌面应用程序的体验
9 |
10 | * 流程:第一次请求时,将导航页传输到客户端。之后,各功能页通过 JS 操作 URL HASH 或者 History API 进行路由切换,利用 ajax 或 fetch 异步获取 REST 接口数据(JSON)
11 |
12 | * 优点:交互流畅;前后端职责分离;减轻服务端压力;后端接口可复用
13 | * 缺点:初次加载相对耗时;前进、后退管理复杂;不利于 SEO
14 |
15 | ### 什么是“前端路由”? 什么时候适用“前端路由”? 有哪些优点和缺点?
16 |
17 | * 前端路由通过 URL HASH 和 History 来实现“无刷新”页面切换
18 | * 应用:前端路由主要适用于“前后端分离”的单页面应用(SPA)项目
19 | * 优点:用户体验好,交互流畅
20 | * 缺点:浏览器“前进”、“后退”会重新请求,无法合理利用缓存,无法记住之前滚动的位置
21 |
22 | ### JS 模块化开发方案有哪些?
23 |
24 | * 封装对象作为命名空间 -- 内部状态可以被外部改写
25 | * 立即执行函数(IIFE) -- 需要依赖多个 JS 文件,并且严格按顺序加载
26 | * 使用模块加载器 -- require.js, sea.js
27 | * 使用 ES6 模块
28 |
29 | ### 通行的 Javascript 模块的规范有哪些?
30 |
31 | * CommonJS -- 主要用在服务器端 node.js
32 |
33 | ```Javascript
34 | var math = require('./math');
35 | math.add(2,3);
36 | ```
37 |
38 | * AMD(异步模块定义) -- require.js
39 |
40 | ```Javascript
41 | require(['./math'], function (math) {
42 | math.add(2, 3);
43 | });
44 | ```
45 |
46 | * CMD(通用模块定义) -- sea.js
47 |
48 | ```Javascript
49 | var math = require('./math');
50 | math.add(2,3);
51 | ```
52 |
53 | * E6 模块
54 |
55 | ```Javascript
56 | import {math} from './math';
57 | math.add(2, 3);
58 | ```
59 |
60 | ### AMD 与 CMD 规范的区别?
61 |
62 | * 规范化产出:
63 | - AMD 由 RequireJS 推广产出(require.js)
64 | - CMD 由 SeaJS 推广产出(sea.js)
65 |
66 | * 模块的依赖:
67 | - AMD 提前执行,推崇依赖前置
68 | - CMD 延迟执行,推崇依赖就近
69 |
70 | * API 功能:
71 | - AMD 的 API 默认多职责(分为全局 require 和局部 require)
72 | - CMD 的 API 推崇职责单一纯粹(没有全局 require)
73 |
74 | * 模块定义规则:
75 |
76 | ```Javascript
77 | // AMD 默认一开始就载入全部依赖模块
78 | define(['./a', './b'], function(a, b) {
79 | a.doSomething();
80 | b.doSomething();
81 | });
82 |
83 | // CMD 依赖模块在用到时才就近载入
84 | define(function(require, exports, module) {
85 | var a = require('./a');
86 | a.doSomething();
87 | var b = require('./b');
88 | b.doSomething();
89 | });
90 | ```
91 |
92 | ### requireJS 的核心原理是什么?
93 |
94 | * 每个模块所依赖模块都会比本模块预先加载
95 |
96 | ### 介绍 Node.js 的优缺点? Node.js 的特点和适用场景?
97 |
98 | * Node.js的特点:单线程,非阻塞 I/O,事件驱动
99 | * Node.js的优点:擅长处理高并发;适合 I/O 密集型应用
100 | * Node.js的缺点:不适合 CPU 密集运算;不能充分利用多核CPU;可靠性低,某个环节出错会导致整个系统崩溃
101 | * Node.js 的适用场景:
102 | - RESTful API
103 | - 实时应用:在线聊天、图文直播
104 | - 工具类应用:前端部署(npm, gulp)
105 | - 表单收集:问卷系统
106 |
107 | ### 介绍 route, SSR, middleware, cluster, nodemon, pm2, node-inspector?
108 |
109 | * route 路由(用来保证用户界面与 URL 的同步)
110 | * SSR 服务器端渲染(在服务器端将模板和数据合成,返回最终的 HTML 页面)
111 | * middleware 中间件(过滤器 + 增强器)
112 | * cluster 多核处理模块(打破 Node.js 只支持单核 CPU 的限制)
113 | * nodemon 监控 Node.js 源代码(修改自动重启服务)
114 | * pm2 Node.js 进程管理器(0秒停机重载进程)
115 | * node-inspector Node.js 调试工具(在浏览器调试服务器端 Node.js 代码)
116 |
117 | ### 如何判断当前脚本运行在浏览器还是 node 环境中?
118 |
119 | * 区分全局环境下 this 的指向
120 | - 在 node 中 this 指向 global,而在浏览器中 this 指向 window
121 |
122 | ## 什么是 npm ?
123 |
124 | * npm(Node Package Manager) 是 Node.js 的模块管理和发布工具
125 |
126 | ### 什么是 WebKit ?
127 |
128 | * WebKit 是一个开源的浏览器内核,由渲染引擎(WebCore)和 JS 解释引擎(JSCore)组成
129 | * 通常所说的 WebKit 指的是 WebKit(WebCore),主要工作是进行 HTML/CSS 渲染
130 | * WebKit 一直是 Safari 和 Chrome 使用的浏览器内核,后来 Chrome 改用 Blink 内核
131 |
132 | ### 如何测试前端代码? 知道 Unit Test,BDD, TDD 么? 怎么测试你的前端工程(mocha, jasmin ...)?
133 |
134 | * 通过为前端代码编写单元测试(Unit Test)来测试前端代码
135 | * Unit Test:一段用于测试一个模块或接口是否能达到预期结果的代码
136 | * BDD:行为驱动开发 -- 业务需求描述产出产品代码的开发方法
137 | * TDD:测试驱动开发 -- 单元测试用例代码产出产品代码的开发方法
138 | * 单元测试框架示例:
139 |
140 | ```Javascript
141 | // mocha 示例
142 | describe('Test add', function() {
143 | it('1 + 2 = 3', function() {
144 | expect(add(1, 2)).to.be.equal(3);
145 | });
146 | });
147 |
148 | // jasmin 示例
149 | describe('Test add', function () {
150 | it('1 + 2 = 3', function () {
151 | expect(add(1, 2)).toEqual(3);
152 | });
153 | });
154 | ```
155 |
156 | ### 介绍你知道的前端模板引擎?
157 |
158 | * artTemplate, underscore, handlebars
159 |
160 | ### 什么是 Shim 和 Polyfill?区别是什么?目标是什么?
161 |
162 | * Shim(垫片):将一个新的 API 引入到一个旧的环境中,仅靠旧环境中已有的手段实现。Shim 有时候也称为 shiv,比如著名的 HTML5 兼容库 html5shiv
163 |
164 | * Polyfill:用在浏览器 API 上的 Shim。在旧版浏览器上复制标准 API(HTML5, CSS3) 的补充。通常的做法是先检查当前浏览器是否支持某个 API,如果不支持的话就加载对应的 polyfill 进行模拟增强
165 | 例如:geolocation polyfill 可以在 navigator 对象上添加全局的 geolocation 对象及相应成员
166 |
167 | * 区别:Polyfill 专门用来兼容浏览器,Shim 的范围更大些
168 |
169 | * 目标:一旦新的标准 API 被普遍的支持,可以方便地去掉 Shim(polyfill),无需做其他额外工作
170 |
171 | ### 什么是 Modernizr?Modernizr 工作原理?
172 |
173 | * Modernizr 是一个开源的 JavaScript 库,用于检测用户浏览器对 HTML5 与 CSS3 的支持情况
174 |
175 | ### 移动端最小触控区域是多大?
176 |
177 | * 44 * 44 px -- 参考《iOS 人机界面指南》
178 |
179 | ### 移动端的点击事件的延迟时间是多长,为什么会有延迟? 如何解决这个延时?
180 |
181 | * 移动端 click 有 300ms 延迟,浏览器为了区分“双击缩放”还是“单击”而设计。浏览器捕获第一次单击后,会先等待一段时间,如果在这段时间区间里用户未进行下一次点击,则浏览器会做单击事件的处理。如果这段时间里用户进行了第二次单击操作,则浏览器会做双击事件处理。这段时间就是上面提到的300毫秒延迟。
182 |
183 | * 解决方案:
184 | - 禁用缩放(对 safari 无效)
185 | - `
`
186 |
187 | - CSS touch-action (IE 私有特性,且仅 IE10+)
188 |
189 | ```css
190 | a, button {
191 | -ms-touch-action: manipulation; /* IE10 */
192 | touch-action: manipulation; /* IE11+ */
193 | }
194 | ```
195 |
196 | - 使用 Zepto 的 tap 事件(有点透 BUG)
197 |
198 | ```javascript
199 | $(document).on('tap', function(e){
200 | $("div.panel").hide();
201 | });
202 | ```
203 |
204 | - 使用 FastClick 插件(体积大[压缩后 8k])
205 |
206 | ```javascript
207 | if ('addEventListener' in document) {
208 | document.addEventListener('DOMContentLoaded', function() {
209 | FastClick.attach(document.body);
210 | }, false);
211 | }
212 | ```
213 |
214 | ### Zepto 的点透问题如何解决?
215 |
216 | * 点透问题的由来:
217 | - Zepto 定义 tap 事件用来消除移动端 click 事件 300ms 延迟现象时产生的新BUG
218 | * tap 实现原理:
219 | - 记录 touchstart 和 touchend 间隔和手指位置,两者变化较小且未触发 touchmove,即认为是 tap 操作
220 | * 点透问题表现:
221 | - 点击弹出层关闭按钮(绑定了tap事件)关闭弹出层时,会误触发按钮正下方内容的 click 事件
222 | * 点透问题原理:
223 | - 事件执行的顺序:touchstart > touchend > click,而 click 事件有 300ms 的延迟。执行 touchstart 300ms 后触发 click,此时弹出层已消失,click 就被派发到弹出层下面元素身上。
224 | * 点透问题解决:
225 | - 为弹出层加入(300ms+)过渡动画渐进消失
226 | - 改用 FastClick 插件
227 | - 升级 Zepto 最新版本(如果新版本修复了点透BUG)
228 |
229 | ### 如何实现页面操作不刷新整页,并且能在浏览器“前进”、“后退”时正确响应?
230 |
231 | * 方案一(HTML5 历史管理 History API):
232 | 1. 每次点击导航菜单,将 Ajax 请求的 URL 查询参数(?后面部分)追加到当前地址栏 URL 后面,并记录到浏览器历史
233 | - 通过 history.pushState 修改地址栏 URL,并记录浏览器历史
234 | 2. “前进”、“后退”操作时,根据地址栏 URL 查询参数变化,反向发起响应 Ajax 请求,实现无刷新效果
235 | - 通过 window.onpopstate 事件,监听浏览器的“前进”、“后退”操作
236 | 3. 页面首次载入的时候,如果没有查询地址或查询地址不匹配,则使用第一个导航菜单的 Ajax 地址的查询内容
237 | - 使用 history.replaceState 替换地址栏URL和浏览器历史,然后触发Ajax请求
238 |
239 | * 方案二(URL锚点变化):
240 | - window.location.hash 属性 + window.onhashchange 事件
241 |
242 | ### 什么是函数式编程?有什么特点和优点?
243 |
244 | * 函数式编程是一种"编程范式",主要思想是把"运算过程"尽量写成一系列嵌套的函数调用
245 | - 例如:var result = subtract(multiply(add(1,2), 3), 4);
246 |
247 | * 函数式编程的特点:
248 | - 函数是一等公民:函数可以作为变量的赋值、另一函数的参数、另一函数的返回值
249 | - 只用“表达式”,不用“语句”:要求每一步都是单纯的运算,都必须有返回值
250 | - 没有"副作用":所有功能只为返回一个新的值,不修改外部变量
251 | - 引用透明:运行不依赖于外部变量,只依赖于输入的参数
252 |
253 | * 函数式编程的优点:
254 | - 代码简洁,接近自然语言,易于理解
255 | - 便于维护,利于测试、除错、组合
256 | - 易于“并发编程“,不用担心一个线程的数据,被另一个线程修改
257 |
258 | ### 什么是函数柯里化(Currying)?
259 |
260 | * 柯里化:
261 | - 通常也称“部分求值”,含义是给函数分步传递参数,每次递参部分应用参数,并返回一个更具体的函数,继续接受剩余参数。期间会连续返回具体函数,直至返回最后结果。因此,函数柯里化是逐步传参,逐步缩小函数的适用范围,逐步求解的过程
262 | - Currying 的重要意义在于可以把函数完全变成「接受一个参数、返回一个值」的固定形式
263 |
264 | * 柯里化的作用:
265 | - 延迟计算;参数复用;动态创建函数
266 |
267 | * 柯里化的缺点:
268 | - 函数柯里化会产生开销(函数嵌套,比普通函数占更多内存),但性能瓶颈首先来自其它原因(DOM 操作等)
269 |
270 | ### 什么是面向切面编程(AOP)?
271 |
272 | * 面向切面的编程(AOP)简单理解:可以在不修改原有代码的情况下,动态添加新功能
273 |
274 | ### 什么是依赖注入?
275 |
276 | * 依赖注入,全称是“依赖注入到容器”, 容器(IOC 容器)是一个设计模式,它也是个对象,把某个类(不管有多少依赖关系)放入这个容器中,可以“解析”出这个类的实例
277 | * 依赖注入将各层的对象以“松耦合”的方式组织在一起,各层对象的调用完全面向接口。当系统重构的时候,代码的改写量将大大减少
278 |
279 | ### 面向对象设计的原则?
280 |
281 | * SRP(Single Responsibility) 单一职责原则 -- 每一个类应该专注于做一件事情
282 | * OCP(Open Close) 开放封闭原则 -- 面向扩展开放,面向修改关闭
283 | * LSP(Liskov Substitution) 里氏替换原则 -- 子类可以替换基类而不会出现错误和异常
284 | * DIP(Dependence Inversion) 依赖倒置原则 -- 实现尽量依赖抽象,不依赖具体实现
285 | * ISP(Interface Segregation) 接口隔离原则 -- 接口应该小而独立,而不是大而全面
286 |
287 | ### 设计模式:什么是 singleton, factory, strategy, decorator?
288 |
289 | * Singleton(单例) 一个类只有唯一实例,这个实例在整个程序中有一个全局的访问点
290 | * Factory (工厂) 解决实列化对象产生重复的问题
291 | * Strategy(策略) 将每一个算法封装起来,使它们还可以相互替换,让算法独立于使用
292 | * Observer(观察者) 多个观察者同时监听一个主体,当主体对象发生改变时,所有观察者都将得到通知
293 | * Prototype(原型) 一个完全初始化的实例,用于拷贝或者克隆
294 | * Adapter(适配器) 将不同类的接口进行匹配调整,尽管内部接口不兼容,不同的类还是可以协同工作
295 | * Proxy(代理模式) 一个充当过滤转发的对象用来代表一个真实的对象
296 | * Iterator(迭代器) 在不需要知道集合内部工作原理的情况下,顺序访问一个集合里面的元素
297 | * Chain of Responsibility(职责连) 处理请求组成的对象一条链,请求链中传递,直到有对象可以处理
298 |
299 | ### 什么是前端工程化?
300 |
301 | * 前端工程化就是把一整套前端工作流程使用工具自动化完成
302 | * 前端开发基本流程:
303 | - 项目初始化:yeoman, FIS
304 | - 引入依赖包:bower, npm
305 | - 模块化管理:npm, browserify, Webpack
306 | - 代码编译:babel, sass, less, stylus
307 | - 代码优化(压缩/合并):Gulp, Grunt
308 | - 代码检查:JSHint, ESLint
309 | - 代码测试:Mocha
310 | * 目前最知名的构建工具:Rollup, npm + Webpack, Gulp, Grunt
311 |
312 | ### 介绍 Yeoman 是什么?
313 |
314 | * Yeoman --前端开发脚手架工具,自动将最佳实践和工具整合起来构建项目骨架
315 | * Yeoman 其实是三类工具的合体,三类工具各自独立:
316 | - yo --- 脚手架,自动生成工具(相当于一个粘合剂,把 Yeoman 工具粘合在一起)
317 | - Grunt、gulp --- 自动化构建工具 (最初只有 grunt,之后加入了 gulp)
318 | - Bower、npm --- 包管理工具 (原来是 bower,之后加入了 npm)
319 |
320 | ### 介绍 Webpack 是什么?有什么优势?
321 |
322 | * Webpack 是一款[模块加载器]兼[打包工具],用于把各种静态资源(js/css/image 等)作为模块来使用
323 |
324 | * Webpack 的优势:
325 | - Webpack 同时支持 commonJS 和 AMD/CMD,方便代码迁移
326 | - 不仅仅能够模块化 JS ,还包括 CSS、Image 等
327 | - 能替代大部分 grunt/gulp 的工作,如打包、压缩混淆、图片 base64
328 | - 扩展性强,插件机制完善,特别是支持 React 热插拔的功能
329 |
330 | ### Webpack 1 / 2 / 3 的 区别
331 | * webpack2 相对 webpack1 的新增了新特性,需要处理配置文件语法兼容:
332 | - 增加对 ES6 模块的原生支持
333 | - 可以混用 ES2015 和 AMD 和 CommonJS
334 | - 支持 tree-shaking(减少打包后的体积)
335 | - 新增更多的 CLI 参数项
336 | - `-p` 指定当前的编译环境为生产环境(修改 process.env.NODE_ENV 为 "production")
337 | - 配置选项语法有较大修改,且不兼容之前版本
338 | - resolve(解析)配置
339 | - 取消了 `extensions ` 空字符串(表示导入文件无后缀名)
340 |
341 | - Webpack1
342 |
343 | ```
344 | resolve: {
345 | extensions: ['', '.js', '.css'],
346 | modulesDirectories: ['node_modules', 'src']
347 | }
348 | ```
349 |
350 | - Webpack2
351 |
352 | ```
353 | resolve: {
354 | extensions: ['.js', '.css'],
355 | modules: [
356 | path.resolve(__dirname, 'node_modules'),
357 | path.join(__dirname, './src')
358 | ]
359 | }
360 | ```
361 |
362 | - module(模块)配置
363 | - 外层 `loaders` 改为 `rules`
364 | - 内层 `loader` 改为 `use`
365 | - 所有插件必须加上 `-loader`,不再允许缩写
366 | - 不再支持使用`!`连接插件,改为数组形式
367 | - `json-loader` 模块已经移除,不再需要手动添加,webpack2 会自动处理
368 |
369 | - Webpack1
370 |
371 | ```
372 | module: {
373 | loaders: [{
374 | test: /\.(less|css)$/,
375 | loader: "style!css!less!postcss"
376 | }, {
377 | test: /\.json$/,
378 | loader: 'json'
379 | }]
380 | }
381 | ```
382 |
383 | - Webpack2
384 |
385 | ```
386 | module: {
387 | rules: [{
388 | test: /\.(less|css)$/,
389 | use: [
390 | "style-loader",
391 | "css-loader",
392 | "less-loader",
393 | "postcss-loader"
394 | ]
395 | }]
396 | };
397 | ```
398 |
399 | - plugins(插件)配置
400 | - 移除(内置)了 OccurenceOrderPlugin 模块、NoErrorsPlugin 模块
401 |
402 | * webpack3 几乎与 webpack2 完全兼容,新增新特性:
403 | - 加入 Scope Hoisting(作用域提升)
404 | - 之前版本将每个依赖都分别封装在一个闭包函数中来独立作用域。这些包装函数闭包函数降低了浏览器 JS 引擎解析速度
405 | - Webpack 团队参考 Closure Compiler 和 Rollup JS,将有联系的模块放到同一闭包函数中,从而减少闭包函数数量,使文件大小的少量精简,提高 JS 执行效率
406 | - 在 Webpack3 配置中加入`ModuleConcatenationPlugin`插件来启用作用域提升
407 |
408 | ```
409 | module.exports = {
410 | plugins: [
411 | new webpack.optimize.ModuleConcatenationPlugin()
412 | ]
413 | };
414 | ```
415 |
416 | - 加入 Magic Comments(魔法注解)
417 | - 在 Webpack2 中引入了 Code Splitting-Async 的新方法 import(),用于动态引入 ES Module,Webpack 将传入 import 方法的模块打包到一个单独的代码块(chunk),但是却不能像 require.ensure 一样,为生成的 chunk 指定chunkName。因此在 Webpack3 中提出了 Magic Comment 用于解决该问题
418 |
419 | ```
420 | import(/* webpackChunkName: "my-chunk-name" */ 'module');
421 | ```
422 |
423 | ### SPA 项目的 Webpack 配置优化
424 |
425 | 1. 分离第三方依赖
426 | - 在开发环境下, 通常会采取 HMR(模块热替换)模式来提高开发效率. 一般情况下, 我们只更改自身项目文件, 不会去更改第三方的依赖. 但 webpack 在 rebuild 的时候, 会 build 所有的依赖. 为减少 rebuild 的时间, 我们可以分离第三方依赖, 在项目启动之前, 将其单独打包和引入. 可以借助 DllPlugin 插件实现
427 | 2. 多进程构建项目
428 | - Webpack 的构建过程是单进程的, 利用 HappyPack 插件可让 loader 对文件进行多进程处理. HappyPack 会充分利用系统的资源来提升 Webpack 的构建效率. 此外, Happypack 会将每一个文件的编译进行缓存,使得 rebuild 更快
429 | 3. 提取公共的依赖模块
430 | - 在生产环境下, 利用 CommonsChunkPlugin 插件提取公共的依赖模块. 提取公共模块式, 不能简单的将`node_modules`下的所有模块都打包在一起, 应该分析业务依赖和路由, 尽可能将各个路由组件的公共依赖分别提取, 避免 vendor 包过于太大
431 | 4. 分离不同类型的静态文件
432 | - 在生产环境下, 应该将图片和 CSS 从 JS 中分离, 控制最终的 bundle 文件的大小. 可以使用 ExtractTextPlugin 来提取 CSS; 通过设置 url-loader 的 limit 字节参数, 小于 limit 则将图片转为 DataURl,大于 limit 则通过 file-loader 将图片拷贝到相应的路径. url-loader 内置了 file-loader, 不需要再单独安装 file-loader
433 |
434 | 5. 优化混淆/压缩速度
435 | - Webpack 提供的 UglifyJS 插件采用单线程压缩, 速度较慢. 可以使用 Parallel 插件(webpack-parallel-uglify-plugin)进行优化
436 |
437 | 6. Gzip 压缩
438 | - 在生产环境下, 如果想进一步减小 bundle 文件的大小, 可以使用 Gzip 压缩. 前端使用 compression-webpack-plugin 插件配置, 同时需要服务端开启 gzip 压缩支持
439 |
440 | 7. 按需加载组件
441 | - 在单页应用中, 一个应用可能会对应很多路由, 每个路由都会对应一个组件. 如果将这些组件全部放进一个 bundle, 会导致最终的 bundle 文件很大. 可以利用 Webpack 的 Code Splitting 功能(CommonsChunkPlugin 插件)将代码进行分割, 实现路由的按需加载
442 |
443 |
444 | ### Grunt / Gulp / Webpack / Rollup 比较
445 |
446 | * Grunt 是一套前端自动化工具,帮助处理反复重复的任务。一般用于:编译,压缩,合并文件,简单语法检查等
447 | * Gulp 是基于“流”的自动化构建工具,采用代码优于配置的策略,更易于学习和使用
448 | * Webpack 是模块化管理工具和打包工具。通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、AMD 模块、ES6 模块、CSS、图片等。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载
449 | * Webpack 的定位是模块打包器,而 Gulp/Grunt 属于构建工具。Webpack 可以代替 Gulp/Grunt 的一些功能,但不是一个职能的工具,可以配合使用
450 | * Rollup 是下一代 ES6 模块化工具,它最大的亮点是利用 ES6 模块设计,生成更简洁、更简单的代码。尽可能高效地构建出能够直接被其它 JavaScript 库引用的模块
451 | - 基于权衡,Rollup 目前还不支持代码拆分(Code Splitting)和模块的热更新(HMR)
452 | - 一般而言,对于应用使用 Webpack,对于类库使用 Rollup;需要代码拆分(Code Splitting),或者很多静态资源需要处理,再或者构建的项目需要引入很多 CommonJS 模块的依赖时,使用 webpack。代码库是基于 ES6 模块,而且希望代码能够被其他人直接使用,使用 Rollup
453 | - Rollup 与 Webpack 有这不同的用途,因此会共同存在,并相互支持
454 | - React 已经将构建工具从 Webpack 换成了 Rollup
455 |
456 | ### 介绍类库和框架的区别?
457 |
458 | * 类库是一些函数的集合,辅助开发者编写应用,起主导作用的是开发者的代码
459 | * 框架是已实现的特殊应用,开发者只需对它填充具体业务逻辑,起主导作用是框架
460 |
461 | ### 介绍什么是“单向绑定”和“双向绑定”,及各自的优缺点?
462 | * 单向绑定
463 | - 把 Model 绑定到 View,当我们用 JS 更新 Model 时,View 就会自动更新
464 | - 优点:所有状态变化都可以被记录、跟踪,源头易追溯。同时组件数据只有唯一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性
465 | - 缺点:代码量会相应的上升,数据的流转过程变长,从而出现很多类似的样板代码
466 | - Store.dispatch(action) => Reducer(state, action) => nextState
467 | * 双向绑定
468 | - 相对于“单向绑定”,如果用户更新 View,Model 的数据也会自动被更新(双向绑定 = 单向绑定 + UI事件监听)
469 | - 当用户填写表单时,更新了 View 的状态,如果此时也自动更新了 Model 的状态,就相当于把 Model 和 View 做了双向绑定
470 | - 优点:在表单交互较多的场景下,会简化大量业务无关的代码
471 | - 缺点:无法追踪局部状态的变化,潜在的行为太多,增加了 debug 错误的难度
472 |
473 | ### 什么是 MVC/MVP/MVVM/Flux?
474 |
475 | * MVC(Model-View-Controller)
476 | - V->C, C->M, M->V
477 | - 通信都是单向的;C 只起路由作用,业务逻辑都部署在 V
478 | - Backbone
479 |
480 | * MVP(Model-View-Presenter)
481 | - V<->P, P<->M
482 | - 通信都是双向的;V 和 M 不发生联系(通过P传);V 非常薄,逻辑都部署在 P
483 | - Riot.js
484 |
485 | * MVVM(Model-View-ViewModel)
486 | - V->VM, VM<->M
487 | - 采用双向数据绑定:View 和 ViewModel 的变动都会相互映射到对象上面
488 | - Angular
489 |
490 | * Flux(Dispatcher-Store-View)
491 | - Action->Dispatcher->Store->View, View->Action
492 | - Facebook 为了解决在 MVC 应用中碰到的工程性问题提出一个架构思想
493 | - 基于一个简单的原则:数据在应用中单向流动(单向数据流)
494 | - React(Flux 中 View,只关注表现层)
495 |
496 | ### 介绍一下 React 组件生命周期?
497 |
498 | * Mounting 初始化阶段
499 | * constructor
500 | * componentWillMount
501 | * render
502 | * componentDidCatch
503 | * componentDidMount
504 |
505 | * Updating 更新阶段
506 | * componentWillReceiveProps
507 | * shouldComponentUpdate
508 | * componentWillUpdate
509 | * render
510 | * componentDidCatch
511 | * componentDidUpdate
512 |
513 | * Unmounting 销毁阶段
514 | * componentWillUnmount
515 |
516 | ### React 16 有哪些更新
517 | * 对核心算法重新实现
518 | - 采用了全新的内部架构 "Fiber"
519 | - 提升复杂 React 应用的可响应性和性能
520 | * 重写服务器端渲染器(randerer)
521 | - 支持流(streaming),可以向客户端更快地发送字节,SSR 速度提高了三倍
522 | * 更好的错误处理机制
523 | - 新增错误处理生命周期函数:componentDidCatch(error, info)
524 | * 新增 Portals 函数
525 | - 可以将子节点渲染到父节点之外的 DOM 节点中:ReactDOM.createPortal(children, domNode)
526 | * 新增 render 返回类型:fragments 和 strings
527 | - `render() { return [
, ]; }`
528 | - `render() { return 'Hello React 16!; }`
529 | * 体积更加小巧
530 | - react + react-dom 相比以前版本减少了 32%(使用 Rollup 构建工具;去除了 React 属性的白名单列表)
531 | * 支持自定义 DOM 属性
532 | - 之前无法识别的 HTML 和 SVG 属性只能忽略,新版本中,可以将它们传递给 DOM 了
533 | * 更新了开源协议(MIT)
534 | - 改为更宽松的 MIT 协议,容易被社区接受
535 |
536 | ### 介绍一下 vue 组件生命周期?
537 |
538 | * beforeCreate 组件创建前
539 | * created 组件创建后
540 | * beforeMount 模板载入前
541 | * mounted 模板载入后
542 | * beforeUpdate 组件更新前
543 | * updated 组件更新后
544 | * beforeDestroy 组件销毁前
545 | * destroyed 组件销毁后
546 |
547 |
548 | ### Angular、React、Vue 的 比较
549 |
550 | * angular
551 | - AngularJS 是一套完整的框架,能够让程序员真正专注于业务逻辑
552 | - Angular 双向数据绑生产效率高,单向数据流更新数据要经过 action、dispatcher、reduce、view 四步,Angular 里一行代码即可
553 | - 双向数据绑定是一把双刃剑。随着组件增加,项目越来越复杂,双向数据绑定带来性能问题
554 |
555 | * react
556 | - 利用 jsx 创建虚拟 dom
557 | - 使用单向绑定,在大型应用中让数据流易于理解
558 | - 同时适用于 web 端和原生 app(React Native)
559 | - React 本身只是一个 view 层,想要一套完整的框架,还需要引入 Redux、route 等
560 |
561 | * vue
562 | - API 设计简单,语法简明,学习成本低
563 | - 构建方面不包含 路由 和 ajax 功能
564 | - 更快的渲染速度和更小的体积
565 |
566 |
567 | ### 在什么情况下你会优先选择使用 Class Component 而不是 Functional Component?
568 | * 在组件需要包含内部状态或者使用到生命周期函数的时候使用 Class Component ,否则使用函数式组件
569 |
570 | ### React 中调用 render 时机
1. 首次渲染(Initial Render)
2. 调用 this.setState
571 | - 并不是一次 setState 触发一次 render,React 可能会合并操作,再一次性进行 render
3. 父组件发生更新
572 | - 一般会引起 props 发生改变,但即使 props 没有改变或者父子组件之间没有数据交换,也会触发 render
4. 调用this.forceUpdate
### React 组件的优化思路
1. 使用 Stateless Component、PureComponent
2. shouldComponentUpdate 中避免不必要的更新
3. 避免在 render 子组件时重新生成属性相关的函数
4. 渲染列表组件正确标识 key
573 |
574 | ### Underscore 对哪些 JS 原生对象进行了扩展?提供了哪些好用的函数方法?
575 |
576 | * Underscore 没有扩展任何 JS 内置对象,只提供了一套函数式编程的实用功能库
577 | * Underscore 主要针对数组、对象、函数提供更方便的调用,弥补了部分 jQuery 没有实现的功能
578 |
579 | * Underscore 好用的函数方法(对应ES6):
580 |
581 | ```Javascipt
582 | _.each(array, iteratee) // array.forEach(iteratee)
583 | _.map(array, iteratee) // array.map(iteratee)
584 | _.find(array, predicate) // array.find(predicate)
585 | _.pluck(array, propertyName) // array.map(value => value[propertyName])
586 | _.contains(array, element) // array.includes(element)
587 | _.toArray(arguments) // Array.from(arguments)
588 | _.compact(array) // array.filter(x => !!x)
589 | _.uniq(array) // [...new Set(array)]
590 | _.indexOf(array, value) // array.indexOf(value)
591 | _.keys(object) // Object.keys(object)
592 | _.isArray(object) // Array.isArray(object)
593 | _.isFinite(object) // Number.isFinite(object)
594 | ```
595 |
596 | ### Backbone 是什么?
597 | * Backbone 是一个基于 jquery 和 underscore 的前端(MVC)框架
598 |
599 | ### Riot.js 是什么?
600 |
601 | * Riot.js 是一个轻量级(1Kb大小)的前端(MVP)框架。提供了模板引擎、路由、MVP模式
602 | * Riot.js 是目前存在的 JavaScript 模板引擎中速度最快的(比 Underscore 的模板快7倍)
603 |
604 | ### AngularJS 是什么?
605 |
606 | * AngularJS 是一个完善的前端 MVVM 框架,包含模板、数据双向绑定、路由、模块化、服务、依赖注入等
607 | * AngularJS 由 Google 维护,用来协助大型单一页面应用开发
608 |
609 | ### React 是什么?
610 |
611 | * React 不属于 MV* 系列的框架,作为构建用户界面的 JavaScript 库,侧重于 View 层
612 | * React 把每一个组件当成了一个状态机,组件内部通过 state 来维护组件状态的变化,当组件的状态发生变化时,React 通过虚拟 DOM 技术来增量并且高效的更新真实 DOM
613 |
614 | * React 主要的原理:
615 | - 虚拟 DOM + diff 算法 -> 不直接操作 DOM 对象
616 | - 组件 -> Virtual DOM 的节点
617 | - State 触发视图的渲染 -> 单向数据绑定
618 |
619 | * React 解决方案:React + Redux + react-router + Fetch + webpack
620 |
621 | ### 简单介绍 Redux 核心 API 及其工作流
622 |
623 | * Redux 核心 API
- Store -- 应用状态 state 的管理者,存储着整个应用 state的对象
- Action -- Action -- 每次数据改变的源头,包含 type 属性,type 是实现用户行为追踪的关键
- Reducer -- 根据 action.type 更新 state 的纯函数
* Redux 工作流:
1. 通过Redux.createStore(reducer) 生成 Store
2. 通过 Store.getState() 获取 state
3. 通过 Store.dispatch(action) 触发 Reducer
4. Reducer 根据 action.type 更新 state,并返回最新的 nextState
624 |
625 | ### 简单介绍 Redux 三大原则
626 |
627 | * 单一数据源
628 | - 整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中
629 | * State 是只读的
630 | - 唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象
631 | * 使用纯函数来执行修改
632 | - 为了描述 action 如何改变 state tree,需要编写 reducers 纯函数
633 |
634 | ### react-router 路由系统的实现原理?
635 |
636 | * 实现原理:location 与 components 之间的同步
637 | - 路由的职责是保证 UI 和 URL 的同步
638 | - 在 react-router 中,URL 对应 Location 对象,react components 决定 UI
639 | - 因此,路由在 react-router 中就转变成 location 与 components 之间的同步
640 |
641 | ### Meteor 是什么?
642 |
643 | * Meteor 是一个全栈开发框架,基础构架是 Node.JS + MongoDB,并把延伸到了浏览器端
644 | * Meteor 统一了服务器端和客户端的数据访问,使开发者可以轻松完成全栈式开发工作
645 |
646 | ### jQuery 的实现原理?
647 |
648 | * jQuery 利用 JS 函数作用域的特性,用立即执行表达式包裹,解决了命名空间和变量污染问题
649 |
650 | ```Javascript
651 | (function(window, undefined) {})(window);
652 | ```
653 |
654 | * 在闭包中把 jQuery 和 $ 绑定到 window 上,从而把 jQuery 和 $ 暴露为全局变量
655 |
656 | ```Javascript
657 | window.jQuery = window.$ = jQuery;
658 | ```
659 |
660 |
661 | ### jQuery.fn 的 init 方法返回的 this 指的是什么对象? 为什么要返回 this?
662 |
663 | * jQuery.fn 的 init 方法 返回的 this 就是 jQuery 对象
664 | * 用户使用 jQuery() 或 $() 即可初始化 jQuery 对象,不需要动态的去调用 init 方法
665 |
666 | ### jQuery.extend 与 jQuery.fn.extend 的区别?
667 |
668 | * $.fn.extend() 和 $.extend() 都是 jQuery 为扩展插件提拱了两个方法
669 |
670 | * `$.extend(object);` // 添加“工具函数”(静态方法),通过 `$.foo()` 调用
671 |
672 | ```Javascript
673 | $.extend({
674 | min: function(a, b) { return a < b ? a : b; },
675 | max: function(a, b) { return a > b ? a : b; }
676 | });
677 | $.min(2, 3); // 2
678 | $.max(4, 5); // 5
679 | ```
680 |
681 | * $.extend([true,] obj, obj11[, obj2]); // 属性拷贝
682 |
683 | ```Javascript
684 | var settings = {validate: false, limit: 5};
685 | var options = {validate: true, name: 'bar'};
686 | $.extend(settings, options); // 注意:不支持第一个参数传 false
687 | //-> settings == {validate: true, limit: 5, name: 'bar'}
688 | ```
689 |
690 | * $.fn.extend(json); // 添加“成员函数”(实例方法),通过 `$(el).foo()` 调用
691 |
692 | ```Javascript
693 | $.fn.extend({
694 | alertValue: function() {
695 | $(this).click(function(){
696 | alert($(this).val());
697 | });
698 | }
699 | });
700 |
701 | $("#email").alertValue();
702 | ```
703 |
704 | ### jQuery 的属性拷贝(extend)的实现原理是什么,如何实现深拷贝?
705 |
706 | * 浅拷贝(只复制一份原始对象的引用)
707 |
708 | `var newObject = $.extend({}, oldObject);`
709 |
710 | * 深拷贝(将对象属性所引用的对象进行进行递归拷贝)
711 |
712 | `var newObject = $.extend(true, {}, oldObject);`
713 |
714 | ### jQuery 的队列是如何实现的?队列可以用在哪些地方?
715 |
716 | * jQuery 核心中有一组队列控制方法,由 queue()/dequeue()/clearQueue() 三个方法组成
717 | * 主要应用于 animate(),ajax 等其他要按时间顺序执行的事件中
718 |
719 | ```Javascript
720 | var fn1 = function () {console.log('事件1');}
721 | var fn2 = function () {console.log('事件2');}
722 | var fn3 = function () {console.log('事件3');}
723 | var fn4 = function () {console.log('事件4');}
724 | ```
725 |
726 | // 入栈队列事件
727 | ```Javascript
728 | $('#box').queue("queue1", fn1); // push fn1 to queue1
729 | $('#box').queue("queue1", fn2); // push fn2 to queue1
730 | ```
731 |
732 | // 替换队列事件
733 | ```Javascript
734 | $('#box').queue("queue1", []); // delete queue1 with empty array
735 | $('#box').queue("queue1", [fn3, fn4]); // replace queue1
736 | ```
737 |
738 | // 获取队列事件(返回一个函数数组)
739 | ```Javascript
740 | $('#box').queue("queue1"); // [fn3, fn4]
741 | ```
742 |
743 | // 出栈队列事件并执行
744 | ```Javascript
745 | $('#box').dequeue("queue1"); // return fn3 and do fn3
746 | $('#box').dequeue("queue1"); // return fn4 and do fn4
747 | ```
748 |
749 | // 清空整个队列
750 | ```Javascript
751 | $('#box').clearQueue("queue1"); // delete queue1 with clearQueue
752 | ```
753 |
754 | ### jQuery 中的 bind(), live(), delegate(), on()的区别?
755 |
756 | * bind 直接绑定事件到在目标元素上
757 | * live 通过冒泡传播事件,默认冒泡到 document 上,支持动态数据
758 | * delegate 更精确的小范围使用事件代理,性能优于 live
759 | * on 1.9 及之后版本整合了之前的三种方式的新事件绑定机制
760 |
761 | ### 是否知道自定义事件?jQuery 里的 fire 函数什么时候用?
762 |
763 | * 事件即“发布/订阅”模式,自定义事件即“消息发布”,事件的监听即“消息订阅”
764 |
765 | * 原生 JS 自定义事件,示例:
766 |
767 | ```javascript
768 | document.createEvent(type); // 创建事件
769 | event.initEvent(eventType, canBubble, prevent); // 初始化事件
770 | target.addEventListener('customEvent', handler, false); // 监听事件
771 | target.dispatchEvent(customEvent); // 触发事件
772 | ```
773 |
774 | * jQuery 里的 fire 函数用于调用 jQuery 自定义事件列表中的事件
775 |
776 | ### jQuery 通过哪个方法和 Sizzle 选择器结合的?
777 |
778 | * Sizzle 选择器采取 Right To Left 的匹配模式,先搜寻所有匹配标签,再判断它的父节点
779 | * jQuery 通过 $(selecter).find(selecter); 和 Sizzle 选择器结合
780 |
781 | ### jQuery 中如何将数组转化为 JSON 字符串,然后再转化回来?
782 |
783 | * 通过原生 JSON.stringify/JSON.parse 扩展 jQuery 实现
784 |
785 | ```javascript
786 | $.array2json = function(array) {
787 | return JSON.stringify(array);
788 | }
789 | ```
790 |
791 | ```javascript
792 | $.json2array = function(array) {
793 | // $.parseJSON(array); // 3.0 开始,已过时
794 | return JSON.parse(array);
795 | }
796 | ```
797 |
798 | // 调用
799 | ```javascript
800 | var json = $.array2json(['a', 'b', 'c']);
801 | var array = $.json2array(json);
802 | ```
803 |
804 | ### jQuery 一个对象可以同时绑定多个事件,如何实现的?
805 |
806 | ```javascript
807 | $("#btn").on("mouseover mouseout", fn);
808 | ```
809 |
810 | ```javascript
811 | $("#btn").on({
812 | mouseover: fn1,
813 | mouseout: fn2,
814 | click: fn3
815 | });
816 | ```
817 |
818 | ### 针对 jQuery 的优化方法?
819 |
820 | * 缓存频繁操作 DOM 对象
821 | * 尽量使用 id 选择器代替 class 选择器
822 | * 总是从 #id 选择器来继承
823 | * 尽量使用链式操作
824 | * 使用时间委托 on 绑定事件
825 | * 采用 jQuery 的内部函数 data() 来存储数据
826 | * 使用最新版本的 jQuery
827 |
828 | ### jQuery 的 slideUp 动画,当鼠标快速连续触发, 动画会滞后反复执行,该如何处理呢?
829 |
830 | * 在触发元素上的事件设置为延迟处理:使用 JS 原生 setTimeout 方法
831 | * 在触发元素的事件时预先停止所有的动画,再执行相应的动画事件:$('.tab').stop().slideUp();
832 |
833 | ### jQuery UI 如何自定义组件?
834 |
835 | 通过向 $.widget() 传递组件名称和一个原型对象来完成
836 |
837 | `$.widget("ns.widgetName", [baseWidget], widgetPrototype);`
838 |
839 | ### jQuery 与 jQuery UI、jQuery Mobile 区别?
840 |
841 | * jQuery 是 JS 库,兼容各种 PC 浏览器,主要应用是更方便地处理 DOM、事件、动画、AJAX
842 | * jQuery UI 是建立在 jQuery 库上的一组用户界面交互、特效、小部件及主题
843 | * jQuery Mobile 以 jQuery 为基础,用于创建“移动 Web 应用”的框架
844 |
845 | ### jQuery 和 Zepto 的区别? 各自的使用场景?
846 |
847 | * jQuery 主要目标是 PC 的网页中,兼容全部主流浏览器。在移动设备方面,单独推出 jQuery Mobile
848 | * Zepto 从一开始就定位移动设备,相对更轻量级。它的 API 基本兼容 jQuery,但对 PC 浏览器兼容不理想
849 |
850 | ## 其他问题
851 |
852 | ### 页面重构怎么操作?
853 |
854 | * 网站重构:不改变 UI 的情况下,对网站进行优化,在扩展的同时保持一致的 UI
855 |
856 | * 页面重构可以考虑的方面:
857 | - 升级第三方依赖
858 | - 使用 HTML5、CSS3、ES6+ 新特性
859 | - 加入响应式布局
860 | - 统一代码风格规范
861 | - 减少代码间的耦合
862 | - 压缩/合并静态资源
863 | - 程序的性能优化
864 | - 采用 CDN 来加速资源加载
865 | - 对于 JS DOM 的优化
866 | - HTTP 服务器的文件缓存
867 |
868 | ### 列举 IE 与其他浏览器不一样的特性?
869 |
870 | * IE 的渲染引擎是 Trident 与 W3C 标准差异较大:例如盒子模型的怪异模式
871 | * JS 方面有很多独立的方法,例如事件处理不同:绑定/删除事件,阻止冒泡,阻止默认事件等
872 | * CSS 方面也有自己独有的处理方式,例如设置透明,低版本 IE 中使用滤镜的方式
873 |
874 | ### 什么叫优雅降级和渐进增强?
875 |
876 | * 优雅降级:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容
877 | * 渐进增强:针对低版本浏览器构建页面,然后再针对高级浏览器进行效果、交互等用户体验的改进
878 |
879 | ### 请你谈谈 Cookie 的弊端?
880 |
881 | * 同一的域名下最多生成的 cookie 个数有限制
882 | * IE 和 Opera 会清理近期最少使用的 cookie,Firefox 会随机清理 cookie
883 | * cookie 的最大约为 4096 字节,为了兼容性一般设置不超过 4095 字节
884 | * 如果 cookie 被人拦截了,就可以取得所有的 session 信息
885 |
886 | ### 是否了解公钥加密和私钥加密?
887 |
888 | * 私钥用于对数据进行签名,公钥用于对签名进行验证
889 | * 网站在浏览器端用公钥加密敏感数据,然后在服务器端再用私钥解密
890 |
891 | ### Web 应用从服务器主动推送 Data 到客户端有那些方式?
892 |
893 | * AJAX 轮询
894 | * html5 服务器推送事件
895 |
896 | `(new EventSource(SERVER_URL)).addEventListener("message", callback);`
897 |
898 | * html5 Websocket
899 |
900 | `(new WebSocket(SERVER_URL)).addEventListener("message", callback);`
901 |
902 |
903 | ### http状态码有那些?分别代表是什么意思?
904 | * 1XX(信息类):表示接收到请求并且继续处理
905 | * 2XX(响应成功):表示动作被成功接收、理解和接受
906 | - 200 OK 请求成功返回
907 | - 201 CREATED 用户新建或修改数据成功
908 | - 202 Accepted 请求已经进入后台排队(异步任务)
909 | - 204 NO CONTENT 用户删除数据成功
910 | * 3XX(重定向类):为了完成指定的动作,必须接受进一步处理
911 | - 301 Moved Permanently 页面重定向
912 | - 302 Temporarily Moved 暂时重定向
913 | - 304 Not Modified 缓存可以继续使用
914 | * 4XX(客户端错误类):请求包含错误语法或不能正确执行
915 | - 400 Bad Request 请求出现语法错误
916 | - 401 Unauthorized 未经授权访问
917 | - 403 Forbidden 资源禁止访问
918 | - 404 Not Found 未找到指定位置的资源
919 | - 406 Not Acceptable 用户请求的数据格式不存在
920 | - 410 Gone 用户请求的资源被永久删除,且不会再得到的
921 | * 5XX(服务端错误类):服务器不能正确执行一个正确的请求
922 | - 500 Internal Server Error 服务器内部错误
923 | - 501 Not Implemented 服务器不支持请求类型
924 | - 502 Bad Gateway 网关错误
925 | - 503 Service Unavailable 服务器不可用
926 | - 504 Gateway Time-out 网关超时
927 |
928 | ### 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?
929 |
930 | 1. DNS 查询(浏览器查找域名对应的 IP 地址)
931 | - DNS 查询流程:浏览器缓存 -> 系统缓存 -> 路由器缓存 -> ISP DNS 缓存 -> 根域名服务器
932 | 2. 浏览器向 Web 服务器发送一个 HTTP 请求(TCP三次握手)
933 | 3. 服务器 301 重定向(从 http://example.com 重定向到 http://www.example.com)
934 | 4. 浏览器跟踪重定向地址,请求另一个带 www 的网址
935 | 5. 服务器处理请求(通过路由读取资源)
936 | 6. 服务器返回一个 HTTP 响应(报头中把 Content-type 设置为 'text/html')
937 | 7. 浏览器解析 DOM 树构
938 | 8. 浏览器发送请求获取嵌在 HTML 中的资源(如图片、音频、视频、CSS、JS等)
939 | 9. 浏览器显示完成页面
940 | 10. 浏览器发送异步请求
941 |
942 | ### 如何看待 Web App/hybrid App/Native App?(移动端前端 和 Web 前端区别?)
943 |
944 | * Web App(HTML5):采用 HTML5 开发,运行在浏览器中的应用,不需要下载安装
945 | - 优点:开发成本低,迭代更新容易,不需用户升级,跨多个平台和终端
946 | - 缺点:消息推送不够及时,图形和动画效果较差,功能使用限制(相机、GPS等)
947 |
948 | * Hybrid App(混合开发):UI WebView,需要下载安装
949 | - 优点:接近 Native App 的体验,部分支持离线功能
950 | - 缺点:性能速度较慢,未知的部署时间,受限于技术尚不成熟
951 |
952 | * Native App(原生开发):依托于操作系统,有很强的交互,需要用户下载安装使用
953 | - 优点:用户体验完美,支持离线工作,可访问本地资源(通讯录,相册)
954 | - 缺点:开发成本高(多系统),开发成本高(版本更新),需要应用商店的审核
955 |
956 | ### Web 前端开发的注意事项?
957 |
958 | * 特别设置 meta 标签 viewport
959 | * 百分比布局宽度,结合 box-sizing: border-box;
960 | * 使用 rem 作为计算单位。rem 只参照跟节点 html 的字体大小计算
961 | * 使用 css3 新特性。弹性盒模型、多列布局、媒体查询等
962 | * 多机型、多尺寸、多系统覆盖测试
963 |
964 | ### 在设计 Web APP 时,应当遵循以下几点:
965 |
966 | * 简化不重要的动画/动效/图形文字样式
967 | * 少用手势,避免与浏览器手势冲突
968 | * 减少页面内容,页面跳转次数,尽量在当前页面显示
969 | * 增强 Loading 趣味性,增强页面主次关系
970 |
971 | ### 平时如何管理你的项目?
972 |
973 | * 严格执行约定的代码规范
974 | * 规定全局样式、公共脚本
975 | * 严格要求代码注释(html/js/css)
976 | * 严格要求静态资源存放路径
977 | * Git 提交必须填写说明
978 |
979 | ### 如何设计突发大规模并发架构?
980 |
981 | * 及时响应(NoSQL缓存)
982 | * 数据安全(数据备份)
983 | * 负载均衡
984 |
985 | ### 如何做 SEO 优化?
986 |
987 | * 标题与关键词
988 | - 设置有吸引力切合实际的标题,标题中要包含所做的关键词
989 | * 网站结构目录
990 | - 最好不要超过三级,每级有“面包屑导航”,使网站成树状结构分布
991 | * 页面元素
992 | - 给图片标注"Alt"可以让搜索引擎更友好的收录
993 | * 网站内容
994 | - 每个月每天有规律的更新网站的内容,会使搜索引擎更加喜欢
995 | * 友情链接
996 | - 对方一定要是正规网站,每天有专业的团队或者个人维护更新
997 | * 内链的布置
998 | - 使网站形成类似蜘蛛网的结构,不会出现单独连接的页面或链接
999 | * 流量分析
1000 | - 通过统计工具(百度统计,CNZZ)分析流量来源,指导下一步的 SEO
1001 |
1002 | ### 移动端(Android、IOS)怎么做好用户体验?
1003 |
1004 | * 清晰的视觉纵线
1005 | * 信息的分组、极致的减法
1006 | * 利用选择代替输入
1007 | * 标签及文字的排布方式
1008 | * 依靠明文确认密码
1009 | * 合理的键盘利用
1010 |
1011 | ### 前端页面有哪三层构成,分别是什么?作用是什么?
1012 |
1013 | * 结构层:由 (X)HTML 标记语言负责,解决页面“内容是什么”的问题
1014 | * 表示层:由 CSS 负责,解决页面“如何显示内容”的问题
1015 | * 行为层:由 JS 脚本负责,解决页面上“内容应该如何对事件作出反应”的问题
1016 |
1017 | ### 是否了解 Web 注入攻击(最常见 XSS 和 CSRF)?
1018 |
1019 | * XSS(Cross Site Script),跨站脚本攻击
1020 | - 攻击者在页面里插入恶意代码,当用户浏览该页之时,执行嵌入的恶意代码达到攻击目的
1021 | - 防御措施
1022 | - 过滤转义输入输出
1023 | - 避免使用 eval、new Function 等执行字符串的方法,除非确定字符串和用户输入无关
1024 | - 设置 Cookie 为 HttpOnly,禁止 JavaScript 操作 Cookie
1025 |
1026 | * CSRF(Cross Site Request Forgery),跨站点请求伪造
1027 | - 伪造合法请求,让用户在不知情的情况下以登录的身份访问,利用用户信任达到攻击目的
1028 | - 防御措施
1029 | - 检测 http referer 是否是同域名
1030 | - 避免登录的 session 长时间存储在客户端中
1031 | - 关键请求使用验证码或者 token 机制验证
1032 |
1033 |
1034 | ### 如何防范 Web 前端攻击?
1035 |
1036 | * 不要信任任何外部传入的数据
1037 | - 针对用户输入作相关的格式检查、过滤等操作
1038 |
1039 | * 不要信任任何第三方数据传入
1040 | - 使用 CORS(跨域资源共享),设置 Access-Control-Allow-Origin
1041 |
1042 | * 更安全地使用 Cookie
1043 | - 设置 Cookie 为 HttpOnly,禁止 JavaScript 操作 Cookie
1044 |
1045 | * 防止网页被其他网站内嵌为 iframe
1046 | - 服务器端设置 X-Frame-Options 响应头,防止页面被内嵌
1047 |
1048 |
1049 | ### 线程与进程的区别?
1050 |
1051 | * 一个程序至少有一个进程,一个进程至少有一个线程
1052 | * 线程的划分尺度小于进程,使得多线程程序的并发性高
1053 | * 进程在执行过程中拥有独立的内存单元,而多个线程共享内存
1054 | * 线程不能够独立执行,必须应用程序提供多个线程执行控制
1055 |
1056 | ### git 命令,如何批量删除分支
1057 |
1058 | * 从分支列表中匹配到指定分支,然后一个一个(分成小块)传递给删除分支的命令,最后进行删除
1059 |
1060 | `git branch |grep 'branchName' |xargs git branch -D`
1061 |
1062 | ### 如何进行前端错误监控,跨域 js 运行错误如何捕获,上报错误的基本原理?
1063 |
1064 | * 前端错误主要分为“即时运行错误(代码错误)”和“资源加载错误”
1065 | * 即时运行错误的捕获方式:
1066 | - `try-catch`
1067 | - `window.onerror`
1068 | * 资源加载错误:
1069 | - object.onerror
1070 | - `img.onerror`
1071 | - `script.onerror`
1072 | - `window.performance.getEntries()`
1073 |
1074 | * 跨域的 js 运行错误捕获
1075 | - 设置 script 标签 crossorigin 属性 `crossorigin="anonymous"`
1076 | - 设置 js 资源响应头 `Access-Control-Allow-Orgin:*`
1077 |
1078 | * 上报错误的基本原理
1079 | - 采用 Ajax 方式上报
1080 | - 利用 Image 对象上报
1081 |
1082 | ### 如何快速合并雪碧图
1083 | * Gulp:gulp-css-spriter
1084 | * webpack:optimize-css-assets-webpack-plugin
1085 | * 在线工具 Go!Png
1086 |
1087 | ### npm 依赖包版本号 ~ 和 ^ 的区别及注意事项
* ~ 会匹配最近的小版本依赖包,比如 ~1.2.3 会匹配所有 1.2.x 版本,但是不包括 1.3.0
* ^ 会匹配最新的大版本依赖包,比如 ^1.2.3 会匹配所有 1.x.x 的包,包括 1.3.0
* 需要注意 ^ 版本更新比较大,可能会造成项目现有代码不兼容
* 建议使用 ~ 来标记版本号,这样既能保证项目不会出现大的问题,也能保证依赖包中的小 bug 可以得到修复
1088 | ### HTTPS 的握手过程
1089 | 1. 浏览器将自己支持的一套加密规则发送给服务器
1090 | 2. 服务器从中选出一组加密算法与 HASH 算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息
1091 | 3. 浏览器获得网站证书之后要做以下工作:
1092 | - 验证证书的合法
1093 | - 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密
1094 | - 使用约定好的 HASH 算法计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给服务器
1095 | 4. 网站接收浏览器发来的数据之后要做以下的操作:
1096 | - 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证 HASH 是否与浏览器发来的一致
1097 | - 使用密码加密一段握手消息,发送给浏览器
1098 | 5. 浏览器解密并计算握手消息的 HASH,如果与服务端发来的 HASH 一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密
1099 |
1100 |
1101 | ## 其他参考
1102 |
1103 | > [我遇到的前端面试题2017](http://www.imooc.com/article/20319)
1104 |
1105 | > [2017年第一波 JS 前端面试题](https://zhuanlan.zhihu.com/p/25424194)
1106 |
1107 | > [2017前端面试题及答案总结(一)](http://blog.csdn.net/u013594331/article/details/77885391)
1108 |
1109 | > [2017前端面试题及答案总结(二)](http://blog.csdn.net/u013594331/article/details/77925881)
1110 |
1111 |
--------------------------------------------------------------------------------
/JavaScript-Answers(3).md:
--------------------------------------------------------------------------------
1 |
2 | ## 编程能力
3 |
4 | - 实现一个函数 clone(),可以对 JavaScript 中的 5 种主要的数据类型(Number、String、Object、Array、Boolean)进行值复制
5 |
6 | ```JavaScript
7 | function clone(obj) {
8 | // 判断是否对象
9 | var isObject = obj => typeof obj === 'object' && typeof obj !== 'null';
10 | //不是对象,直接把值返回
11 | if (!isObject(obj)) {
12 | return obj;
13 | }
14 | // 区分是数组还是对象,创建空的数组或对象
15 | var isArray = obj => Object.prototype.toString.call(obj).slice(8, -1) === "Array";
16 | var o = isArray(obj) ? [] : {};
17 | for (var k in obj) {
18 | // 如果属性值又为对象,则递归复制
19 | if (isObject(obj[k])) {
20 | o[k] = clone(obj[k])
21 | }
22 | else {
23 | o[k] = obj[k];
24 | }
25 | }
26 | return o;
27 | }
28 | ```
29 |
30 | - 手写事件侦听器,并要求兼容浏览器
31 |
32 | ```JavaScript
33 | var eventUtil = {
34 | getEvent: function(event) {
35 | return event || window.event;
36 | },
37 |
38 | getTarget: function(event) {
39 | return event.target || event.srcElement;
40 | },
41 |
42 | addListener: function(element, type, hander) {
43 | if (element.addEventListener) {
44 | element.addEventListener(type, hander, false);
45 | } else if (element.attachEvent) {
46 | element.attachEvent('on' + type, hander);
47 | } else {
48 | element['on' + type] = hander;
49 | }
50 | },
51 |
52 | removeListener: function(element, type, hander) {
53 | if (element.removeEventListener) {
54 | element.removeEventListener(type, hander, false);
55 | } else if (element.deattachEvent) {
56 | element.detachEvent(type, hander);
57 | } else {
58 | element['on' + type] = null;
59 | }
60 | },
61 |
62 | preventDefault: function(event) {
63 | if (event.preventDefault) {
64 | event.preventDefault();
65 | } else {
66 | event.returnValue = false;
67 | }
68 | },
69 |
70 | stopPropagation: function(event) {
71 | if (event.stopPropagation) {
72 | event.stopPropagation();
73 | } else {
74 | event.cancelBubble = true;
75 | }
76 | }
77 | };
78 |
79 | // 调用
80 | (function() {
81 | var btn = document.getElementById("btn");
82 | var link = document.getElementsByTagName("a")[0];
83 |
84 | eventUtil.addListener(btn, "click", function(event) {
85 | var event = eventUtil.getEvent(event);
86 | var target = eventUtil.getTarget(event);
87 | alert(event.type);
88 | alert(target);
89 | eventUtil.stopPropagation(event);
90 | });
91 |
92 | eventUtil.addListener(link, "click", function(event) {
93 | alert("prevent default event");
94 | var event = eventUtil.getEvent(event);
95 | eventUtil.preventDefault(event);
96 | });
97 |
98 | eventUtil.addListener(document.body, "click", function() {
99 | alert("click body");
100 | });
101 | })();
102 | ```
103 |
104 | - 手写事件模型
105 |
106 | ```JavaScript
107 | var Event = (function () {
108 | var list = {}, bind, trigger, remove;
109 | bind = function (key, fn) {
110 | if (!list[key]) {
111 | list[key] = [];
112 | }
113 | list[key].push(fn);
114 | };
115 | trigger = function () {
116 | var key = Array.prototype.shift.call(arguments);
117 | var fns = list[key];
118 | if (!fns || fns.length === 0) {
119 | return false;
120 | }
121 | for (var i = 0, fn; fn = fns[i++];) {
122 | fn.apply(this, arguments);
123 | }
124 | };
125 | remove = function (key, fn) {
126 | var fns = list[key];
127 | if (!fns) {
128 | return false;
129 | }
130 | if (!fn) {
131 | fns & (fns.length = 0);
132 | } else {
133 | for (var i = fns.length - 1; i >= 0; i--) {
134 | var _fn = fns[i];
135 | if (_fn === fn) {
136 | fns.splice(i, 1);
137 | }
138 | }
139 | }
140 | };
141 | return {
142 | bind: bind,
143 | trigger: trigger,
144 | remove: remove
145 | }
146 | })();
147 |
148 | // 调用
149 | Event.bind('Hit', function(){ console.log('bind event'); }); // 绑定事件
150 | Event.trigger("Hit", function(){ console.log('trigger event'); }); // 触发事件
151 | ```
152 |
153 | - 手写事件代理,并要求兼容浏览器
154 |
155 | ```JavaScript
156 | function delegateEvent(parentEl, selector, type, fn) {
157 | var handler = function(e){
158 | var e = e || window.event;
159 | var target = e.target || e.srcElement;
160 | if (matchSelector(target, selector)) {
161 | if(fn) {
162 | fn.call(target, e);
163 | }
164 | }
165 | };
166 | if(parentEl.addEventListener){
167 | parentEl.addEventListener(type, handler);
168 | }else{
169 | parentEl.attachEvent("on" + type, handler);
170 | }
171 | }
172 | /**
173 | * support #id, tagName, .className
174 | */
175 | function matchSelector(ele, selector) {
176 | // if use id
177 | if (selector.charAt(0) === "#") {
178 | return ele.id === selector.slice(1);
179 | }
180 | // if use class
181 | if (selector.charAt(0) === ".") {
182 | return (" " + ele.className + " ").indexOf(" " + selector.slice(1) + " ") != -1;
183 | }
184 | // if use tagName
185 | return ele.tagName.toLowerCase() === selector.toLowerCase();
186 | }
187 |
188 | // 调用
189 | var box = document.getElementById("box");
190 | delegateEvent(box, "a", "click", function(){
191 | console.log(this.href);
192 | })
193 | ```
194 |
195 | - 手写事件触发器,并要求兼容浏览器
196 |
197 | ```JavaScript
198 | var fireEvent = function(element, event) {
199 | if (document.createEventObject) {
200 | var mockEvent = document.createEventObject();
201 | return element.fireEvent('on' + event, mockEvent)
202 | } else {
203 | var mockEvent = document.createEvent('HTMLEvents');
204 | mockEvent.initEvent(event, true, true);
205 | return element.dispatchEvent(mockEvent);
206 | }
207 | }
208 | ```
209 |
210 | - 手写 Function.bind 函数
211 |
212 | ```JavaScript
213 | if (!Function.prototype.bind) {
214 | Function.prototype.bind = function (oThis) {
215 | if (typeof this !== "function") {
216 | throw new TypeError("'this' is not function");
217 | }
218 |
219 | // bind's default arguments, array without first element
220 | // first part arguments for the function
221 | var aBindArgs = Array.prototype.slice.call(arguments, 1);
222 | var fToBind = this; // the function will be binding
223 | var fNOP = function () {};
224 | var fBound = function () {
225 | // target this will be binding
226 | var oThis = this instanceof fNOP ? this : oThis || this;
227 | // last part arguments for the function
228 | var aCallArgs = Array.prototype.slice.call(arguments);
229 | // complete arguments for the function
230 | var aFuncArgs = aBindArgs.concat(aCallArgs);
231 | return fToBind.apply(oThis, aFuncArgs);
232 | };
233 |
234 | // fBound extends fToBind
235 | fNOP.prototype = this.prototype;
236 | fBound.prototype = new fNOP();
237 |
238 | return fBound;
239 | };
240 | }
241 |
242 | // 调用
243 | var add = function(a, b, c){ return a + b + c;};
244 | var newAdd = add.bind(null, 1, 2);
245 | var result = newAdd(3);
246 | ```
247 |
248 | - 手写数组快速排序
249 |
250 | ```JavaScript
251 | var quickSort = function (arr) {
252 | if (arr.length <= 1) { return arr; }
253 | var pivotIndex = Math.floor(arr.length / 2);
254 | var pivot = arr.splice(pivotIndex, 1)[0];
255 | var left = [];
256 | var right = [];
257 | for (var i = 0, len = arr.length; i < len; i++){
258 | if (arr[i] < pivot) {
259 | left.push(arr[i]);
260 | } else {
261 | right.push(arr[i]);
262 | }
263 | }
264 | return quickSort(left).concat([pivot], quickSort(right));
265 | };
266 |
267 | // 调用
268 | quickSort([9, 4, 2, 8, 1, 5, 3, 7]);
269 | ```
270 |
271 | - 手写数组冒泡排序
272 |
273 | ```JavaScript
274 | var bubble = function (arr) {
275 | var maxIndex = arr.length - 1, temp, flag;
276 | for (var i = maxIndex; i > 0; i--) {
277 | flag = true
278 | for (var j = 0; j < i; j++) {
279 | if (arr[j] > arr[j + 1]) {
280 | temp = arr[j];
281 | arr[j] = arr[j + 1];
282 | arr[j + 1] = temp;
283 | flag = false;
284 | }
285 | }
286 | if(! flag){
287 | break;
288 | }
289 | }
290 | return arr;
291 | }
292 | // 调用
293 | var arr = bubble([13, 69, 28, 93, 55, 75, 34]);
294 | ```
295 |
296 | - 封装函数去除 [1, 2, 3, NaN, 3, '0', '2', 4, 5, NaN] 数组中的重复元素
297 |
298 | 要求:
299 | 1. 不改变原数组
300 | 2. 只保留一个 NaN
301 | 3. 至少两种实现方法
302 |
303 |
304 | ```JavaScript
305 | // 方案一:ES6 Set()
306 | /*
307 | Array.prototype.unique = function () {
308 | return [...new Set(this)];
309 | };
310 | const arr = [1, 2, 3, NaN, 3, '0', '1', '2', 4, 5, NaN];
311 | arr.unique();
312 | */
313 |
314 | // 向 Set 加入值的时候,不会发生类型转换,所以 2 和 '2' 是不同的值
315 | // Set 内部判断两个值是否不同采用“同值相等”算法,认为 NaN 不等于自身
316 | const unique = arr => [...new Set(arr)];
317 |
318 | // 方案二:利用 hash 对象 + 数组函数 push()
319 | function unique(arr) {
320 | const hash = {};
321 | const uniqueArr = [];
322 | for (let i = 0; i < arr.length; i++) {
323 | const key = arr[i] + ({}).toString.call(arr[i]);
324 | if (!hash[key]) {
325 | uniqueArr.push(arr[i]);
326 | hash[key] = true;
327 | }
328 | }
329 | return uniqueArr;
330 | }
331 |
332 | // 方案三:数组函数 sort(), shift(), push() + Object.is()
333 | function unique(arr) {
334 | const sortArr = [...arr].sort();
335 | const uniqueArr = [];
336 | while (sortArr.length > 0) {
337 | // Object.is() 采用“Same-value equality”(同值相等)算法
338 | // Object.is(NaN, NaN) // true
339 | // Object.is(+0, -0) // false
340 | if (Object.is(sortArr[0], sortArr[1])) {
341 | sortArr.shift();
342 | } else {
343 | uniqueArr.push(sortArr.shift());
344 | }
345 | }
346 | return uniqueArr;
347 | }
348 |
349 | // 方案四:for 循环 + 数组函数 includes()
350 | function unique(arr) {
351 | const uniqueArr = [];
352 | for (let i = 0; i < arr.length; i++) {
353 | // includes() 内部调用 Object.is() 判断 NaN
354 | if (!uniqueArr.includes(arr[i])) {
355 | uniqueArr.push(arr[i]);
356 | }
357 | }
358 | return uniqueArr;
359 | }
360 | ```
361 |
362 | - 将url的查询参数解析成字典对象
363 |
364 | ```JavaScript
365 | function parseQuery(url) {
366 | url = url == null ? window.location.href : url;
367 | var search = url.substring(url.lastIndexOf("?") + 1);
368 | var hash = {};
369 | var reg = /([^?&=]+)=([^?&=]*)/g;
370 | search.replace(reg, function (match, $1, $2) {
371 | var name = decodeURIComponent($1);
372 | var val = decodeURIComponent($2);
373 | hash[name] = String(val);
374 | return match;
375 | });
376 | return hash;
377 | }
378 | ```
379 |
380 | - 封装函数节流函数
381 |
382 | ```JavaScript
383 | var throttle = function (fn, delay, mustRunDelay) {
384 | var timer = null;
385 | var t_start;
386 | return function () {
387 | var context = this, args = arguments, t_curr = +new Date();
388 | clearTimeout(timer);
389 | if(!t_start){
390 | t_start = t_curr;
391 | }
392 | if(t_curr - t_start >= mustRunDelay) {
393 | fn.apply(context, args);
394 | t_start = t_curr;
395 | } else {
396 | timer = setTimeout(function () {
397 | fn.apply(context, args);
398 | }, delay);
399 | }
400 | };
401 | };
402 |
403 | // 调用(两次间隔50ms内连续触发不执行,但每累计100ms至少执行一次
404 | window.onresize = throttle(myFunc, 50, 100);
405 | ```
406 |
407 | - 用JS实现千位分隔符
408 |
409 | ```JavaScript
410 | function test1(num) {
411 | var str = (+ num) + '';
412 | var len = str.length;
413 | if(len <= 3) return str;
414 | num = '';
415 | while(len > 3){
416 | len -= 3;
417 | num = ',' + str.substr(len, 3) + num;
418 | }
419 | return str.substr(0, len) + num;
420 | }
421 |
422 | function test2(num) {
423 | // ?= 正向匹配:匹配位置
424 | // ?! 正向不匹配:排除位置
425 | var str = (+num).toString();
426 | var reg = /(?=(?!\b)(\d{3})+$)/g;
427 | return str.replace(reg, ',');
428 | }
429 | ```
430 |
431 | - 写一个跨浏览器兼容的 trim 函数
432 |
433 | ```JavaScript
434 | var trim = function (str) {
435 | // 将 null 和 undefined 装换成空字符串
436 | if (str == null) {
437 | return '';
438 | }
439 | return String.prototype.trim
440 | ? String.prototype.trim.call(str)
441 | : str.toString().replace(/^\s+|\s+$/g, '');
442 | }
443 | ```
444 |
445 | - 为 Date 对象扩展一个方法 dateTotal,获取任意 Date 实例日期所在月份的总天数
446 |
447 | ```JavaScript
448 | Date.prototype.dateTotal = function () {
449 | var y = this.getFullYear();
450 | var m = this.getMonth();
451 | // new Date(yyyy, mm, dd) 日期的构造方法有一个特殊用法:
452 | // 当传入 `yyyy, mm, 0` 时,得到的是 mm 月上一个月的最后一天
453 | return new Date(y, m + 1, 0).getDate();
454 | }
455 | ```
456 |
457 |
458 |
--------------------------------------------------------------------------------
/JavaScript常用代码.doc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/JavaScript常用代码.doc
--------------------------------------------------------------------------------
/URL 常用编码.txt:
--------------------------------------------------------------------------------
1 |
2 | %23 #
3 | %24 $
4 | %26 &
5 | %2B +
6 | %3A :
7 | %3C <
8 | %3E >
9 | %3D =
10 | %2F /
11 | %3F ?
12 | %40 @
13 | %5B [
14 | %5D ]
15 | %5E ^
16 | %60 `
17 | %7B {
18 | %7D }
19 | %7C |
20 |
--------------------------------------------------------------------------------
/algorithm/cartesianProduct.js:
--------------------------------------------------------------------------------
1 | {
2 | // var goods = [
3 | // { 'colour': ['red', 'green'] },
4 | // { 'material': ['cotton', 'wool', 'silk'] },
5 | // { 'shape': ['round', 'square', 'rhombus'] }
6 | // ];
7 | var goods = [
8 | { '颜色': ['红色', '绿色'] },
9 | { '材料': ['棉花', '羊毛', '丝绸'] },
10 | { '形状': ['圆形', '正方形', '菱形'] }
11 | ];
12 |
13 | function cartesianProduct(input, current) {
14 | if (!input || !input.length) {
15 | return [];
16 | }
17 |
18 | var head = input[0];
19 | var tail = input.slice(1);
20 | var output = [];
21 |
22 | for (var key in head) {
23 | for (var i = 0; i < head[key].length; i++) {
24 | var newCurrent = copy(current);
25 | newCurrent[key] = head[key][i];
26 | if (tail.length) {
27 | var productOfTail = cartesianProduct(tail, newCurrent);
28 | output = output.concat(productOfTail);
29 | } else {
30 | output.push(newCurrent);
31 | }
32 | }
33 | }
34 |
35 | return output;
36 | }
37 |
38 | function copy(obj) {
39 | var res = {};
40 | for (var p in obj) {
41 | res[p] = obj[p];
42 | }
43 | return res;
44 | }
45 |
46 | var ret = cartesianProduct(goods);
47 | console.log(ret);
48 | }
49 |
50 |
51 | {
52 | var arr = [
53 | ['白色', '黑色'],
54 | ['64G', '128G'],
55 | ['移动', '联通', '电信']
56 | ];
57 |
58 | function combination(arr) {
59 | var len = arr.length;
60 |
61 | if (len < 2) {
62 | return arr[0] || [];
63 | }
64 |
65 | var len1 = arr[0].length;
66 | var len2 = arr[1].length;
67 | var lenBoth = len1 * len2;
68 | var items = new Array(lenBoth);
69 | var index = 0;
70 | for (var i = 0; i < len1; i++) {
71 | for (var j = 0; j < len2; j++) {
72 | if (arr[0][i] instanceof Array) {
73 | items[index] = arr[0][i].concat(arr[1][j]);
74 | } else {
75 | items[index] = [arr[0][i]].concat(arr[1][j]);
76 | }
77 | index ++;
78 | }
79 | }
80 | var newArr = new Array(len - 1);
81 | for (var i = 2; i < arr.length; i++) {
82 | newArr[i - 1] = arr[i];
83 | }
84 | newArr[0] = items;
85 |
86 | return combination(newArr);
87 | }
88 |
89 | var ret = combination(arr);
90 | console.log(ret);
91 | }
92 |
93 |
--------------------------------------------------------------------------------
/algorithm/clock.js:
--------------------------------------------------------------------------------
1 | const genClock = function*() {
2 | while (true) {
3 | console.log('Tick!');
4 | yield;
5 | console.log('Tock!');
6 | yield;
7 | }
8 | };
9 |
10 | const clock = genClock();
11 |
12 | clock.next();
13 | clock.next();
14 | clock.next();
15 | clock.next();
16 | clock.next();
--------------------------------------------------------------------------------
/algorithm/fibonacci.js:
--------------------------------------------------------------------------------
1 | function* fibonacci() {
2 | let [prev, curr] = [0, 1];
3 | for (;;) {
4 | [prev, curr] = [curr, prev + curr];
5 | yield curr;
6 | }
7 | }
8 |
9 | for (let n of fibonacci()) {
10 | if (n > 1000) break;
11 | console.log(n);
12 | }
--------------------------------------------------------------------------------
/algorithm/findindex.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
10 |
11 |
12 | Benchmark is running ...
13 | Open the Developer Tools console to view the execution results.
14 | Benchmark has been completed!
15 |
16 |
17 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/algorithm/flat.js:
--------------------------------------------------------------------------------
1 | function* iterTree(tree) {
2 | if (Array.isArray(tree)) {
3 | for (let i = 0; i < tree.length; i++) {
4 | yield* iterTree(tree[i]);
5 | }
6 | }
7 | else {
8 | yield tree;
9 | }
10 | }
11 |
12 | const tree = [
13 | 'a',
14 | ['b', 'c'],
15 | ['d', 'e']
16 | ];
17 |
18 | for (let x of iterTree(tree)) {
19 | console.log(x);
20 | }
--------------------------------------------------------------------------------
/algorithm/fotmatBigNum.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | numeraljs
6 |
7 |
8 |
9 |
10 |
19 |
20 |
--------------------------------------------------------------------------------
/algorithm/permutation.js:
--------------------------------------------------------------------------------
1 | {
2 | /*
3 | * arr = [1, 2, 3], size = 2
4 | * return [[1, 2], [1, 3], [2, 3]]
5 | */
6 | function permutation(arr, size) {
7 | const ret = [];
8 |
9 | function groupSplit(target, source, size) {
10 | if (size === 0) { // 退出递归
11 | ret[ret.length] = target;
12 | return;
13 | }
14 | for (let i = 0; i <= (source.length - size); i++) {
15 | // const _target = (target.slice()).concat(source[i]);
16 | const _target = [...target, source[i]];
17 | const _source = source.slice(i + 1);
18 | const _size = size - 1;
19 | groupSplit(_target, _source, _size);
20 | }
21 | }
22 |
23 | groupSplit([], arr, size);
24 |
25 | return ret;
26 | }
27 |
28 | const arr = [1, 2, 3];
29 | const size = 2;
30 | const ret = permutation(arr, size);
31 | console.log(ret);
32 | }
33 |
34 | {
35 | function combination(arr) {
36 | var tmp = [];
37 | var ret = [];
38 |
39 | function groupSplit(arr, i) {
40 | for (var j = 0; j < arr[i].length; j++) {
41 | tmp[i] = arr[i][j];
42 | if (i === arr.length - 1) {
43 | ret.push(tmp.join('|'));
44 | } else {
45 | groupSplit(arr, i + 1);
46 | }
47 | }
48 | }
49 |
50 | groupSplit(arr, 0);
51 |
52 | return ret;
53 | }
54 |
55 | var arr = [
56 | ['a', 'b', 'c'],
57 | ['1', '2']
58 | ]
59 | var ret = combination(arr);
60 | console.log(ret);
61 | }
--------------------------------------------------------------------------------
/algorithm/sumMapArr.js:
--------------------------------------------------------------------------------
1 | // 给定一个很大的数组,数组里面有许多整数,要求:
2 | // 将数组中【和】为 10 的每一对数配对并找出,返回这些数配对后的数组
3 | // 例如:[11, 3, 8, 9, 7, -1, 1, 2, 4...]
4 | // 得到:[[11,-1],[3,7],[8,2],[9,1]...]
5 |
6 | // 版本一
7 | function getMapArr(list) {
8 | const resArr = [];
9 | const len = list.length;
10 |
11 | for (let i = 0; i < len; i++) {
12 | for (let j = i + 1; j < len; j++) {
13 | if (list[i] + list[j] === 10) {
14 | resArr.push([list[i], list[j]]);
15 | }
16 | }
17 | }
18 |
19 | return resArr;
20 | }
21 |
22 | // 版本二
23 | function getMapArr2(list) {
24 | const resArr = [];
25 | list = list.sort((a, b) => a - b);
26 |
27 | for (let i = 0, j = list.length - 1; i < j;) {
28 | const a = list[i];
29 | const b = list[j];
30 | if (a + b === 10) {
31 | resArr.push([a, b]);
32 | i++;
33 | j--;
34 | }
35 | else if (a + b < 10) {
36 | i++;
37 | }
38 | else {
39 | j--;
40 | }
41 | }
42 |
43 | return resArr;
44 | }
45 |
46 | const list = [11, 4, 9, 3, -1, -3, 6, 7, 9, 13, 8];
47 | console.log(JSON.stringify(getMapArr(list)));
48 | console.log(JSON.stringify(getMapArr2(list)));
49 |
--------------------------------------------------------------------------------
/algorithm/toThousands.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
10 |
11 |
12 | Benchmark is running ...
13 | Open the Developer Tools console to view the execution results.
14 | Benchmark has been completed!
15 |
16 |
17 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/algorithm/twoSum.js:
--------------------------------------------------------------------------------
1 | function twoSum1(arr, sum) {
2 | for (var i = 0, len = arr.length; i < len; i++) {
3 | for (var j = i + 1; j < len; j++) {
4 | if (arr[i] + arr[j] === sum) {
5 | return [i, j];
6 | }
7 | }
8 | }
9 | return [];
10 | }
11 |
12 | function twoSum2 (arr, sum) {
13 | var hash = {};
14 | var len = arr.length
15 | for(var i = 0; i < len; i++) {
16 | var n = arr[i];
17 | if(hash[n] !== undefined) {
18 | return [hash[n], i];
19 | }
20 | else {
21 | // 关键:hash[n] 与 hash[sum - n] 对应
22 | hash[sum - n] = i;
23 | }
24 | }
25 | return [];
26 | }
27 |
28 | function twoSum3 (arr, sum) {
29 | const map = new Map();
30 | const len = arr.length;
31 | for(var i = 0; i < len; i++) {
32 | var n = arr[i];
33 | if(map.has(n)) {
34 | return [map.get(n), i];
35 | }
36 | else {
37 | map.set(sum - n, i);
38 | }
39 | }
40 | return [];
41 | }
42 |
43 |
44 | var arr = [2, 7, 11, 15];
45 | var sum = 9;
46 |
47 | // var len = 10000;
48 | // var arr = [...Array(len).keys()];
49 | // var elem = len;
50 | // var elem = Math.floor(len / 2);
51 | // var sum = (elem - 2) + (elem - 1);
52 |
53 | twoSum1(arr, sum);
54 |
--------------------------------------------------------------------------------
/code/Class/createNew.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 极简主义法定义类
3 | */
4 |
5 | // 封装 -- 不使用 this 和 prototype
6 | var Cat = {
7 | createNew: function () {
8 | var cat = {};
9 | cat.name = "包子";
10 | cat.makeSound = function () {
11 | alert("喵喵喵");
12 | };
13 | return cat;
14 | }
15 | };
16 |
17 | // 实例化 -- 调用 createNew() 方法
18 | var cat = Cat.createNew();
19 | cat1.makeSound(); // 喵喵喵
20 |
21 | // 继承 -- 在子类的 createNew() 方法中,调用父类的 createNew() 方法
22 | var Animal = {
23 | createNew: function () {
24 | var animal = {};
25 | animal.sleep = function () {
26 | alert("睡懒觉");
27 | };
28 | return animal;
29 | }
30 | };
31 |
32 | var Cat = {
33 | createNew: function () {
34 | var cat = Animal.createNew(); // 继承
35 | cat.name = "包子";
36 | cat.makeSound = function () {
37 | alert("喵喵喵");
38 | };
39 | return cat;
40 | }
41 | };
42 |
43 | // 定义私有属性和共享数据
44 | var Cat = {
45 | // 数据共享 -- 定义在类对象内、createNew() 方法外即可
46 | age: 3, // 共享数据
47 | createNew: function () {
48 | var cat = {};
49 | cat.name = "包子";
50 | // 私有属性和方法 -- 定义在 createNew() 方法内,不在输出对象上即可
51 | var sound = "喵喵喵"; // 私有属性--外部无法读取,只能通过公共方法 makeSound() 来读取
52 | cat.makeSound = function () {
53 | alert(sound);
54 | };
55 | cat.changeAge = function(num){
56 | Cat.age = num; // 修改共享数据
57 | };
58 | return cat;
59 | }
60 | };
61 |
--------------------------------------------------------------------------------
/code/Class/extends.js:
--------------------------------------------------------------------------------
1 | // 实现继承的方式1:
2 | if(! Object.create){
3 | Object.create = function(proto){
4 | function F(){}
5 | F.prototype = proto;
6 | return new F;
7 | };
8 | }
9 |
10 | Student.prototype = Object.create(Person.prototype);
11 | Student.prototype.constructor = Student;
12 |
13 | // 实现继承的方式2:
14 | function extend(Child, Parent) {
15 | var F = function(){};
16 | F.prototype = Parent.prototype;
17 | Child.prototype = new F();
18 | Child.prototype.constructor = Child;
19 | Child.uber = Parent.prototype;
20 | }
21 |
22 | extend(Student, Person);
--------------------------------------------------------------------------------
/code/bind/bind.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 手写 bind() 函数
7 |
8 |
9 |
10 |
40 |
41 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/code/bind/mvvm.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 数据双向绑定
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
25 |
26 |
27 |
28 | I am . My job is .
29 |
30 |
31 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/code/bubble/bubble-capture.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Title
6 |
35 |
36 |
37 |
38 |
39 | div1
40 |
41 | div2
42 |
43 | div3
44 |
45 | div4
46 |
47 |
48 |
49 |
50 |
51 |
99 |
100 |
--------------------------------------------------------------------------------
/code/closure/closuer.js:
--------------------------------------------------------------------------------
1 | function fun(n, o) {
2 | console.log(o);
3 | return {
4 | fun: function (m) {
5 | return fun(m, n);
6 | }
7 | };
8 | }
9 | var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
10 | var b = fun(0).fun(1).fun(2).fun(3);
11 | var c = fun(0).fun(1); c.fun(2); c.fun(3);
12 |
13 | // 问题:a, b, c 三行分别输出的值是什么?
14 |
15 | // 解题关键:3 个 fun 的调用关系,参数的传递的闭包,使用的是哪次闭包的值
16 | function fun(n, o) { // fun1
17 | console.log(o);
18 | return { // oFun
19 | fun: function(m){ // fun2
20 | // 参数 n 被闭包
21 | return fun(m, n); // fun3
22 | }
23 | };
24 | }
25 | /*
26 | 1. fun3 调用的是最外层的 fun1;fun1 只会被调用,不会产生调用
27 | 2. fun1 的返回值是对象 oFun,外部通过 oFun.fun 访问 fun2
28 | 3. 函数调用顺序:fun1 或 fun2 -> fun3 -> fun1
29 | 4. fun1 的参数 n 传递到 fun3,变量 n 在 fun2 作用域下被闭包
30 | 5. 最终输入结果由 fun1 的 参数 o 或 fun3 的参数 n 决定
31 | */
32 |
33 | // 答案:
34 | /*
35 | var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
36 | // undefined, 0, 0, 0
37 |
38 | var b = fun(0).fun(1).fun(2).fun(3);
39 | // undefined, 0, 1, 2
40 |
41 | var c = fun(0).fun(1); c.fun(2); c.fun(3);
42 | // undefined, 0, 1, 1
43 | */
--------------------------------------------------------------------------------
/code/closure/closure1.js:
--------------------------------------------------------------------------------
1 | function fun(n, o) { // fun1
2 | console.log(o);
3 | return {
4 | fun: function(m){ // fun2
5 | // closure n here
6 | return fun(m, n); // fun3
7 | }
8 | };
9 | }
10 |
11 | var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
12 |
13 | // 1. var a = fun(0) // n = 0
14 | // do: fun1(0, undefined);
15 | // closure-1 n = 0, print undefined
16 |
17 | // 2. a.fun(1) // n = 0 from closure-1, m = 1
18 | // do: oFun.fun2(1) -> fun3(1, 0) -> fun1(1, 0)
19 | // closure-2 n = 1, print 0
20 |
21 | // 3. a.fun(2) // n = 0 from closure-1, m = 2
22 | // do: oFun.fun2(2) -> fun3(2, 0) -> fun1(2, 0)
23 | // closure-3 n = 2, print 0
24 |
25 | // 4. a.fun(3) // n = 0 from closure-1, m = 3
26 | // do: oFun.fun2(3) -> fun3(3, 0) -> fun1(3, 0)
27 | // closure-4 n = 3, print 0
28 |
--------------------------------------------------------------------------------
/code/closure/closure2.js:
--------------------------------------------------------------------------------
1 | function fun(n, o) { // fun1
2 | console.log(o);
3 | return {
4 | fun: function(m){ // fun2
5 | // closure n here
6 | return fun(m, n); // fun3
7 | }
8 | };
9 | }
10 |
11 | var b = fun(0).fun(1).fun(2).fun(3);
12 |
13 | // 1. fun(0) // n = 0
14 | // do: fun1(0, undefined);
15 | // closure-1 n = 0, print undefined
16 |
17 | // 2. fun(0).fun(1) // n = 0 from closure-1, m = 1
18 | // do: oFun.fun2(1) -> fun3(1, 0) -> fun1(1, 0)
19 | // closure-2 n = 1, print 0
20 |
21 | // 3. fun(0).fun(1).fun(2) // n = 1 from closure-2, m = 2
22 | // do: oFun.fun2(2) -> fun3(2, 1) -> fun1(2, 1)
23 | // closure-3 n = 2, print 1
24 |
25 | // 4. fun(0).fun(1).fun(2).fun(3) // n = 2 from closure-3, m = 3
26 | // do: oFun.fun2(3) -> fun3(3, 2) -> fun1(3, 2)
27 | // closure-4 n = 3, print 2
28 |
--------------------------------------------------------------------------------
/code/closure/closure3.js:
--------------------------------------------------------------------------------
1 | function fun(n, o) { // fun1
2 | console.log(o);
3 | return { // oFun
4 | fun: function(m){ // fun2
5 | // closure n here
6 | return fun(m, n); // fun3
7 | }
8 | };
9 | }
10 |
11 | var c = fun(0).fun(1); c.fun(2); c.fun(3);
12 |
13 | // 1. fun(0) // n = 0
14 | // do: fun1(0, undefined);
15 | // closure-1 n = 0, print undefined
16 |
17 | // 2. var c = fun(0).fun(1) // n = 0 from closure-1, m = 1
18 | // do: oFun.fun2(1) -> fun3(1, 0) -> fun1(1, 0)
19 | // closure-2 n = 1, print 0
20 |
21 | // 3. c.fun(2) // n = 1 from closure-2, m = 2
22 | // do: oFun.fun2(2) -> fun3(2, 1) -> fun1(2, 1)
23 | // closure-3 n = 2, print 1
24 |
25 | // 4. c.fun(3) // n = 1 from closure-2, m = 3
26 | // do: oFun.fun2(3) -> fun3(3, 1) -> fun1(3, 1)
27 | // closure-4 n = 3, print 1
28 |
29 |
--------------------------------------------------------------------------------
/code/eventUtil/customEvent.js:
--------------------------------------------------------------------------------
1 | var $ = function(el) {
2 | return new _$(el);
3 | };
4 | var _$ = function(el) {
5 | this.el = (el && el.nodeType == 1)? el: document;
6 | };
7 | _$.prototype = {
8 | constructor: this,
9 | addEvent: function(type, fn, capture) {
10 | var el = this.el;
11 |
12 | if (window.addEventListener) {
13 | el.addEventListener(type, fn, capture);
14 |
15 | var ev = document.createEvent("HTMLEvents");
16 | ev.initEvent(type, capture || false, false);
17 | // 在元素上存储创建的事件,方便自定义触发
18 | if (!el["ev" + type]) {
19 | el["ev" + type] = ev;
20 | }
21 |
22 | } else if (window.attachEvent) {
23 | el.attachEvent("on" + type, fn);
24 | if (isNaN(el["cu" + type])) {
25 | // 自定义属性,触发事件用
26 | el["cu" + type] = 0;
27 | }
28 |
29 | var fnEv = function(event) {
30 | if (event.propertyName == "cu" + type) {
31 | fn.call(el);
32 | }
33 | };
34 |
35 | el.attachEvent("onpropertychange", fnEv);
36 |
37 | // 在元素上存储绑定的propertychange事件,方便删除
38 | if (!el["ev" + type]) {
39 | el["ev" + type] = [fnEv];
40 | } else {
41 | el["ev" + type].push(fnEv);
42 | }
43 | }
44 |
45 | return this;
46 | },
47 | fireEvent: function(type) {
48 | var el = this.el;
49 | if (typeof type === "string") {
50 | if (document.dispatchEvent) {
51 | if (el["ev" + type]) {
52 | el.dispatchEvent(el["ev" + type]);
53 | }
54 | } else if (document.attachEvent) {
55 | // 改变对应自定义属性,触发自定义事件
56 | el["cu" + type]++;
57 | }
58 | }
59 | return this;
60 | },
61 | removeEvent: function(type, fn, capture) {
62 | var el = this.el;
63 | if (window.removeEventListener) {
64 | el.removeEventListener(type, fn, capture || false);
65 | } else if (document.attachEvent) {
66 | el.detachEvent("on" + type, fn);
67 | var arrEv = el["ev" + type];
68 | if (arrEv instanceof Array) {
69 | for (var i=0; i
2 |
3 |
4 |
5 | 事件侦听器函数
6 |
7 |
8 |
9 |
10 | 百度
11 |
12 |
35 |
36 |
--------------------------------------------------------------------------------
/code/function/Thunk.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Thunk 函数
3 | * 将一个多参数函数,经过转换处理,变成了一个单参数函数,只接受回调函数作为参数
4 | * 这个单参数版本的函数,就叫做 Thunk 函数
5 | * @param {Function} fn
6 | */
7 |
8 | /*
9 | var Thunk = function(fn) {
10 | return function() {
11 | var args = Array.prototype.slice.call(arguments);
12 | return function(callback) {
13 | args.push(callback);
14 | return fn.apply(this, args);
15 | }
16 | };
17 | };
18 | */
19 |
20 | // ES6 版本
21 | const Thunk = function(fn) {
22 | return function(...args) {
23 | return function(callback) {
24 | return fn.call(this, ...args, callback);
25 | }
26 | };
27 | };
28 |
29 | // var readFileThunk = Thunk(fs.readFile);
30 | // readFileThunk(fileA)(callback);
--------------------------------------------------------------------------------
/code/function/arguments.js:
--------------------------------------------------------------------------------
1 | /**
2 | * arguments 对象示例
3 | * @method add
4 | */
5 | function add() {
6 | //var args = [].slice.call(arguments);
7 | let args = Array.from(arguments);
8 | return args.reduce((a, b) => a + b);
9 | }
10 | console.log(add(1, 2, 3, 4));
11 |
12 | /**
13 | * ...args 示例
14 | * @method add2
15 | * @param {number} args
16 | */
17 | let add2 = (...args) => args.reduce((a, b) => a + b);
18 | console.log(add2(1, 2, 3, 4));
19 |
--------------------------------------------------------------------------------
/code/function/compose.js:
--------------------------------------------------------------------------------
1 | function compose(...functors) {
2 | return functors.reduceRight((a, b) => b(a));
3 | }
4 |
--------------------------------------------------------------------------------
/code/function/debounce.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 防抖动(debounce)
3 | */
4 | function debounce(fn, delay) {
5 | let timer = null;
6 | return function(...args) {
7 | clearTimeout(timer);
8 | timer = setTimeout(() => fn.apply(this, args), delay);
9 | }
10 | }
11 |
12 | // 注意: 回调函数必须包裹在 debounce 内
13 | document.addEventListener('click', debounce(function() {
14 | console.log("clicked");
15 | }, 200), false);
16 |
--------------------------------------------------------------------------------
/code/function/deepClone.js:
--------------------------------------------------------------------------------
1 | function deepClone(obj) {
2 | const getType = o => Object.prototype.toString.call(o).slice(8, -1);
3 | const isObject = o => getType(o) === 'Object';
4 | const isArray = o => getType(o) === 'Array';
5 | const isIterable = o => typeof o === 'object' && typeof o !== 'null';
6 | let newObj;
7 | if (isIterable(obj)) {
8 | newObj = isArray(obj) ? [] : {};
9 | for (let [k, v] of Object.entries(obj)) {
10 | if (isIterable(v)) {
11 | // 递归调用
12 | newObj[k] = deepClone(v);
13 | } else {
14 | newObj[k] = v;
15 | }
16 | }
17 | } else {
18 | return obj;
19 | }
20 | return newObj;
21 | }
22 |
--------------------------------------------------------------------------------
/code/function/eqFunction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 等价函数
3 | * @method equal
4 | * @param {Function} fn [description]
5 | * @return {*}
6 | */
7 | function equal(fn) {
8 | return function(...args) {
9 | return fn.apply(this, args);
10 | }
11 | }
12 |
13 | // 函数
14 | function add(x, y) {
15 | return x + y;
16 | }
17 |
18 | console.log(add(3, 4));
19 | add = equal(add);
20 | console.log(add(3, 4));
21 |
22 | // 对象方法
23 | const obj = {
24 | x: 10,
25 | y: 20,
26 | add: function() {
27 | return this.x + this.y;
28 | }
29 | }
30 |
31 | console.log(obj.add());
32 | const objAdd = equal(obj.add);
33 | console.log(objAdd.call(obj));
34 |
--------------------------------------------------------------------------------
/code/function/factorial.js:
--------------------------------------------------------------------------------
1 | // 求阶乘 复杂度 O(n) 递归
2 | /* function factorial(n) {
3 | if (n === 1) {
4 | return 1;
5 | }
6 | return n * factorial(n - 1);
7 | } */
8 |
9 | // 柯里化
10 | function currying(fn, n) {
11 | return function (m) {
12 | return fn.call(this, m, n);
13 | };
14 | }
15 |
16 | function tailFactorial(n, res) {
17 | if (n === 1) {
18 | return total;
19 | }
20 | return tailFactorial(n - 1, n * res);
21 | }
22 |
23 | const factorial2 = currying(tailFactorial, 1);
24 |
25 | factorial2(5);
26 |
27 | /**
28 | * 求阶乘 复杂度 O(1) 尾递归
29 | * @method factorial
30 | * @param {number} n 阶乘长度
31 | * @param {number} res 上一次的乘机
32 | * @return {number} 阶乘最终结果
33 | */
34 | function factorial(n, res = 1) {
35 | if (n === 1) {
36 | return res;
37 | }
38 | return factorial(n - 1, n * res);
39 | }
40 |
41 | factorial(5); // 120
42 |
--------------------------------------------------------------------------------
/code/function/fibonacci.js:
--------------------------------------------------------------------------------
1 | // 菲波那切数列(1, 1, 2, 3, 5, 8, 13 ...) 递归
2 | function fibonacci(n) {
3 | if (n <= 1) {
4 | return 1;
5 | };
6 |
7 | return fibonacci(n - 1) + fibonacci(n - 2);
8 | }
9 |
10 | fibonacci(10) // 89
11 | fibonacci(100) // 堆栈溢出
12 |
13 | /**
14 | * 菲波那切数列(1, 1, 2, 3, 5, 8, 13 ...) 尾递归
15 | * @method fibonacci2
16 | * @param {number} n 数列元素索引(从 0 开始)
17 | * @param {number} [n1=1] 本次递归数列的第 1 个元素
18 | * @param {number} [n2=1] 本次递归数列的第 2 个元素
19 | * @return {number} 指定位置的数列元素
20 | */
21 | function fibonacci2(n, n1 = 1, n2 = 1) {
22 | if (n <= 1) {
23 | return n2;
24 | };
25 |
26 | return fibonacci2(n - 1, n2, n1 + n2);
27 | }
28 |
29 | fibonacci2(10) // 89
30 | fibonacci2(100) // 573147844013817200000
31 |
--------------------------------------------------------------------------------
/code/function/function.length.js:
--------------------------------------------------------------------------------
1 | let add = (a, b, c) => console.log(a + b + c);
2 | add(1, 2);
3 |
4 | /**
5 | * 验证参数个数
6 | * @method matchArgs
7 | * @param {Function} fun
8 | * @return {*}
9 | */
10 | const matchArgs = fun => {
11 | return function(...args) {
12 | if (fun.length === args.length) {
13 | return fun.apply(this, args);
14 | }
15 | else {
16 | throw RangeError('Arguments not match!');
17 | }
18 | }
19 | }
20 |
21 | add = matchArgs(add);
22 | add(1, 2);
23 |
--------------------------------------------------------------------------------
/code/function/hookFunction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 拦截和监控
3 | */
4 | /* function watch(fn) {
5 | return function f(...args) {
6 | if (f.before) {
7 | f.before(this, ...args);
8 | }
9 | const res = fn.apply(this, args);
10 | if (f.after) {
11 | f.after(this, res, ...args);
12 | }
13 | return res;
14 | }
15 | }
16 |
17 | // 示例需要引入 jquery
18 | $ = watch($);
19 |
20 | $.after = function(thisObj, resVal) {
21 | if (resVal.css) {
22 | resVal.css = watch(resVal.css);
23 | resVal.css.before = function() {
24 | console.log('不推荐使用 css() 修改样式, 建议使用 addClass()');
25 | }
26 | }
27 | }
28 |
29 | $('#datalist > li').css('color', '#ccc'); */
30 |
31 |
32 | /**
33 | * 用 requestAnimationFrame 优化性能
34 | */
35 | function watch(fn) {
36 | return function f(...args) {
37 | let blocked = false;
38 | if (f.before) {
39 | blocked = f.before(this, ...args) === true;
40 | }
41 | if (!blocked) {
42 | let res = fn.apply(this, args);
43 | if (f.after) {
44 | f.after(this, res, ...args);
45 | }
46 | return res;
47 | }
48 | }
49 | }
50 |
51 | $ = watch($);
52 |
53 | $.after = function(thisObj, retVal) {
54 | if (retVal.css) {
55 | const origin = retVal.css;
56 | retVal.css = watch(origin);
57 | retVal.css.before = function(dest, ...args) {
58 | //用 requestAnimationFrame 优化性能
59 | requestAnimationFrame(() => origin.apply(dest, args));
60 | //返回 true 阻止默认行为
61 | return true;
62 | }
63 | }
64 | }
65 |
66 | $('#datalist > li').css('color', 'red');
67 |
--------------------------------------------------------------------------------
/code/function/isType.js:
--------------------------------------------------------------------------------
1 | var isType = function(type) {
2 | return function(obj) {
3 | return Object.prototype.toString.call(obj) === '[object ' + type + ']';
4 | }
5 | };
6 |
7 | var isString = isType('String');
8 | var isArray = isType('Array');
9 | var isNumber = isType('Number');
10 | console.log(isArray([1, 2, 3])); //true
--------------------------------------------------------------------------------
/code/function/iterator.js:
--------------------------------------------------------------------------------
1 | function makeIterator(array) {
2 | var nextIndex = 0;
3 | return {
4 | next: function () {
5 | return nextIndex < array.length
6 | ? { value: array[nextIndex++], done: false }
7 | : { value: undefined, done: true };
8 | }
9 | };
10 | }
11 |
12 | var it = makeIterator(['a', 'b']);
13 |
14 | it.next(); // { value: "a", done: false }
15 | it.next(); // { value: "b", done: false }
16 | it.next(); // { value: undefined, done: true }
--------------------------------------------------------------------------------
/code/function/monopoly.js:
--------------------------------------------------------------------------------
1 | // 获得函数调用权限,排斥其他函数的调用
2 | function monopoly(fn, duration = 100) {
3 | return function () {
4 | if (!monopoly.permit) {
5 | monopoly.permit = fn;
6 | }
7 | if (monopoly.permit === fn) {
8 | clearTimeout(monopoly.permitTimer);
9 | monopoly.permitTimer = setTimeout(function() {
10 | delete monopoly.permit;
11 | }, duration);
12 | return fn.apply(this, arguments);
13 | }
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/code/function/multicast.js:
--------------------------------------------------------------------------------
1 | /**
2 | * multicast 批量操作 DOM 元素
3 | */
4 | function multicast(fn) {
5 | return function (list, ...args) {
6 | if (list && list.length != 0) {
7 | return Array.from(list).map(item => fn.apply(this, [item, ...args]));
8 | }
9 | else {
10 | return fn.apply(this, [list, ...args]);
11 | }
12 | }
13 | }
14 |
15 | function setColor(el, color) {
16 | return el.style.color = color;
17 | }
18 |
19 | const setMultColor = multicast(setColor);
20 |
21 | var list = document.querySelectorAll('li:nth-child(2n + 1)');
22 | setMultColor(list, 'orange');
23 |
--------------------------------------------------------------------------------
/code/function/newFunction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 打印从 1 ~ 10 的整数
3 | * 不使用循环、跳转和递归
4 | * @type {Number}
5 | */
6 | var i = 0;
7 | new Function(new Array(11).join('console.log(++i);'))();
8 | // eval(new Array(11).join('console.log(++i);'));
9 |
10 |
11 | /**
12 | * 解析末尾含逗号的 JSON 字符串
13 | */
14 | function parseJSON(data) {
15 | try {
16 | return JSON.parse(data);
17 | }
18 | catch (e) {
19 | console.warn(e.message);
20 | return (new Function('return ' + data))();
21 | }
22 | }
23 |
24 | var data = `{
25 | a: 1,
26 | b: 2,
27 | c: '3',
28 | }`;
29 | console.log(parseJSON(data));
30 |
--------------------------------------------------------------------------------
/code/function/once.js:
--------------------------------------------------------------------------------
1 | // 限制函数只调用一次
2 | function once(fn) {
3 | return function() {
4 | var ret = fn && fn.apply(this, arguments);
5 | fn = null;
6 | return ret;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/code/function/performance.js:
--------------------------------------------------------------------------------
1 | // 利用“切片”监控函数性能
2 | if (window.performance) {
3 | performance.watch = function(fn, mark) {
4 | var idx = 0;
5 | return function() {
6 | idx++;
7 | var markStart = mark + idx + '_start';
8 | var markEnd = mark + idx + '_end';
9 | performance.mark(markStart);
10 |
11 | var res = fn.apply(this, arguments);
12 |
13 | if (res instanceof Promise) {
14 | return res.then(r => {
15 | performance.mark(markEnd);
16 | performance.measure(mark, markStart, markEnd)
17 | return r;
18 | });
19 | }
20 | else {
21 | performance.mark(markEnd);
22 | return res;
23 | }
24 | }
25 | }
26 | }
27 |
28 | var asyncTask = function() {
29 | return new Promise((resolve) => {
30 | setTimeout(() => resolve(10), 1000);
31 | });
32 | };
33 |
34 | asyncTask = performance.watch(asyncTask, 'my-task');
35 |
36 |
37 |
38 | Promise.all([asyncTask(), asyncTask()])
39 | .then(function(resp) {
40 | console.log('done', resp);
41 | var measures = performance.getEntriesByType('measure');
42 | console.log(measures);
43 | });
44 |
--------------------------------------------------------------------------------
/code/function/pipeFunction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * reduce 归纳
3 | */
4 | function reducer(...fnList) {
5 | return function(...args) {
6 | if (fnList.length <= 0) {
7 | return;
8 | }
9 | const firstVal = fnList[0].apply(this, args);
10 | fnList[0] = firstVal;
11 | return fnList.reduce((params, fn) => fn.call(this, params));
12 | }
13 | }
14 |
15 | function twofold(n) {
16 | return 2 * n;
17 | }
18 |
19 | function tenfold(n) {
20 | return 10 * n;
21 | }
22 |
23 | var reduceFun = reducer(tenfold, twofold, tenfold);
24 | console.log(reduceFun(3));
25 |
26 |
27 | /**
28 | * pipe 管道
29 | */
30 | function pipe(...fnList) {
31 | return function(...args) {
32 |
33 | // const fn = fnList.reduceRight(function (a, b) {
34 | // return function (...args) {
35 | // b.apply(this, [...args, a]);
36 | // }
37 | // });
38 |
39 | // const fn = fnList.reduceRight((a, b) => {
40 | // return (...args) => {
41 | // b.apply(this, [...args, a]);
42 | // }
43 | // });
44 |
45 | const fn = fnList.reduceRight((a, b) => (...args) => b.apply(this, [...args, a]));
46 | return fn.apply(this, args);
47 | }
48 | }
49 |
50 | function taskA(p, next) {
51 | console.log(`Task A`);
52 | // console.log(`Params: ${p}`);
53 | next(10 * p);
54 | }
55 | function taskB(p, next) {
56 | console.log('Task B');
57 | // console.log(`Params: ${p}`);
58 | next(10 * p);
59 | }
60 | function taskC(p, next) {
61 | console.log('Task C');
62 | // console.log(`Params: ${p}`);
63 | }
64 |
65 | var pipeFun = pipe(taskA, taskB, taskC);
66 | pipeFun(10);
67 |
--------------------------------------------------------------------------------
/code/function/promise.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Promise 实现延迟执行
3 | */
4 | function wait(ms) {
5 | return new Promise(resolve => {
6 | setTimeout(resolve, ms);
7 | });
8 | }
9 |
10 | wait(100).then(function () {
11 | console.log('step 1');
12 | return wait(400);
13 | }).then(function () {
14 | console.log('step 2');
15 | return wait(700);
16 | }).then(function () {
17 | console.log('step 3');
18 | }).then(function () {
19 | // async/await 实现延迟执行
20 | (async () => {
21 | await wait(200);
22 | console.log('step 4');
23 | await wait(500);
24 | console.log('step 5');
25 | await wait(800);
26 | console.log('step 6');
27 | })();
28 | });
29 |
--------------------------------------------------------------------------------
/code/function/proxy.js:
--------------------------------------------------------------------------------
1 | // 代理请求拦截代理示例
2 | (function() {
3 | var open = XMLHttpRequest.prototype.open;
4 | XMLHttpRequest.prototype.open = function() {
5 | var url = arguments[1];
6 | var serverNameRegex = /^http:\/\/api.test.com/;
7 | if (serverNameRegex.test(url)) {
8 | arguments[1] = url.replace(serverNameRegex, "http://localhost:8080");
9 | }
10 | return open.apply(this, arguments);
11 | }
12 | })();
13 |
--------------------------------------------------------------------------------
/code/function/pureFunction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 什么是纯函数?
3 | * 一个函数输入参数确定时,输出结果是唯一确定的,那么它就是纯函数。
4 | * 过程抽象,可以提升函数纯度
5 | */
6 |
7 | function setColor(el, color) {
8 | el.style.color = color;
9 | }
10 | // function setColors(els, color) {
11 | // Array.from(els).map(el => setColor(el, color));
12 | // }
13 | //
14 | // setColors(document.querySelectorAll('#datalist > li'), '#ccc');
15 |
16 | // 过程抽象
17 | function multi(fn) {
18 | return function(arrayLike, ...args) {
19 | return Array.from(arrayLike).map(item => fn(item, ...args));
20 | }
21 | }
22 | const setColors = multi(setColor);
23 |
24 | setColors(document.querySelectorAll('#datalist > li'), '#ccc');
25 |
--------------------------------------------------------------------------------
/code/function/reduceFunction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * reduce 过程抽象
3 | * @method reducer
4 | * @param {Function} fn
5 | * @constructor
6 | * @return {*}
7 | */
8 | function reducer(fn) {
9 | return function(...args) {
10 | return args.reduce(fn.bind(this));
11 | }
12 | }
13 |
14 | /* function reducer(fn, isAsync) {
15 | if (isAsync) {
16 | return function(...args) {
17 | return args.reduce((a, b) => {
18 | return Promise.resolve(a).then(v => fn.call(this, v, b));
19 | });
20 | }
21 | }
22 |
23 | return function(...args) {
24 | return args.reduce(fn.bind(this));
25 | }
26 | } */
27 |
28 | function add(x, y) {
29 | return x + y;
30 | }
31 |
32 | function concat(arr1, arr2) {
33 | return arr1.concat(arr2);
34 | }
35 |
36 | function asyncMult(x, y) {
37 | return new Promise((resolve, reject) => {
38 | setTimeout(() => {
39 | console.log(`${x} * ${y} = ${x * y}`);
40 | resolve(x * y);
41 | }, 500);
42 | });
43 | }
44 |
45 | add = reducer(add);
46 | concat = reducer(concat);
47 |
48 | console.log(add(1, 2, 3));
49 | console.log(concat([1], [2, 3]));
50 |
51 | /* asyncMult = reducer(asyncMult, true);
52 | asyncMult(1, 2, 3, 4, 5, 6).then(v => console.log(v) );
53 | */
54 |
--------------------------------------------------------------------------------
/code/function/serialFunction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 函数异步化和串行执行
3 | */
4 | function delay(time, fn) {
5 | return function() {
6 | var self = this;
7 | var args = [].slice.call(arguments);
8 | var callback;
9 |
10 | // args.length 是函数 fn 的参数
11 | // 如果原始函数的实参多于形参,说明末尾多传入的一个参数为回调函数
12 | if (fn.length < args.length) {
13 | // 获取参数中的回调函数
14 | callback = args[args.length - 1];
15 | // 剔除参数中的回调函数
16 | args.length--;
17 | }
18 | setTimeout(function() {
19 | var res = fn.apply(this, args);
20 | callback && callback(res);
21 | }, time);
22 | }
23 | }
24 |
25 | function add(x, y) {
26 | return x + y;
27 | }
28 | add = delay(500, add);
29 | add(10, 20, r => console.log(r));
30 |
31 |
32 | function output(msg) {
33 | console.log(msg);
34 | }
35 | output = delay(500, output);
36 |
37 | function pipe() {
38 | var fnList = [].slice.call(arguments);
39 |
40 | // return fnList.reduceRight(function (a, b) {
41 | // return function () {
42 | // return b(a);
43 | // }
44 | // });
45 |
46 | // return fnList.reduceRight((a, b) => {
47 | // return () => {
48 | // return b(a);
49 | // }
50 | // });
51 |
52 | return fnList.reduceRight((a, b) => () => b(a));
53 | }
54 |
55 | // 如果 output 不做延时,只会输出 'message 1'
56 | var outputOneByOne = pipe(
57 | output.bind(null, 'message 1'),
58 | output.bind(null, 'message 2'),
59 | output.bind(null, 'message 3'),
60 | output.bind(null, 'message 4')
61 | );
62 |
63 | outputOneByOne();
64 |
--------------------------------------------------------------------------------
/code/function/setTimeout.js:
--------------------------------------------------------------------------------
1 | for (var i = 0; i < 5; i++) {
2 | setTimeout(function() {
3 | console.log(i);
4 | }, 1000);
5 | }
6 |
7 | console.log(i);
8 |
9 |
10 | // 题目:
11 | // 改造代码,期望输出变成 0 -> 1 -> 2 -> 3 -> 4 -> 5
12 |
13 | // ES 6
14 | const tasks = [];
15 | const output = i => new Promise(resolve => {
16 | setTimeout(() => {
17 | console.log(i);
18 | resolve();
19 | }, 1000 * i);
20 | });
21 |
22 | // 生成全部的异步操作
23 | for (var i = 0; i < 5; i++) {
24 | tasks.push(output(i));
25 | }
26 |
27 | // 异步操作完成之后,输出最后的 i
28 | Promise.all(tasks).then(() => {
29 | setTimeout(() => {
30 | console.log(i);
31 | }, 1000);
32 | });
33 |
34 |
35 | // ES 7
36 | const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
37 |
38 | (async () => {
39 | for (var i = 0; i < 5; i++) {
40 | await delay(1000);
41 | console.log(i);
42 | }
43 |
44 | await delay(1000);
45 | console.log(i);
46 | })();
--------------------------------------------------------------------------------
/code/function/tailcall.js:
--------------------------------------------------------------------------------
1 | function tailcall(f) {
2 | var value;
3 | var active = false;
4 | var accumulated = [];
5 |
6 | return function accumulator() {
7 | accumulated.push(arguments);
8 | if (!active) {
9 | active = true;
10 | while (accumulated.length) {
11 | args = accumulated.shift();
12 | // console.log(args);
13 | // args -> 1, 3
14 | // args -> 2, 2
15 | // args -> 3, 1
16 | // args -> 4, 0
17 | value = f.apply(this, args);
18 | }
19 | active = false;
20 | return value;
21 | }
22 | };
23 | }
24 |
25 | var sum = tailcall(function(x, y) {
26 | if (y > 0) {
27 | var s = sum(x + 1, y - 1);
28 | // console.log(s);
29 | // s -> undefined
30 | // s -> undefined
31 | // s -> undefined
32 | return s;
33 | } else {
34 | return x;
35 | }
36 | });
37 |
38 | sum(1, 3);
39 |
--------------------------------------------------------------------------------
/code/function/templet.js:
--------------------------------------------------------------------------------
1 | // 实现方法 `render(tmpl, data)`,将模板 `tmpl` 中的占位符,替换填充为 `data` 数据
2 |
3 | const tmpl = "I'm {{name}}. I'm {{age}} years old.";
4 | const data = { name: "Lucy", age: "23" };
5 | // --> const result = "I'm Lucy. I'm 23 years old.";
6 |
7 |
8 | {
9 | function render(tmpl, data) {
10 | return tmpl.replace(/\{\{(.*?)\}\}/g, (match, key) => data[key.trim()]);
11 | }
12 |
13 | const result = render(tmpl, data);
14 | console.log(result);
15 | }
16 |
17 |
18 | {
19 | String.prototype.render = function (data) {
20 | return this.replace(/{{(.*?)}}/g, (match, key) => data[key.trim()]);
21 | };
22 |
23 | const result = tmpl.render(data);
24 | console.log(result);
25 | }
--------------------------------------------------------------------------------
/code/function/throttle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 节流(throttle)
3 | */
4 | function throttle(fn, wait) {
5 | let timer;
6 | return function(...args) {
7 | if (!timer) {
8 | timer = setTimeout(() => timer = null, wait);
9 | return fn.apply(this, args);
10 | }
11 | }
12 | }
13 |
14 | // 注意: 回调函数必须包裹在 throttle 内
15 | document.addEventListener('click', throttle(function() {
16 | console.log("button clicked");
17 | }, 200), false);
18 |
--------------------------------------------------------------------------------
/code/function/trampoline.js:
--------------------------------------------------------------------------------
1 | /* function sum(x, y) {
2 | console.log([x, y]);
3 | if (y > 0) {
4 | return sum(x + 1, y - 1);
5 | }
6 | else {
7 | return x;
8 | }
9 | }
10 | sum(1, 100000) */
11 |
12 |
13 | /**
14 | * 蹦床函数
15 | * 将递归执行转为循环
16 | * 能够防止调用栈溢出
17 | * 并不是真正的尾递归优化
18 | * @method trampoline
19 | * @param {Function} f 递归函数
20 | * @return {Function} 循环函数
21 | */
22 | function trampoline(f) {
23 | while (f && f instanceof Function) {
24 | f = f();
25 | }
26 | return f;
27 | }
28 |
29 | function sum(x, y) {
30 | if (y > 0) {
31 | return sum.bind(null, x + 1, y - 1);
32 | }
33 | else {
34 | return x;
35 | }
36 | }
37 |
38 | trampoline(sum(1, 100000));
39 |
--------------------------------------------------------------------------------
/code/promise/traffic lights/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Traffic lights
9 |
10 |
11 |
12 |
13 |
18 |
19 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/code/promise/traffic lights/traffic-array.js:
--------------------------------------------------------------------------------
1 | const lightsEl = document.querySelector('#lights');
2 |
3 | function start(lightsEl, stateList) {
4 | const len = stateList.length;
5 | let currIndex = 0;
6 |
7 | setInterval(() => {
8 | const state = stateList[currIndex];
9 | lightsEl.className = state;
10 | currIndex = (currIndex + 1) % len;
11 | }, 1000);
12 | }
13 |
14 | start(lightsEl, ['stop', 'wait', 'pass']);
15 |
--------------------------------------------------------------------------------
/code/promise/traffic lights/traffic-poll.js:
--------------------------------------------------------------------------------
1 | const lightsEl = document.querySelector('#lights');
2 |
3 | function poll(...fnList) {
4 | let stateIndex = 0;
5 |
6 | return function(...args) {
7 | let fn = fnList[stateIndex++ % fnList.length];
8 | return fn.apply(this, args);
9 | }
10 | }
11 |
12 | function setState(state) {
13 | lightsEl.className = state;
14 | }
15 |
16 | let trafficStatePoll = poll(
17 | setState.bind(null, 'stop'),
18 | setState.bind(null, 'wait'),
19 | setState.bind(null, 'pass')
20 | );
21 |
22 | setInterval(trafficStatePoll, 1000);
23 |
--------------------------------------------------------------------------------
/code/promise/traffic lights/traffic-promise.js:
--------------------------------------------------------------------------------
1 | const lightsEl = document.querySelector('#lights');
2 |
3 | function wait(time) {
4 | return new Promise(resolve => setTimeout(resolve, time));
5 | }
6 |
7 | function setState(state) {
8 | lightsEl.className = state;
9 | }
10 |
11 | function reset() {
12 | Promise.resolve()
13 | .then(setState.bind(null, 'stop'))
14 | .then(wait.bind(null, 1000))
15 | .then(setState.bind(null, 'wait'))
16 | .then(wait.bind(null, 1500))
17 | .then(setState.bind(null, 'pass'))
18 | .then(wait.bind(null, 500))
19 | .then(reset);
20 | }
21 |
22 | // async function reset() {
23 | // //noprotect
24 | // while (1) {
25 | // setState('stop');
26 | // await wait(1500);
27 | // setState('wait');
28 | // await wait(1000);
29 | // setState('pass');
30 | // await wait(500);
31 | // }
32 | // }
33 |
34 | reset();
35 |
--------------------------------------------------------------------------------
/code/promise/traffic lights/traffic-state-machine.js:
--------------------------------------------------------------------------------
1 | const lightsEl = document.querySelector('#lights');
2 |
3 | /**
4 | * 状态机
5 | */
6 | function TrafficProtocol(el, isReset) {
7 | this.subject = el;
8 | this.isAutoReset = isReset;
9 | this.stateList = [];
10 | }
11 |
12 | TrafficProtocol.prototype.putState = function(fn) {
13 | this.stateList.push(fn);
14 | }
15 |
16 | TrafficProtocol.prototype.reset = function() {
17 | const subject = this.subject;
18 | this.statePromise = Promise.resolve();
19 |
20 | this.stateList.forEach((stateFn) => {
21 | this.statePromise = this.statePromise.then(() => {
22 | return new Promise(resolve => {
23 | stateFn(subject, resolve);
24 | });
25 | });
26 | });
27 |
28 | if (this.isAutoReset) {
29 | this.statePromise.then(this.reset.bind(this));
30 | }
31 | }
32 |
33 | TrafficProtocol.prototype.start = function() {
34 | this.reset();
35 | }
36 |
37 | // 实例化
38 | const traffic = new TrafficProtocol(lightsEl, true);
39 |
40 | traffic.putState(function(subject, next) {
41 | subject.className = 'stop';
42 | setTimeout(next, 2000);
43 | });
44 |
45 | traffic.putState(function(subject, next) {
46 | subject.className = 'wait';
47 | setTimeout(next, 1000);
48 | });
49 |
50 | traffic.putState(function(subject, next) {
51 | subject.className = 'pass';
52 | setTimeout(next, 3000);
53 | });
54 |
55 | traffic.start();
56 |
--------------------------------------------------------------------------------
/code/promise/traffic lights/traffic.css:
--------------------------------------------------------------------------------
1 | #lights > li {
2 | display: block;
3 | }
4 |
5 | #lights span {
6 | display: inline-block;
7 | width: 50px;
8 | height: 50px;
9 | background-color: gray;
10 | margin: 5px;
11 | border-radius: 50%;
12 | }
13 |
14 | #lights.stop li:nth-child(1) span {
15 | background-color: #a00;
16 | }
17 |
18 | #lights.wait li:nth-child(2) span {
19 | background-color: #aa0;
20 | }
21 |
22 | #lights.pass li:nth-child(3) span {
23 | background-color: #0a0;
24 | }
25 |
--------------------------------------------------------------------------------
/code/proxy/es6-proxy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 代理 Proxy
3 | */
4 | {
5 | let obj = {
6 | time: '2017-03-11',
7 | name: 'net',
8 | _no: 100
9 | };
10 |
11 | let monitor = new Proxy(obj, {
12 |
13 | // 拦截对象属性的读取
14 | get(target, key) {
15 | return target[key].replace('2017', '2018')
16 | },
17 |
18 | // 拦截对象设置属性
19 | set(target, key, value) {
20 | if (key === 'name') {
21 | return target[key] = value;
22 | } else {
23 | return target[key];
24 | }
25 | },
26 |
27 | // 拦截 key in object 操作
28 | has(target, key) {
29 | if (key === 'name') {
30 | return target[key]
31 | } else {
32 | return false;
33 | }
34 | },
35 |
36 | // 拦截 delete
37 | deleteProperty(target, key) {
38 | if (key.indexOf('_') > -1) {
39 | delete target[key];
40 | return true;
41 | } else {
42 | return target[key];
43 | }
44 | },
45 |
46 | // 拦截 Object.keys ,Object.getOwnPropertySymbols, Object.getOwnPropertyNames
47 | ownKeys(target) {
48 | return Object.keys(target).filter(item => item != 'time')
49 | }
50 | });
51 |
52 | console.log('get', monitor.time);
53 |
54 | monitor.time = '2018';
55 | monitor.name = 'Lucy';
56 | console.log('set', monitor.time, monitor);
57 |
58 | console.log('has', 'name' in monitor, 'time' in monitor);
59 |
60 | // delete monitor.time;
61 | // console.log('delete',monitor);
62 |
63 | delete monitor._no;
64 | console.log('delete',monitor);
65 |
66 | console.log('ownKeys', Object.keys(monitor));
67 |
68 | }
69 |
70 | /**
71 | * 反射 Reflect
72 | */
73 | {
74 | let obj = {
75 | time: '2017-03-11',
76 | name: 'net',
77 | _no: 100
78 | };
79 |
80 | console.log('Reflect get', Reflect.get(obj, 'time'));
81 |
82 | Reflect.set(obj, 'name', 'Lucy');
83 | console.log(obj);
84 |
85 | console.log('has', Reflect.has(obj, 'name'));
86 | }
--------------------------------------------------------------------------------
/code/redux/Redux API.md:
--------------------------------------------------------------------------------
1 | Redux 核心 API
2 | ---
3 |
4 | ## Store
5 |
6 | 1. Store 是应用状态 state 的管理者,包含下列四个函数:
7 |
8 | 1. `Store.getState()` // 获取整个 state 树
9 | 2. `Store.dispatch(action)` // 触发 state 改变的【唯一途径】
10 | 3. `Store.subscribe(listener)` // 可以理解为 DOM 中的 addEventListener
11 | 4. `Store.replaceReducer(nextReducer)` // 一般在 Webpack 按需加载时用到
12 |
13 | 2. Store 与 state 的关系:
14 |
15 | 1. `const store = createStore(reducer, initialState)`
16 | 2. `const state = store.getState()`
17 |
18 |
19 | ## Action
20 |
21 | 1. Action 就是包含 type 属性的普通对象,type 是实现用户行为追踪的关键,示例:
22 |
23 | ```javascript
24 | {
25 | type: 'ADD_TODO',
26 | payload: {
27 | id: 1,
28 | content: '待办事项1',
29 | completed: false
30 | }
31 | }
32 |
33 | ```
34 |
35 | 2. Action Creator 就是创造 action 的函数,返回值是一个 action 对象,示例:
36 |
37 | ```javascript
38 | function addTodo(text) {
39 | return {
40 | type: 'ADD_TODO',
41 | payload: {
42 | id: 2,
43 | content: text, // 待办事项内容
44 | completed: false
45 | }
46 | };
47 | }
48 |
49 | ```
50 |
51 | 通常,Action Creator 绑定到用户的操作(点击按钮等),根据参数不同,返回需要的 action。然后,通过 Store.dispatch(action) 改变 state,示例:
52 |
53 | ```javascript
54 | dispatch(addTodo(text))} />
55 | ```
56 |
57 | 3. Reducer
58 |
59 | 1. 用户的每次 dispatch(action) 操作都会触发 Reducer 执行
60 | 2. Reducer 就是一个函数,根据 action.type 更新 state
61 | 3. 最后,Reducer 返回最新的 state,完全替换原来的 state
62 |
63 | Reducer 示例:
64 |
65 | ```javascript
66 | function todos(state = [], action) {
67 | switch (action.type) {
68 | case ADD_TODO:
69 | return [
70 | ...state, {
71 | text: action.text,
72 | completed: false
73 | }
74 | ];
75 | default:
76 | return state;
77 | }
78 | }
79 |
80 | ```
81 |
82 |
83 | ## § 总结
84 |
85 | 1. Store 由 Redux.createStore(reducer) 生成,
86 | state 通过 Store.getState() 获取,Store 就是一个存储着整个应用状态的对象
87 |
88 | 2. Action 本质上是一个包含 type 属性的普通对象,由 Action Creator (函数) 产生。要改变 state,必须(只能)通过 Store.dispatch(action) 触发
89 |
90 | 3. Reducer 根据 action.type 来更新 state,并返回最新的 nextState。
91 | reducer 必须有确定返回值,否则 nextState 即为 undefined。
92 | 所以,state 可以看做 Reducer 返回值的汇总
93 |
94 | 4. Redux 数据流演示:
95 |
96 | ```
97 | Action Creator(action)
98 | => Store.dispatch(action)
99 | => Reducer(state, action)
100 | => state = nextState
101 | ```
102 |
103 |
104 | ## Redux 简单示例
105 |
106 |
107 | ```javascript
108 | /**
109 | * Action Creators
110 | * @method increment, decrement
111 | * @return {Object} Action
112 | */
113 | function increment() {
114 | return {
115 | type: 'INCREMENT'
116 | };
117 | }
118 |
119 | function decrement() {
120 | return {
121 | type: 'DECREMENT'
122 | };
123 | }
124 |
125 | /**
126 | * Reducer
127 | * @method reducer
128 | * @param {Objcet} state
129 | * @param {Objcet} action
130 | * @return {Objcet} nextState
131 | */
132 | function reducer(state, action) {
133 | // 首次调用设置初始 state
134 | state = state || {
135 | counter: 0
136 | };
137 |
138 | switch (action.type) {
139 | case 'INCREMENT':
140 | return {
141 | counter: state.counter + 1
142 | };
143 | case 'DECREMENT':
144 | return {
145 | counter: state.counter - 1
146 | };
147 | default:
148 | return state;
149 | }
150 | }
151 |
152 | /**
153 | * Store
154 | * @type {Object}
155 | */
156 | const store = Redux.createStore(reducer);
157 |
158 | // 初始化 state
159 | const state = store.getState();
160 |
161 | // 更新 state
162 | store.dispatch(increment());
163 | const state1 = store.getState();
164 |
165 | // 更新 state
166 | store.dispatch(increment());
167 | const state2 = store.getState();
168 |
169 | // 更新 state
170 | store.dispatch(decrement());
171 | const state3 = store.getState();
172 |
173 | const printState = (state) => console.log(JSON.stringify(state));
174 |
175 | printState(state);
176 | printState(state1);
177 | printState(state2);
178 | printState(state3);
179 | ```
180 |
181 |
182 |
183 |
--------------------------------------------------------------------------------
/code/redux/redex-combineReducers/reducer1.js:
--------------------------------------------------------------------------------
1 | const initState = {
2 | counter: 0,
3 | todos: []
4 | };
5 |
6 | function reducer(state, action) {
7 | if (!state) {
8 | return initState; // 若是初始化,可立即返回应用初始状态
9 | }
10 | const nextState = _.cloneDeep(state); // 否则,克隆原始状态
11 |
12 | switch (action.type) {
13 | case 'ADD_TODO': // 新增待办事项
14 | nextState.todos.push(action.payload);
15 | break;
16 | case 'INCREMENT': // 计数器加 1
17 | nextState.counter = nextState.counter + 1;
18 | break;
19 | }
20 |
21 | return nextState;
22 | }
23 |
--------------------------------------------------------------------------------
/code/redux/redex-combineReducers/reducers2/counterReducer.js:
--------------------------------------------------------------------------------
1 | export default function counterReducer(counter = 0, action) {
2 | // 传入的 state 其实是 state.counter
3 | switch (action.type) {
4 | case 'INCREMENT':
5 | // counter 是值传递,因此可以直接返回一个值
6 | return counter + 1;
7 | default:
8 | return counter;
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/code/redux/redex-combineReducers/reducers2/index.js:
--------------------------------------------------------------------------------
1 | import {combineReducers} from 'redux';
2 | import counterReducer from './counterReducer';
3 | import todosReducer from './todosReducer';
4 |
5 | const rootReducer = combineReducers({
6 | // 键名对应 reducer 管理的 state
7 | counter: counterReducer,
8 | todos: todosReducer
9 | })
10 |
11 | export default rootReducer;
12 |
--------------------------------------------------------------------------------
/code/redux/redex-combineReducers/reducers2/todosReducer.js:
--------------------------------------------------------------------------------
1 | export default function todosReducer(todos = [], action) {
2 | // 传入的 state 其实是 state.todos
3 | switch (action.type) {
4 | case 'ADD_TODO':
5 | return [
6 | ...todos,
7 | action.payload
8 | ];
9 | default:
10 | return todos;
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/code/redux/redux-compose.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Redux Stream
9 |
10 |
11 |
12 |
13 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/code/redux/redux-middleware.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Redux Synthesize
9 |
10 |
11 |
12 |
13 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/code/redux/redux-stream.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Redux Stream
9 |
10 |
11 |
12 |
13 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/code/redux/redux.js:
--------------------------------------------------------------------------------
1 | const createStore = (reducer) => {
2 | let state;
3 | let listeners = [];
4 |
5 | const getState = () => state;
6 |
7 | const dispatch = (action) => {
8 | state = reducer(state, action);
9 | listeners.forEach(listener => listener());
10 | };
11 |
12 | const subscribe = (listener) => {
13 | listeners.push(listener);
14 | const unsubscribe = () => {
15 | listeners = listeners.filter(currListener => currListener !== listener);
16 | }
17 | return unsubscribe;
18 | };
19 |
20 | dispatch({});
21 |
22 | return {
23 | getState,
24 | dispatch,
25 | subscribe
26 | };
27 | };
--------------------------------------------------------------------------------
/code/reflect/MVVM-Reflect.js:
--------------------------------------------------------------------------------
1 | function watch (obj, prop, cb) {
2 | return new Proxy(obj, {
3 | get: function (target, key, receiver) {
4 | return Reflect.get(target, key, receiver)
5 | },
6 | set: function (target, key, value, receiver) {
7 | const oldValue = target[key]
8 | const result = Reflect.set(target, key, value, receiver)
9 |
10 | if (prop === key) {
11 | cb(value, oldValue)
12 | }
13 |
14 | return result
15 | }
16 | })
17 | }
18 |
19 | let obj = watch({ a: 1 }, 'a', (newValue, oldValue) => {
20 | console.log('属性被修改: 新值:', newValue, ',旧值:', oldValue)
21 | })
22 |
23 | obj.a = 2
24 | obj.a = 3
25 |
--------------------------------------------------------------------------------
/code/scope/scope.js:
--------------------------------------------------------------------------------
1 |
2 | function Foo() {
3 | getName = function () { alert (1); };
4 | return this;
5 | }
6 | Foo.getName = function () { alert (2);};
7 | Foo.prototype.getName = function () { alert (3);};
8 | var getName = function () { alert (4);};
9 | function getName() { alert (5);}
10 |
11 | /**
12 | * 问题:说出以下输出结果
13 | * */
14 | Foo.getName(); // 2
15 | getName(); // 4
16 | Foo().getName(); // 1
17 | getName(); // 1
18 | new Foo.getName(); // 2
19 | new Foo().getName(); // 3
20 | new new Foo().getName(); // 3
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | /**
43 | * 答案:2 4 1 1 2 3 3
44 | */
45 |
--------------------------------------------------------------------------------
/code/scope/scope1.js:
--------------------------------------------------------------------------------
1 | // === 分析 === //
2 |
3 | // [1]定义了函数 Foo
4 | function Foo() {
5 | getName = function () { alert (1); };
6 | return this;
7 | }
8 |
9 | // [2]为 Foo 创建了一个静态属性 getName 并赋值一个匿名函数
10 | Foo.getName = function () { alert (2);};
11 |
12 | // [3]在 Foo 的原型对象上新创建了一个叫 getName 的匿名函数
13 | Foo.prototype.getName = function () { alert (3);};
14 |
15 | // [4]通过函数表达式创建了一个 getName 函数
16 | var getName = function () { alert (4);};
17 |
18 | // [5]声明一个叫 getName 函数
19 | function getName() { alert (5);}
20 |
21 | // === 结果 === //
22 | // 访问 Foo 函数的静态属性[2]
23 | Foo.getName(); // 输出 2
24 |
--------------------------------------------------------------------------------
/code/scope/scope2.js:
--------------------------------------------------------------------------------
1 | // === 分析 === //
2 | function Foo() {
3 | getName = function () { alert (1); };
4 | return this;
5 | }
6 |
7 | // [1]变量声明提升
8 | var getName;
9 |
10 | // [2]函数声明提升,覆盖 var 声明[1]
11 | function getName() { alert (5);}
12 |
13 | Foo.getName = function () { alert (2);};
14 | Foo.prototype.getName = function () { alert (3);};
15 |
16 | // [3]覆盖 function 声明[2]
17 | getName = function () { alert (4);};
18 |
19 | // === 结果 === //
20 | getName(); // 最终输出 4
--------------------------------------------------------------------------------
/code/scope/scope3.js:
--------------------------------------------------------------------------------
1 | // === 分析 === //
2 | function Foo() {
3 | getName = function () { alert (1); }; // [1]
4 | return this;
5 | }
6 | var getName;
7 | function getName() { alert (5);}
8 | Foo.getName = function () { alert (2);};
9 | Foo.prototype.getName = function () { alert (3);};
10 | getName = function () { alert (4);};
11 |
12 | // === 结果 === //
13 | // 先执行 Foo 函数,再调用 Foo 函数返回值对象的 getName 方法
14 | Foo().getName();
15 | // 1 执行 Foo 函数,覆盖外层作用域的 getName 函数
16 | // 2 返回 this 对象,Foo()直接调用,this 指向 window 对象
17 | // 3 执行 window.getName(),此时 window.getName 已被覆盖[1]
18 | // 4 最终输出 1
--------------------------------------------------------------------------------
/code/scope/scope4.js:
--------------------------------------------------------------------------------
1 | // === 分析 === //
2 | function Foo() {
3 | getName = function () { alert (1); }; // [1]
4 | return this;
5 | }
6 | var getName;
7 | function getName() { alert (5);}
8 | Foo.getName = function () { alert (2);};
9 | Foo.prototype.getName = function () { alert (3);};
10 | getName = function () { alert (4);};
11 | Foo().getName(); // [2]
12 |
13 | // === 结果 === //
14 | // 直接调用 getName 函数,相当于 window.getName()
15 | getName();
16 | // 1 window.getName 已被覆盖[2]<-[1]
17 | // 2 最终输出 3
--------------------------------------------------------------------------------
/code/scope/scope5.js:
--------------------------------------------------------------------------------
1 | // === 分析 === //
2 | function Foo() {
3 | getName = function () { alert (1); };
4 | return this;
5 | }
6 | var getName;
7 | function getName() { alert (5);}
8 | Foo.getName = function () { alert (2);};
9 | Foo.prototype.getName = function () { alert (3);};
10 | getName = function () { alert (4);};
11 | Foo().getName();
12 |
13 | // === 结果 === //
14 | new Foo.getName();
15 | // 1 优先级比较:. 运算符 高于 new 运算符
16 | // 2 转化为 new (Foo.getName)();
17 | // 3 转化为 var bar = function () { alert (2);}; new bar;
18 | // 4 相当于将 function () { alert (2);}; 作为了构造函数执行
19 | // 5 最终输出 2
--------------------------------------------------------------------------------
/code/scope/scope6.js:
--------------------------------------------------------------------------------
1 | // === 分析 === //
2 | function Foo() {
3 | getName = function () { alert (1); };
4 | return this;
5 | }
6 | var getName;
7 | function getName() { alert (5);}
8 | Foo.getName = function () { alert (2);};
9 | Foo.prototype.getName = function () { alert (3);}; // [3]
10 | getName = function () { alert (4);};
11 | Foo().getName();
12 |
13 | // === 结果 === //
14 | new Foo().getName();
15 | // 1 优先级比较:() 运算符 高于 new 运算符
16 | // 2 转化为 (new Foo()).getName();
17 | // 3 执行 new Foo(); 此时 Foo 作为构造函数却有返回值
18 | // 3.1 如果返回值为基本类型,则忽略该返回值,返回构造函数的实例化对象
19 | // 3.2 如果返回值为引用类型,则返回该引用类型,覆盖构造函数的实例化对象
20 | // 4 this 在构造函数中本来就代表当前实例化对象,所以返回 Foo 的实例化对象
21 | // 5 调用 Foo 实例化对象的 getName 方法
22 | // 6 发现 Foo 构造函数没有为实例化对象添加 this.getName 属性方法
23 | // 7 此时去当前对象的原型对象 Foo.prototype 上找,找到[3]
24 | // 9 最终输出 3
--------------------------------------------------------------------------------
/code/scope/scope7.js:
--------------------------------------------------------------------------------
1 | // === 分析 === //
2 | function Foo() {
3 | getName = function () { alert (1); };
4 | return this;
5 | }
6 | var getName;
7 | function getName() { alert (5);}
8 | Foo.getName = function () { alert (2);};
9 | Foo.prototype.getName = function () { alert (3);}; // [bar]
10 | getName = function () { alert (4);};
11 | Foo().getName();
12 |
13 | // === 结果 === //
14 | new new Foo().getName();
15 | // 1 运算符关联性:new 运算符从右向左
16 | // 2 new ((new Foo()).getName)();
17 | // 3 执行 new Foo(); 得到 Foo 的实例化对象
18 | // 4 找到 Foo.prototype.getName 作为构造函数
19 | // 5 转化为 new bar();
20 | // 6 最终输出 3
--------------------------------------------------------------------------------
/jQuery之DOM操作.doc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/jQuery之DOM操作.doc
--------------------------------------------------------------------------------
/property和attribute的区别.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/property和attribute的区别.docx
--------------------------------------------------------------------------------
/代码规范/React 编码规范.md:
--------------------------------------------------------------------------------
1 | # React/JSX 编码规范(Airbnb)
2 |
3 | > 翻译整理自 *[Airbnb React/JSX Style Guide](https://github.com/airbnb/javascript/tree/master/react)*
4 |
5 | ## 目录
6 |
7 | 1. [基本规范](#基本规范)
8 | 1. [创建组件的三种方式](#创建组件的三种方式)
9 | 1. [弃用 Mixins](#Mixins)
10 | 1. [命名](#命名)
11 | 1. [声明组件](#声明组件)
12 | 1. [代码缩进](#代码缩进)
13 | 1. [使用双引号](#使用双引号)
14 | 1. [空格](#空格)
15 | 1. [属性](#属性)
16 | 1. [Refs](#refs)
17 | 1. [括号](#括号)
18 | 1. [标签](#t标签)
19 | 1. [函数/方法](#函数/方法)
20 | 1. [组件生命周期书写顺序](#组件生命周期书写顺序)
21 | 1. [弃用 isMounted](#isMounted)
22 |
23 | ## 基本规范
24 |
25 | - 原则上每个文件只写一个组件, 多个[无状态组件](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions)可以放在单个文件中. eslint: [`react/no-multi-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md#ignorestateless).
26 | - 推荐使用 JSX 语法编写 React 组件, 而不是 `React.createElement`
27 | ## 创建组件的三种方式
28 | Class vs React.createClass vs stateless
29 |
30 | - 如果你的组件有内部状态或者是`refs`, 推荐使用 `class extends React.Component` 而不是 `React.createClass`.
31 | eslint: [`react/prefer-es6-class`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md) [`react/prefer-stateless-function`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md)
32 |
33 | ```jsx
34 | // bad
35 | const Listing = React.createClass({
36 | // ...
37 | render() {
38 | return {this.state.hello}
;
39 | }
40 | });
41 |
42 | // good
43 | class Listing extends React.Component {
44 | // ...
45 | render() {
46 | return {this.state.hello}
;
47 | }
48 | }
49 | ```
50 |
51 | 如果你的组件没有状态或是没有引用`refs`, 推荐使用普通函数(非箭头函数)而不是类:
52 |
53 | ```jsx
54 | // bad
55 | class Listing extends React.Component {
56 | render() {
57 | return {this.props.hello}
;
58 | }
59 | }
60 |
61 | // bad (relying on function name inference is discouraged)
62 | const Listing = ({ hello }) => (
63 | {hello}
64 | );
65 |
66 | // good
67 | function Listing({ hello }) {
68 | return {hello}
;
69 | }
70 | ```
71 |
72 | ## Mixins
73 |
74 | - [不要使用 mixins](https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html).
75 |
76 | > 因为 Mixins 会增加隐式依赖, 导致命名冲突, 并且会增加复杂度.大多数情况下, 可以更好的方法代替 Mixins, 如:组件化, 高阶组件, 工具组件等.
77 |
78 | ## 命名
79 |
80 | - **扩展名**: React 组件使用 `.jsx` 扩展名.
81 | - **文件名**: 文件名使用帕斯卡命名. 如, `ReservationCard.jsx`.
82 | - **引用命名**: React组件名使用帕斯卡命名, 实例使用骆驼式命名. eslint: [`react/jsx-pascal-case`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md)
83 |
84 | ```jsx
85 | // bad
86 | import reservationCard from './ReservationCard';
87 |
88 | // good
89 | import ReservationCard from './ReservationCard';
90 |
91 | // bad
92 | const ReservationItem = ;
93 |
94 | // good
95 | const reservationItem = ;
96 | ```
97 |
98 | - **组件命名**: 组件名与当前文件名一致. 如 `ReservationCard.jsx` 应该包含名为 `ReservationCard`的组件. 如果整个目录是一个组件, 使用 `index.js`作为入口文件, 然后直接使用 `index.js` 或者目录名作为组件的名称:
99 |
100 | ```jsx
101 | // bad
102 | import Footer from './Footer/Footer';
103 |
104 | // bad
105 | import Footer from './Footer/index';
106 |
107 | // good
108 | import Footer from './Footer';
109 | ```
110 | - **高阶组件命名**: 生成一个新的组件时, 其中的组件名 `displayName` 应该为高阶组件名和传入组件名的组合. 例如, 高阶组件 `withFoo()`, 当传入一个 `Bar` 组件的时候, 生成的组件名 `displayName` 应该为 `withFoo(Bar)`.
111 |
112 | > 因为一个组件的 `displayName` 可能在调试工具或错误信息中使用到. 清晰合理的命名, 能帮助我们更好的理解组件执行过程, 更好的 Debug.
113 |
114 | ```jsx
115 | // bad
116 | export default function withFoo(WrappedComponent) {
117 | return function WithFoo(props) {
118 | return ;
119 | }
120 | }
121 |
122 | // good
123 | export default function withFoo(WrappedComponent) {
124 | function WithFoo(props) {
125 | return ;
126 | }
127 |
128 | const wrappedComponentName = WrappedComponent.displayName
129 | || WrappedComponent.name
130 | || 'Component';
131 |
132 | WithFoo.displayName = `withFoo(${wrappedComponentName})`;
133 | return WithFoo;
134 | }
135 | ```
136 |
137 | - **属性命名**: 避免使用 DOM 相关的属性来用命名自定义属性
138 |
139 | > 因为对于`style` 和 `className` 这样的属性名, 我们都会默认它们代表一些特殊的含义.
140 |
141 | ```jsx
142 | // bad
143 |
144 |
145 | // good
146 |
147 | ```
148 |
149 | ## 声明组件
150 |
151 | - 不要使用 `displayName` 来命名 React 组件, 而是使用引用来命名组件, 如 class 名称.
152 |
153 | ```jsx
154 | // bad
155 | export default React.createClass({
156 | displayName: 'ReservationCard',
157 | // stuff goes here
158 | });
159 |
160 | // good
161 | export default class ReservationCard extends React.Component {
162 | }
163 | ```
164 |
165 | ## 代码缩进
166 |
167 | - 遵循以下的 JSX 语法缩进/格式. eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md)
168 |
169 | ```jsx
170 | // bad
171 |
173 |
174 | // good, 有多行属性的话, 新建一行关闭标签
175 |
179 |
180 | // 若能在一行中显示, 直接写成一行
181 |
182 |
183 | // 子元素按照常规方式缩进
184 |
188 |
189 |
190 | ```
191 |
192 | ## 使用双引号
193 |
194 | - 对于 JSX 属性值总是使用双引号(`"`), 其他均使用单引号(`'`). eslint: [`jsx-quotes`](http://eslint.org/docs/rules/jsx-quotes)
195 |
196 | > 因为 HTML 属性也是用双引号, 因此 JSX 的属性也遵循此约定.
197 |
198 | ```jsx
199 | // bad
200 |
201 |
202 | // good
203 |
204 |
205 | // bad
206 |
207 |
208 | // good
209 |
210 | ```
211 |
212 | ## 空格
213 |
214 | - 总是在自动关闭的标签前加一个空格, 正常情况下不需要换行. eslint: [`no-multi-spaces`](http://eslint.org/docs/rules/no-multi-spaces), [`react/jsx-tag-spacing`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-tag-spacing.md)
215 |
216 | ```jsx
217 | // bad
218 |
219 |
220 | // very bad
221 |
222 |
223 | // bad
224 |
226 |
227 | // good
228 |
229 | ```
230 |
231 | - 不要在JSX `{}` 引用括号里两边加空格. eslint: [`react/jsx-curly-spacing`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md)
232 |
233 | ```jsx
234 | // bad
235 |
236 |
237 | // good
238 |
239 | ```
240 |
241 | ## 属性
242 |
243 | - JSX 属性名使用骆驼式风格`camelCase`.
244 |
245 | ```jsx
246 | // bad
247 |
251 |
252 | // good
253 |
257 | ```
258 |
259 | - 如果属性值为 `true`, 可以直接省略. eslint: [`react/jsx-boolean-value`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md)
260 |
261 | ```jsx
262 | // bad
263 |
264 |
265 | // good
266 |
267 | ```
268 |
269 | - `
` 标签总是添加 `alt` 属性. 如果图片以陈述方式显示, `alt` 可为空, 或者`
` 要包含`role="presentation"`. eslint: [`jsx-a11y/alt-text`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/alt-text.md)
270 |
271 | ```jsx
272 | // bad
273 |
274 |
275 | // good
276 |
277 |
278 | // good
279 |
280 |
281 | // good
282 |
283 | ```
284 |
285 | - 不要在 `alt` 值里使用如 "image", "photo", or "picture" 包括图片含义这样的词, 中文同理. eslint: [`jsx-a11y/img-redundant-alt`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/img-redundant-alt.md)
286 |
287 | > 因为屏幕助读器已经把 `img` 标签标注为图片了, 所以没有必要再在 `alt` 里重复说明.
288 |
289 | ```jsx
290 | // bad
291 |
292 |
293 | // good
294 |
295 | ```
296 |
297 | - 使用有效正确的 aria `role`属性值 [ARIA roles](https://www.w3.org/TR/wai-aria/roles#role_definitions). eslint: [`jsx-a11y/aria-role`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/aria-role.md)
298 |
299 | ```jsx
300 | // bad - not an ARIA role
301 |
302 |
303 | // bad - abstract ARIA role
304 |
305 |
306 | // good
307 |
308 | ```
309 |
310 | - 不要在标签上使用 `accessKey` 属性. eslint: [`jsx-a11y/no-access-key`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-access-key.md)
311 |
312 | > 因为屏幕助读器在键盘快捷键与键盘命令时造成的不统一性会导致阅读性更加复杂.
313 |
314 | ```jsx
315 | // bad
316 |
317 |
318 | // good
319 |
320 | ```
321 | - 避免使用数组的 index 来作为属性`key`的值, 推荐使用唯一ID. ([Index as a key is an anti pattern](https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318))
322 |
323 | ```jsx
324 | // bad
325 | {todos.map((todo, index) =>
326 |
330 | )}
331 |
332 | // good
333 | {todos.map(todo => (
334 |
338 | ))}
339 | ```
340 |
341 | - 对于所有非必填写属性, 总是手动去定义`defaultProps`属性.
342 |
343 | > 因为 propTypes 可以作为组件的文档说明, 而声明 defaultProps 使阅读代码的人不需要去假设默认值. 另外, 显式的声明默认属性可以让你的组件跳过属性类型的检查.
344 |
345 | ```jsx
346 | // bad
347 | function SFC({ foo, bar, children }) {
348 | return {foo}{bar}{children}
;
349 | }
350 |
351 | SFC.propTypes = {
352 | foo: PropTypes.number.isRequired,
353 | bar: PropTypes.string,
354 | children: PropTypes.node,
355 | };
356 |
357 | // good
358 | function SFC({ foo, bar, children }) {
359 | return {foo}{bar}{children}
;
360 | }
361 | SFC.propTypes = {
362 | foo: PropTypes.number.isRequired,
363 | bar: PropTypes.string,
364 | children: PropTypes.node,
365 | };
366 | SFC.defaultProps = {
367 | bar: '',
368 | children: null,
369 | };
370 | ```
371 |
372 | ## Refs
373 |
374 | - 总是使用回调函数方式定义 ref. eslint: [`react/no-string-refs`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md)
375 |
376 | ```jsx
377 | // bad
378 |
381 |
382 | // good
383 | { this.myRef = ref; }}
385 | />
386 | ```
387 |
388 |
389 | ## 括号
390 |
391 | - 将多行 JSX 标签写在 `()`里. eslint: [`react/jsx-wrap-multilines`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md)
392 |
393 | ```jsx
394 | // bad
395 | render() {
396 | return
397 |
398 | ;
399 | }
400 |
401 | // good
402 | render() {
403 | return (
404 |
405 |
406 |
407 | );
408 | }
409 |
410 | // good, 单行可以不需要
411 | render() {
412 | const body = hello
;
413 | return {body};
414 | }
415 | ```
416 |
417 | ## 标签
418 |
419 | - 对于没有子元素的标签, 总是自关闭标签. eslint: [`react/self-closing-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md)
420 |
421 | ```jsx
422 | // bad
423 |
424 |
425 | // good
426 |
427 | ```
428 |
429 | - 如果组件有多行的属性, 关闭标签时新建一行. eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md)
430 |
431 | ```jsx
432 | // bad
433 |
436 |
437 | // good
438 |
442 | ```
443 |
444 | ## 函数/方法
445 |
446 | - 使用箭头函数来获取本地变量.
447 |
448 | ```jsx
449 | function ItemList(props) {
450 | return (
451 |
452 | {props.items.map((item, index) => (
453 | - doSomethingWith(item.name, index)}
456 | />
457 | ))}
458 |
459 | );
460 | }
461 | ```
462 |
463 | - 当在 `render()` 里使用事件处理方法时, 提前在构造函数里把 `this` 绑定上去. eslint: [`react/jsx-no-bind`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md)
464 |
465 | > 因为在每次 `render` 过程中, 再调用 `bind` 都会新建一个新的函数, 浪费资源.
466 |
467 | ```jsx
468 | // bad
469 | class extends React.Component {
470 | onClickDiv() {
471 | // do stuff
472 | }
473 |
474 | render() {
475 | return ;
476 | }
477 | }
478 |
479 | // good
480 | class extends React.Component {
481 | constructor(props) {
482 | super(props);
483 |
484 | this.onClickDiv = this.onClickDiv.bind(this);
485 | }
486 |
487 | onClickDiv() {
488 | // do stuff
489 | }
490 |
491 | render() {
492 | return ;
493 | }
494 | }
495 | ```
496 |
497 | - 在React组件中, 不要给所谓的私有函数添加 `_` 前缀, 本质上它并不是私有的.
498 | > 因为`_` 下划线前缀在某些语言中通常被用来表示私有变量或者函数. 但是不像其他的一些语言, 在 JS 中没有原生支持所谓的私有变量, 所有的变量函数都是共有的. 了解更多详情请查看 Issue [#1024](https://github.com/airbnb/javascript/issues/1024), 和 [#490](https://github.com/airbnb/javascript/issues/490) .
499 |
500 | ```jsx
501 | // bad
502 | React.createClass({
503 | _onClickSubmit() {
504 | // do stuff
505 | },
506 |
507 | // other stuff
508 | });
509 |
510 | // good
511 | class extends React.Component {
512 | onClickSubmit() {
513 | // do stuff
514 | }
515 |
516 | // other stuff
517 | }
518 | ```
519 |
520 | - 在 `render` 方法中总是确保 `return` 返回值. eslint: [`react/require-render-return`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-render-return.md)
521 |
522 | ```jsx
523 | // bad
524 | render() {
525 | ();
526 | }
527 |
528 | // good
529 | render() {
530 | return ();
531 | }
532 | ```
533 |
534 | ## 组件生命周期书写顺序
535 |
536 | - `class extends React.Component` 的生命周期函数:
537 |
538 | 1. `static` 方法(可选)
539 | 1. `constructor` 构造函数
540 | 1. `getChildContext` 获取子元素内容
541 | 1. `componentWillMount` 组件渲染前
542 | 1. `componentDidMount` 组件渲染后
543 | 1. `componentWillReceiveProps` 组件将接受新的数据
544 | 1. `shouldComponentUpdate` 判断组件是否需要重新渲染
545 | 1. `componentWillUpdate` 上面的方法返回 `true`时, 组件将重新渲染
546 | 1. `componentDidUpdate` 组件渲染结束
547 | 1. `componentWillUnmount` 组件将从DOM中清除, 做一些清理任务
548 | 1. *事件绑定* 如 `onClickSubmit()` 或 `onChangeDescription()`
549 | 1. *`render` 里的 getter 方法* 如 `getSelectReason()` 或 `getFooterContent()`
550 | 1. *可选的 render 方法* 如 `renderNavigation()` 或 `renderProfilePicture()`
551 | 1. `render` render() 方法
552 |
553 | - 如何定义 `propTypes`, `defaultProps`, `contextTypes` 等属性...
554 |
555 | ```jsx
556 | import React, { PropTypes } from 'react';
557 |
558 | const propTypes = {
559 | id: PropTypes.number.isRequired,
560 | url: PropTypes.string.isRequired,
561 | text: PropTypes.string,
562 | };
563 |
564 | const defaultProps = {
565 | text: 'Hello World',
566 | };
567 |
568 | class Link extends React.Component {
569 | static methodsAreOk() {
570 | return true;
571 | }
572 |
573 | render() {
574 | return {this.props.text};
575 | }
576 | }
577 |
578 | Link.propTypes = propTypes;
579 | Link.defaultProps = defaultProps;
580 |
581 | export default Link;
582 | ```
583 |
584 | - `React.createClass` 方式的生命周期函数(不推荐)与 ES6 class 方式稍有不同: eslint: [`react/sort-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md)
585 |
586 | 1. `displayName` 设定组件名称
587 | 1. `propTypes` 设置属性的类型
588 | 1. `contextTypes` 设置上下文类型
589 | 1. `childContextTypes` 设置子元素上下文类型
590 | 1. `mixins` 添加一些 mixins
591 | 1. `statics`
592 | 1. `defaultProps` 设置默认的属性值
593 | 1. `getDefaultProps` 获取默认属性值
594 | 1. `getInitialState` 获取初始状态
595 | 1. `getChildContext`
596 | 1. `componentWillMount`
597 | 1. `componentDidMount`
598 | 1. `componentWillReceiveProps`
599 | 1. `shouldComponentUpdate`
600 | 1. `componentWillUpdate`
601 | 1. `componentDidUpdate`
602 | 1. `componentWillUnmount`
603 | 1. *clickHandlers or eventHandlers* like `onClickSubmit()` or `onChangeDescription()`
604 | 1. *getter methods for `render`* like `getSelectReason()` or `getFooterContent()`
605 | 1. *Optional render methods* like `renderNavigation()` or `renderProfilePicture()`
606 | 1. `render`
607 |
608 | ## isMounted
609 |
610 | - 不要再使用 `isMounted`. eslint: [`react/no-is-mounted`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md)
611 |
612 | > 因为`isMounted` [设计模式](https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html) 在 ES6 class 中无法使用, 官方将在未来的版本里删除此方法.
613 |
614 | **[⬆ 回到顶部](#目录)**
615 |
--------------------------------------------------------------------------------
/代码规范/关于程序中命名的总结.md:
--------------------------------------------------------------------------------
1 | # 关于程序中命名的总结
2 |
3 | ### 良好命名的必要
4 |
5 | 1. 代码命名是件很头疼的事情,既要`语义化`又要`简洁`,最终目的为了代码的`可维护性`
6 | 2. 合适的命名会使代码读起来优雅流畅、耐人寻味,让你的代码如艺术品一般。比如:
7 | * `promise` - 异步请求数据时,“承诺”一定会回应(成功或失败),很有画面感
8 | * `on`/`off` - jquery 中表示监听和销毁事件,言简意赅,方便易用
9 | * `fork` - github 的 fork 操作,由“叉子”引申为复刻属于自己的分叉,形象生动
10 |
11 | ### 良好命名的要点
12 |
13 | 1. 使用能够表达意图的名字
14 |
15 | 良好命名能够告诉我们它要做什么,为什么存在,以及是如何工作的。选择能够表达意图的名字,有利于我们清楚理解程序含义。相反,名不副实则会误导阅读代码的人,有时需要打断点才能判断出程序的真实功能。
16 |
17 | 2. 不要怕在选择名字上花时间
18 |
19 | 多尝试几种不同的名字,直到足以描述程序含义,不要害怕在命名上花时间。以后阅读你代码的人(包括自己)会因此受益。而且,一个具有清晰描述性的命名,还有助于你在心中理清模块的设计。良好的命名可能需要花费额外的时间,但是从长远来看,利大于弊。
20 |
21 | 3. 类名
22 |
23 | 类名应该是个名词或名词词组,如 Customer、Account、AddressParser。父类的命名应该简短,子类的应该详尽,通过形容词来描述其不同于父类之处,如 SavingsAccount 继承自 Account。
24 |
25 | 4. 变量名
26 |
27 | 变量名一般应该是名词。如果是布尔变量,应写成谓词的形式,如 isEmpty,这样放到if 语句才便于理解。
28 |
29 |
30 | 5. 方法名
31 |
32 | 方法名应该是一个动词或动词词组,如 sendMessage()、getUser()、save()。访问器和设置器应该分别带有 get 和 set 前缀。返回布尔值的方法应该前缀 `is`,如 isPostable()。
33 |
34 | ### 常用动词、对仗词
35 |
36 | | 单词 | 语义 |
37 | | :--: | :--: |
38 | |add / remove|添加 / 移除|
39 | |add / delete|添加 / 删除|
40 | |able / disable| 可用 / 不可用|
41 | |insert / delete|插入 / 删除|
42 | |start / stop|开始 / 停止|
43 | |begin / end|开始 / 结束|
44 | |send / receive|发送 / 接收|
45 | |get / set|取出 / 设置|
46 | |get / release|获取 / 释放|
47 | |put / get|放入 / 取出|
48 | |up / down|向上 / 向下|
49 | |show / hide|显示 / 隐藏|
50 | |open / close|打开 / 关闭|
51 | |source / target|源 / 目标|
52 | |source / destination|源 / 目的地|
53 | |serialize / unserialize|序列化 / 反序列化|
54 | |increment / decrement|增加 / 减少|
55 | |lock / unlock|锁 / 解锁|
56 | |old / new|旧的 / 新的|
57 | |first / last|第一个 / 最后一个|
58 | |next / previous|下一个 / 前一个|
59 | |create / destroy|创建 / 销毁|
60 | |min / max|最小 / 最大|
61 | |visible / invisible|可见 / 不可见|
62 | | valid / invalid |验证有效 / 验证无效|
63 | |pop / push|出栈 / 入栈|
64 | |store / query|存储 / 查询|
65 | |reset | 重置|
66 | |format|格式化|
67 | | validate | 验证|
68 | |handle | 处理|
69 | |toggle | 开关 |
70 | |dispatch|分派|
71 |
72 | ### 常用名词
73 |
74 | | 单词 | 语义 |
75 | | :--: | :--: |
76 | |settings|配置|
77 | |iterator|迭代器|
78 | |adapter|适配器|
79 | |listener|监听器|
80 | |trigger|触发器|
81 | |acceptor|接收器|
82 | |connector|连接器|
83 | |dispatcher|分派器|
84 | |reactor|反应器|
85 | |executor|执行器|
86 | |parser|解析器|
87 | |builder|生成器|
88 | |handler|处理器|
89 | |Invoker|调用方|
90 | |validator|验证器|
91 | |modle|模型|
92 |
93 | ### 常用缩写
94 |
95 | 建议在 css 中命名多用缩写,在 js 中根据实际场景选择是否使用
96 |
97 | | 原始单词| 缩写 |
98 | | :--: | :--: |
99 | |abbreviation | abbr|
100 | |address | addr|
101 | |administrator | admin|
102 | |analysis | anlys|
103 | |architecture | arch|
104 | |ascending | asc|
105 | |attribute |attr|
106 | |authentication |auth|
107 | |background |bg|
108 | |body |bd|
109 | |business |biz|
110 | |button |btn|
111 | |buffer |buf|
112 | |calculator |calc|
113 | |column |col|
114 | |command |cmd|
115 | |configuration |conf|
116 | |constant |const|
117 | |content |cnt|
118 | |context |cxt|
119 | |control |ctrl|
120 | |copy |cp|
121 | |delete |del|
122 | |dependency |dep|
123 | |develop |dev|
124 | |distribution |dist|
125 | |document |doc|
126 | |element |el|
127 | |environment |env|
128 | |escape |esc|
129 | |execute |exec|
130 | |footer |ft|
131 | |format |fmt|
132 | |frequently asked questions |faqs|
133 | |function |fn|
134 | |header |hd|
135 | |information |info|
136 | |insert |ins|
137 | |introduction |intro|
138 | |label |lbl|
139 | |library |lib|
140 | |list |ls|
141 | |lock |lk|
142 | |manager |mgr|
143 | |maximum |max|
144 | |message |msg|
145 | |millimeter |ml|
146 | |minimum |min|
147 | |module |mod|
148 | |move |mv|
149 | |multiply |mul|
150 | |navigation |nav|
151 | |number |num|
152 | |option |opt|
153 | |package |pkg|
154 | |page |pg|
155 | |parameter |param|
156 | |password |pwd|
157 | |picture |pic|
158 | |position |pos|
159 | |property |prop|
160 | |recommendation |rec|
161 | |register |reg|
162 | |remove |rm|
163 | |repository |repo|
164 | |request |req|
165 | |response |resp|
166 | |screen |scr|
167 | |second |sec|
168 | |something |sth|
169 | |source |src|
170 | |server |sev|
171 | |system |sys|
172 | |table |tbl|
173 | |telephone |tel|
174 | |template |tpl|
175 | |to |2|
176 | |user |usr|
177 | |value |val|
178 | |version |ver|
179 | |window |win|
180 |
181 | ### 常用前缀
182 |
183 | | 语义 | 命名 |
184 | | :--: | :--: |
185 | |re|再次执行
186 | |pre|之前|
187 | |post|之后|
188 | |multi|多项|
189 | |un|否定|
190 | |per|每项|
191 |
192 | ### 常用后缀
193 |
194 | | 语义 | 命名 |
195 | | :--: | :--: |
196 | |ed|过去式 |
197 | |ing| 进行时|
198 | |able| 具备能力|
199 | |s / es|名词复数|
200 |
201 | ### 布局相关
202 |
203 |
204 | | 语义 | 命名 |
205 | | :--: | :--: |
206 | |布局|layout|
207 | |主体内容|container|
208 | |头部|header|
209 | |主要部分|main|
210 | |侧边栏|sidebar|
211 | |子容器|sub-|
212 | |包含块|-wrap|
213 | |内部的|-inner|
214 | |外部的|-outer|
215 | |行|row|
216 | |列|column|
217 | |区域|region / area / section|
218 | |底部|footer|
219 | |清除浮动|clearfix|
220 | |项|item|
221 |
222 | ### 组件相关
223 |
224 | | 语义 | 命名 |
225 | | :--: | :--: |
226 | |导航|nav|
227 | |面包屑|crumb|
228 | |菜单|menu|
229 | |选项卡|tab|
230 | |内容|content|
231 | |列表|list|
232 | |排行|rank|
233 | |商标|logo|
234 | |幻灯片|slide|
235 | |提示|tip / msg / hint|
236 | |注册|register|
237 | |弹出框|dialog / model|
238 | |合作伙伴|partner|
239 | |工具条|toolbar|
240 | |标语|banner|
241 | |版权|copyright|
242 | |评论|comment|
243 | |面板|panel|
244 | |手风琴|accordion|
245 | |加载|loading|
246 | |头像|avatar|
247 | |标签|tag|
248 | |表格|table|
249 | |下拉框|dropdown|
250 |
251 |
252 |
253 | > 参考文章
254 |
255 | > [awesome-name](https://github.com/sindresorhus/awesome)
256 |
257 | > [每个程序员需掌握的20个代码命名小贴士](http://blog.csdn.net/discover0704/article/details/44905811)
258 |
259 | > [计算机变量命名常用词汇包括对仗词
260 | ](http://www.lxway.com/4049516184.htm)
--------------------------------------------------------------------------------
/原生JS的DOM操作.doc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shurong-wang/JavaScript/b614836edc904234fd34b6d55cc9e8c7aa86c9f0/原生JS的DOM操作.doc
--------------------------------------------------------------------------------