[
  {
    "path": "1 移动端 适配.md",
    "content": "# 移动端 适配\n\n**demo : https://github.com/HuJiaoHJ/h5-layout\n\n## vw + rem 方案\n- 1、设置 html font-size 为 10vw\n```\nhtml {\n    font-size: 10vw;\n}\n```\n- 2、以750UI图为例，在 css 中，直接使用UI图上的长度值，单位设置为 px\n```\n.head {\n    width: 750px;\n}\n```\n- 3、引入 postcss-pxtorem 插件，配置如下：\n  - https://github.com/HuJiaoHJ/h5-layout/blob/master/.postcssrc.js\n```\nrequire('postcss-pxtorem')({\n    rootValue: 75,\n    unitPrecision: 5,\n    propList: ['*'],\n    selectorBlackList: [],\n    replace: true,\n    mediaQuery: false,\n    minPixelValue: 0\n})\n```\n以上，就可以使用了 vw + rem 方案实现了移动端适配\n\n## 1px border 问题\n- 再谈Retina下1px的解决方案： https://www.w3cplus.com/css/fix-1px-for-retina.html\n\n```\n<script>\n(function () {\n    // 1px\n    var dpr = window.devicePixelRatio;\n    var isIPhone = window.navigator.appVersion.match(/iphone/gi);\n    var UA = window.navigator.userAgent;\n    var hacks = ['m1 note']; // 对 meizu 某型号进行hack，主要原因是 dpr为3，但是手机屏幕分辨率不够，会出现 1px border 过细的问题，这种问题主要出现在部分魅族手机上\n    var flag = false;\n    hacks.forEach(function (item) {\n        if (UA.indexOf(item) >= 0) {\n            flag = true;\n            return;\n        }\n    });\n    if (!isIPhone && flag) {\n        dpr = dpr >= 2 ? 2 : dpr;\n    }\n    var scale = 1 / dpr;\n    var metaEl = document.createElement('meta');\n    metaEl.setAttribute('name', 'viewport');\n    metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no, viewport-fit=cover');\n    if (document.documentElement.firstElementChild) {\n        document.documentElement.firstElementChild.appendChild(metaEl);\n    } else {\n        var wrap = document.createElement('div');\n        wrap.appendChild(metaEl);\n        document.write(wrap.innerHTML);\n    }\n})();\n</script>\n```\n\n## 参考\n- https://juejin.im/post/5b6575b0518825196b01fd85\n- https://github.com/cuth/postcss-pxtorem#readme\n- https://github.com/HuJiaoHJ/blog/issues/6\n- vw ployfill: https://github.com/HuJiaoHJ/viewport-units-polyfill/blob/master/index.js\n- 使用Flexible实现手淘H5页面的终端适配: http://www.w3cplus.com/mobile/lib-flexible-for-html5-layout.html\n- postcss-pxtorem： https://www.npmjs.com/package/postcss-pxtorem\n- sublime 3 插件 cssrem： https://github.com/flashlizi/cssrem\n"
  },
  {
    "path": "1.0 web 前端基本素养.md",
    "content": "# web 前端基本素养\n\n---:: You are the owner of your career.\n\n找一些比较准确的，你可以确定它真的足够全面的资料当作线索。   \n对 Web 平台的 API，用反射：\n```\nfor(var p in window){console.log(p)}  \n```\n浏览器里给出来的这个属性列表是不会骗人的，用这个东西作为线索\n\n# 高效率工作与学习  \n## 合理利用搜索引擎\n\n远离百度，拥抱谷歌，一般程序员 + google = 超级程序员。  \n掌握搜索引擎的高级用法，比如双引号(精确搜索) 站内搜索(site:) 通配符(星号) 文档类型(filetype:pdf)  \n## 选择合适工具  \n\nDash， Alfred， Zeal， Wox， Everything， Emment， Markdown….  \n时间少，不被打断 coding 思路，从而提升效率。  \n \n## 做好时间管理  \n\n明确「最小可用时间」的概念  \n凡事都要列计划，计划之外无工作  \n利用好工具：便签，番茄工作法，forest；让自己更有压迫感.TED 公开课自行学习  \n## 阅读优质资源  \n\n阅读聚合筛选后的文章，比如稀土掘进，企业前端团队博客，其他个人专业博客等，订阅号，有道笔记。可以通过 RSS 订阅自己整合。  \n\n## 养成总结习惯\n\n书写是为了更好的思考，可以通过书写进行知识的沉淀，刚开始先将工作学习中遇到的问题，选择的方案以及解决的方法记录下来。持续一段时间后，然后对这类事情进行挖掘再整理。转载收藏，网络存档， 自己的理解提炼主旨进一步再整理。\n\n写博客进行知识沉淀。 搭建博客 （Hexo + Github page + 七牛）\n\n## 正确的沟通方式\n异步沟通，谨慎；语言组织；有迹可循；防止被人中途思路被打断，看似回复变慢，其实更加高效。\n\n## 正确的提问\n\n提问前做足够研究。（你做过足够的研究么，你尝试过搜索么，你试过 debug 么，仔细检查、避免低级错误）  \n在适当的地方提问（比如你在 stackoverflow，而非在百度知道， 国内 segmentfault， gitter（举例：Ant-design）， github 提issue）  \n直入主题，少说废话。精确描述问题。（提问的智慧）  \n用清晰、正确、精准并语法正确的语句。反面例子：(冰天雪地跪求解答，十万火急。)  \n\n使用明确、有意义的标题。 \n\n语法正确、格式清晰。\n\n描述事实、而不是猜测。\n\n要有具体场景，描述问题发生的环境\n\n如果是业务bug提前demo，jsbin，jsfiddle， codepen 复现）并写下你自己尝试过的解决方法。\n\n降低别人对问题理解的时间成本，对自己对问题提炼有帮助。有时候问题描述清楚，问题也就解决了。\n\n\n\n## 编写优雅的代码\n可读性目的：使别人理解它所需的时间最小化。\n可读性代码就是一种艺术，优雅程度决定是否好的艺术品。\n\n## 提高代码的可读性\n\n风格统一\n把信息装到名字里\n注释言简意赅\n简化循环和逻辑\n参考AirBnb， Google， facebook\n举例：\n```\n// bad\nconsole.log(object.hasOwnProperty(key));\n// good\nconsole.log(Object.prototype.hasOwnProperty.call(object, key));\n// best\nconst has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.\n/* or */\nimport has from 'has';\n// ...\nconsole.log(has.call(object, key));\n```\n## HOW\n\n- 借助自动化工具（e: esLint， pre-commit）\n- Code Review (不局限几天，一个人，结对互评也可以做)\n* 一些新的 web 技术\n\n  - shadow DOM：隔离组件代码作用域\n  - Service Worker ：网络代理，数据缓存，离线应用，推送通知\n  - WebRTC：通过 JavaScript api 接口网页实时通讯\n  - PWA ：网页可以渐进式地变成 App 可以添加到主屏幕图标，具有 Service Worker 的能力\n  - Web Components ：组件化标准，只规范接口，而底层的实现是完全自由的。包括（HTML Templates、HTML Import、Custom Element、Shadow DOM）\n  - WebAssembly： Web 的二进制格式（一般的在 JavaScript 文档其实就是简单的文本文件，先是从服务器下载，然后由浏览器中的 JavaScript 引擎解析并编译）可以让不能用来开发 web 应用的语言来进行 web 开发\n\n\n\n# 代码规范\n\n参见 [编码规范 by @mdo](http://zoomzhao.github.io/code-guide/)\n\n\n\n* 中英文之间留一个空格\n* 代码注释\n* 避免使用大长段代码\n\n[前端工作流和规范](https://github.com/fairyly/html-demo/blob/gh-pages/WEB%E5%89%8D%E7%AB%AF%E5%B7%A5%E4%BD%9C%E6%B5%81%E5%92%8C%E8%A7%84%E8%8C%83.md)\n"
  },
  {
    "path": "1.0.0 代码规范.md",
    "content": "# 代码规范\n\n\n## Baidu EFE team\n* [website](http://efe.baidu.com/)\n* [GitHub](https://github.com/ecomfe)\n\n- [Javascript编码规范](javascript-style-guide.md) <span class=\"std-rec\">[1.3]</span>\n- [Javascript编码规范 - ESNext补充篇](es-next-style-guide.md) <span class=\"std-rec\">[draft]</span>\n- [HTML编码规范](html-style-guide.md) <span class=\"std-rec\">[1.2]</span>\n- [CSS编码规范](css-style-guide.md) <span class=\"std-rec\">[1.2]</span>\n- [Less编码规范](less-code-style.md) <span class=\"std-rec\">[1.1]</span>\n- [E-JSON数据传输标准](e-json.md) <span class=\"std-rec\">[1.0]</span>\n- [模块和加载器规范](module.md) <span class=\"std-rec\">[1.1]</span>\n- [包结构规范](package.md) <span class=\"std-rec\">[1.1]</span>\n- [项目目录结构规范](directory.md) <span class=\"std-rec\">[1.1]</span>\n- [图表库标准](chart.md) <span class=\"std-rec\">[1.0]</span>\n- [react编码规范](react-style-guide.md) <span class=\"std-rec\">[draft]</span>\n\n\n\n## AlloyTeam\n* [website](http://alloyteam.github.io/CodeGuide/)\n* [GitHub](https://github.com/AlloyTeam/CodeGuide/tree/gh-pages)\n* [GitHub-腾讯IVWEB前端工作流和规范](https://github.com/feflow)\n* [git-commit-style-guide](https://github.com/feflow/git-commit-style-guide)\n* [README规范](https://github.com/feflow/readme-style-guide)\n* [eslint-config-ivweb](https://github.com/feflow/eslint-config-ivweb)\n\n\n## google\n* [website](https://opensource.google.com/)\n* [Github](https://github.com/google)\n* [Github-google styleguide](https://github.com/google/styleguide)\n* [TypeScript style guide, formatter, and linter.](https://github.com/google/ts-style)\n* [eslint-config-google](https://github.com/google/eslint-config-google)\n\n\n## airbnb\n* [GitHub](https://github.com/airbnb)\n* [GitHub-JavaScript Style Guide](https://github.com/airbnb/javascript)\n\n\n\n\n\n## 参考\n- [JavaScript Style Guide, with linter & automatic code fixer](https://github.com/standard/standard)\n  - [website](https://standardjs.com/)\n"
  },
  {
    "path": "1.1. 解决各种IE兼容问题,IE6,IE7,IE8,IE9,IE10.md",
    "content": "# 解决各种IE兼容问题,IE6,IE7,IE8,IE9,IE10\n\n* http://www.cnblogs.com/mfc-itblog/p/5949251.html\n\n\n```\n2. Google Chrome Frame也可以让IE用上Chrome的引擎:\n\n<meta http-equiv=“X-UA-Compatible” content=“chrome=1″ />\n\n<meta http-equiv=”X-UA-Compatible” content=”IE=edge,chrome=1″ />\n创建html5时发现这么一句话，百度如下：\n这样写可以达到的效果是如果安装了GCF，则使用GCF来渲染页面，如果没安装GCF，则使用最高版本的IE内核进行渲染。\nGoogle Chrome Frame（谷歌内嵌浏览器框架GCF）。\n这个插件可以让用户的IE浏览器外不变，但用户在浏览网页时，实际上使用的是Google Chrome浏览器内核，而且支持IE6、7、8等多个版本的IE浏览器。\n```\n"
  },
  {
    "path": "1.1.0 统一代码风格---.editorconfig.md",
    "content": "# .editorconfig\n\n## 1.了解\n\n- 【通配符】\n\n  ```\n  *                匹配除/之外的任意字符串\n  **               匹配任意字符串\n  ?                匹配任意单个字符\n  [name]           匹配name中的任意一个单一字符\n  [!name]          匹配不存在name中的任意一个单一字符\n  {s1,s2,s3}       匹配给定的字符串中的任意一个(用逗号分隔) \n  {num1..num2}   　匹配num1到num2之间的任意一个整数, 这里的num1和num2可以为正整数也可以为负整数\n  ```\n\n- 各个属性\n\n  ```\n  indent_style    设置缩进风格(tab是硬缩进，space为软缩进)\n  indent_size     用一个整数定义的列数来设置缩进的宽度，如果indent_style为tab，则此属性默认为  tab_width\n  tab_width       用一个整数来设置tab缩进的列数。默认是indent_size\n  end_of_line     设置换行符，值为lf、cr和crlf\n  charset         设置编码，值为latin1、utf-8、utf-8-bom、utf-16be和utf-16le，不建议使用utf-8-bom\n  trim_trailing_whitespace  设为true表示会去除换行行首的任意空白字符。\n  insert_final_newline      设为true表示使文件以一个空白行结尾\n  root        　　　表示是最顶层的配置文件，发现设为true时，才会停止查找.editorconfig文件    \n  ```\n\n## 2.使用\n\n>有些编辑器则需要安装editorConfig插件，如ATOM、Sublime、VS Code等\n\n- 项目目录创建 `.editorconfig` 文件\n\n  ```\n  $ touch .editorconfig\n  ```\n\n\n- `.editorconfig` 常用内容设置：\n\n```\n# editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ntrim_trailing_whitespace = true\ninsert_final_newline = true\ntab_width = 2\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[Makefile]\nindent_style = tab\n```\n"
  },
  {
    "path": "1.1.1 忽略提交文件---.gitignore.md",
    "content": "# .gitignore\n\n\n```\n.DS_Store\nnode_modules/\ndist/\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Editor directories and files\n.idea\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n```\n\n## 参考\n- [github/gitignore](https://github.com/github/gitignore)\n"
  },
  {
    "path": "1.1.2 语法检查---ESLint.md",
    "content": "# ESLint\n\n\n```\nnpm i eslint -g\n\neslint --init\n\neslint 会创建一个 .eslintrc.json 的配置文件，同时自动安装并添加相关的模块到 devDependencies。\n\n这里我们使用 Standard 规范，其主要特点是不加分号。\n\n\n```\n\n- rules:\n\n```\n'rules': {\n    // no-var\n    'no-var': 'error',\n    // 要求或禁止 var 声明中的初始化\n    'init-declarations': 2,\n    // 强制使用单引号\n    'quotes': ['error', 'single'],\n    // 要求或禁止使用分号而不是 ASI\n    'semi': ['error', 'never'],\n    // 禁止不必要的分号\n    'no-extra-semi': 'error',\n    // 强制使用一致的换行风格\n    'linebreak-style': ['error', 'unix'],\n    // 空格2个\n    'indent': ['error', 2, {'SwitchCase': 1}],\n    // 指定数组的元素之间要以空格隔开(,后面)， never参数：[ 之前和 ] 之后不能带空格，always参数：[ 之前和 ] 之后必须带空格\n    'array-bracket-spacing': [2, 'never'],\n    // 在块级作用域外访问块内定义的变量是否报错提示\n    'block-scoped-var': 0,\n    // if while function 后面的{必须与if在同一行，java风格。\n    'brace-style': [2, '1tbs', {'allowSingleLine': true}],\n    // 双峰驼命名格式\n    'camelcase': 2,\n    // 数组和对象键值对最后一个逗号， never参数：不能带末尾的逗号, always参数：必须带末尾的逗号， \n    'comma-dangle': [2, 'never'],\n    // 控制逗号前后的空格\n    'comma-spacing': [2, {'before': false, 'after': true}],\n    // 控制逗号在行尾出现还是在行首出现\n    'comma-style': [2, 'last'],\n    // 圈复杂度\n    'complexity': [2, 9],\n    // 以方括号取对象属性时，[ 后面和 ] 前面是否需要空格, 可选参数 never, always\n    'computed-property-spacing': [2, 'never'],\n    // TODO 关闭 强制方法必须返回值，TypeScript强类型，不配置\n    // 'consistent-return': 0\n  }\n```\n\n- vue demo:\n\n\n\nESLint的规则有三种级别：\n\n  - \"off\"或者0，不启用这个规则\n  - \"warn\"或者1，出现问题会有警告\n  - \"error\"或者2，出现问题会报错\n\nvue-cli在init初始化时会询问是否需要添加ESLint，确认之后在创建的项目中就会出现.eslintignore和.eslintrc.js两个文件。\n\n- .eslintignore类似Git的.gitignore用来忽略一些文件不使用ESLint检查。\n- .eslintrc.js是ESLint配置文件，用来设置插件、自定义规则、解析器等配置。\n\n- 解析器(parser)：使用了babel-eslint，这个可以在package.json中找到，说明我们已经安装过该解析器了。\n- 环境配置(env)：在浏览器中使用eslint。\n- 继承(extends)：该配置文件继承了standard规则，具体规则自己看文档，看不懂有中文版的。\n- 规则(rules)：\n  - arrow-parems 允许箭头函数参数使用括号,具体操作请看文档\n  - generator-star-spacing 允许方法之间加星号，如function * generator() {}。文档在此。特地查了下，发现这是ES6提供的生成器函数，回头学习下。\n  - no-debugger' 允许在开发环境下使用debugger。\n\n\n常用规则：\n\n```\n'rules': {\n   \"comma-dangle\": [\"error\", \"never\"], //是否允许对象中出现结尾逗号\n   \"no-cond-assign\": 2, //条件语句的条件中不允许出现赋值运算符\n   \"no-console\": 2, //不允许出现console语句\n   \"no-constant-condition\": 2, //条件语句的条件中不允许出现恒定不变的量\n   \"no-control-regex\": 2, //正则表达式中不允许出现控制字符\n   \"no-debugger\": 2, //不允许出现debugger语句\n   \"no-dupe-args\": 2, //函数定义的时候不允许出现重复的参数\n   \"no-dupe-keys\": 2, //对象中不允许出现重复的键\n   \"no-duplicate-case\": 2, //switch语句中不允许出现重复的case标签\n   \"no-empty\": 2, //不允许出现空的代码块\n   \"no-empty-character-class\": 2, //正则表达式中不允许出现空的字符组\n   \"no-ex-assign\": 2, //在try catch语句中不允许重新分配异常变量\n   \"no-extra-boolean-cast\": 2, //不允许出现不必要的布尔值转换\n   \"no-extra-parens\": 0, //不允许出现不必要的圆括号\n   \"no-extra-semi\": 2, //不允许出现不必要的分号\n   \"no-func-assign\": 2, //不允许重新分配函数声明\n   \"no-inner-declarations\": [\"error\", \"functions\"], //不允许在嵌套代码块里声明函数\n   \"no-invalid-regexp\": 2, //不允许在RegExp构造函数里出现无效的正则表达式\n   \"no-irregular-whitespace\": 2, //不允许出现不规则的空格\n   \"no-negated-in-lhs\": 2, //不允许在in表达式语句中对最左边的运算数使用取反操作\n   \"no-obj-calls\": 2, //不允许把全局对象属性当做函数来调用\n   \"no-regex-spaces\": 2, //正则表达式中不允许出现多个连续空格\n   \"quote-props\": 2, //对象中的属性名是否需要用引号引起来\n   \"no-sparse-arrays\": 2, //数组中不允许出现空位置\n   \"no-unreachable\": 2, //在return，throw，continue，break语句后不允许出现不可能到达的语句\n   \"use-isnan\": 2, //要求检查NaN的时候使用isNaN()\n   \"valid-jsdoc\": [\"error\", {\n     \"requireReturn\": false,\n     \"requireParamDescription\": false,\n     \"requireReturnDescription\": true\n   }], //强制JSDoc注释\n   \"valid-typeof\": [\"error\", {\n     \"requireStringLiterals\": true\n   }], //在使用typeof表达式比较的时候强制使用有效的字符串\n   \"block-scoped-var\": 2, //将变量声明放在合适的代码块里\n   \"complexity\": 0, //限制条件语句的复杂度\n   \"consistent-return\": 2, //无论有没有返回值都强制要求return语句返回一个值\n   \"curly\": [\"error\", \"all\"], //强制使用花括号的风格\n   \"default-case\": 0, //在switch语句中需要有default语句\n   \"dot-notation\": [\"error\", {\"allowKeywords\": false, \"allowPattern\": \"\"}], //获取对象属性的时候使用点号\n   \"eqeqeq\": [\"error\", \"smart\"], //比较的时候使用严格等于\n   \"no-alert\": 1, //不允许使用alert，confirm，prompt语句\n   \"no-caller\": 2, //不允许使用arguments.callee和arguments.caller属性\n   \"guard-for-in\": 0, //监视for in循环，防止出现不可预料的情况\n   \"no-div-regex\": 2, //不能使用看起来像除法的正则表达式\n   \"no-else-return\": 0, //如果if语句有return，else里的return不用放在else里\n   \"no-labels\": [\"error\", {\n     \"allowLoop\": false,\n     \"allowSwitch\": false\n   }], //不允许标签语句\n   \"no-eq-null\": 2, //不允许对null用==或者!=\n   \"no-eval\": 2, //不允许使用eval()\n   \"no-extend-native\": 2, //不允许扩展原生对象\n   \"no-extra-bind\": 2, //不允许不必要的函数绑定\n   \"no-fallthrough\": 2, //不允许switch按顺序全部执行所有case\n   \"no-floating-decimal\": 2, //不允许浮点数缺失数字\n   \"no-implied-eval\": 2, //不允许使用隐式eval()\n   \"no-iterator\": 2, //不允许使用__iterator__属性\n   \"no-lone-blocks\": 2, //不允许不必要的嵌套代码块\n   \"no-loop-func\": 2, //不允许在循环语句中进行函数声明\n   \"no-multi-spaces\": 2, //不允许出现多余的空格\n   \"no-multi-str\": 2, //不允许用\\来让字符串换行\n   \"no-global-assign\": 2, //不允许重新分配原生对象\n   \"no-new\": 2, //不允许new一个实例后不赋值或者不比较\n   \"no-new-func\": 2, //不允许使用new Function\n   \"no-new-wrappers\": 2, //不允许使用new String，Number和Boolean对象\n   \"no-octal\": 2, //不允许使用八进制字面值\n   \"no-octal-escape\": 2, //不允许使用八进制转义序列\n   \"no-param-reassign\": 0, //不允许重新分配函数参数\"no-proto\": 2, //不允许使用__proto__属性\n   \"no-redeclare\": 2, //不允许变量重复声明\n   \"no-return-assign\": 2, //不允许在return语句中使用分配语句\n   \"no-script-url\": 2, //不允许使用javascript:void(0)\n   \"no-self-compare\": 2, //不允许自己和自己比较\n   \"no-sequences\": 2, //不允许使用逗号表达式\n   \"no-throw-literal\": 2, //不允许抛出字面量错误 throw \"error\"\n   \"no-unused-expressions\": 2, //不允许无用的表达式\n   \"no-void\": 2, //不允许void操作符\n   \"no-warning-comments\": [1, {\"terms\": [\"todo\", \"fixme\", \"any other term\"]}], //不允许警告备注\n   \"no-with\": 2, //不允许使用with语句\n   \"radix\": 1, //使用parseInt时强制使用基数来指定是十进制还是其他进制\n   \"vars-on-top\": 0, //var必须放在作用域顶部\n   \"wrap-iife\": [2, \"any\"], //立即执行表达式的括号风格\n   \"yoda\": [2, \"never\", {\"exceptRange\": true}], //不允许在if条件中使用yoda条件\n   \"strict\": [2, \"function\"], //使用严格模式\n   \"no-catch-shadow\": 2, //不允许try catch语句接受的err变量与外部变量重名\"no-delete-var\": 2, //不允许使用delete操作符\n   \"no-label-var\": 2, //不允许标签和变量同名\n   \"no-shadow\": 2, //外部作用域中的变量不能与它所包含的作用域中的变量或参数同名\n   \"no-shadow-restricted-names\": 2, //js关键字和保留字不能作为函数名或者变量名\n   \"no-undef\": 2, //不允许未声明的变量\n   \"no-undef-init\": 2, //不允许初始化变量时给变量赋值undefined\n   \"no-undefined\": 2, //不允许把undefined当做标识符使用\n   \"no-unused-vars\": [2, {\"vars\": \"all\", \"args\": \"after-used\"}], //不允许有声明后未使用的变量或者参数\n   \"no-use-before-define\": [2, \"nofunc\"], //不允许在未定义之前就使用变量\"indent\": 2, //强制一致的缩进风格\n   \"brace-style\": [2, \"1tbs\", { \"allowSingleLine\": false}], //大括号风格\n   \"camelcase\": [2, {\"properties\": \"never\"}], //强制驼峰命名规则\n   \"comma-style\": [2, \"last\"], //逗号风格\n   \"consistent-this\": [0, \"self\"], //当获取当前环境的this是用一样的风格\n   \"eol-last\": 2, //文件以换行符结束\n   \"func-names\": 0, //函数表达式必须有名字\n   \"func-style\": 0, //函数风格，规定只能使用函数声明或者函数表达式\n   \"key-spacing\": [2, {\"beforeColon\": false, \"afterColon\": true}], //对象字面量中冒号的前后空格\n   \"max-nested-callbacks\": 0, //回调嵌套深度\n   \"new-cap\": [2, {\"newIsCap\": true, \"capIsNew\": false}], //构造函数名字首字母要大写\n   \"new-parens\": 2, //new时构造函数必须有小括号\n   \"newline-after-var\": 0, //变量声明后必须空一行\n   \"no-array-constructor\": 2, //不允许使用数组构造器\n   \"no-inline-comments\": 0, //不允许行内注释\n   \"no-lonely-if\": 0, //不允许else语句内只有if语句\n   \"no-mixed-spaces-and-tabs\": [2, \"smart-tabs\"], //不允许混用tab和空格\n   \"no-multiple-empty-lines\": [2, {\"max\": 2}], //空行最多不能超过两行\n   \"no-nested-ternary\": 2, //不允许使用嵌套的三目运算符\n   \"no-new-object\": 2, //禁止使用new Object()\n   \"fun-call-spacing\": 2, //函数调用时，函数名与()之间不能有空格\n   \"no-ternary\": 0, //不允许使用三目运算符\n   \"no-trailing-spaces\": 2, //一行最后不允许有空格\n   \"no-underscore-dangle\": 2, //不允许标识符以下划线开头\n   \"no-extra-parens\": 0, //不允许出现多余的括号\n   \"one-var\": 0, //强制变量声明放在一起\n   \"operator-assignment\": 0, //赋值运算符的风格\n   \"padded-blocks\": [2, \"never\"], //块内行首行尾是否空行\n   \"quote-props\": 0, //对象字面量中属性名加引号\n   \"quotes\": [1, \"single\", \"avoid-escape\"], //引号风格\n   \"semi\": [2, \"always\"], //强制语句分号结尾\n   \"semi-spacing\": [2, {\"before\": false, \"after\": true}], //分后前后空格\n   \"sort-vars\": 0, //变量声明时排序\n   \"space-before-blocks\": [2, \"always\"], //块前的空格\n   \"space-before-function-paren\": [2, {\"anonymous\": \"always\", \"named\": \"never\"}], //函数定义时括号前的空格\n   \"space-infix-ops\": [2, {\"int32Hint\": true}], //操作符周围的空格\n   \"keyword-spacing\": 2, //关键字前后的空格\n   \"space-unary-ops\": [2, { \"words\": true, \"nonwords\": false}], //一元运算符前后不要加空格\n   \"wrap-regex\": 2, //正则表达式字面量用括号括起来\n   \"no-var\": 0, //使用let和const代替var\n   \"generator-star-spacing\": [2, \"both\"], //生成器函数前后空格\n   \"max-depth\": 0, //嵌套块深度\n   \"max-len\": 0, //一行最大长度，单位为字符\n   \"max-params\": 0, //函数最多能有多少个参数\n   \"max-statements\": 0, //函数内最多有几个声明\n   \"no-bitwise\": 0, //不允许使用位运算符\n   \"no-plusplus\": 0 //不允许使用++ --运算符\n }\n```\n\n- .eslintrc.js\n\n```\n// http://eslint.org/docs/user-guide/configuring\n\nmodule.exports = {\n  root: true,\n  parser: 'babel-eslint',\n  parserOptions: {\n    sourceType: 'module'\n  },\n  env: {\n    browser: true,\n  },\n  // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style\n  extends: 'standard',\n  // required to lint *.vue files\n  plugins: [\n    'html'\n  ],\n  // add your custom rules here\n  'rules': {\n    // allow paren-less arrow functions\n    'arrow-parens': 0,\n    // allow async-await\n    'generator-star-spacing': 0,\n    // allow debugger during development\n    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0\n  }\n}\n\n\n```\n\n## 参考\n- http://eslint.org/docs/rules/\n- https://www.jb51.net/article/124945.htm\n- https://www.jianshu.com/p/efb6fbed6fac\n- https://github.com/babel/babel-eslint\n\n\n## 参考\n- https://eslint.org/\n- 腾讯IVWEB前端工作流和规范: https://github.com/feflow\n"
  },
  {
    "path": "1.1.3 前端小团队建设.md",
    "content": "# 前端小团队建设\n\n\n## 参考\n- [一些规范](https://mp.weixin.qq.com/s?__biz=MjM5NTEwMTAwNg==&mid=2650215305&idx=1&sn=c05c83f51813919c6ae7db50c9d89387&chksm=befe13a889899abe428cc6564fc9857e4403f7bd6245c7f9cf892fc69c10b8608a08d6c8a70f&mpshare=1&scene=23&srcid=092701H4FKxu5n1VcnK5M5jJ#rd)\n"
  },
  {
    "path": "1.1.4 提问的智慧.md",
    "content": "# 提问的智慧\n\n## 参考\n- [issue: 提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md)\n"
  },
  {
    "path": "2.0 html 代码规范.md",
    "content": "# html代码规范\n\n* 缩进 以前说使用 **2** 个空格，现在一般使用 **4** 个空格；\n* 在属性上，使用双引号 **“”**，不要使用单引号 **‘’**；\n* 标签正确关闭；\n* 在自动闭合标签结尾处使用斜线；\n* 头部要声明 doctype : <!DOCTYPE html>\n* 指出页面的语言: <html lang=\"en-us\">  <html lang=\"zh-cmn-Hans\">\n* 声明一个明确的字符编码: <head> <meta charset=\"UTF-8\"></head>\n* IE 兼容模式: 使用兼容性 <meta> 标签来指定使用什么版本的 IE 来渲染页面。如果不是特殊需要，通常通过 edge mode 来通知 IE 使用最新的兼容模式\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge\">\n* 通常在引入 CSS 和 JavaScript 时不需要指明 type，css 放 head 头部\n\n* 属性顺序： HTML 属性应该按照特定的顺序出现以保证易读性。尽量大部分用 class，少使用 id\n  - class\n  - id, name\n  - data-*\n  - src, for, type, href, value\n  - title, alt\n  - role, aria-*\n  ```\n    <a class=\"...\" id=\"...\" data-toggle=\"modal\" href=\"#\">\n      Example link\n    </a>\n\n    <input class=\"form-control\" type=\"text\">\n\n    <img src=\"...\" alt=\"...\">\n  ```\n* Boolean 属性指不需要声明取值的属性\n  <input type=\"checkbox\" value=\"1\" checked>  <input type=\"text\" disabled>\n"
  },
  {
    "path": "2.1 TCP连接复用.md",
    "content": "# TCP连接复用\n\n>1. 负载均衡技术简介\n现代企业信息化应用越来越多的采用B/S应用架构来承载企业的关键业务，因此，确保这些任务的可靠运行就变得日益重要。  \n随着越来越多的企业实施数据集中，应用的扩展性、安全性和可靠性也越来越受到企业的重视。\n负载均衡技术通过设置虚拟服务器IP（VIP），将后端多台真实服务器的应用资源虚拟成一台高性能的应用服务器，  \n通过负载均衡算法，将大量来自客户端的应用请求分配到后端的服务器进行处理。  \n负载均衡设备持续的对服务器上的应用状态进行检查，并自动对无效的应用服务器进行隔离，实现了一个简单、扩展性强、可靠性高的应用解决方案。  \n解决了单台服务器处理性能不足，扩展性不够，可靠性较低的问题。\n\n## 1. TCP连接复用（TCP Connection Reuse）\n>TCP连接复用技术通过将前端多个客户的HTTP请求复用到后端与服务器建立的一个TCP连接上。    \n这种技术能够大大减小服务器的性能负载，减少与服务器之间新建TCP连接所带来的延时，    \n并最大限度的降低客户端对后端服务器的并发连接数请求，减少服务器的资源占用。  \n\n\n- TCP连接复用是将多个客户端的HTTP请求复用到一个服务器端TCP连接上，而HTTP复用则是一个客户端的多个HTTP请求通过一个TCP连接进行处理。  \n- 前者是负载均衡设备的独特功能；而后者是HTTP 1.1协议所支持的新功能，目前被大多数浏览器所支持。\n\n\nSYN表示建立连接，\n\nFIN表示关闭连接，\n\nACK表示响应，\n\nPSH表示有 DATA数据传输，\n \nRST表示连接重置。\n\n## http 连接复用\n\n主要的思路\n\n在发送 http 的请求头中设置 Connection: keep-alive。\n\n\n\n## 参考\n- [TCP连接复用（TCP Connection Reuse）](http://blog.51cto.com/gaibianziji/1211940)\n"
  },
  {
    "path": "2.1 http 协议.md",
    "content": "# http 协议\n  Hyper Text Transfer Protocol（超文本传输协议）\n  \n  HTTP是一个应用层协议，由请求和响应构成，是一个标准的客户端服务器模型。HTTP是一个无状态的协议。\n  \n  \n\n* http 状态值  ajax 的 readyState  \n  - 0 － （未初始化）还没有调用send()方法 \n  - 1 － （载入）已调用send()方法，正在发送请求 \n  - 2 － （载入完成）send()方法执行完成，已经接收到全部响应内容 \n  - 3 － （交互）正在解析响应内容 \n  - 4 － （完成）响应内容解析完成，可以在客户端调用了\n  \n* http 状态码\n  ```\n  1xx：请求收到，继续处理\n  2xx：操作成功收到，分析、接受\n  3xx：完成此请求必须进一步处理\n  4xx：请求包含一个错误语法或不能完成\n  5xx：服务器执行一个完全有效请求失\n  ```\n  - 200, OK，访问正常\n  - 301, Moved Permanently，永久移动\n  - 302, Move temporarily，暂时移动\n  - 304, Not Modified，未修改\n  - 307, Temporary Redirect，暂时重定向\n  - 401, Unauthorized，未授权\n  - 403, Forbidden，禁止访问\n  - 404, Not Found，未发现指定网址\n  - 500, Internal Server Error，服务器发生错误\n\n\n## HTTP报文\n- HTTP请求报文由**请求行（request line）**、**请求头部（header）**、**空行**和**请求数据**四个部分组成\n- HTTP响应报文由**状态行（status-line）**、**消息报头（header）**和**响应正文（response-body）**三个部分组成。\n\n\n### 1.1 请求行\n请求行由三部分组成：请求方法，请求URL（不包括域名），HTTP协议版本\n\n请求方法比较多：GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT\n\n## 1.2 请求头部\n请求头部由关键字/值对组成，每行一对\n```\nUser-Agent : 产生请求的浏览器类型\nAccept : 客户端希望接受的数据类型，比如 Accept：text/xml（application/json）表示希望接受到的是xml（json）类型\nContent-Type：发送端发送的实体数据的数据类型。\n比如，Content-Type：text/html（application/json）表示发送的是html类型。\nHost : 请求的主机名，允许多个域名同处一个IP地址，即虚拟主机\n\n1.2.1 Content-Type\n常见的Content-Type：\n\nContent-Type\n解释\n\ntext/html\nhtml格式\n\ntext/plain\n纯文本格式\n\ntext/css\nCSS格式\n\ntext/javascript\njs格式\n\nimage/gif\ngif图片格式\n\nimage/jpeg\njpg图片格式\n\nimage/png\npng图片格式\n\napplication/x-www-form-urlencoded\nPOST专用：普通的表单提交默认是通过这种方式。form表单数据被编码为key/value格式发送到服务器。\n\n\napplication/json\nPOST专用：用来告诉服务端消息主体是序列化后的 JSON 字符串\n\n\ntext/xml\nPOST专用：发送xml数据\n\n\nmultipart/form-data\nPOST专用：下面讲解\n\n作者：Lxylona\n链接：https://www.jianshu.com/p/0015277c6575\n```\n\n\n\n\n## 状态行\n状态行也由三部分组成：服务器HTTP协议版本，响应状态码，状态码的文本描述\n\n格式：HTTP-Version Status-Code Reason-Phrase CRLF\n\n比如：HTTP/1.1 200 OK\n\n状态码：由3位数字组成，第一个数字定义了响应的类别\n\n\n\n- General:概括信息，浏览器自动生成概括，不是http协议内容。\n\n\n```\nAccept:告诉服务器能够发送的媒体类型。\nAccept-Encoding:告诉服务器能发送哪些压缩格式。\nConnection:客户端和服务器是否保持连接。\nHost:接收请求的服务器的主机名和端口号。\nOrigin:请求的网站源信息。\nReferer:提供了包含当前请求URI的文档的URL，告诉服务器自己的来源。\nUser-Agent:发送请求的客户端应用程序。\ncookie：客户端的一些信息保存用户的登录状态等。\n```\n\n\n## 参考\n- [HTTP请求及响应报文](https://www.jianshu.com/p/8e8b6a8048da)\n- [HTTPS从认识到线上实战全记录](http://blog.haoji.me/https.html)\n"
  },
  {
    "path": "2.1 http2.0 详解.md",
    "content": "# 2.1 http2.0 详解\n\n- 新的二进制格式（Binary Format），HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷，文本的表现形式有多样性，要做到健壮性考虑的场景必然很多，二进制则不同，只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式，实现方便且健壮。\n\n- 多路复用（MultiPlexing），即连接共享，即每一个request都是是用作连接共享机制的。一个request对应一个id，这样一个连接上可以有多个request，每个连接的request可以随机的混杂在一起，接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。\n  - 所有的请求都是通过一个 TCP 连接并发完成\n\n- header压缩，如上文中所言，对前面提到过HTTP1.x的header带有大量信息，而且每次都要重复发送，HTTP2.0使用encoder来减少需要传输的header大小，通讯双方各自cache一份header fields表，既避免了重复header的传输，又减小了需要传输的大小。\n\n- 服务端推送（server push），同SPDY一样，HTTP2.0也具有server push功能。\n\n## 参考\n- []()\n"
  },
  {
    "path": "2.1 https 协议全记录.md",
    "content": "# 2.1 https 协议全记录\n\n>HTTPS全称Hyper Text Transfer Protocol over Secure Socket Layer，  \n直译过来就是通过SSL实现的超文本传输协议，简单来讲就是加密版的HTTP协议，也就是HTTP+SSL/TLS\n\n>HTTP是明文传输的，因此使用HTTP协议传输隐私信息非常不安全，很容易在传输过程中被窃取，或者通信内容被人篡改冒充，使用HTTPS可以避免这些问题。\n\n>实际上我们现在的HTTPS都是用的TLS协议，但是由于SSL出现的时间比较早，并且依旧被现在浏览器所支持，因此SSL依然是HTTPS的代名词。\n上面一大段话是转载的，简单而言就是TLS是SSL的升级版，现在浏览器一般用的都是TLS\n\n## 2.1.3 HTTPS的优点\n- 从底层分析HTTPS有以下3大优点：\n  - 防窃听：信息被加密了，第三方拿不到密码，所以可以防窃听；\n  - 防篡改：还是因为信息被加密了，如果执意篡改的话，客户端无法解密不说，而且数据完整性校验这一关也过不了；\n  - 防冒充：防止攻击者冒充他人身份参与通信，比如HTTPS的双向认证；\n- 从实际出发来看，HTTPS主要有以下优点：\n\n  - 防止私密信息泄露，防止信息被篡改；\n  - 有助于SEO，百度、谷歌均明确表示会优先收录、展示HTTPS站点的内容；\n  - 完全杜绝运营商HTTP劫持问题；\n  - 有效解决运营商DNS劫持问题，降低网站被劫持的风险;\n  - HTTPS的小绿锁表示可以提升用户对网站信任程度（当然不是说有小绿锁的都是安全的）;\n  - 可以有效防止山寨、镜像网站等；\n  - 为未来升级HTTP/2做准备，HTTP/2必须基于HTTPS部署；\n\n## HTTPS的缺点\n- 服务器性能下降，开启HTTPS会增加内存、CPU、网络带宽的开销，特别是非对称加密这一块；\n- 访问速度下降，HTTP连接的建立需要3次握手，HTTPS还需要加上ssl的几次握手（具体是几次没去考究，有说是9次），当然下降主要是在第一次建立连接的时候，后面正常通信速度一般没啥变化；\n- 除了握手部分外，所有信息传输之后浏览器和服务器都要进行加密解密，又是一笔额外的开销；\n- 申请证书需要一笔花费，当然现在免费证书也很容易申请到，这个不算明显缺点；\n\n>一句话概括就是：性能和速度下降，但是具体下降多少呢，这个不太好讲，可以延伸阅读：[HTTPS 要比 HTTP 多用多少服务器资源？](https://www.zhihu.com/question/21518760)\n\n## 2.1.5. HTTPS原理\nHTTPS主要分为单向认证和双向认证，99%的场景都只需要单向认证，\n\n#### 2.1.5.2 单向认证\n\n>具体过程如下：\n浏览器访问一个https地址，同时向服务端发送支持的SSL协议版本、支持的加密算法、一个客户端生成的随机数（用于稍后生成”对话密钥”）等。\n服务端给客户端返回要使用的SSL协议版本号、加密算法、一个服务器生成的随机数（也同样用于稍后生成”对话密钥”），另外还有一个最重要的，返回服务器端的证书，即公钥证书；\n客户端收到服务器回应以后，首先验证证书是否合法，如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期、或者返回的公钥不能正确解开返回证书中的数字签名，就会向访问者显示一个警告，由其选择是否还要继续通信。如果证书没有问题，客户端就会从证书中取出服务器的公钥继续；\n客户端向服务端发送自己所能支持的对称加密方案（注意是对称加密），供服务器端进行选择；\n服务器端在客户端提供的加密方案中选择加密程度最高的加密方式；\n服务器将选择好的加密方案通过明文方式返回给客户端；\n客户端接收到服务端返回的加密方式后，使用该加密方式生成产生一个随机密钥（后续用作通信过程中对称加密的密钥），然后将该随机数用用证书中的公钥加密发给服务端；\n服务器收到客户端返回的加密信息后，使用自己的私钥进行解密，获得最终的对称加密密钥。至此，客户端和服务端都得到一个随机密钥，并且这个密钥别人没法知道；\n在接下来的会话中，服务器和客户端将会使用该密码进行对称加密解密，保证通信过程中信息的安全。\n\n\n#### 2.1.5.4 双向认证\n>双向认证和单向认证原理基本差不多，主要区别是除了客户端需要认证服务端以外，服务端对客户端也需要认证。什么场景下需要验证客户端呢？比如一些银行业务，银行会要求客户必须在电脑上插入它们签发的U盾之类的东西，或者安装什么控件，这里就类似客户端证书的概念，没有这个证书的人无法使用银行提供的业务。\n\n- 参考：https://kyfxbl.iteye.com/blog/1910891\n\n![](http://image.haoji.me/201712/20171221_203457_865_4667.png)\n\n\n\n\n\n## 2.2 CA\n2.2.1 何为CA\n>CA(Certificate Authority)，即证书认证机构，它的作用就是提供证书（即服务器证书，由域名、公司信息、序列号和签名信息组成）加强服务端和客户端之间信息交互的安全性，以及证书运维相关服务。任何个体/组织都可以扮演 CA 的角色，难的是你要能得到全世界各大操作系统、浏览器等的默认信任，能得到他们的默认信任，你也可以自己开一家CA公司了，这类证书通常叫做根证书。浏览器默认信任的 CA 大厂商有很多，比如 Symantec赛门铁克、Comodo、Godaddy、GolbalSign(百度微博等都是它签发的) 和 Digicert。\n\n## 2.3 SSL证书\n### 2.3.1 证书的种类\nSSL 证书按大类一般可分为DV SSL 、OV SSL、EV SSL证书，又叫域名型、企业型、增强型证书\n\n\n### 2.3.2. 证书的格式\n一般来说，主流的 Web 服务软件，通常都基于 OpenSSL 和 Java 两种基础密码库。\n>\nTomcat、Weblogic、JBoss等Web服务软件，一般使用JDK自带的Keytool工具，生成Java Keystore（JKS）格式的证书文件。\nApache、Nginx等Web服务软件，一般使用OpenSSL工具提供的密码库，生成 .key、.crt等格式的证书文件。\nIBM 的 Web 服务产品，如 Websphere、IBM Http Server（IHS）等，一般使用 IBM 产品自带的 iKeyman 工具，生成 KDB 格式的证书文件。\n微软的IIS使用Windows自带的证书库生成pfx格式的证书文件。\n\n\n## 3.1第一步，生成证书\n\n>证书一般都是用openssl来生成，当然也可以用jdk自带的keytool来生成，但是最终还是要用openssl来转换格式，所以一般还是推荐用openssl来生成。\n\n#### 3.1.2.1 安装openssl\n首先当然还是安装openssl，这里我们只介绍Windows平台，Linux系统的请[参考这里](http://blog.haoji.me/linux-nginx.html#openssl--an-zhuang)，点击这里[下载Win64位的安装包](http://slproweb.com/products/Win32OpenSSL.html)\n\n>安装很简单，安装完毕之后为了使用方便，建议配置一下环境变量：\n\n\n## 3.1.2.2. 生成CA根证书\n>我们先生成一个自己的CA根证书ca.crt，然后再用这个根证书生成服务端证书server.crt，有人会问，\n为啥不直接生成服务端证书呢？因为这样做的话将来我们只要导入一个CA根证书，其它所有用它生成的证书都默认是可信任的，方便嘛！\n\n好了，说了这么多废话，下面开始了：\n```\n# 生成CA私钥\nopenssl genrsa -out ca.key 1024\n# 生成CA根证书，-day指定证书有效期\nopenssl req -new -x509 -key ca.key -out ca.crt -days 365\n```\n\n>就这么简单，第二步需要输入几个东西，虽然说乱填也可以，但建议按照提示来填，其中Common Name需要特别注意，\n如果是生成CA证书的话，可以输入诸如My CA之类的，如果是生成服务端证书的话，必须输入网站的域名，可以输入泛域名，如*.haoji.me\n\n\n### 3.1.2.3. 生成服务端证书\n>和CA证书的生成不同的时，我们需要先生成一个csr证书请求文件文件(CSR,Cerificate Signing Request)，\n有了这个文件之后再利用CA根证书生成最终的证书：\n\n```\n# 生成服务端私钥\nopenssl genrsa -out server.key 1024\n# 生成证书请求文件\nopenssl req -new -key server.key -out server.csr\n# 生成最终证书文件，-day指定证书有效期\nopenssl x509 -req -days 365 -sha256 -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt\n```\n\n以上证书在Chrome55下测试没问题，但是Chrome61下测试提示Subject Alternative Name missing错误：\n\n```\nThe certificate for this site does not contain a Subject Alternative Name extension containing a domain name or IP address.\n```\n\n>提示必须指定DNS Name或者IP地址，而指定这个必须要用v3的证书。\n生成v3证书只需要比上面多加2个参数-extfile openssl.cnf -extensions v3_req，但是多了一个额外的配置文件，\n这个文件里面可以填很多东西（完整配置文件参考这里），我们这里仅仅需要指定subjectAltName即可。\n\n先准备一个名为openssl.cnf的文件，内容如下：\n\n```\n[v3_req]\nsubjectAltName = @alt_names\n\n[alt_names]\nDNS.1 = localhost.com\nDNS.2 = www.localhost.com\nDNS.3 = www.test123.com\n#IP.1 = 127.0.0.1\n```\n\n解释一下，这里的alt_names指的是最终可以访问的域名或者IP，所以，其实一个证书是可以多个网站同时使用的。被访问域名只要满足DNS和IP中的一个，其证书就是合法的。\n\n然后再来重新生成证书，为了对大家产生误导，我们再来一遍完整的过程：\n\n```\n# 生成服务端私钥\nopenssl genrsa -out server.key 1024\n# 生成证书请求文件\nopenssl req -new -key server.key -out server.csr\n# 生成最终证书文件，-day指定证书有效期\nopenssl x509 -req -days 365 -sha256 -CA ca.crt -CAkey ca.key -CAcreateserial -extfile openssl.cnf -extensions v3_req -in server.csr -out server.crt\n\n```\n\n#### 3.1.2.4. 生成客户端证书\n如果需要实现HTTPS双向认证，还要按照上述服务端一模一样的操作再生成一个client.crt和client.key，这里我们就不重述了，90%的场景都是不需要双向认证的。\n\n#### 3.1.2.5. Windows导入证书\n上述步骤执行完之后生成了如下文件：\n\n\n\n我们只需要双击ca.crt导入这个自制根证书即可，以后只要是利用这个证书生成证书浏览器都会认为是可信任的，这让我想起了12306干的勾当，如果你能把你这个根证书推广到全世界的电脑和手机，那么你也可以成立一家CA公司专门卖证书了，哈哈。\n\n导入的时候注意证书存储位置\n\n\n\n\n## 参考\n- [HTTPS从认识到线上实战全记录](http://blog.haoji.me/https.html)\n- [SSL证书安装指引](https://cloud.tencent.com/document/product/400/4143)\n- [Linux下nginx的安装部署和配置](http://blog.haoji.me/linux-nginx.html#openssl--an-zhuang)\n"
  },
  {
    "path": "2.1.0 HTML 全局属性.md",
    "content": "# HTML 全局属性\n\n\n## 1、accesskey\n\n规定激活元素的快捷键\n```\n<a href=\"http://www.w3school.com.cn/html/\" accesskey=\"h\">HTML</a><br />\n<a href=\"http://www.w3school.com.cn/css/\" accesskey=\"c\">CSS</a>\n```\n\nalt+或者alt+shift+\n除了Opera几乎都支持\n\n\n\n只有`<a>,<area>,<button>,<input>,<label>,<legend>`以及`<textarea>`\n\n\n\n## 2、class\n\n规定元素的类名。\n\nclass 属性大多数用于指向样式表中的类，不过，也可以利用他通过 JavaScript 来改变带有指定 class 的 HTML 元素。\n\n\nclass 不能在以下HTML元素中使用：base,head,html,meta,param,script,style以及title\n\n可以给 class 赋予多个 class，例如\n```\n<span class=\"left_menu important\">\n```\n\n## 3、contenteditable  5\n```\n<p contenteditable=\"true\">这是一个可编辑的段落。</p>\n```\n规定元素内容是否可编辑。\n所有浏览器都支持\n\n\n\n## 4、contextmenu  5\n\n`contextmenu`当右键点击元素时，会出现上下文菜单。\n\n`contextmenu`属性的值是要打开的`<menu>`元素的 id。\n\n只有`FireFox`支持\n\n语法\n```\n<element contextmenu=\"menu_id\">\n```\n## 5、data-* 5\n\n使用`data-*`属性来嵌入自定义数据\n```\n<ul>\n<li data-animal-type=\"bird\">Owl</li>\n<li data-animal-type=\"fish\">Salmon</li> \n<li data-animal-type=\"spider\">Tarantula</li> \n</ul>\n```\n所有浏览器都支持\n`data-*`用来存储页面或者应用程序的私有自定义数据。\n\n`data-*`属性赋予我们所在 HTML 元素上嵌入自定义 data 属性的能力。\n\n存储的（自定义）数据可以被页面的 javascript 所使用，以创建更好的用户体验（不进行 Ajax 调用或者服务端进行查询）。\n\n`data-*`属性包括两部分：\n\n属性名不应该包含任何的大写部分，并且在前缀 data- 之后必须有一个字符\n\n属性值可以是任意字符串\n\n所有浏览器都支持\n\n\n\n## 6、dir \n\n规定文本的方向\n```\n<p dir=\"rtl\">Write this text right-to-left!</p>\n``\n在`<base>,<br>,<frame>,<frameset>,<hr>,<iframe>,<param>`以及`<script>`无效\n左到右 右到左\n```\n<element dir=\"ltr|rtl\">\n```\n\n## 7、draggable 5\n```\n<p draggable=\"true\">这是一个可拖动的段落。</p>\n```\n\nIE8以前不支持\n\n规定元素是否可以被拖动\n\n链接和图像是默认可拖动的\n\n值：\n\ntrue、flase、auto浏览器默认\n\n\n\n## 8、dropzone 5\n\n拖动数据会产生被拖动数据的副本\n\n所有浏览器都不支持\n\n值：\n\ncopy：产生副本\n\nmove：拖动数据会导致被拖动数据被移动到新位置\n\nlink：拖动数据会产生指向原始数据的链接 \n\n\n\n## 9、hidden 5\n\n隐藏控件\n\nhidden 是个 boolean，如果设置该属性，他规定元素仍为或者不再相关\n\n浏览器不应显示已规定 hidden 属性的元素\n\nhidden 属性也可以用于防止用户查看元素，直到匹配某些条件，然后 javascript 可以删除复选框，使得元素可见\n\n除了IE都支持\n\n\n\n## 10、id\n\nid属性规定HTML元素的唯一id。\n\nid在HTML文档中必须是唯一的。\n\nid属性可以作为链接锚，通过javascript或通过css为带有指定id的元素改变或者添加样式\n\n\n\n## 11、lang\n\n规定元素内部的语言\n```\n<p lang=\"fr\">Ceci est un paragraphe.</p>\n```\n所有浏览器都支持\n\n\n\n## 12、spellcheck 5\n\n进行拼写检查的可编辑段落\n\n\n```\n<p contenteditable=\"true\" spellcheck=\"true\">这是一个段落。</p>\n\nIE10,FireFox,Opera,chrome以及safari支持\n```\n\n对元素进行拼写和语法检查，可以对以下内容进行检查\n\ninput 的文本值 非密码\n\n`<textarea> `元素中的文本\n\n可编辑元素中的文本\n\n值：true false\n\n\n\n## 13、style\n\nstyle规定元素的行内样式\n\nstyle将覆盖任何全局的样式设定\n\n\n\n## 14、tabindex\n\n带有指定 tab 键顺序的链接\n\ntabindex 属性规定元素的 tab 键控制次序。\n\n除了 safari 都支持\n\n以下元素支持 tabindex 属性，`<a>,<area>,<button>,<input>,<object>`以及`textarea`\n\n语法\n```\n<element tabindex=\"number\">\n```\n\n## 15、title\ntitle 属性规定元素的基本信息。\n\n这些信息会在鼠标移到元素上显示一段工具提示文本\n\ntitle 经常与 form 以及 a 元素一起使用，已提供关于输入格式和链接目标的信息。\n\n\n\n## 16、translate  5\n\n是否翻译元素内容\n\n规定不应翻译某些内容\n```\n<p translate=\"no\">请勿翻译本段。</p>\n<p>本段可被译为任意语言。</p>\n```\n\n所有主流浏览器都无法支持\n提示：请使用 class=notranslate 替代\n\n\n值：yes，no\n\n## 参考\n- [HTML 全局属性](https://blog.csdn.net/qq_30970807/article/details/53165935)\n"
  },
  {
    "path": "2.1.1 从输入URL到页面加载发生了什么.md",
    "content": "# 从输入URL到页面加载发生了什么\n\n* segmentdefault: [从输入URL到页面加载发生了什么](https://segmentfault.com/a/1190000006879700)\n\n\n```\n总体来说分为以下几个过程:\n\nDNS解析\n\nTCP连接\n\n发送HTTP请求\n\n服务器处理请求并返回HTTP报文\n\n浏览器解析渲染页面\n\n连接结束\n```\n\n\n##  通常经过\n\n1、首先，在浏览器地址栏中输入url\n\n2、浏览器先查看浏览器缓存-系统缓存-路由器缓存，如果缓存中有，会直接在屏幕中显示页面内容。若没有，则跳到第三步操作。\n\n3、在发送http请求前，需要域名解析(DNS解析)，解析获取相应的IP地址。\n\n4、浏览器向服务器发起tcp连接，与浏览器建立tcp三次握手。\n\n5、握手成功后，浏览器向服务器发送http请求，请求数据包。\n\n6、服务器处理收到的请求，将数据返回至浏览器\n\n7、浏览器收到HTTP响应\n\n8、读取页面内容，浏览器渲染，解析html源码\n\n9、生成Dom树、解析css样式、js交互\n\n10、客户端和服务器交互\n\n11、ajax查询\n\n \n\n- 其中，步骤2的具体过程是：\n\n  - 浏览器缓存：浏览器会记录DNS一段时间，因此，只是第一个地方解析DNS请求；\n  - 操作系统缓存：如果在浏览器缓存中不包含这个记录，则会使系统调用操作系统，获取操作系统的记录(保存最近的DNS查询缓存)；\n  - 路由器缓存：如果上述两个步骤均不能成功获取DNS记录，继续搜索路由器缓存；\n  - ISP缓存：若上述均失败，继续向ISP搜索。\n\n##  “三次握手”的过程是，\n>发送端先发送一个带有SYN（synchronize）标志的数据包给接收端，在一定的延迟时间内等待接收的回复。  \n接收端收到数据包后，传回一个带有SYN/ACK标志的数据包以示传达确认信息。  \n接收方收到后再发送一个带有ACK标志的数据包给接收端以示握手成功。  \n在这个过程中，如果发送端在规定延迟时间内没有收到回复则默认接收方没有收到请求，而再次发送，直到收到回复为止。\n![](http://images0.cnblogs.com/blog/622045/201507/020946557039933.png)\n\n- 第一次\n  - 第一次握手：建立连接时，客户端发送syn包（syn=j）到服务器，并进入SYN_SENT状态，等待服务器确认；SYN：同步序列编号（Synchronize Sequence Numbers）。\n- 第二次\n  - 第二次握手：服务器收到syn包，必须确认客户的SYN（ack=j+1），同时自己也发送一个SYN包（syn=k），即SYN+ACK包，此时服务器进入SYN_RECV状态；\n- 第三次\n  - 第三次握手：客户端收到服务器的SYN+ACK包，向服务器发送确认包ACK(ack=k+1），此包发送完毕，客户端和服务器进入ESTABLISHED（TCP连接成功）状态，完成三次握手。\n完成三次握手，客户端与服务器开始传送数据，在上述过程中，还有一些重要的概念：\n\n\n- SYN：同步序列编号（Synchronize Sequence Numbers）。\n- ACK (Acknowledgement）即是确认字符\n\n\n## 参考\n- [从输入url到页面展示到底发生了什么](https://www.jianshu.com/p/23b388f8e5aa)\n- [从输入URL到页面展示的详细过程](https://blog.csdn.net/wlk2064819994/article/details/79756669)\n- [从输入URL到浏览器显示页面发生了什么](https://www.cnblogs.com/kongxy/p/4615226.html)\n- [一个页面从输入URL到页面加载显示完成，这个过程都发生什么？](https://www.cnblogs.com/WaTa/p/5477374.html)\n"
  },
  {
    "path": "2.1.2 把一个简单的div元素的属性都打印出来.md",
    "content": "# 把一个简单的div元素的属性都打印出来\n\n```\nvar docu = document.createElement('div')\nvar str = '';\nfor(var key in docu){\n\tstr += key+'';\n}\n\"aligntitlelangtranslatedirdatasethiddentabIndexaccessKeydraggablespellcheckautocapitalizecontentEditableisContentEditableinputModeoffsetParentoffsetTopoffsetLeftoffsetWidthoffsetHeightstyleinnerTextouterTextonabortonbluroncanceloncanplayoncanplaythroughonchangeonclickoncloseoncontextmenuoncuechangeondblclickondragondragendondragenterondragleaveondragoverondragstartondropondurationchangeonemptiedonendedonerroronfocusoninputoninvalidonkeydownonkeypressonkeyuponloadonloadeddataonloadedmetadataonloadstartonmousedownonmouseenteronmouseleaveonmousemoveonmouseoutonmouseoveronmouseuponmousewheelonpauseonplayonplayingonprogressonratechangeonresetonresizeonscrollonseekedonseekingonselectonstalledonsubmitonsuspendontimeupdateontoggleonvolumechangeonwaitingonwheelonauxclickongotpointercaptureonlostpointercaptureonpointerdownonpointermoveonpointeruponpointercancelonpointeroveronpointeroutonpointerenteronpointerleavenonceclickfocusblurnamespaceURIprefixlocalNametagNameidclassNameclassListslotattributesshadowRootassignedSlotinnerHTMLouterHTMLscrollTopscrollLeftscrollWidthscrollHeightclientTopclientLeftclientWidthclientHeightonbeforecopyonbeforecutonbeforepasteoncopyoncutonpasteonsearchonselectstartpreviousElementSiblingnextElementSiblingchildrenfirstElementChildlastElementChildchildElementCountonwebkitfullscreenchangeonwebkitfullscreenerrorsetPointerCapturereleasePointerCapturehasPointerCapturehasAttributesgetAttributeNamesgetAttributegetAttributeNSsetAttributesetAttributeNSremoveAttributeremoveAttributeNShasAttributehasAttributeNSgetAttributeNodegetAttributeNodeNSsetAttributeNodesetAttributeNodeNSremoveAttributeNodeclosestmatcheswebkitMatchesSelectorattachShadowgetElementsByTagNamegetElementsByTagNameNSgetElementsByClassNameinsertAdjacentElementinsertAdjacentTextinsertAdjacentHTMLrequestPointerLockgetClientRectsgetBoundingClientRectscrollIntoViewscrollIntoViewIfNeededanimatebeforeafterreplaceWithremoveprependappendquerySelectorquerySelectorAllwebkitRequestFullScreenwebkitRequestFullscreenattributeStyleMapscrollscrollToscrollBycreateShadowRootgetDestinationInsertionPointscomputedStyleMapELEMENT_NODEATTRIBUTE_NODETEXT_NODECDATA_SECTION_NODEENTITY_REFERENCE_NODEENTITY_NODEPROCESSING_INSTRUCTION_NODECOMMENT_NODEDOCUMENT_NODEDOCUMENT_TYPE_NODEDOCUMENT_FRAGMENT_NODENOTATION_NODEDOCUMENT_POSITION_DISCONNECTEDDOCUMENT_POSITION_PRECEDINGDOCUMENT_POSITION_FOLLOWINGDOCUMENT_POSITION_CONTAINSDOCUMENT_POSITION_CONTAINED_BYDOCUMENT_POSITION_IMPLEMENTATION_SPECIFICnodeTypenodeNamebaseURIisConnectedownerDocumentparentNodeparentElementchildNodesfirstChildlastChildpreviousSiblingnextSiblingnodeValuetextContenthasChildNodesgetRootNodenormalizecloneNodeisEqualNodeisSameNodecompareDocumentPositioncontainslookupPrefixlookupNamespaceURIisDefaultNamespaceinsertBeforeappendChildreplaceChildremoveChildaddEventListenerremoveEventListenerdispatchEvent\"\n```\n\n## 参考\n- https://github.com/livoras/blog/issues/13\n"
  },
  {
    "path": "2.1.3 html中的b和strong标签有什么区别.md",
    "content": "# html中的`<b>`和`<strong>`有什么区别\n\n两者虽然在网页中显示效果一样，但实际目的不同。\n\n\n\n`<b>`这个标签对应 `bold`，即文本加粗，其目的仅仅是为了加粗显示文本，是一种样式／风格需求；\n\n\n`<strong>`这个标签意思是加强，表示该文本比较重要，提醒读者／终端注意。为了达到这个目的，浏览器等终端将其加粗显示；\n\n总结：`<b>`为了加粗而加粗，`<strong>`为了标明重点而加粗。\n\n<b></b>标签的加粗只是为了加粗\n\n<strong></strong>标签加粗是为了突出重点\n\n在网页中使用<strong></strong>突出的内容更容易被网页搜索蜘蛛搜索到。\n\n=====================================\n\n- title与h1的区别: \n定义：title是网站标题，h1是文章主题\n\n作用：title概括网站信息，可以直接告诉搜索引擎和用户这个网站是关于什么主题和内容的，是显示在网页Tab栏里的；\n\nh1突出文章主题，面对用户，更突出其视觉效果，指向页面主体信息，是显示在网页中的\n\n- b与strong,i与em的区别: \n但从视觉上效果观看b与strong、i与em是没有区别的,唯一区别是搜索引擎检索的时候搜索引擎可以识别strong、em标签、而不能识别b与i标签\n\n建议：为了符合CSS3的规范，b应尽量少用而改用strong ,i应尽量少用而改用em\n\n- img中的alt与title属性区别: \nalt属性是在你的图片因为某种原因不能加载时在页面显示的提示信息，它会直接输出在原本加载图片的地方\n\ntitle属性是在你鼠标悬停在该图片上时显示一个小提示，鼠标离开就没有了，有点类似jQuery的hover\n\n- src与href的区别: \nhref，.是超文本引用，指向需要连结的地方，是与该页面有关联的，是引用。在 link和a 等元素上使用。   \nsrc是指向物件的来源地址，是引入。在 img、script、iframe 等元素上使用   \nsrc通常用作“拿取”（引入），href 用作 “连结前往”（引用）  \n\n--------------------- \n\n\n\n## 参考\n- [HTML 元素 <b> 和 <strong> 有什么区别](https://www.zhihu.com/question/20961933)\n- [title与h1、b与strong、i与em、img的alt与title、src与href有什么区别？](https://blog.csdn.net/vivian_1122/article/details/80235052)\n"
  },
  {
    "path": "2.1.4 img标签的方方面面.md",
    "content": "# IMG>标签的方方面面\n\n\n## 问题1：如果在一个页面上插入<img>标签，有哪些属性是必需的？\n\n答案是src和alt。\n\n\n## 问题2：<img>标签在HTML和XHTML中有什么区别？\n\n在 HTML 中，<img> 标签没有结束标签。例如：\n```\n<img src=\" http://www.w3school.com.cn/i/eg_tulip.jpg\"  alt=\"上海鲜花港 - 郁金香\" >\n```\n \n在 XHTML 中，<img> 标签必须被正确地关闭。\n```\n<img src=\" http://www.w3school.com.cn/i/eg_tulip.jpg\"  alt=\"上海鲜花港 - 郁金香\" />\n```\n\n## 问题3：在一个页面上插入<img>标签，为什么说最好要使用height和width属性？\n\n您在浏览网页的时候，可能会遇到这种情况：随着页面中的图像加载并成功显示，页面上的内容会随之发生不规律的移动，影响您的阅读。这种情况就是因为页面上的图像没有定义height和width属性而导致的。\n\n如果没有定义图片的height和width属性，那么浏览器为了能够显示每一个加载的图像，它需要先下载图像，然后解析出图像的高度和宽度，并在显示窗口留出相应的屏幕空间，这样就会导致浏览器不断地重新计算/调整页面的布局，这可能会延迟文档的显示，并导致页面重绘。\n\n因此，笔者建议使用<img>的 height 和 width 属性来指定图像的尺寸。这样的话，浏览器在下载图像之前就为其预留出了空间，从而可以加速文档的显示，还可以避免文档内容的移动而引起页面重绘。\n\n## 问题4：<img>标签的onload/onerror/onabort事件，在什么情况下会被触发？\n\nonload: 事件会在图像加载完成后立即发生。\n\nonerror: 事件会在文档或图像加载过程中发生错误时被触发。\n\nonabort: 事件会在图像加载被中断时发生。例如用户单击了浏览器的Stop按钮，或者在图像下载的过程中。\n\n\n\n\n\n\n\n\n\n\n\n\n## 参考\n- [IMG>标签的方方面面](http://www.cnblogs.com/hencehong/archive/2012/10/06/something_interesting_about_image.html)\n"
  },
  {
    "path": "2.1.5 前端 performance.md",
    "content": "# performance\n\n>Web Performance API允许网页访问某些函数来测量网页和Web应用程序的性能，包括 Navigation Timing API和高分辨率时间数据。\n\n- [caniuse](https://caniuse.com/#feat=nav-timing)\n\n## performance\n\n>Chrome 浏览器控制台中执行 window.performance ,看到\n```\nperformance： {\n  // memory 是非标准属性，只在 Chrome 有\n  memory: {\n    jsHeapSizeLimit: 2181038080, // JS 对象（包括V8引擎内部对象）占用的内存，一定小于 totalJSHeapSize\n    totalJSHeapSize: 31213287, // 可使用的内存\n    usedJSHeapSize: 25468639 // 内存大小限制\n  },\n  navigation: {\n    redirectCount: 0,  // 如果有重定向的话，页面通过几次重定向跳转而来\n    type: 0  // 0   即 TYPE_NAVIGATENEXT 正常进入的页面（非刷新、非重定向等）\n            // 1   即 TYPE_RELOAD       通过 window.location.reload() 刷新的页面\n            // 2   即 TYPE_BACK_FORWARD 通过浏览器的前进后退按钮进入的页面（历史记录）\n            // 255 即 TYPE_UNDEFINED    非以上方式进入的页面\n  },\n  onresourcetimingbufferfull: null,\n  timeOrigin: 1555038706939.7961,\n  timing: {\n    connectEnd: 1555038706941,\n    connectStart: 1555038706941,\n    domComplete: 1555038712474,\n    domContentLoadedEventEnd: 1555038708147,\n    domContentLoadedEventStart: 1555038708146,\n    domInteractive: 1555038708146,\n    domLoading: 1555038707812,\n    domainLookupEnd: 1555038706941,\n    domainLookupStart: 1555038706941,\n    fetchStart: 1555038706941,\n    loadEventEnd: 1555038712482,\n    loadEventStart: 1555038712474,\n    navigationStart: 1555038706939,\n    redirectEnd: 0,\n    redirectStart: 0,\n    requestStart: 1555038706943,\n    responseEnd: 1555038708125,\n    responseStart: 1555038707802,\n    secureConnectionStart: 0,\n    unloadEventEnd: 1555038707807,\n    unloadEventStart: 1555038707807\n\n    // 注释解析\n    // 在同一个浏览器上下文中，前一个网页（与当前页面不一定同域）unload 的时间戳，如果无前一个网页 unload ，则与 fetchStart 值相等\n    navigationStart: 1441112691935,\n\n    // 前一个网页（与当前页面同域）unload 的时间戳，如果无前一个网页 unload 或者前一个网页与当前页面不同域，则值为 0\n    unloadEventStart: 0,\n\n    // 和 unloadEventStart 相对应，返回前一个网页 unload 事件绑定的回调函数执行完毕的时间戳\n    unloadEventEnd: 0,\n\n    // 第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算，否则值为 0 \n    redirectStart: 0,\n\n    // 最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内部的重定向才算，否则值为 0 \n    redirectEnd: 0,\n\n    // 浏览器准备好使用 HTTP 请求抓取文档的时间，这发生在检查本地缓存之前\n    fetchStart: 1441112692155,\n\n    // DNS 域名查询开始的时间，如果使用了本地缓存（即无 DNS 查询）或持久连接，则与 fetchStart 值相等\n    domainLookupStart: 1441112692155,\n\n    // DNS 域名查询完成的时间，如果使用了本地缓存（即无 DNS 查询）或持久连接，则与 fetchStart 值相等\n    domainLookupEnd: 1441112692155,\n\n    // HTTP（TCP） 开始建立连接的时间，如果是持久连接，则与 fetchStart 值相等\n    // 注意如果在传输层发生了错误且重新建立连接，则这里显示的是新建立的连接开始的时间\n    connectStart: 1441112692155,\n\n    // HTTP（TCP） 完成建立连接的时间（完成握手），如果是持久连接，则与 fetchStart 值相等\n    // 注意如果在传输层发生了错误且重新建立连接，则这里显示的是新建立的连接完成的时间\n    // 注意这里握手结束，包括安全连接建立完成、SOCKS 授权通过\n    connectEnd: 1441112692155,\n\n    // HTTPS 连接开始的时间，如果不是安全连接，则值为 0\n    secureConnectionStart: 0,\n\n    // HTTP 请求读取真实文档开始的时间（完成建立连接），包括从本地读取缓存\n    // 连接错误重连时，这里显示的也是新建立连接的时间\n    requestStart: 1441112692158,\n\n    // HTTP 开始接收响应的时间（获取到第一个字节），包括从本地读取缓存\n    responseStart: 1441112692686,\n\n    // HTTP 响应全部接收完成的时间（获取到最后一个字节），包括从本地读取缓存\n    responseEnd: 1441112692687,\n\n    // 开始解析渲染 DOM 树的时间，此时 Document.readyState 变为 loading，并将抛出 readystatechange 相关事件\n    domLoading: 1441112692690,\n\n    // 完成解析 DOM 树的时间，Document.readyState 变为 interactive，并将抛出 readystatechange 相关事件\n    // 注意只是 DOM 树解析完成，这时候并没有开始加载网页内的资源\n    domInteractive: 1441112693093,\n\n    // DOM 解析完成后，网页内资源加载开始的时间\n    // 在 DOMContentLoaded 事件抛出前发生\n    domContentLoadedEventStart: 1441112693093,\n\n    // DOM 解析完成后，网页内资源加载完成的时间（如 JS 脚本加载执行完毕）\n    domContentLoadedEventEnd: 1441112693101,\n\n    // DOM 树解析完成，且资源也准备就绪的时间，Document.readyState 变为 complete，并将抛出 readystatechange 相关事件\n    domComplete: 1441112693214,\n\n    // load 事件发送给文档，也即 load 回调函数开始执行的时间\n    // 注意如果没有绑定 load 事件，值为 0\n    loadEventStart: 1441112693214,\n\n    // load 事件的回调函数执行完毕的时间\n    loadEventEnd: 1441112693215\n  }\n}\n```\n\n## 参考\n- [MDN-performance](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/performance)\n- [alloyteam.com/2015/09/explore-performance/](http://www.alloyteam.com/2015/09/explore-performance/)\n"
  },
  {
    "path": "2.1.6 HTTP URL最大长度.md",
    "content": "# 2.1.6 HTTP URL最大长度\n\n>HTTP RFC2616协议没有规定URL的最大长度，但规定服务器如果不能处理太长的URL，就得返回414状态码（Request-URI Too Long）。\n\n>HTTP RFC2616协议指出，某些旧的客户端或代理，可能无法处理超过255个字节的URI。\n\n>既然HTTP协议没有规定URL的最大长度，那么URL的最大长度与用户的浏览器有关，同时和服务器能够处理的最大长度URL有关。\n\n>站点地图协议（sitemap protocol），限制URL的长度是2048，如果你想让搜索引擎能够搜索到你，2048是最大极限了。\n\n>Google最大能抓取和索引2047个字节的URL，但是，用户点击的时候会返回414，经过测试，Google的SERP连接，最大不能超过1855个字符，超过这个数，用户点击则返回414。\n\n>IE8的URL的最大URL长度是2083个字节，其中路径部分（Path）最大长度是2048个字节。IE9也有类似限制，IE10也只接受2083个字节。虽然点击URL长度比这个长的连接依然可以工作，但地址栏只显示前2083个字节。\n\n有网友测试了个浏览器和服务器支持的最大URL长度，附表如下：\n\n\n| 浏览器 | URL最大限制|\n| --:| --:|\nIE       | URL最大限制是2083个字节，Path长度最大是2048字节（Get请求）。\nFirefox  |  65536\nSafari   |   80000以上\nOpera    |   190000字节以上\nChrome    |   8182字节\nApache Server |   8192字节\nIIS      |16384字节\nPerl HTTP::Daemon   | 至少8000字节\n\n\n\n## 参考\n- [HTTP URL最大长度](https://blog.csdn.net/woxueliuyun/article/details/41866611)\n"
  },
  {
    "path": "2.1.7 文件上传中的content-type.md",
    "content": "# 2.1.7 文件上传中的content-type\n\n\n## application/x-www-form-urlencoded\n\n>在最开始的请求方式中，请求参数都是放在url中，表单提交的时候，都是以key=&value=的方式写在url后面。这也是浏览器表单提交的默认方式。\n\n\n## multipart/form-data\n\n>此种方式多用于**文件上传**，表单数据都保存在http的正文部分，各个表单项之间用boundary分开；\n- 使用FormData，在构造这个对象的时候，把表单的对象，作为一个参数放进去，就可以了，然后FormData，就会得到这个表单对象里面的所有的参数，\n甚至我们在表单中，都不需要声明enctype =\"multipart/form-data\" ，就可以直接提交。\n\n\n## application/json\n>现在越来越多的应用使用application/json,用来告诉服务端消息主体是序列化的json字符串。\n由于json规范的流行，各大浏览器都开始原生支持JSON.stringfy\n"
  },
  {
    "path": "2.1.7.1 文件上传 Media Types.md",
    "content": "# 2.1.7.1 文件上传 Media Types\n\n1d-interleaved-parityfec\tapplication/1d-interleaved-parityfec\t[RFC6015]\n3gpdash-qoe-report+xml\tapplication/3gpdash-qoe-report+xml\t[_3GPP][Ozgur_Oyman]\n3gppHal+json\tapplication/3gppHal+json\t[_3GPP][Ulrich_Wiehe]\n3gppHalForms+json\tapplication/3gppHalForms+json\t[_3GPP][Ulrich_Wiehe]\n3gpp-ims+xml\tapplication/3gpp-ims+xml\t[_3GPP][John_M_Meredith]\nA2L\tapplication/A2L\t[ASAM][Thomas_Thomsen]\nactivemessage\tapplication/activemessage\t[Ehud_Shapiro]\nactivity+json\tapplication/activity+json\t[W3C][Benjamin_Goering]\nalto-costmap+json\tapplication/alto-costmap+json\t[RFC7285]\nalto-costmapfilter+json\tapplication/alto-costmapfilter+json\t[RFC7285]\nalto-directory+json\tapplication/alto-directory+json\t[RFC7285]\nalto-endpointprop+json\tapplication/alto-endpointprop+json\t[RFC7285]\nalto-endpointpropparams+json\tapplication/alto-endpointpropparams+json\t[RFC7285]\nalto-endpointcost+json\tapplication/alto-endpointcost+json\t[RFC7285]\nalto-endpointcostparams+json\tapplication/alto-endpointcostparams+json\t[RFC7285]\nalto-error+json\tapplication/alto-error+json\t[RFC7285]\nalto-networkmapfilter+json\tapplication/alto-networkmapfilter+json\t[RFC7285]\nalto-networkmap+json\tapplication/alto-networkmap+json\t[RFC7285]\nalto-updatestreamcontrol+json\tapplication/alto-updatestreamcontrol+json\t[RFC8895]\nalto-updatestreamparams+json\tapplication/alto-updatestreamparams+json\t[RFC8895]\nAML\tapplication/AML\t[ASAM][Thomas_Thomsen]\nandrew-inset\tapplication/andrew-inset\t[Nathaniel_Borenstein]\napplefile\tapplication/applefile\t[Patrik_Faltstrom]\nATF\tapplication/ATF\t[ASAM][Thomas_Thomsen]\nATFX\tapplication/ATFX\t[ASAM][Thomas_Thomsen]\natom+xml\tapplication/atom+xml\t[RFC4287][RFC5023]\natomcat+xml\tapplication/atomcat+xml\t[RFC5023]\natomdeleted+xml\tapplication/atomdeleted+xml\t[RFC6721]\natomicmail\tapplication/atomicmail\t[Nathaniel_Borenstein]\natomsvc+xml\tapplication/atomsvc+xml\t[RFC5023]\natsc-dwd+xml\tapplication/atsc-dwd+xml\t[ATSC]\natsc-dynamic-event-message\tapplication/atsc-dynamic-event-message\t[ATSC]\natsc-held+xml\tapplication/atsc-held+xml\t[ATSC]\natsc-rdt+json\tapplication/atsc-rdt+json\t[ATSC]\natsc-rsat+xml\tapplication/atsc-rsat+xml\t[ATSC]\nATXML\tapplication/ATXML\t[ASAM][Thomas_Thomsen]\nauth-policy+xml\tapplication/auth-policy+xml\t[RFC4745]\nbacnet-xdd+zip\tapplication/bacnet-xdd+zip\t[ASHRAE][Dave_Robin]\nbatch-SMTP\tapplication/batch-SMTP\t[RFC2442]\nbeep+xml\tapplication/beep+xml\t[RFC3080]\ncalendar+json\tapplication/calendar+json\t[RFC7265]\ncalendar+xml\tapplication/calendar+xml\t[RFC6321]\ncall-completion\tapplication/call-completion\t[RFC6910]\nCALS-1840\tapplication/CALS-1840\t[RFC1895]\ncaptive+json\tapplication/captive+json\t[RFC8908]\ncbor\tapplication/cbor\t[RFC8949]\ncbor-seq\tapplication/cbor-seq\t[RFC8742]\ncccex\tapplication/cccex\t[_3GPP]\nccmp+xml\tapplication/ccmp+xml\t[RFC6503]\nccxml+xml\tapplication/ccxml+xml\t[RFC4267]\nCDFX+XML\tapplication/CDFX+XML\t[ASAM][Thomas_Thomsen]\ncdmi-capability\tapplication/cdmi-capability\t[RFC6208]\ncdmi-container\tapplication/cdmi-container\t[RFC6208]\ncdmi-domain\tapplication/cdmi-domain\t[RFC6208]\ncdmi-object\tapplication/cdmi-object\t[RFC6208]\ncdmi-queue\tapplication/cdmi-queue\t[RFC6208]\ncdni\tapplication/cdni\t[RFC7736]\nCEA\tapplication/CEA\t[ASAM][Thomas_Thomsen]\ncea-2018+xml\tapplication/cea-2018+xml\t[Gottfried_Zimmermann]\ncellml+xml\tapplication/cellml+xml\t[RFC4708]\ncfw\tapplication/cfw\t[RFC6230]\nclr\tapplication/clr\t[IMS_Global][Andy_Miller]\nclue_info+xml\tapplication/clue_info+xml\t[RFC8846]\nclue+xml\tapplication/clue+xml\t[RFC8847]\ncms\tapplication/cms\t[RFC7193]\ncnrp+xml\tapplication/cnrp+xml\t[RFC3367]\ncoap-group+json\tapplication/coap-group+json\t[RFC7390]\ncoap-payload\tapplication/coap-payload\t[RFC8075]\ncommonground\tapplication/commonground\t[David_Glazer]\nconference-info+xml\tapplication/conference-info+xml\t[RFC4575]\ncpl+xml\tapplication/cpl+xml\t[RFC3880]\ncose\tapplication/cose\t[RFC-ietf-cose-rfc8152bis-struct-15]\ncose-key\tapplication/cose-key\t[RFC-ietf-cose-rfc8152bis-struct-15]\ncose-key-set\tapplication/cose-key-set\t[RFC-ietf-cose-rfc8152bis-struct-15]\ncsrattrs\tapplication/csrattrs\t[RFC7030]\ncsta+xml\tapplication/csta+xml\t[Ecma_International_Helpdesk]\nCSTAdata+xml\tapplication/CSTAdata+xml\t[Ecma_International_Helpdesk]\ncsvm+json\tapplication/csvm+json\t[W3C][Ivan_Herman]\ncwt\tapplication/cwt\t[RFC8392]\ncybercash\tapplication/cybercash\t[Donald_E._Eastlake_3rd]\ndash+xml\tapplication/dash+xml\t[Thomas_Stockhammer][ISO-IEC_JTC1]\ndashdelta\tapplication/dashdelta\t[David_Furbeck]\ndavmount+xml\tapplication/davmount+xml\t[RFC4709]\ndca-rft\tapplication/dca-rft\t[Larry_Campbell]\nDCD\tapplication/DCD\t[ASAM][Thomas_Thomsen]\ndec-dx\tapplication/dec-dx\t[Larry_Campbell]\ndialog-info+xml\tapplication/dialog-info+xml\t[RFC4235]\ndicom\tapplication/dicom\t[RFC3240]\ndicom+json\tapplication/dicom+json\t[DICOM_Standards_Committee][David_Clunie][James_F_Philbin]\ndicom+xml\tapplication/dicom+xml\t[DICOM_Standards_Committee][David_Clunie][James_F_Philbin]\nDII\tapplication/DII\t[ASAM][Thomas_Thomsen]\nDIT\tapplication/DIT\t[ASAM][Thomas_Thomsen]\ndns\tapplication/dns\t[RFC4027]\ndns+json\tapplication/dns+json\t[RFC8427]\ndns-message\tapplication/dns-message\t[RFC8484]\ndots+cbor\tapplication/dots+cbor\t[RFC8782]\ndskpp+xml\tapplication/dskpp+xml\t[RFC6063]\ndssc+der\tapplication/dssc+der\t[RFC5698]\ndssc+xml\tapplication/dssc+xml\t[RFC5698]\ndvcs\tapplication/dvcs\t[RFC3029]\necmascript\tapplication/ecmascript\t[RFC4329]\nEDI-consent\tapplication/EDI-consent\t[RFC1767]\nEDIFACT\tapplication/EDIFACT\t[RFC1767]\nEDI-X12\tapplication/EDI-X12\t[RFC1767]\nefi\tapplication/efi\t[UEFI_Forum][Samer_El-Haj-Mahmoud]\nelm+json\tapplication/elm+json\t[HL7][Bryn_Rhodes]\nelm+xml\tapplication/elm+xml\t[HL7][Bryn_Rhodes]\nEmergencyCallData.cap+xml\tapplication/EmergencyCallData.cap+xml\t[RFC8876]\nEmergencyCallData.Comment+xml\tapplication/EmergencyCallData.Comment+xml\t[RFC7852]\nEmergencyCallData.Control+xml\tapplication/EmergencyCallData.Control+xml\t[RFC8147]\nEmergencyCallData.DeviceInfo+xml\tapplication/EmergencyCallData.DeviceInfo+xml\t[RFC7852]\nEmergencyCallData.eCall.MSD\tapplication/EmergencyCallData.eCall.MSD\t[RFC8147]\nEmergencyCallData.ProviderInfo+xml\tapplication/EmergencyCallData.ProviderInfo+xml\t[RFC7852]\nEmergencyCallData.ServiceInfo+xml\tapplication/EmergencyCallData.ServiceInfo+xml\t[RFC7852]\nEmergencyCallData.SubscriberInfo+xml\tapplication/EmergencyCallData.SubscriberInfo+xml\t[RFC7852]\nEmergencyCallData.VEDS+xml\tapplication/EmergencyCallData.VEDS+xml\t[RFC8148]\nemma+xml\tapplication/emma+xml\t[W3C][http://www.w3.org/TR/2007/CR-emma-20071211/#media-type-registration][ISO-IEC_JTC1]\nemotionml+xml\tapplication/emotionml+xml\t[W3C][Kazuyuki_Ashimura]\nencaprtp\tapplication/encaprtp\t[RFC6849]\nepp+xml\tapplication/epp+xml\t[RFC5730]\nepub+zip\tapplication/epub+zip\t[International_Digital_Publishing_Forum][William_McCoy]\neshop\tapplication/eshop\t[Steve_Katz]\nexample\tapplication/example\t[RFC4735]\nexi\tapplication/exi\t[W3C][http://www.w3.org/TR/2009/CR-exi-20091208/#mediaTypeRegistration]\nexpect-ct-report+json\tapplication/expect-ct-report+json\t[RFC-ietf-httpbis-expect-ct-08]\nfastinfoset\tapplication/fastinfoset\t[ITU-T_ASN.1_Rapporteur][ISO-IEC_JTC1_SC6_ASN.1_Rapporteur]\nfastsoap\tapplication/fastsoap\t[ITU-T_ASN.1_Rapporteur][ISO-IEC_JTC1_SC6_ASN.1_Rapporteur]\nfdt+xml\tapplication/fdt+xml\t[RFC6726]\nfhir+json\tapplication/fhir+json\t[HL7][Grahame_Grieve]\nfhir+xml\tapplication/fhir+xml\t[HL7][Grahame_Grieve]\nfits\tapplication/fits\t[RFC4047]\nflexfec\tapplication/flexfec\t[RFC8627]\nfont-sfnt - DEPRECATED in favor of font/sfnt\tapplication/font-sfnt\t[Levantovsky][ISO-IEC_JTC1][RFC8081]\nfont-tdpfr\tapplication/font-tdpfr\t[RFC3073]\nfont-woff - DEPRECATED in favor of font/woff\tapplication/font-woff\t[W3C][RFC8081]\nframework-attributes+xml\tapplication/framework-attributes+xml\t[RFC6230]\ngeo+json\tapplication/geo+json\t[RFC7946]\ngeo+json-seq\tapplication/geo+json-seq\t[RFC8142]\ngeopackage+sqlite3\tapplication/geopackage+sqlite3\t[OGC][Scott_Simmons]\ngeoxacml+xml\tapplication/geoxacml+xml\t[OGC][Scott_Simmons]\ngltf-buffer\tapplication/gltf-buffer\t[Khronos][Saurabh_Bhatia]\ngml+xml\tapplication/gml+xml\t[OGC][Clemens_Portele]\ngzip\tapplication/gzip\t[RFC6713]\nH224\tapplication/H224\t[RFC4573]\nheld+xml\tapplication/held+xml\t[RFC5985]\nhttp\tapplication/http\t[RFC7230]\nhyperstudio\tapplication/hyperstudio\t[Michael_Domino]\nibe-key-request+xml\tapplication/ibe-key-request+xml\t[RFC5408]\nibe-pkg-reply+xml\tapplication/ibe-pkg-reply+xml\t[RFC5408]\nibe-pp-data\tapplication/ibe-pp-data\t[RFC5408]\niges\tapplication/iges\t[Curtis_Parks]\nim-iscomposing+xml\tapplication/im-iscomposing+xml\t[RFC3994]\nindex\tapplication/index\t[RFC2652]\nindex.cmd\tapplication/index.cmd\t[RFC2652]\nindex.obj\tapplication/index.obj\t[RFC2652]\nindex.response\tapplication/index.response\t[RFC2652]\nindex.vnd\tapplication/index.vnd\t[RFC2652]\ninkml+xml\tapplication/inkml+xml\t[Kazuyuki_Ashimura]\nIOTP\tapplication/IOTP\t[RFC2935]\nipfix\tapplication/ipfix\t[RFC5655]\nipp\tapplication/ipp\t[RFC8010]\nISUP\tapplication/ISUP\t[RFC3204]\nits+xml\tapplication/its+xml\t[W3C][ITS-IG-W3C]\njavascript\tapplication/javascript\t[RFC4329]\njf2feed+json\tapplication/jf2feed+json\t[W3C][Ivan_Herman]\njose\tapplication/jose\t[RFC7515]\njose+json\tapplication/jose+json\t[RFC7515]\njrd+json\tapplication/jrd+json\t[RFC7033]\njscalendar+json\tapplication/jscalendar+json\t[RFC-ietf-calext-jscalendar-32]\njson\tapplication/json\t[RFC8259]\njson-patch+json\tapplication/json-patch+json\t[RFC6902]\njson-seq\tapplication/json-seq\t[RFC7464]\njwk+json\tapplication/jwk+json\t[RFC7517]\njwk-set+json\tapplication/jwk-set+json\t[RFC7517]\njwt\tapplication/jwt\t[RFC7519]\nkpml-request+xml\tapplication/kpml-request+xml\t[RFC4730]\nkpml-response+xml\tapplication/kpml-response+xml\t[RFC4730]\nld+json\tapplication/ld+json\t[W3C][Ivan_Herman]\nlgr+xml\tapplication/lgr+xml\t[RFC7940]\nlink-format\tapplication/link-format\t[RFC6690]\nload-control+xml\tapplication/load-control+xml\t[RFC7200]\nlost+xml\tapplication/lost+xml\t[RFC5222]\nlostsync+xml\tapplication/lostsync+xml\t[RFC6739]\nlpf+zip\tapplication/lpf+zip\t[W3C][Ivan_Herman]\nLXF\tapplication/LXF\t[ASAM][Thomas_Thomsen]\nmac-binhex40\tapplication/mac-binhex40\t[Patrik_Faltstrom]\nmacwriteii\tapplication/macwriteii\t[Paul_Lindner]\nmads+xml\tapplication/mads+xml\t[RFC6207]\nmarc\tapplication/marc\t[RFC2220]\nmarcxml+xml\tapplication/marcxml+xml\t[RFC6207]\nmathematica\tapplication/mathematica\t[Wolfram]\nmathml+xml\tapplication/mathml+xml\t[W3C][http://www.w3.org/TR/MathML3/appendixb.html]\nmathml-content+xml\tapplication/mathml-content+xml\t[W3C][http://www.w3.org/TR/MathML3/appendixb.html]\nmathml-presentation+xml\tapplication/mathml-presentation+xml\t[W3C][http://www.w3.org/TR/MathML3/appendixb.html]\nmbms-associated-procedure-description+xml\tapplication/mbms-associated-procedure-description+xml\t[_3GPP]\nmbms-deregister+xml\tapplication/mbms-deregister+xml\t[_3GPP]\nmbms-envelope+xml\tapplication/mbms-envelope+xml\t[_3GPP]\nmbms-msk-response+xml\tapplication/mbms-msk-response+xml\t[_3GPP]\nmbms-msk+xml\tapplication/mbms-msk+xml\t[_3GPP]\nmbms-protection-description+xml\tapplication/mbms-protection-description+xml\t[_3GPP]\nmbms-reception-report+xml\tapplication/mbms-reception-report+xml\t[_3GPP]\nmbms-register-response+xml\tapplication/mbms-register-response+xml\t[_3GPP]\nmbms-register+xml\tapplication/mbms-register+xml\t[_3GPP]\nmbms-schedule+xml\tapplication/mbms-schedule+xml\t[_3GPP][Eric_Turcotte]\nmbms-user-service-description+xml\tapplication/mbms-user-service-description+xml\t[_3GPP]\nmbox\tapplication/mbox\t[RFC4155]\nmedia_control+xml\tapplication/media_control+xml\t[RFC5168]\nmedia-policy-dataset+xml\tapplication/media-policy-dataset+xml\t[RFC6796]\nmediaservercontrol+xml\tapplication/mediaservercontrol+xml\t[RFC5022]\nmerge-patch+json\tapplication/merge-patch+json\t[RFC7396]\nmetalink4+xml\tapplication/metalink4+xml\t[RFC5854]\nmets+xml\tapplication/mets+xml\t[RFC6207]\nMF4\tapplication/MF4\t[ASAM][Thomas_Thomsen]\nmikey\tapplication/mikey\t[RFC3830]\nmipc\tapplication/mipc\t[NCGIS][Bryan_Blank]\nmmt-aei+xml\tapplication/mmt-aei+xml\t[ATSC]\nmmt-usd+xml\tapplication/mmt-usd+xml\t[ATSC]\nmods+xml\tapplication/mods+xml\t[RFC6207]\nmoss-keys\tapplication/moss-keys\t[RFC1848]\nmoss-signature\tapplication/moss-signature\t[RFC1848]\nmosskey-data\tapplication/mosskey-data\t[RFC1848]\nmosskey-request\tapplication/mosskey-request\t[RFC1848]\nmp21\tapplication/mp21\t[RFC6381][David_Singer]\nmp4\tapplication/mp4\t[RFC4337][RFC6381]\nmpeg4-generic\tapplication/mpeg4-generic\t[RFC3640]\nmpeg4-iod\tapplication/mpeg4-iod\t[RFC4337]\nmpeg4-iod-xmt\tapplication/mpeg4-iod-xmt\t[RFC4337]\nmrb-consumer+xml\tapplication/mrb-consumer+xml\t[RFC6917]\nmrb-publish+xml\tapplication/mrb-publish+xml\t[RFC6917]\nmsc-ivr+xml\tapplication/msc-ivr+xml\t[RFC6231]\nmsc-mixer+xml\tapplication/msc-mixer+xml\t[RFC6505]\nmsword\tapplication/msword\t[Paul_Lindner]\nmud+json\tapplication/mud+json\t[RFC8520]\nmultipart-core\tapplication/multipart-core\t[RFC8710]\nmxf\tapplication/mxf\t[RFC4539]\nn-quads\tapplication/n-quads\t[W3C][Eric_Prudhommeaux]\nn-triples\tapplication/n-triples\t[W3C][Eric_Prudhommeaux]\nnasdata\tapplication/nasdata\t[RFC4707]\nnews-checkgroups\tapplication/news-checkgroups\t[RFC5537]\nnews-groupinfo\tapplication/news-groupinfo\t[RFC5537]\nnews-transmission\tapplication/news-transmission\t[RFC5537]\nnlsml+xml\tapplication/nlsml+xml\t[RFC6787]\nnode\tapplication/node\t[Node.js_TSC]\nnss\tapplication/nss\t[Michael_Hammer]\noauth-authz-req+jwt\tapplication/oauth-authz-req+jwt\t[RFC-ietf-oauth-jwsreq-34]\nocsp-request\tapplication/ocsp-request\t[RFC6960]\nocsp-response\tapplication/ocsp-response\t[RFC6960]\noctet-stream\tapplication/octet-stream\t[RFC2045][RFC2046]\nODA\tapplication/ODA\t[RFC1494]\nodm+xml\tapplication/odm+xml\t[CDISC][Sam_Hume]\nODX\tapplication/ODX\t[ASAM][Thomas_Thomsen]\noebps-package+xml\tapplication/oebps-package+xml\t[RFC4839]\nogg\tapplication/ogg\t[RFC5334][RFC7845]\nopc-nodeset+xml\tapplication/opc-nodeset+xml\t[OPC_Foundation]\noscore\tapplication/oscore\t[RFC8613]\noxps\tapplication/oxps\t[Ecma_International_Helpdesk]\np2p-overlay+xml\tapplication/p2p-overlay+xml\t[RFC6940]\nparityfec\tapplication/parityfec\t[RFC3009]\npassport\tapplication/passport\t[RFC8225]\npatch-ops-error+xml\tapplication/patch-ops-error+xml\t[RFC5261]\npdf\tapplication/pdf\t[RFC8118]\nPDX\tapplication/PDX\t[ASAM][Thomas_Thomsen]\npem-certificate-chain\tapplication/pem-certificate-chain\t[RFC8555]\npgp-encrypted\tapplication/pgp-encrypted\t[RFC3156]\npgp-keys\tapplication/pgp-keys\t[RFC3156]\npgp-signature\tapplication/pgp-signature\t[RFC3156]\npidf-diff+xml\tapplication/pidf-diff+xml\t[RFC5262]\npidf+xml\tapplication/pidf+xml\t[RFC3863]\npkcs10\tapplication/pkcs10\t[RFC5967]\npkcs7-mime\tapplication/pkcs7-mime\t[RFC8551][RFC7114]\npkcs7-signature\tapplication/pkcs7-signature\t[RFC8551]\npkcs8\tapplication/pkcs8\t[RFC5958]\npkcs8-encrypted\tapplication/pkcs8-encrypted\t[RFC8351]\npkcs12\tapplication/pkcs12\t[IETF]\npkix-attr-cert\tapplication/pkix-attr-cert\t[RFC5877]\npkix-cert\tapplication/pkix-cert\t[RFC2585]\npkix-crl\tapplication/pkix-crl\t[RFC2585]\npkix-pkipath\tapplication/pkix-pkipath\t[RFC6066]\npkixcmp\tapplication/pkixcmp\t[RFC2510]\npls+xml\tapplication/pls+xml\t[RFC4267]\npoc-settings+xml\tapplication/poc-settings+xml\t[RFC4354]\npostscript\tapplication/postscript\t[RFC2045][RFC2046]\nppsp-tracker+json\tapplication/ppsp-tracker+json\t[RFC7846]\nproblem+json\tapplication/problem+json\t[RFC7807]\nproblem+xml\tapplication/problem+xml\t[RFC7807]\nprovenance+xml\tapplication/provenance+xml\t[W3C][Ivan_Herman]\nprs.alvestrand.titrax-sheet\tapplication/prs.alvestrand.titrax-sheet\t[Harald_T._Alvestrand]\nprs.cww\tapplication/prs.cww\t[Khemchart_Rungchavalnont]\nprs.cyn\tapplication/prs.cyn\t[Cynthia_Revström]\nprs.hpub+zip\tapplication/prs.hpub+zip\t[Giulio_Zambon]\nprs.nprend\tapplication/prs.nprend\t[Jay_Doggett]\nprs.plucker\tapplication/prs.plucker\t[Bill_Janssen]\nprs.rdf-xml-crypt\tapplication/prs.rdf-xml-crypt\t[Toby_Inkster]\nprs.xsf+xml\tapplication/prs.xsf+xml\t[Maik_Stührenberg]\npskc+xml\tapplication/pskc+xml\t[RFC6030]\npvd+json\tapplication/pvd+json\t[RFC8801]\nrdf+xml\tapplication/rdf+xml\t[RFC3870]\nroute-apd+xml\tapplication/route-apd+xml\t[ATSC]\nroute-s-tsid+xml\tapplication/route-s-tsid+xml\t[ATSC]\nroute-usd+xml\tapplication/route-usd+xml\t[ATSC]\nQSIG\tapplication/QSIG\t[RFC3204]\nraptorfec\tapplication/raptorfec\t[RFC6682]\nrdap+json\tapplication/rdap+json\t[RFC-ietf-regext-rfc7483bis-05]\nreginfo+xml\tapplication/reginfo+xml\t[RFC3680]\nrelax-ng-compact-syntax\tapplication/relax-ng-compact-syntax\t[http://www.jtc1sc34.org/repository/0661.pdf]\nremote-printing\tapplication/remote-printing\t[RFC1486][Marshall_Rose]\nreputon+json\tapplication/reputon+json\t[RFC7071]\nresource-lists-diff+xml\tapplication/resource-lists-diff+xml\t[RFC5362]\nresource-lists+xml\tapplication/resource-lists+xml\t[RFC4826]\nrfc+xml\tapplication/rfc+xml\t[RFC7991]\nriscos\tapplication/riscos\t[Nick_Smith]\nrlmi+xml\tapplication/rlmi+xml\t[RFC4662]\nrls-services+xml\tapplication/rls-services+xml\t[RFC4826]\nrpki-ghostbusters\tapplication/rpki-ghostbusters\t[RFC6493]\nrpki-manifest\tapplication/rpki-manifest\t[RFC6481]\nrpki-publication\tapplication/rpki-publication\t[RFC8181]\nrpki-roa\tapplication/rpki-roa\t[RFC6481]\nrpki-updown\tapplication/rpki-updown\t[RFC6492]\nrtf\tapplication/rtf\t[Paul_Lindner]\nrtploopback\tapplication/rtploopback\t[RFC6849]\nrtx\tapplication/rtx\t[RFC4588]\nsamlassertion+xml\tapplication/samlassertion+xml\t[OASIS_Security_Services_Technical_Committee_SSTC]\nsamlmetadata+xml\tapplication/samlmetadata+xml\t[OASIS_Security_Services_Technical_Committee_SSTC]\nsarif-external-properties+json\tapplication/sarif-external-properties+json\t[OASIS][David_Keaton][Michael_C._Fanning]\nsarif+json\tapplication/sarif+json\t[OASIS][Michael_C._Fanning][Laurence_J._Golding]\nsbe\tapplication/sbe\t[FIX_Trading_Community][Donald_L._Mendelson]\nsbml+xml\tapplication/sbml+xml\t[RFC3823]\nscaip+xml\tapplication/scaip+xml\t[SIS][Oskar_Jonsson]\nscim+json\tapplication/scim+json\t[RFC7644]\nscvp-cv-request\tapplication/scvp-cv-request\t[RFC5055]\nscvp-cv-response\tapplication/scvp-cv-response\t[RFC5055]\nscvp-vp-request\tapplication/scvp-vp-request\t[RFC5055]\nscvp-vp-response\tapplication/scvp-vp-response\t[RFC5055]\nsdp\tapplication/sdp\t[RFC8866]\nsecevent+jwt\tapplication/secevent+jwt\t[RFC8417]\nsenml-etch+cbor\tapplication/senml-etch+cbor\t[RFC8790]\nsenml-etch+json\tapplication/senml-etch+json\t[RFC8790]\nsenml-exi\tapplication/senml-exi\t[RFC8428]\nsenml+cbor\tapplication/senml+cbor\t[RFC8428]\nsenml+json\tapplication/senml+json\t[RFC8428]\nsenml+xml\tapplication/senml+xml\t[RFC8428]\nsensml-exi\tapplication/sensml-exi\t[RFC8428]\nsensml+cbor\tapplication/sensml+cbor\t[RFC8428]\nsensml+json\tapplication/sensml+json\t[RFC8428]\nsensml+xml\tapplication/sensml+xml\t[RFC8428]\nsep-exi\tapplication/sep-exi\t[Robby_Simpson][ZigBee]\nsep+xml\tapplication/sep+xml\t[Robby_Simpson][ZigBee]\nsession-info\tapplication/session-info\t[_3GPP][Frederic_Firmin]\nset-payment\tapplication/set-payment\t[Brian_Korver]\nset-payment-initiation\tapplication/set-payment-initiation\t[Brian_Korver]\nset-registration\tapplication/set-registration\t[Brian_Korver]\nset-registration-initiation\tapplication/set-registration-initiation\t[Brian_Korver]\nSGML\tapplication/SGML\t[RFC1874]\nsgml-open-catalog\tapplication/sgml-open-catalog\t[Paul_Grosso]\nshf+xml\tapplication/shf+xml\t[RFC4194]\nsieve\tapplication/sieve\t[RFC5228]\nsimple-filter+xml\tapplication/simple-filter+xml\t[RFC4661]\nsimple-message-summary\tapplication/simple-message-summary\t[RFC3842]\nsimpleSymbolContainer\tapplication/simpleSymbolContainer\t[_3GPP]\nsipc\tapplication/sipc\t[NCGIS][Bryan_Blank]\nslate\tapplication/slate\t[Terry_Crowley]\nsmil - OBSOLETED in favor of application/smil+xml\tapplication/smil\t[RFC4536]\nsmil+xml\tapplication/smil+xml\t[RFC4536]\nsmpte336m\tapplication/smpte336m\t[RFC6597]\nsoap+fastinfoset\tapplication/soap+fastinfoset\t[ITU-T_ASN.1_Rapporteur][ISO-IEC_JTC1_SC6_ASN.1_Rapporteur]\nsoap+xml\tapplication/soap+xml\t[RFC3902]\nsparql-query\tapplication/sparql-query\t[W3C][http://www.w3.org/TR/2007/CR-rdf-sparql-query-20070614/#mediaType]\nsparql-results+xml\tapplication/sparql-results+xml\t[W3C][http://www.w3.org/TR/2007/CR-rdf-sparql-XMLres-20070925/#mime]\nspirits-event+xml\tapplication/spirits-event+xml\t[RFC3910]\nsql\tapplication/sql\t[RFC6922]\nsrgs\tapplication/srgs\t[RFC4267]\nsrgs+xml\tapplication/srgs+xml\t[RFC4267]\nsru+xml\tapplication/sru+xml\t[RFC6207]\nssml+xml\tapplication/ssml+xml\t[RFC4267]\nstix+json\tapplication/stix+json\t[OASIS][Chet_Ensign]\nswid+xml\tapplication/swid+xml\t[ISO-IEC_JTC1][David_Waltermire][Ron_Brill]\ntamp-apex-update\tapplication/tamp-apex-update\t[RFC5934]\ntamp-apex-update-confirm\tapplication/tamp-apex-update-confirm\t[RFC5934]\ntamp-community-update\tapplication/tamp-community-update\t[RFC5934]\ntamp-community-update-confirm\tapplication/tamp-community-update-confirm\t[RFC5934]\ntamp-error\tapplication/tamp-error\t[RFC5934]\ntamp-sequence-adjust\tapplication/tamp-sequence-adjust\t[RFC5934]\ntamp-sequence-adjust-confirm\tapplication/tamp-sequence-adjust-confirm\t[RFC5934]\ntamp-status-query\tapplication/tamp-status-query\t[RFC5934]\ntamp-status-response\tapplication/tamp-status-response\t[RFC5934]\ntamp-update\tapplication/tamp-update\t[RFC5934]\ntamp-update-confirm\tapplication/tamp-update-confirm\t[RFC5934]\ntaxii+json\tapplication/taxii+json\t[OASIS][Chet_Ensign]\ntd+json\tapplication/td+json\t[W3C][Matthias_Kovatsch]\ntei+xml\tapplication/tei+xml\t[RFC6129]\nTETRA_ISI\tapplication/TETRA_ISI\t[ETSI][Miguel_Angel_Reina_Ortega]\nthraud+xml\tapplication/thraud+xml\t[RFC5941]\ntimestamp-query\tapplication/timestamp-query\t[RFC3161]\ntimestamp-reply\tapplication/timestamp-reply\t[RFC3161]\ntimestamped-data\tapplication/timestamped-data\t[RFC5955]\ntlsrpt+gzip\tapplication/tlsrpt+gzip\t[RFC8460]\ntlsrpt+json\tapplication/tlsrpt+json\t[RFC8460]\ntnauthlist\tapplication/tnauthlist\t[RFC8226]\ntrickle-ice-sdpfrag\tapplication/trickle-ice-sdpfrag\t[RFC8840]\ntrig\tapplication/trig\t[W3C][W3C_RDF_Working_Group]\nttml+xml\tapplication/ttml+xml\t[W3C][W3C_Timed_Text_Working_Group]\ntve-trigger\tapplication/tve-trigger\t[Linda_Welsh]\ntzif\tapplication/tzif\t[RFC8536]\ntzif-leap\tapplication/tzif-leap\t[RFC8536]\nulpfec\tapplication/ulpfec\t[RFC5109]\nurc-grpsheet+xml\tapplication/urc-grpsheet+xml\t[Gottfried_Zimmermann][ISO-IEC_JTC1]\nurc-ressheet+xml\tapplication/urc-ressheet+xml\t[Gottfried_Zimmermann][ISO-IEC_JTC1]\nurc-targetdesc+xml\tapplication/urc-targetdesc+xml\t[Gottfried_Zimmermann][ISO-IEC_JTC1]\nurc-uisocketdesc+xml\tapplication/urc-uisocketdesc+xml\t[Gottfried_Zimmermann]\nvcard+json\tapplication/vcard+json\t[RFC7095]\nvcard+xml\tapplication/vcard+xml\t[RFC6351]\nvemmi\tapplication/vemmi\t[RFC2122]\nvnd.1000minds.decision-model+xml\tapplication/vnd.1000minds.decision-model+xml\t[Franz_Ombler]\nvnd.3gpp.5gnas\tapplication/vnd.3gpp.5gnas\t[_3GPP][Jones_Lu_Yunjie]\nvnd.3gpp.access-transfer-events+xml\tapplication/vnd.3gpp.access-transfer-events+xml\t[Frederic_Firmin]\nvnd.3gpp.bsf+xml\tapplication/vnd.3gpp.bsf+xml\t[John_M_Meredith]\nvnd.3gpp.GMOP+xml\tapplication/vnd.3gpp.GMOP+xml\t[Frederic_Firmin]\nvnd.3gpp.gtpc\tapplication/vnd.3gpp.gtpc\t[_3GPP][Yang_Yong]\nvnd.3gpp.interworking-data\tapplication/vnd.3gpp.interworking-data\t[Frederic_Firmin]\nvnd.3gpp.lpp\tapplication/vnd.3gpp.lpp\t[_3GPP][Jones_Lu_Yunjie]\nvnd.3gpp.mc-signalling-ear\tapplication/vnd.3gpp.mc-signalling-ear\t[Tim_Woodward]\nvnd.3gpp.mcdata-affiliation-command+xml\tapplication/vnd.3gpp.mcdata-affiliation-command+xml\t[Frederic_Firmin]\nvnd.3gpp.mcdata-info+xml\tapplication/vnd.3gpp.mcdata-info+xml\t[Frederic_Firmin]\nvnd.3gpp.mcdata-payload\tapplication/vnd.3gpp.mcdata-payload\t[Frederic_Firmin]\nvnd.3gpp.mcdata-service-config+xml\tapplication/vnd.3gpp.mcdata-service-config+xml\t[Frederic_Firmin]\nvnd.3gpp.mcdata-signalling\tapplication/vnd.3gpp.mcdata-signalling\t[Frederic_Firmin]\nvnd.3gpp.mcdata-ue-config+xml\tapplication/vnd.3gpp.mcdata-ue-config+xml\t[Frederic_Firmin]\nvnd.3gpp.mcdata-user-profile+xml\tapplication/vnd.3gpp.mcdata-user-profile+xml\t[Frederic_Firmin]\nvnd.3gpp.mcptt-affiliation-command+xml\tapplication/vnd.3gpp.mcptt-affiliation-command+xml\t[Frederic_Firmin]\nvnd.3gpp.mcptt-floor-request+xml\tapplication/vnd.3gpp.mcptt-floor-request+xml\t[Frederic_Firmin]\nvnd.3gpp.mcptt-info+xml\tapplication/vnd.3gpp.mcptt-info+xml\t[Frederic_Firmin]\nvnd.3gpp.mcptt-location-info+xml\tapplication/vnd.3gpp.mcptt-location-info+xml\t[Frederic_Firmin]\nvnd.3gpp.mcptt-mbms-usage-info+xml\tapplication/vnd.3gpp.mcptt-mbms-usage-info+xml\t[Frederic_Firmin]\nvnd.3gpp.mcptt-service-config+xml\tapplication/vnd.3gpp.mcptt-service-config+xml\t[Frederic_Firmin]\nvnd.3gpp.mcptt-signed+xml\tapplication/vnd.3gpp.mcptt-signed+xml\t[Frederic_Firmin]\nvnd.3gpp.mcptt-ue-config+xml\tapplication/vnd.3gpp.mcptt-ue-config+xml\t[Frederic_Firmin]\nvnd.3gpp.mcptt-ue-init-config+xml\tapplication/vnd.3gpp.mcptt-ue-init-config+xml\t[Frederic_Firmin]\nvnd.3gpp.mcptt-user-profile+xml\tapplication/vnd.3gpp.mcptt-user-profile+xml\t[Frederic_Firmin]\nvnd.3gpp.mcvideo-affiliation-command+xml\tapplication/vnd.3gpp.mcvideo-affiliation-command+xml\t[Frederic_Firmin]\nvnd.3gpp.mcvideo-affiliation-info+xml - OBSOLETED in favor of application/vnd.3gpp.mcvideo-info+xml\tapplication/vnd.3gpp.mcvideo-affiliation-info+xml\t[Frederic_Firmin]\nvnd.3gpp.mcvideo-info+xml\tapplication/vnd.3gpp.mcvideo-info+xml\t[Frederic_Firmin]\nvnd.3gpp.mcvideo-location-info+xml\tapplication/vnd.3gpp.mcvideo-location-info+xml\t[Frederic_Firmin]\nvnd.3gpp.mcvideo-mbms-usage-info+xml\tapplication/vnd.3gpp.mcvideo-mbms-usage-info+xml\t[Frederic_Firmin]\nvnd.3gpp.mcvideo-service-config+xml\tapplication/vnd.3gpp.mcvideo-service-config+xml\t[Frederic_Firmin]\nvnd.3gpp.mcvideo-transmission-request+xml\tapplication/vnd.3gpp.mcvideo-transmission-request+xml\t[Frederic_Firmin]\nvnd.3gpp.mcvideo-ue-config+xml\tapplication/vnd.3gpp.mcvideo-ue-config+xml\t[Frederic_Firmin]\nvnd.3gpp.mcvideo-user-profile+xml\tapplication/vnd.3gpp.mcvideo-user-profile+xml\t[Frederic_Firmin]\nvnd.3gpp.mid-call+xml\tapplication/vnd.3gpp.mid-call+xml\t[Frederic_Firmin]\nvnd.3gpp.ngap\tapplication/vnd.3gpp.ngap\t[_3GPP][Yang_Yong]\nvnd.3gpp.pfcp\tapplication/vnd.3gpp.pfcp\t[_3GPP][Bruno_Landais]\nvnd.3gpp.pic-bw-large\tapplication/vnd.3gpp.pic-bw-large\t[John_M_Meredith]\nvnd.3gpp.pic-bw-small\tapplication/vnd.3gpp.pic-bw-small\t[John_M_Meredith]\nvnd.3gpp.pic-bw-var\tapplication/vnd.3gpp.pic-bw-var\t[John_M_Meredith]\nvnd.3gpp-prose-pc3ch+xml\tapplication/vnd.3gpp-prose-pc3ch+xml\t[Frederic_Firmin]\nvnd.3gpp-prose+xml\tapplication/vnd.3gpp-prose+xml\t[Frederic_Firmin]\nvnd.3gpp.s1ap\tapplication/vnd.3gpp.s1ap\t[_3GPP][Yang_Yong]\nvnd.3gpp.sms\tapplication/vnd.3gpp.sms\t[John_M_Meredith]\nvnd.3gpp.sms+xml\tapplication/vnd.3gpp.sms+xml\t[Frederic_Firmin]\nvnd.3gpp.srvcc-ext+xml\tapplication/vnd.3gpp.srvcc-ext+xml\t[Frederic_Firmin]\nvnd.3gpp.SRVCC-info+xml\tapplication/vnd.3gpp.SRVCC-info+xml\t[Frederic_Firmin]\nvnd.3gpp.state-and-event-info+xml\tapplication/vnd.3gpp.state-and-event-info+xml\t[Frederic_Firmin]\nvnd.3gpp.ussd+xml\tapplication/vnd.3gpp.ussd+xml\t[Frederic_Firmin]\nvnd.3gpp-v2x-local-service-information\tapplication/vnd.3gpp-v2x-local-service-information\t[Frederic_Firmin]\nvnd.3gpp2.bcmcsinfo+xml\tapplication/vnd.3gpp2.bcmcsinfo+xml\t[Andy_Dryden]\nvnd.3gpp2.sms\tapplication/vnd.3gpp2.sms\t[AC_Mahendran]\nvnd.3gpp2.tcap\tapplication/vnd.3gpp2.tcap\t[AC_Mahendran]\nvnd.3lightssoftware.imagescal\tapplication/vnd.3lightssoftware.imagescal\t[Gus_Asadi]\nvnd.3M.Post-it-Notes\tapplication/vnd.3M.Post-it-Notes\t[Michael_OBrien]\nvnd.accpac.simply.aso\tapplication/vnd.accpac.simply.aso\t[Steve_Leow]\nvnd.accpac.simply.imp\tapplication/vnd.accpac.simply.imp\t[Steve_Leow]\nvnd.acucobol\tapplication/vnd.acucobol\t[Dovid_Lubin]\nvnd.acucorp\tapplication/vnd.acucorp\t[Dovid_Lubin]\nvnd.adobe.flash.movie\tapplication/vnd.adobe.flash.movie\t[Henrik_Andersson]\nvnd.adobe.formscentral.fcdt\tapplication/vnd.adobe.formscentral.fcdt\t[Chris_Solc]\nvnd.adobe.fxp\tapplication/vnd.adobe.fxp\t[Robert_Brambley][Steven_Heintz]\nvnd.adobe.partial-upload\tapplication/vnd.adobe.partial-upload\t[Tapani_Otala]\nvnd.adobe.xdp+xml\tapplication/vnd.adobe.xdp+xml\t[John_Brinkman]\nvnd.adobe.xfdf\tapplication/vnd.adobe.xfdf\t[Roberto_Perelman]\nvnd.aether.imp\tapplication/vnd.aether.imp\t[Jay_Moskowitz]\nvnd.afpc.afplinedata\tapplication/vnd.afpc.afplinedata\t[Jörg_Palmer]\nvnd.afpc.afplinedata-pagedef\tapplication/vnd.afpc.afplinedata-pagedef\t[Jörg_Palmer]\nvnd.afpc.cmoca-cmresource\tapplication/vnd.afpc.cmoca-cmresource\t[Jörg_Palmer]\nvnd.afpc.foca-charset\tapplication/vnd.afpc.foca-charset\t[Jörg_Palmer]\nvnd.afpc.foca-codedfont\tapplication/vnd.afpc.foca-codedfont\t[Jörg_Palmer]\nvnd.afpc.foca-codepage\tapplication/vnd.afpc.foca-codepage\t[Jörg_Palmer]\nvnd.afpc.modca\tapplication/vnd.afpc.modca\t[Jörg_Palmer]\nvnd.afpc.modca-cmtable\tapplication/vnd.afpc.modca-cmtable\t[Jörg_Palmer]\nvnd.afpc.modca-formdef\tapplication/vnd.afpc.modca-formdef\t[Jörg_Palmer]\nvnd.afpc.modca-mediummap\tapplication/vnd.afpc.modca-mediummap\t[Jörg_Palmer]\nvnd.afpc.modca-objectcontainer\tapplication/vnd.afpc.modca-objectcontainer\t[Jörg_Palmer]\nvnd.afpc.modca-overlay\tapplication/vnd.afpc.modca-overlay\t[Jörg_Palmer]\nvnd.afpc.modca-pagesegment\tapplication/vnd.afpc.modca-pagesegment\t[Jörg_Palmer]\nvnd.ah-barcode\tapplication/vnd.ah-barcode\t[Katsuhiko_Ichinose]\nvnd.ahead.space\tapplication/vnd.ahead.space\t[Tor_Kristensen]\nvnd.airzip.filesecure.azf\tapplication/vnd.airzip.filesecure.azf\t[Daniel_Mould][Gary_Clueit]\nvnd.airzip.filesecure.azs\tapplication/vnd.airzip.filesecure.azs\t[Daniel_Mould][Gary_Clueit]\nvnd.amadeus+json\tapplication/vnd.amadeus+json\t[Patrick_Brosse]\nvnd.amazon.mobi8-ebook\tapplication/vnd.amazon.mobi8-ebook\t[Kim_Scarborough]\nvnd.americandynamics.acc\tapplication/vnd.americandynamics.acc\t[Gary_Sands]\nvnd.amiga.ami\tapplication/vnd.amiga.ami\t[Kevin_Blumberg]\nvnd.amundsen.maze+xml\tapplication/vnd.amundsen.maze+xml\t[Mike_Amundsen]\nvnd.android.ota\tapplication/vnd.android.ota\t[Greg_Kaiser]\nvnd.anki\tapplication/vnd.anki\t[Kerrick_Staley]\nvnd.anser-web-certificate-issue-initiation\tapplication/vnd.anser-web-certificate-issue-initiation\t[Hiroyoshi_Mori]\nvnd.antix.game-component\tapplication/vnd.antix.game-component\t[Daniel_Shelton]\nvnd.apache.thrift.binary\tapplication/vnd.apache.thrift.binary\t[Roger_Meier]\nvnd.apache.thrift.compact\tapplication/vnd.apache.thrift.compact\t[Roger_Meier]\nvnd.apache.thrift.json\tapplication/vnd.apache.thrift.json\t[Roger_Meier]\nvnd.api+json\tapplication/vnd.api+json\t[Steve_Klabnik]\nvnd.aplextor.warrp+json\tapplication/vnd.aplextor.warrp+json\t[Oleg_Uryutin]\nvnd.apothekende.reservation+json\tapplication/vnd.apothekende.reservation+json\t[Adrian_Föder]\nvnd.apple.installer+xml\tapplication/vnd.apple.installer+xml\t[Peter_Bierman]\nvnd.apple.keynote\tapplication/vnd.apple.keynote\t[Manichandra_Sajjanapu]\nvnd.apple.mpegurl\tapplication/vnd.apple.mpegurl\t[RFC8216]\nvnd.apple.numbers\tapplication/vnd.apple.numbers\t[Manichandra_Sajjanapu]\nvnd.apple.pages\tapplication/vnd.apple.pages\t[Manichandra_Sajjanapu]\nvnd.arastra.swi - OBSOLETED in favor of application/vnd.aristanetworks.swi\tapplication/vnd.arastra.swi\t[Bill_Fenner]\nvnd.aristanetworks.swi\tapplication/vnd.aristanetworks.swi\t[Bill_Fenner]\nvnd.artisan+json\tapplication/vnd.artisan+json\t[Brad_Turner]\nvnd.artsquare\tapplication/vnd.artsquare\t[Christopher_Smith]\nvnd.astraea-software.iota\tapplication/vnd.astraea-software.iota\t[Christopher_Snazell]\nvnd.audiograph\tapplication/vnd.audiograph\t[Horia_Cristian_Slusanschi]\nvnd.autopackage\tapplication/vnd.autopackage\t[Mike_Hearn]\nvnd.avalon+json\tapplication/vnd.avalon+json\t[Ben_Hinman]\nvnd.avistar+xml\tapplication/vnd.avistar+xml\t[Vladimir_Vysotsky]\nvnd.balsamiq.bmml+xml\tapplication/vnd.balsamiq.bmml+xml\t[Giacomo_Guilizzoni]\nvnd.banana-accounting\tapplication/vnd.banana-accounting\t[José_Del_Romano]\nvnd.bbf.usp.error\tapplication/vnd.bbf.usp.error\t[Broadband_Forum]\nvnd.bbf.usp.msg\tapplication/vnd.bbf.usp.msg\t[Broadband_Forum]\nvnd.bbf.usp.msg+json\tapplication/vnd.bbf.usp.msg+json\t[Broadband_Forum]\nvnd.balsamiq.bmpr\tapplication/vnd.balsamiq.bmpr\t[Giacomo_Guilizzoni]\nvnd.bekitzur-stech+json\tapplication/vnd.bekitzur-stech+json\t[Jegulsky]\nvnd.bint.med-content\tapplication/vnd.bint.med-content\t[Heinz-Peter_Schütz]\nvnd.biopax.rdf+xml\tapplication/vnd.biopax.rdf+xml\t[Pathway_Commons]\nvnd.blink-idb-value-wrapper\tapplication/vnd.blink-idb-value-wrapper\t[Victor_Costan]\nvnd.blueice.multipass\tapplication/vnd.blueice.multipass\t[Thomas_Holmstrom]\nvnd.bluetooth.ep.oob\tapplication/vnd.bluetooth.ep.oob\t[Mike_Foley]\nvnd.bluetooth.le.oob\tapplication/vnd.bluetooth.le.oob\t[Mark_Powell]\nvnd.bmi\tapplication/vnd.bmi\t[Tadashi_Gotoh]\nvnd.bpf\tapplication/vnd.bpf\t[NCGIS][Bryan_Blank]\nvnd.bpf3\tapplication/vnd.bpf3\t[NCGIS][Bryan_Blank]\nvnd.businessobjects\tapplication/vnd.businessobjects\t[Philippe_Imoucha]\nvnd.byu.uapi+json\tapplication/vnd.byu.uapi+json\t[Brent_Moore]\nvnd.cab-jscript\tapplication/vnd.cab-jscript\t[Joerg_Falkenberg]\nvnd.canon-cpdl\tapplication/vnd.canon-cpdl\t[Shin_Muto]\nvnd.canon-lips\tapplication/vnd.canon-lips\t[Shin_Muto]\nvnd.capasystems-pg+json\tapplication/vnd.capasystems-pg+json\t[Yüksel_Aydemir]\nvnd.cendio.thinlinc.clientconf\tapplication/vnd.cendio.thinlinc.clientconf\t[Peter_Astrand]\nvnd.century-systems.tcp_stream\tapplication/vnd.century-systems.tcp_stream\t[Shuji_Fujii]\nvnd.chemdraw+xml\tapplication/vnd.chemdraw+xml\t[Glenn_Howes]\nvnd.chess-pgn\tapplication/vnd.chess-pgn\t[Kim_Scarborough]\nvnd.chipnuts.karaoke-mmd\tapplication/vnd.chipnuts.karaoke-mmd\t[Chunyun_Xiong]\nvnd.ciedi\tapplication/vnd.ciedi\t[Hidekazu_Enjo]\nvnd.cinderella\tapplication/vnd.cinderella\t[Ulrich_Kortenkamp]\nvnd.cirpack.isdn-ext\tapplication/vnd.cirpack.isdn-ext\t[Pascal_Mayeux]\nvnd.citationstyles.style+xml\tapplication/vnd.citationstyles.style+xml\t[Rintze_M._Zelle]\nvnd.claymore\tapplication/vnd.claymore\t[Ray_Simpson]\nvnd.cloanto.rp9\tapplication/vnd.cloanto.rp9\t[Mike_Labatt]\nvnd.clonk.c4group\tapplication/vnd.clonk.c4group\t[Guenther_Brammer]\nvnd.cluetrust.cartomobile-config\tapplication/vnd.cluetrust.cartomobile-config\t[Gaige_Paulsen]\nvnd.cluetrust.cartomobile-config-pkg\tapplication/vnd.cluetrust.cartomobile-config-pkg\t[Gaige_Paulsen]\nvnd.coffeescript\tapplication/vnd.coffeescript\t[Devyn_Collier_Johnson]\nvnd.collabio.xodocuments.document\tapplication/vnd.collabio.xodocuments.document\t[Alexey_Meandrov]\nvnd.collabio.xodocuments.document-template\tapplication/vnd.collabio.xodocuments.document-template\t[Alexey_Meandrov]\nvnd.collabio.xodocuments.presentation\tapplication/vnd.collabio.xodocuments.presentation\t[Alexey_Meandrov]\nvnd.collabio.xodocuments.presentation-template\tapplication/vnd.collabio.xodocuments.presentation-template\t[Alexey_Meandrov]\nvnd.collabio.xodocuments.spreadsheet\tapplication/vnd.collabio.xodocuments.spreadsheet\t[Alexey_Meandrov]\nvnd.collabio.xodocuments.spreadsheet-template\tapplication/vnd.collabio.xodocuments.spreadsheet-template\t[Alexey_Meandrov]\nvnd.collection.doc+json\tapplication/vnd.collection.doc+json\t[Irakli_Nadareishvili]\nvnd.collection+json\tapplication/vnd.collection+json\t[Mike_Amundsen]\nvnd.collection.next+json\tapplication/vnd.collection.next+json\t[Ioseb_Dzmanashvili]\nvnd.comicbook-rar\tapplication/vnd.comicbook-rar\t[Kim_Scarborough]\nvnd.comicbook+zip\tapplication/vnd.comicbook+zip\t[Kim_Scarborough]\nvnd.commerce-battelle\tapplication/vnd.commerce-battelle\t[David_Applebaum]\nvnd.commonspace\tapplication/vnd.commonspace\t[Ravinder_Chandhok]\nvnd.coreos.ignition+json\tapplication/vnd.coreos.ignition+json\t[Alex_Crawford]\nvnd.cosmocaller\tapplication/vnd.cosmocaller\t[Steve_Dellutri]\nvnd.contact.cmsg\tapplication/vnd.contact.cmsg\t[Frank_Patz]\nvnd.crick.clicker\tapplication/vnd.crick.clicker\t[Andrew_Burt]\nvnd.crick.clicker.keyboard\tapplication/vnd.crick.clicker.keyboard\t[Andrew_Burt]\nvnd.crick.clicker.palette\tapplication/vnd.crick.clicker.palette\t[Andrew_Burt]\nvnd.crick.clicker.template\tapplication/vnd.crick.clicker.template\t[Andrew_Burt]\nvnd.crick.clicker.wordbank\tapplication/vnd.crick.clicker.wordbank\t[Andrew_Burt]\nvnd.criticaltools.wbs+xml\tapplication/vnd.criticaltools.wbs+xml\t[Jim_Spiller]\nvnd.cryptii.pipe+json\tapplication/vnd.cryptii.pipe+json\t[Fränz_Friederes]\nvnd.crypto-shade-file\tapplication/vnd.crypto-shade-file\t[Connor_Horman]\nvnd.cryptomator.encrypted\tapplication/vnd.cryptomator.encrypted\t[Sebastian_Stenzel]\nvnd.cryptomator.vault\tapplication/vnd.cryptomator.vault\t[Sebastian_Stenzel]\nvnd.ctc-posml\tapplication/vnd.ctc-posml\t[Bayard_Kohlhepp]\nvnd.ctct.ws+xml\tapplication/vnd.ctct.ws+xml\t[Jim_Ancona]\nvnd.cups-pdf\tapplication/vnd.cups-pdf\t[Michael_Sweet]\nvnd.cups-postscript\tapplication/vnd.cups-postscript\t[Michael_Sweet]\nvnd.cups-ppd\tapplication/vnd.cups-ppd\t[Michael_Sweet]\nvnd.cups-raster\tapplication/vnd.cups-raster\t[Michael_Sweet]\nvnd.cups-raw\tapplication/vnd.cups-raw\t[Michael_Sweet]\nvnd.curl\tapplication/vnd.curl\t[Robert_Byrnes]\nvnd.cyan.dean.root+xml\tapplication/vnd.cyan.dean.root+xml\t[Matt_Kern]\nvnd.cybank\tapplication/vnd.cybank\t[Nor_Helmee]\nvnd.cyclonedx+json\tapplication/vnd.cyclonedx+json\t[Patrick_Dwyer]\nvnd.cyclonedx+xml\tapplication/vnd.cyclonedx+xml\t[Patrick_Dwyer]\nvnd.d2l.coursepackage1p0+zip\tapplication/vnd.d2l.coursepackage1p0+zip\t[Viktor_Haag]\nvnd.d3m-dataset\tapplication/vnd.d3m-dataset\t[Mi_Tar]\nvnd.d3m-problem\tapplication/vnd.d3m-problem\t[Mi_Tar]\nvnd.dart\tapplication/vnd.dart\t[Anders_Sandholm]\nvnd.data-vision.rdz\tapplication/vnd.data-vision.rdz\t[James_Fields]\nvnd.datapackage+json\tapplication/vnd.datapackage+json\t[Paul_Walsh]\nvnd.dataresource+json\tapplication/vnd.dataresource+json\t[Paul_Walsh]\nvnd.dbf\tapplication/vnd.dbf\t[Mi_Tar]\nvnd.debian.binary-package\tapplication/vnd.debian.binary-package\t[Charles_Plessy]\nvnd.dece.data\tapplication/vnd.dece.data\t[Michael_A_Dolan]\nvnd.dece.ttml+xml\tapplication/vnd.dece.ttml+xml\t[Michael_A_Dolan]\nvnd.dece.unspecified\tapplication/vnd.dece.unspecified\t[Michael_A_Dolan]\nvnd.dece.zip\tapplication/vnd.dece.zip\t[Michael_A_Dolan]\nvnd.denovo.fcselayout-link\tapplication/vnd.denovo.fcselayout-link\t[Michael_Dixon]\nvnd.desmume.movie\tapplication/vnd.desmume.movie\t[Henrik_Andersson]\nvnd.dir-bi.plate-dl-nosuffix\tapplication/vnd.dir-bi.plate-dl-nosuffix\t[Yamanaka]\nvnd.dm.delegation+xml\tapplication/vnd.dm.delegation+xml\t[Axel_Ferrazzini]\nvnd.dna\tapplication/vnd.dna\t[Meredith_Searcy]\nvnd.document+json\tapplication/vnd.document+json\t[Tom_Christie]\nvnd.dolby.mobile.1\tapplication/vnd.dolby.mobile.1\t[Steve_Hattersley]\nvnd.dolby.mobile.2\tapplication/vnd.dolby.mobile.2\t[Steve_Hattersley]\nvnd.doremir.scorecloud-binary-document\tapplication/vnd.doremir.scorecloud-binary-document\t[Erik_Ronström]\nvnd.dpgraph\tapplication/vnd.dpgraph\t[David_Parker]\nvnd.dreamfactory\tapplication/vnd.dreamfactory\t[William_C._Appleton]\nvnd.drive+json\tapplication/vnd.drive+json\t[Keith_Kester]\nvnd.dtg.local\tapplication/vnd.dtg.local\t[Ali_Teffahi]\nvnd.dtg.local.flash\tapplication/vnd.dtg.local.flash\t[Ali_Teffahi]\nvnd.dtg.local.html\tapplication/vnd.dtg.local.html\t[Ali_Teffahi]\nvnd.dvb.ait\tapplication/vnd.dvb.ait\t[Peter_Siebert][Michael_Lagally]\nvnd.dvb.dvbisl+xml\tapplication/vnd.dvb.dvbisl+xml\t[Emily_DUBS]\nvnd.dvb.dvbj\tapplication/vnd.dvb.dvbj\t[Peter_Siebert][Michael_Lagally]\nvnd.dvb.esgcontainer\tapplication/vnd.dvb.esgcontainer\t[Joerg_Heuer]\nvnd.dvb.ipdcdftnotifaccess\tapplication/vnd.dvb.ipdcdftnotifaccess\t[Roy_Yue]\nvnd.dvb.ipdcesgaccess\tapplication/vnd.dvb.ipdcesgaccess\t[Joerg_Heuer]\nvnd.dvb.ipdcesgaccess2\tapplication/vnd.dvb.ipdcesgaccess2\t[Jerome_Marcon]\nvnd.dvb.ipdcesgpdd\tapplication/vnd.dvb.ipdcesgpdd\t[Jerome_Marcon]\nvnd.dvb.ipdcroaming\tapplication/vnd.dvb.ipdcroaming\t[Yiling_Xu]\nvnd.dvb.iptv.alfec-base\tapplication/vnd.dvb.iptv.alfec-base\t[Jean-Baptiste_Henry]\nvnd.dvb.iptv.alfec-enhancement\tapplication/vnd.dvb.iptv.alfec-enhancement\t[Jean-Baptiste_Henry]\nvnd.dvb.notif-aggregate-root+xml\tapplication/vnd.dvb.notif-aggregate-root+xml\t[Roy_Yue]\nvnd.dvb.notif-container+xml\tapplication/vnd.dvb.notif-container+xml\t[Roy_Yue]\nvnd.dvb.notif-generic+xml\tapplication/vnd.dvb.notif-generic+xml\t[Roy_Yue]\nvnd.dvb.notif-ia-msglist+xml\tapplication/vnd.dvb.notif-ia-msglist+xml\t[Roy_Yue]\nvnd.dvb.notif-ia-registration-request+xml\tapplication/vnd.dvb.notif-ia-registration-request+xml\t[Roy_Yue]\nvnd.dvb.notif-ia-registration-response+xml\tapplication/vnd.dvb.notif-ia-registration-response+xml\t[Roy_Yue]\nvnd.dvb.notif-init+xml\tapplication/vnd.dvb.notif-init+xml\t[Roy_Yue]\nvnd.dvb.pfr\tapplication/vnd.dvb.pfr\t[Peter_Siebert][Michael_Lagally]\nvnd.dvb.service\tapplication/vnd.dvb.service\t[Peter_Siebert][Michael_Lagally]\nvnd.dxr\tapplication/vnd.dxr\t[Michael_Duffy]\nvnd.dynageo\tapplication/vnd.dynageo\t[Roland_Mechling]\nvnd.dzr\tapplication/vnd.dzr\t[Carl_Anderson]\nvnd.easykaraoke.cdgdownload\tapplication/vnd.easykaraoke.cdgdownload\t[Iain_Downs]\nvnd.ecip.rlp\tapplication/vnd.ecip.rlp\t[Wei_Tang]\nvnd.ecdis-update\tapplication/vnd.ecdis-update\t[Gert_Buettgenbach]\nvnd.ecowin.chart\tapplication/vnd.ecowin.chart\t[Thomas_Olsson]\nvnd.ecowin.filerequest\tapplication/vnd.ecowin.filerequest\t[Thomas_Olsson]\nvnd.ecowin.fileupdate\tapplication/vnd.ecowin.fileupdate\t[Thomas_Olsson]\nvnd.ecowin.series\tapplication/vnd.ecowin.series\t[Thomas_Olsson]\nvnd.ecowin.seriesrequest\tapplication/vnd.ecowin.seriesrequest\t[Thomas_Olsson]\nvnd.ecowin.seriesupdate\tapplication/vnd.ecowin.seriesupdate\t[Thomas_Olsson]\nvnd.efi.img\tapplication/vnd.efi.img\t[UEFI_Forum][Fu_Siyuan]\nvnd.efi.iso\tapplication/vnd.efi.iso\t[UEFI_Forum][Fu_Siyuan]\nvnd.emclient.accessrequest+xml\tapplication/vnd.emclient.accessrequest+xml\t[Filip_Navara]\nvnd.enliven\tapplication/vnd.enliven\t[Paul_Santinelli_Jr.]\nvnd.enphase.envoy\tapplication/vnd.enphase.envoy\t[Chris_Eich]\nvnd.eprints.data+xml\tapplication/vnd.eprints.data+xml\t[Tim_Brody]\nvnd.epson.esf\tapplication/vnd.epson.esf\t[Shoji_Hoshina]\nvnd.epson.msf\tapplication/vnd.epson.msf\t[Shoji_Hoshina]\nvnd.epson.quickanime\tapplication/vnd.epson.quickanime\t[Yu_Gu]\nvnd.epson.salt\tapplication/vnd.epson.salt\t[Yasuhito_Nagatomo]\nvnd.epson.ssf\tapplication/vnd.epson.ssf\t[Shoji_Hoshina]\nvnd.ericsson.quickcall\tapplication/vnd.ericsson.quickcall\t[Paul_Tidwell]\nvnd.espass-espass+zip\tapplication/vnd.espass-espass+zip\t[Marcus_Ligi_Büschleb]\nvnd.eszigno3+xml\tapplication/vnd.eszigno3+xml\t[Szilveszter_Tóth]\nvnd.etsi.aoc+xml\tapplication/vnd.etsi.aoc+xml\t[Shicheng_Hu]\nvnd.etsi.asic-s+zip\tapplication/vnd.etsi.asic-s+zip\t[Miguel_Angel_Reina_Ortega]\nvnd.etsi.asic-e+zip\tapplication/vnd.etsi.asic-e+zip\t[Miguel_Angel_Reina_Ortega]\nvnd.etsi.cug+xml\tapplication/vnd.etsi.cug+xml\t[Shicheng_Hu]\nvnd.etsi.iptvcommand+xml\tapplication/vnd.etsi.iptvcommand+xml\t[Shicheng_Hu]\nvnd.etsi.iptvdiscovery+xml\tapplication/vnd.etsi.iptvdiscovery+xml\t[Shicheng_Hu]\nvnd.etsi.iptvprofile+xml\tapplication/vnd.etsi.iptvprofile+xml\t[Shicheng_Hu]\nvnd.etsi.iptvsad-bc+xml\tapplication/vnd.etsi.iptvsad-bc+xml\t[Shicheng_Hu]\nvnd.etsi.iptvsad-cod+xml\tapplication/vnd.etsi.iptvsad-cod+xml\t[Shicheng_Hu]\nvnd.etsi.iptvsad-npvr+xml\tapplication/vnd.etsi.iptvsad-npvr+xml\t[Shicheng_Hu]\nvnd.etsi.iptvservice+xml\tapplication/vnd.etsi.iptvservice+xml\t[Miguel_Angel_Reina_Ortega]\nvnd.etsi.iptvsync+xml\tapplication/vnd.etsi.iptvsync+xml\t[Miguel_Angel_Reina_Ortega]\nvnd.etsi.iptvueprofile+xml\tapplication/vnd.etsi.iptvueprofile+xml\t[Shicheng_Hu]\nvnd.etsi.mcid+xml\tapplication/vnd.etsi.mcid+xml\t[Shicheng_Hu]\nvnd.etsi.mheg5\tapplication/vnd.etsi.mheg5\t[Miguel_Angel_Reina_Ortega][Ian_Medland]\nvnd.etsi.overload-control-policy-dataset+xml\tapplication/vnd.etsi.overload-control-policy-dataset+xml\t[Miguel_Angel_Reina_Ortega]\nvnd.etsi.pstn+xml\tapplication/vnd.etsi.pstn+xml\t[Jiwan_Han][Thomas_Belling]\nvnd.etsi.sci+xml\tapplication/vnd.etsi.sci+xml\t[Shicheng_Hu]\nvnd.etsi.simservs+xml\tapplication/vnd.etsi.simservs+xml\t[Shicheng_Hu]\nvnd.etsi.timestamp-token\tapplication/vnd.etsi.timestamp-token\t[Miguel_Angel_Reina_Ortega]\nvnd.etsi.tsl+xml\tapplication/vnd.etsi.tsl+xml\t[Shicheng_Hu]\nvnd.etsi.tsl.der\tapplication/vnd.etsi.tsl.der\t[Shicheng_Hu]\nvnd.evolv.ecig.profile\tapplication/vnd.evolv.ecig.profile\t[James_Bellinger]\nvnd.evolv.ecig.settings\tapplication/vnd.evolv.ecig.settings\t[James_Bellinger]\nvnd.evolv.ecig.theme\tapplication/vnd.evolv.ecig.theme\t[James_Bellinger]\nvnd.eudora.data\tapplication/vnd.eudora.data\t[Pete_Resnick]\nvnd.exstream-empower+zip\tapplication/vnd.exstream-empower+zip\t[Bill_Kidwell]\nvnd.exstream-package\tapplication/vnd.exstream-package\t[Bill_Kidwell]\nvnd.ezpix-album\tapplication/vnd.ezpix-album\t[ElectronicZombieCorp]\nvnd.ezpix-package\tapplication/vnd.ezpix-package\t[ElectronicZombieCorp]\nvnd.f-secure.mobile\tapplication/vnd.f-secure.mobile\t[Samu_Sarivaara]\nvnd.fastcopy-disk-image\tapplication/vnd.fastcopy-disk-image\t[Thomas_Huth]\nvnd.fdf\tapplication/vnd.fdf\t[Steve_Zilles]\nvnd.fdsn.mseed\tapplication/vnd.fdsn.mseed\t[Chad_Trabant]\nvnd.fdsn.seed\tapplication/vnd.fdsn.seed\t[Chad_Trabant]\nvnd.ffsns\tapplication/vnd.ffsns\t[Holstage]\nvnd.ficlab.flb+zip\tapplication/vnd.ficlab.flb+zip\t[Steve_Gilberd]\nvnd.filmit.zfc\tapplication/vnd.filmit.zfc\t[Harms_Moeller]\nvnd.fints\tapplication/vnd.fints\t[Ingo_Hammann]\nvnd.firemonkeys.cloudcell\tapplication/vnd.firemonkeys.cloudcell\t[Alex_Dubov]\nvnd.FloGraphIt\tapplication/vnd.FloGraphIt\t[Dick_Floersch]\nvnd.fluxtime.clip\tapplication/vnd.fluxtime.clip\t[Marc_Winter]\nvnd.font-fontforge-sfd\tapplication/vnd.font-fontforge-sfd\t[George_Williams]\nvnd.framemaker\tapplication/vnd.framemaker\t[Mike_Wexler]\nvnd.frogans.fnc - OBSOLETE\tapplication/vnd.frogans.fnc\t[OP3FT][Alexis_Tamas]\nvnd.frogans.ltf - OBSOLETE\tapplication/vnd.frogans.ltf\t[OP3FT][Alexis_Tamas]\nvnd.fsc.weblaunch\tapplication/vnd.fsc.weblaunch\t[Derek_Smith]\nvnd.fujifilm.fb.docuworks\tapplication/vnd.fujifilm.fb.docuworks\t[Kazuya_Iimura]\nvnd.fujifilm.fb.docuworks.binder\tapplication/vnd.fujifilm.fb.docuworks.binder\t[Kazuya_Iimura]\nvnd.fujifilm.fb.docuworks.container\tapplication/vnd.fujifilm.fb.docuworks.container\t[Kazuya_Iimura]\nvnd.fujifilm.fb.jfi+xml\tapplication/vnd.fujifilm.fb.jfi+xml\t[Keitaro_Ishida]\nvnd.fujitsu.oasys\tapplication/vnd.fujitsu.oasys\t[Nobukazu_Togashi]\nvnd.fujitsu.oasys2\tapplication/vnd.fujitsu.oasys2\t[Nobukazu_Togashi]\nvnd.fujitsu.oasys3\tapplication/vnd.fujitsu.oasys3\t[Seiji_Okudaira]\nvnd.fujitsu.oasysgp\tapplication/vnd.fujitsu.oasysgp\t[Masahiko_Sugimoto]\nvnd.fujitsu.oasysprs\tapplication/vnd.fujitsu.oasysprs\t[Masumi_Ogita]\nvnd.fujixerox.ART4\tapplication/vnd.fujixerox.ART4\t[Fumio_Tanabe]\nvnd.fujixerox.ART-EX\tapplication/vnd.fujixerox.ART-EX\t[Fumio_Tanabe]\nvnd.fujixerox.ddd\tapplication/vnd.fujixerox.ddd\t[Masanori_Onda]\nvnd.fujixerox.docuworks\tapplication/vnd.fujixerox.docuworks\t[Takatomo_Wakibayashi]\nvnd.fujixerox.docuworks.binder\tapplication/vnd.fujixerox.docuworks.binder\t[Takashi_Matsumoto]\nvnd.fujixerox.docuworks.container\tapplication/vnd.fujixerox.docuworks.container\t[Kiyoshi_Tashiro]\nvnd.fujixerox.HBPL\tapplication/vnd.fujixerox.HBPL\t[Fumio_Tanabe]\nvnd.fut-misnet\tapplication/vnd.fut-misnet\t[Jann_Pruulman]\nvnd.futoin+cbor\tapplication/vnd.futoin+cbor\t[Andrey_Galkin]\nvnd.futoin+json\tapplication/vnd.futoin+json\t[Andrey_Galkin]\nvnd.fuzzysheet\tapplication/vnd.fuzzysheet\t[Simon_Birtwistle]\nvnd.genomatix.tuxedo\tapplication/vnd.genomatix.tuxedo\t[Torben_Frey]\nvnd.gentics.grd+json\tapplication/vnd.gentics.grd+json\t[Philipp_Gortan]\nvnd.geo+json (OBSOLETED by [RFC7946] in favor of application/geo+json)\tapplication/vnd.geo+json\t[Sean_Gillies]\nvnd.geocube+xml - OBSOLETED by request\tapplication/vnd.geocube+xml\t[Francois_Pirsch]\nvnd.geogebra.file\tapplication/vnd.geogebra.file\t[GeoGebra][Yves_Kreis]\nvnd.geogebra.slides\tapplication/vnd.geogebra.slides\t[GeoGebra][Michael_Borcherds][Markus_Hohenwarter]\nvnd.geogebra.tool\tapplication/vnd.geogebra.tool\t[GeoGebra][Yves_Kreis]\nvnd.geometry-explorer\tapplication/vnd.geometry-explorer\t[Michael_Hvidsten]\nvnd.geonext\tapplication/vnd.geonext\t[Matthias_Ehmann]\nvnd.geoplan\tapplication/vnd.geoplan\t[Christian_Mercat]\nvnd.geospace\tapplication/vnd.geospace\t[Christian_Mercat]\nvnd.gerber\tapplication/vnd.gerber\t[Thomas_Weyn]\nvnd.globalplatform.card-content-mgt\tapplication/vnd.globalplatform.card-content-mgt\t[Gil_Bernabeu]\nvnd.globalplatform.card-content-mgt-response\tapplication/vnd.globalplatform.card-content-mgt-response\t[Gil_Bernabeu]\nvnd.gmx - DEPRECATED\tapplication/vnd.gmx\t[Christian_V._Sciberras]\nvnd.google-earth.kml+xml\tapplication/vnd.google-earth.kml+xml\t[Michael_Ashbridge]\nvnd.google-earth.kmz\tapplication/vnd.google-earth.kmz\t[Michael_Ashbridge]\nvnd.gov.sk.e-form+xml\tapplication/vnd.gov.sk.e-form+xml\t[Peter_Biro][Stefan_Szilva]\nvnd.gov.sk.e-form+zip\tapplication/vnd.gov.sk.e-form+zip\t[Peter_Biro][Stefan_Szilva]\nvnd.gov.sk.xmldatacontainer+xml\tapplication/vnd.gov.sk.xmldatacontainer+xml\t[Peter_Biro][Stefan_Szilva]\nvnd.grafeq\tapplication/vnd.grafeq\t[Jeff_Tupper]\nvnd.gridmp\tapplication/vnd.gridmp\t[Jeff_Lawson]\nvnd.groove-account\tapplication/vnd.groove-account\t[Todd_Joseph]\nvnd.groove-help\tapplication/vnd.groove-help\t[Todd_Joseph]\nvnd.groove-identity-message\tapplication/vnd.groove-identity-message\t[Todd_Joseph]\nvnd.groove-injector\tapplication/vnd.groove-injector\t[Todd_Joseph]\nvnd.groove-tool-message\tapplication/vnd.groove-tool-message\t[Todd_Joseph]\nvnd.groove-tool-template\tapplication/vnd.groove-tool-template\t[Todd_Joseph]\nvnd.groove-vcard\tapplication/vnd.groove-vcard\t[Todd_Joseph]\nvnd.hal+json\tapplication/vnd.hal+json\t[Mike_Kelly]\nvnd.hal+xml\tapplication/vnd.hal+xml\t[Mike_Kelly]\nvnd.HandHeld-Entertainment+xml\tapplication/vnd.HandHeld-Entertainment+xml\t[Eric_Hamilton]\nvnd.hbci\tapplication/vnd.hbci\t[Ingo_Hammann]\nvnd.hc+json\tapplication/vnd.hc+json\t[Jan_Schütze]\nvnd.hcl-bireports\tapplication/vnd.hcl-bireports\t[Doug_R._Serres]\nvnd.hdt\tapplication/vnd.hdt\t[Javier_D._Fernández]\nvnd.heroku+json\tapplication/vnd.heroku+json\t[Wesley_Beary]\nvnd.hhe.lesson-player\tapplication/vnd.hhe.lesson-player\t[Randy_Jones]\nvnd.hp-HPGL\tapplication/vnd.hp-HPGL\t[Bob_Pentecost]\nvnd.hp-hpid\tapplication/vnd.hp-hpid\t[Aloke_Gupta]\nvnd.hp-hps\tapplication/vnd.hp-hps\t[Steve_Aubrey]\nvnd.hp-jlyt\tapplication/vnd.hp-jlyt\t[Amir_Gaash]\nvnd.hp-PCL\tapplication/vnd.hp-PCL\t[Bob_Pentecost]\nvnd.hp-PCLXL\tapplication/vnd.hp-PCLXL\t[Bob_Pentecost]\nvnd.httphone\tapplication/vnd.httphone\t[Franck_Lefevre]\nvnd.hydrostatix.sof-data\tapplication/vnd.hydrostatix.sof-data\t[Allen_Gillam]\nvnd.hyper-item+json\tapplication/vnd.hyper-item+json\t[Mario_Demuth]\nvnd.hyper+json\tapplication/vnd.hyper+json\t[Irakli_Nadareishvili]\nvnd.hyperdrive+json\tapplication/vnd.hyperdrive+json\t[Daniel_Sims]\nvnd.hzn-3d-crossword\tapplication/vnd.hzn-3d-crossword\t[James_Minnis]\nvnd.ibm.afplinedata - OBSOLETED in favor of vnd.afpc.afplinedata\tapplication/vnd.ibm.afplinedata\t[Roger_Buis]\nvnd.ibm.electronic-media\tapplication/vnd.ibm.electronic-media\t[Bruce_Tantlinger]\nvnd.ibm.MiniPay\tapplication/vnd.ibm.MiniPay\t[Amir_Herzberg]\nvnd.ibm.modcap - OBSOLETED in favor of application/vnd.afpc.modca\tapplication/vnd.ibm.modcap\t[Reinhard_Hohensee]\nvnd.ibm.rights-management\tapplication/vnd.ibm.rights-management\t[Bruce_Tantlinger]\nvnd.ibm.secure-container\tapplication/vnd.ibm.secure-container\t[Bruce_Tantlinger]\nvnd.iccprofile\tapplication/vnd.iccprofile\t[Phil_Green]\nvnd.ieee.1905\tapplication/vnd.ieee.1905\t[Purva_R_Rajkotia]\nvnd.igloader\tapplication/vnd.igloader\t[Tim_Fisher]\nvnd.imagemeter.folder+zip\tapplication/vnd.imagemeter.folder+zip\t[Dirk_Farin]\nvnd.imagemeter.image+zip\tapplication/vnd.imagemeter.image+zip\t[Dirk_Farin]\nvnd.immervision-ivp\tapplication/vnd.immervision-ivp\t[Mathieu_Villegas]\nvnd.immervision-ivu\tapplication/vnd.immervision-ivu\t[Mathieu_Villegas]\nvnd.ims.imsccv1p1\tapplication/vnd.ims.imsccv1p1\t[Lisa_Mattson]\nvnd.ims.imsccv1p2\tapplication/vnd.ims.imsccv1p2\t[Lisa_Mattson]\nvnd.ims.imsccv1p3\tapplication/vnd.ims.imsccv1p3\t[Lisa_Mattson]\nvnd.ims.lis.v2.result+json\tapplication/vnd.ims.lis.v2.result+json\t[Lisa_Mattson]\nvnd.ims.lti.v2.toolconsumerprofile+json\tapplication/vnd.ims.lti.v2.toolconsumerprofile+json\t[Lisa_Mattson]\nvnd.ims.lti.v2.toolproxy.id+json\tapplication/vnd.ims.lti.v2.toolproxy.id+json\t[Lisa_Mattson]\nvnd.ims.lti.v2.toolproxy+json\tapplication/vnd.ims.lti.v2.toolproxy+json\t[Lisa_Mattson]\nvnd.ims.lti.v2.toolsettings+json\tapplication/vnd.ims.lti.v2.toolsettings+json\t[Lisa_Mattson]\nvnd.ims.lti.v2.toolsettings.simple+json\tapplication/vnd.ims.lti.v2.toolsettings.simple+json\t[Lisa_Mattson]\nvnd.informedcontrol.rms+xml\tapplication/vnd.informedcontrol.rms+xml\t[Mark_Wahl]\nvnd.infotech.project\tapplication/vnd.infotech.project\t[Charles_Engelke]\nvnd.infotech.project+xml\tapplication/vnd.infotech.project+xml\t[Charles_Engelke]\nvnd.informix-visionary - OBSOLETED in favor of application/vnd.visionary\tapplication/vnd.informix-visionary\t[Christopher_Gales]\nvnd.innopath.wamp.notification\tapplication/vnd.innopath.wamp.notification\t[Takanori_Sudo]\nvnd.insors.igm\tapplication/vnd.insors.igm\t[Jon_Swanson]\nvnd.intercon.formnet\tapplication/vnd.intercon.formnet\t[Tom_Gurak]\nvnd.intergeo\tapplication/vnd.intergeo\t[Yves_Kreis_2]\nvnd.intertrust.digibox\tapplication/vnd.intertrust.digibox\t[Luke_Tomasello]\nvnd.intertrust.nncp\tapplication/vnd.intertrust.nncp\t[Luke_Tomasello]\nvnd.intu.qbo\tapplication/vnd.intu.qbo\t[Greg_Scratchley]\nvnd.intu.qfx\tapplication/vnd.intu.qfx\t[Greg_Scratchley]\nvnd.iptc.g2.catalogitem+xml\tapplication/vnd.iptc.g2.catalogitem+xml\t[Michael_Steidl]\nvnd.iptc.g2.conceptitem+xml\tapplication/vnd.iptc.g2.conceptitem+xml\t[Michael_Steidl]\nvnd.iptc.g2.knowledgeitem+xml\tapplication/vnd.iptc.g2.knowledgeitem+xml\t[Michael_Steidl]\nvnd.iptc.g2.newsitem+xml\tapplication/vnd.iptc.g2.newsitem+xml\t[Michael_Steidl]\nvnd.iptc.g2.newsmessage+xml\tapplication/vnd.iptc.g2.newsmessage+xml\t[Michael_Steidl]\nvnd.iptc.g2.packageitem+xml\tapplication/vnd.iptc.g2.packageitem+xml\t[Michael_Steidl]\nvnd.iptc.g2.planningitem+xml\tapplication/vnd.iptc.g2.planningitem+xml\t[Michael_Steidl]\nvnd.ipunplugged.rcprofile\tapplication/vnd.ipunplugged.rcprofile\t[Per_Ersson]\nvnd.irepository.package+xml\tapplication/vnd.irepository.package+xml\t[Martin_Knowles]\nvnd.is-xpr\tapplication/vnd.is-xpr\t[Satish_Navarajan]\nvnd.isac.fcs\tapplication/vnd.isac.fcs\t[Ryan_Brinkman]\nvnd.jam\tapplication/vnd.jam\t[Brijesh_Kumar]\nvnd.iso11783-10+zip\tapplication/vnd.iso11783-10+zip\t[Frank_Wiebeler]\nvnd.japannet-directory-service\tapplication/vnd.japannet-directory-service\t[Kiyofusa_Fujii]\nvnd.japannet-jpnstore-wakeup\tapplication/vnd.japannet-jpnstore-wakeup\t[Jun_Yoshitake]\nvnd.japannet-payment-wakeup\tapplication/vnd.japannet-payment-wakeup\t[Kiyofusa_Fujii]\nvnd.japannet-registration\tapplication/vnd.japannet-registration\t[Jun_Yoshitake]\nvnd.japannet-registration-wakeup\tapplication/vnd.japannet-registration-wakeup\t[Kiyofusa_Fujii]\nvnd.japannet-setstore-wakeup\tapplication/vnd.japannet-setstore-wakeup\t[Jun_Yoshitake]\nvnd.japannet-verification\tapplication/vnd.japannet-verification\t[Jun_Yoshitake]\nvnd.japannet-verification-wakeup\tapplication/vnd.japannet-verification-wakeup\t[Kiyofusa_Fujii]\nvnd.jcp.javame.midlet-rms\tapplication/vnd.jcp.javame.midlet-rms\t[Mikhail_Gorshenev]\nvnd.jisp\tapplication/vnd.jisp\t[Sebastiaan_Deckers]\nvnd.joost.joda-archive\tapplication/vnd.joost.joda-archive\t[Joost]\nvnd.jsk.isdn-ngn\tapplication/vnd.jsk.isdn-ngn\t[Yokoyama_Kiyonobu]\nvnd.kahootz\tapplication/vnd.kahootz\t[Tim_Macdonald]\nvnd.kde.karbon\tapplication/vnd.kde.karbon\t[David_Faure]\nvnd.kde.kchart\tapplication/vnd.kde.kchart\t[David_Faure]\nvnd.kde.kformula\tapplication/vnd.kde.kformula\t[David_Faure]\nvnd.kde.kivio\tapplication/vnd.kde.kivio\t[David_Faure]\nvnd.kde.kontour\tapplication/vnd.kde.kontour\t[David_Faure]\nvnd.kde.kpresenter\tapplication/vnd.kde.kpresenter\t[David_Faure]\nvnd.kde.kspread\tapplication/vnd.kde.kspread\t[David_Faure]\nvnd.kde.kword\tapplication/vnd.kde.kword\t[David_Faure]\nvnd.kenameaapp\tapplication/vnd.kenameaapp\t[Dirk_DiGiorgio-Haag]\nvnd.kidspiration\tapplication/vnd.kidspiration\t[Jack_Bennett]\nvnd.Kinar\tapplication/vnd.Kinar\t[Hemant_Thakkar]\nvnd.koan\tapplication/vnd.koan\t[Pete_Cole]\nvnd.kodak-descriptor\tapplication/vnd.kodak-descriptor\t[Michael_J._Donahue]\nvnd.las\tapplication/vnd.las\t[NCGIS][Bryan_Blank]\nvnd.las.las+json\tapplication/vnd.las.las+json\t[Rob_Bailey]\nvnd.las.las+xml\tapplication/vnd.las.las+xml\t[Rob_Bailey]\nvnd.laszip\tapplication/vnd.laszip\t[NCGIS][Bryan_Blank]\nvnd.leap+json\tapplication/vnd.leap+json\t[Mark_C_Fralick]\nvnd.liberty-request+xml\tapplication/vnd.liberty-request+xml\t[Brett_McDowell]\nvnd.llamagraphics.life-balance.desktop\tapplication/vnd.llamagraphics.life-balance.desktop\t[Catherine_E._White]\nvnd.llamagraphics.life-balance.exchange+xml\tapplication/vnd.llamagraphics.life-balance.exchange+xml\t[Catherine_E._White]\nvnd.logipipe.circuit+zip\tapplication/vnd.logipipe.circuit+zip\t[Victor_Kuchynsky]\nvnd.loom\tapplication/vnd.loom\t[Sten_Linnarsson]\nvnd.lotus-1-2-3\tapplication/vnd.lotus-1-2-3\t[Paul_Wattenberger]\nvnd.lotus-approach\tapplication/vnd.lotus-approach\t[Paul_Wattenberger]\nvnd.lotus-freelance\tapplication/vnd.lotus-freelance\t[Paul_Wattenberger]\nvnd.lotus-notes\tapplication/vnd.lotus-notes\t[Michael_Laramie]\nvnd.lotus-organizer\tapplication/vnd.lotus-organizer\t[Paul_Wattenberger]\nvnd.lotus-screencam\tapplication/vnd.lotus-screencam\t[Paul_Wattenberger]\nvnd.lotus-wordpro\tapplication/vnd.lotus-wordpro\t[Paul_Wattenberger]\nvnd.macports.portpkg\tapplication/vnd.macports.portpkg\t[James_Berry]\nvnd.mapbox-vector-tile\tapplication/vnd.mapbox-vector-tile\t[Blake_Thompson]\nvnd.marlin.drm.actiontoken+xml\tapplication/vnd.marlin.drm.actiontoken+xml\t[Gary_Ellison]\nvnd.marlin.drm.conftoken+xml\tapplication/vnd.marlin.drm.conftoken+xml\t[Gary_Ellison]\nvnd.marlin.drm.license+xml\tapplication/vnd.marlin.drm.license+xml\t[Gary_Ellison]\nvnd.marlin.drm.mdcf\tapplication/vnd.marlin.drm.mdcf\t[Gary_Ellison]\nvnd.mason+json\tapplication/vnd.mason+json\t[Jorn_Wildt]\nvnd.maxmind.maxmind-db\tapplication/vnd.maxmind.maxmind-db\t[William_Stevenson]\nvnd.mcd\tapplication/vnd.mcd\t[Tadashi_Gotoh]\nvnd.medcalcdata\tapplication/vnd.medcalcdata\t[Frank_Schoonjans]\nvnd.mediastation.cdkey\tapplication/vnd.mediastation.cdkey\t[Henry_Flurry]\nvnd.meridian-slingshot\tapplication/vnd.meridian-slingshot\t[Eric_Wedel]\nvnd.MFER\tapplication/vnd.MFER\t[Masaaki_Hirai]\nvnd.mfmp\tapplication/vnd.mfmp\t[Yukari_Ikeda]\nvnd.micro+json\tapplication/vnd.micro+json\t[Dali_Zheng]\nvnd.micrografx.flo\tapplication/vnd.micrografx.flo\t[Joe_Prevo]\nvnd.micrografx.igx\tapplication/vnd.micrografx.igx\t[Joe_Prevo]\nvnd.microsoft.portable-executable\tapplication/vnd.microsoft.portable-executable\t[Henrik_Andersson]\nvnd.microsoft.windows.thumbnail-cache\tapplication/vnd.microsoft.windows.thumbnail-cache\t[Henrik_Andersson]\nvnd.miele+json\tapplication/vnd.miele+json\t[Nils_Langhammer]\nvnd.mif\tapplication/vnd.mif\t[Mike_Wexler]\nvnd.minisoft-hp3000-save\tapplication/vnd.minisoft-hp3000-save\t[Chris_Bartram]\nvnd.mitsubishi.misty-guard.trustweb\tapplication/vnd.mitsubishi.misty-guard.trustweb\t[Tanaka]\nvnd.Mobius.DAF\tapplication/vnd.Mobius.DAF\t[Allen_K._Kabayama]\nvnd.Mobius.DIS\tapplication/vnd.Mobius.DIS\t[Allen_K._Kabayama]\nvnd.Mobius.MBK\tapplication/vnd.Mobius.MBK\t[Alex_Devasia]\nvnd.Mobius.MQY\tapplication/vnd.Mobius.MQY\t[Alex_Devasia]\nvnd.Mobius.MSL\tapplication/vnd.Mobius.MSL\t[Allen_K._Kabayama]\nvnd.Mobius.PLC\tapplication/vnd.Mobius.PLC\t[Allen_K._Kabayama]\nvnd.Mobius.TXF\tapplication/vnd.Mobius.TXF\t[Allen_K._Kabayama]\nvnd.mophun.application\tapplication/vnd.mophun.application\t[Bjorn_Wennerstrom]\nvnd.mophun.certificate\tapplication/vnd.mophun.certificate\t[Bjorn_Wennerstrom]\nvnd.motorola.flexsuite\tapplication/vnd.motorola.flexsuite\t[Mark_Patton]\nvnd.motorola.flexsuite.adsi\tapplication/vnd.motorola.flexsuite.adsi\t[Mark_Patton]\nvnd.motorola.flexsuite.fis\tapplication/vnd.motorola.flexsuite.fis\t[Mark_Patton]\nvnd.motorola.flexsuite.gotap\tapplication/vnd.motorola.flexsuite.gotap\t[Mark_Patton]\nvnd.motorola.flexsuite.kmr\tapplication/vnd.motorola.flexsuite.kmr\t[Mark_Patton]\nvnd.motorola.flexsuite.ttc\tapplication/vnd.motorola.flexsuite.ttc\t[Mark_Patton]\nvnd.motorola.flexsuite.wem\tapplication/vnd.motorola.flexsuite.wem\t[Mark_Patton]\nvnd.motorola.iprm\tapplication/vnd.motorola.iprm\t[Rafie_Shamsaasef]\nvnd.mozilla.xul+xml\tapplication/vnd.mozilla.xul+xml\t[Braden_N_McDaniel]\nvnd.ms-artgalry\tapplication/vnd.ms-artgalry\t[Dean_Slawson]\nvnd.ms-asf\tapplication/vnd.ms-asf\t[Eric_Fleischman]\nvnd.ms-cab-compressed\tapplication/vnd.ms-cab-compressed\t[Kim_Scarborough]\nvnd.ms-3mfdocument\tapplication/vnd.ms-3mfdocument\t[Shawn_Maloney]\nvnd.ms-excel\tapplication/vnd.ms-excel\t[Sukvinder_S._Gill]\nvnd.ms-excel.addin.macroEnabled.12\tapplication/vnd.ms-excel.addin.macroEnabled.12\t[Chris_Rae]\nvnd.ms-excel.sheet.binary.macroEnabled.12\tapplication/vnd.ms-excel.sheet.binary.macroEnabled.12\t[Chris_Rae]\nvnd.ms-excel.sheet.macroEnabled.12\tapplication/vnd.ms-excel.sheet.macroEnabled.12\t[Chris_Rae]\nvnd.ms-excel.template.macroEnabled.12\tapplication/vnd.ms-excel.template.macroEnabled.12\t[Chris_Rae]\nvnd.ms-fontobject\tapplication/vnd.ms-fontobject\t[Kim_Scarborough]\nvnd.ms-htmlhelp\tapplication/vnd.ms-htmlhelp\t[Anatoly_Techtonik]\nvnd.ms-ims\tapplication/vnd.ms-ims\t[Eric_Ledoux]\nvnd.ms-lrm\tapplication/vnd.ms-lrm\t[Eric_Ledoux]\nvnd.ms-office.activeX+xml\tapplication/vnd.ms-office.activeX+xml\t[Chris_Rae]\nvnd.ms-officetheme\tapplication/vnd.ms-officetheme\t[Chris_Rae]\nvnd.ms-playready.initiator+xml\tapplication/vnd.ms-playready.initiator+xml\t[Daniel_Schneider]\nvnd.ms-powerpoint\tapplication/vnd.ms-powerpoint\t[Sukvinder_S._Gill]\nvnd.ms-powerpoint.addin.macroEnabled.12\tapplication/vnd.ms-powerpoint.addin.macroEnabled.12\t[Chris_Rae]\nvnd.ms-powerpoint.presentation.macroEnabled.12\tapplication/vnd.ms-powerpoint.presentation.macroEnabled.12\t[Chris_Rae]\nvnd.ms-powerpoint.slide.macroEnabled.12\tapplication/vnd.ms-powerpoint.slide.macroEnabled.12\t[Chris_Rae]\nvnd.ms-powerpoint.slideshow.macroEnabled.12\tapplication/vnd.ms-powerpoint.slideshow.macroEnabled.12\t[Chris_Rae]\nvnd.ms-powerpoint.template.macroEnabled.12\tapplication/vnd.ms-powerpoint.template.macroEnabled.12\t[Chris_Rae]\nvnd.ms-PrintDeviceCapabilities+xml\tapplication/vnd.ms-PrintDeviceCapabilities+xml\t[Justin_Hutchings]\nvnd.ms-PrintSchemaTicket+xml\tapplication/vnd.ms-PrintSchemaTicket+xml\t[Justin_Hutchings]\nvnd.ms-project\tapplication/vnd.ms-project\t[Sukvinder_S._Gill]\nvnd.ms-tnef\tapplication/vnd.ms-tnef\t[Sukvinder_S._Gill]\nvnd.ms-windows.devicepairing\tapplication/vnd.ms-windows.devicepairing\t[Justin_Hutchings]\nvnd.ms-windows.nwprinting.oob\tapplication/vnd.ms-windows.nwprinting.oob\t[Justin_Hutchings]\nvnd.ms-windows.printerpairing\tapplication/vnd.ms-windows.printerpairing\t[Justin_Hutchings]\nvnd.ms-windows.wsd.oob\tapplication/vnd.ms-windows.wsd.oob\t[Justin_Hutchings]\nvnd.ms-wmdrm.lic-chlg-req\tapplication/vnd.ms-wmdrm.lic-chlg-req\t[Kevin_Lau]\nvnd.ms-wmdrm.lic-resp\tapplication/vnd.ms-wmdrm.lic-resp\t[Kevin_Lau]\nvnd.ms-wmdrm.meter-chlg-req\tapplication/vnd.ms-wmdrm.meter-chlg-req\t[Kevin_Lau]\nvnd.ms-wmdrm.meter-resp\tapplication/vnd.ms-wmdrm.meter-resp\t[Kevin_Lau]\nvnd.ms-word.document.macroEnabled.12\tapplication/vnd.ms-word.document.macroEnabled.12\t[Chris_Rae]\nvnd.ms-word.template.macroEnabled.12\tapplication/vnd.ms-word.template.macroEnabled.12\t[Chris_Rae]\nvnd.ms-works\tapplication/vnd.ms-works\t[Sukvinder_S._Gill]\nvnd.ms-wpl\tapplication/vnd.ms-wpl\t[Dan_Plastina]\nvnd.ms-xpsdocument\tapplication/vnd.ms-xpsdocument\t[Jesse_McGatha]\nvnd.msa-disk-image\tapplication/vnd.msa-disk-image\t[Thomas_Huth]\nvnd.mseq\tapplication/vnd.mseq\t[Gwenael_Le_Bodic]\nvnd.msign\tapplication/vnd.msign\t[Malte_Borcherding]\nvnd.multiad.creator\tapplication/vnd.multiad.creator\t[Steve_Mills]\nvnd.multiad.creator.cif\tapplication/vnd.multiad.creator.cif\t[Steve_Mills]\nvnd.musician\tapplication/vnd.musician\t[Greg_Adams]\nvnd.music-niff\tapplication/vnd.music-niff\t[Tim_Butler]\nvnd.muvee.style\tapplication/vnd.muvee.style\t[Chandrashekhara_Anantharamu]\nvnd.mynfc\tapplication/vnd.mynfc\t[Franck_Lefevre]\nvnd.ncd.control\tapplication/vnd.ncd.control\t[Lauri_Tarkkala]\nvnd.ncd.reference\tapplication/vnd.ncd.reference\t[Lauri_Tarkkala]\nvnd.nearst.inv+json\tapplication/vnd.nearst.inv+json\t[Thomas_Schoffelen]\nvnd.nebumind.line\tapplication/vnd.nebumind.line\t[Andreas_Molzer]\nvnd.nervana\tapplication/vnd.nervana\t[Steve_Judkins]\nvnd.netfpx\tapplication/vnd.netfpx\t[Andy_Mutz]\nvnd.neurolanguage.nlu\tapplication/vnd.neurolanguage.nlu\t[Dan_DuFeu]\nvnd.nimn\tapplication/vnd.nimn\t[Amit_Kumar_Gupta]\nvnd.nintendo.snes.rom\tapplication/vnd.nintendo.snes.rom\t[Henrik_Andersson]\nvnd.nintendo.nitro.rom\tapplication/vnd.nintendo.nitro.rom\t[Henrik_Andersson]\nvnd.nitf\tapplication/vnd.nitf\t[Steve_Rogan]\nvnd.noblenet-directory\tapplication/vnd.noblenet-directory\t[Monty_Solomon]\nvnd.noblenet-sealer\tapplication/vnd.noblenet-sealer\t[Monty_Solomon]\nvnd.noblenet-web\tapplication/vnd.noblenet-web\t[Monty_Solomon]\nvnd.nokia.catalogs\tapplication/vnd.nokia.catalogs\t[Nokia]\nvnd.nokia.conml+wbxml\tapplication/vnd.nokia.conml+wbxml\t[Nokia]\nvnd.nokia.conml+xml\tapplication/vnd.nokia.conml+xml\t[Nokia]\nvnd.nokia.iptv.config+xml\tapplication/vnd.nokia.iptv.config+xml\t[Nokia]\nvnd.nokia.iSDS-radio-presets\tapplication/vnd.nokia.iSDS-radio-presets\t[Nokia]\nvnd.nokia.landmark+wbxml\tapplication/vnd.nokia.landmark+wbxml\t[Nokia]\nvnd.nokia.landmark+xml\tapplication/vnd.nokia.landmark+xml\t[Nokia]\nvnd.nokia.landmarkcollection+xml\tapplication/vnd.nokia.landmarkcollection+xml\t[Nokia]\nvnd.nokia.ncd\tapplication/vnd.nokia.ncd\t[Nokia]\nvnd.nokia.n-gage.ac+xml\tapplication/vnd.nokia.n-gage.ac+xml\t[Nokia]\nvnd.nokia.n-gage.data\tapplication/vnd.nokia.n-gage.data\t[Nokia]\nvnd.nokia.n-gage.symbian.install - OBSOLETE; no replacement given\tapplication/vnd.nokia.n-gage.symbian.install\t[Nokia]\nvnd.nokia.pcd+wbxml\tapplication/vnd.nokia.pcd+wbxml\t[Nokia]\nvnd.nokia.pcd+xml\tapplication/vnd.nokia.pcd+xml\t[Nokia]\nvnd.nokia.radio-preset\tapplication/vnd.nokia.radio-preset\t[Nokia]\nvnd.nokia.radio-presets\tapplication/vnd.nokia.radio-presets\t[Nokia]\nvnd.novadigm.EDM\tapplication/vnd.novadigm.EDM\t[Janine_Swenson]\nvnd.novadigm.EDX\tapplication/vnd.novadigm.EDX\t[Janine_Swenson]\nvnd.novadigm.EXT\tapplication/vnd.novadigm.EXT\t[Janine_Swenson]\nvnd.ntt-local.content-share\tapplication/vnd.ntt-local.content-share\t[Akinori_Taya]\nvnd.ntt-local.file-transfer\tapplication/vnd.ntt-local.file-transfer\t[NTT-local]\nvnd.ntt-local.ogw_remote-access\tapplication/vnd.ntt-local.ogw_remote-access\t[NTT-local]\nvnd.ntt-local.sip-ta_remote\tapplication/vnd.ntt-local.sip-ta_remote\t[NTT-local]\nvnd.ntt-local.sip-ta_tcp_stream\tapplication/vnd.ntt-local.sip-ta_tcp_stream\t[NTT-local]\nvnd.oasis.opendocument.chart\tapplication/vnd.oasis.opendocument.chart\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.chart-template\tapplication/vnd.oasis.opendocument.chart-template\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.database\tapplication/vnd.oasis.opendocument.database\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.formula\tapplication/vnd.oasis.opendocument.formula\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.formula-template\tapplication/vnd.oasis.opendocument.formula-template\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.graphics\tapplication/vnd.oasis.opendocument.graphics\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.graphics-template\tapplication/vnd.oasis.opendocument.graphics-template\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.image\tapplication/vnd.oasis.opendocument.image\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.image-template\tapplication/vnd.oasis.opendocument.image-template\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.presentation\tapplication/vnd.oasis.opendocument.presentation\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.presentation-template\tapplication/vnd.oasis.opendocument.presentation-template\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.spreadsheet\tapplication/vnd.oasis.opendocument.spreadsheet\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.spreadsheet-template\tapplication/vnd.oasis.opendocument.spreadsheet-template\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.text\tapplication/vnd.oasis.opendocument.text\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.text-master\tapplication/vnd.oasis.opendocument.text-master\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.text-template\tapplication/vnd.oasis.opendocument.text-template\t[Svante_Schubert][OASIS]\nvnd.oasis.opendocument.text-web\tapplication/vnd.oasis.opendocument.text-web\t[Svante_Schubert][OASIS]\nvnd.obn\tapplication/vnd.obn\t[Matthias_Hessling]\nvnd.ocf+cbor\tapplication/vnd.ocf+cbor\t[Michael_Koster]\nvnd.oci.image.manifest.v1+json\tapplication/vnd.oci.image.manifest.v1+json\t[Steven_Lasker]\nvnd.oftn.l10n+json\tapplication/vnd.oftn.l10n+json\t[Eli_Grey]\nvnd.oipf.contentaccessdownload+xml\tapplication/vnd.oipf.contentaccessdownload+xml\t[Claire_DEsclercs]\nvnd.oipf.contentaccessstreaming+xml\tapplication/vnd.oipf.contentaccessstreaming+xml\t[Claire_DEsclercs]\nvnd.oipf.cspg-hexbinary\tapplication/vnd.oipf.cspg-hexbinary\t[Claire_DEsclercs]\nvnd.oipf.dae.svg+xml\tapplication/vnd.oipf.dae.svg+xml\t[Claire_DEsclercs]\nvnd.oipf.dae.xhtml+xml\tapplication/vnd.oipf.dae.xhtml+xml\t[Claire_DEsclercs]\nvnd.oipf.mippvcontrolmessage+xml\tapplication/vnd.oipf.mippvcontrolmessage+xml\t[Claire_DEsclercs]\nvnd.oipf.pae.gem\tapplication/vnd.oipf.pae.gem\t[Claire_DEsclercs]\nvnd.oipf.spdiscovery+xml\tapplication/vnd.oipf.spdiscovery+xml\t[Claire_DEsclercs]\nvnd.oipf.spdlist+xml\tapplication/vnd.oipf.spdlist+xml\t[Claire_DEsclercs]\nvnd.oipf.ueprofile+xml\tapplication/vnd.oipf.ueprofile+xml\t[Claire_DEsclercs]\nvnd.oipf.userprofile+xml\tapplication/vnd.oipf.userprofile+xml\t[Claire_DEsclercs]\nvnd.olpc-sugar\tapplication/vnd.olpc-sugar\t[John_Palmieri]\nvnd.oma.bcast.associated-procedure-parameter+xml\tapplication/vnd.oma.bcast.associated-procedure-parameter+xml\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.drm-trigger+xml\tapplication/vnd.oma.bcast.drm-trigger+xml\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.imd+xml\tapplication/vnd.oma.bcast.imd+xml\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.ltkm\tapplication/vnd.oma.bcast.ltkm\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.notification+xml\tapplication/vnd.oma.bcast.notification+xml\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.provisioningtrigger\tapplication/vnd.oma.bcast.provisioningtrigger\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.sgboot\tapplication/vnd.oma.bcast.sgboot\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.sgdd+xml\tapplication/vnd.oma.bcast.sgdd+xml\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.sgdu\tapplication/vnd.oma.bcast.sgdu\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.simple-symbol-container\tapplication/vnd.oma.bcast.simple-symbol-container\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.smartcard-trigger+xml\tapplication/vnd.oma.bcast.smartcard-trigger+xml\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.sprov+xml\tapplication/vnd.oma.bcast.sprov+xml\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.bcast.stkm\tapplication/vnd.oma.bcast.stkm\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.cab-address-book+xml\tapplication/vnd.oma.cab-address-book+xml\t[Hao_Wang][OMA]\nvnd.oma.cab-feature-handler+xml\tapplication/vnd.oma.cab-feature-handler+xml\t[Hao_Wang][OMA]\nvnd.oma.cab-pcc+xml\tapplication/vnd.oma.cab-pcc+xml\t[Hao_Wang][OMA]\nvnd.oma.cab-subs-invite+xml\tapplication/vnd.oma.cab-subs-invite+xml\t[Hao_Wang][OMA]\nvnd.oma.cab-user-prefs+xml\tapplication/vnd.oma.cab-user-prefs+xml\t[Hao_Wang][OMA]\nvnd.oma.dcd\tapplication/vnd.oma.dcd\t[Avi_Primo][Open_Mobile_Naming_Authority]\nvnd.oma.dcdc\tapplication/vnd.oma.dcdc\t[Avi_Primo][Open_Mobile_Naming_Authority]\nvnd.oma.dd2+xml\tapplication/vnd.oma.dd2+xml\t[Jun_Sato][Open_Mobile_Alliance_BAC_DLDRM_Working_Group]\nvnd.oma.drm.risd+xml\tapplication/vnd.oma.drm.risd+xml\t[Uwe_Rauschenbach][Open_Mobile_Naming_Authority]\nvnd.oma.group-usage-list+xml\tapplication/vnd.oma.group-usage-list+xml\t[Sean_Kelley][OMA_Presence_and_Availability_PAG_Working_Group]\nvnd.oma.lwm2m+cbor\tapplication/vnd.oma.lwm2m+cbor\t[Open_Mobile_Naming_Authority][John_Mudge]\nvnd.oma.lwm2m+json\tapplication/vnd.oma.lwm2m+json\t[Open_Mobile_Naming_Authority][John_Mudge]\nvnd.oma.lwm2m+tlv\tapplication/vnd.oma.lwm2m+tlv\t[Open_Mobile_Naming_Authority][John_Mudge]\nvnd.oma.pal+xml\tapplication/vnd.oma.pal+xml\t[Brian_McColgan][Open_Mobile_Naming_Authority]\nvnd.oma.poc.detailed-progress-report+xml\tapplication/vnd.oma.poc.detailed-progress-report+xml\t[OMA_Push_to_Talk_over_Cellular_POC_Working_Group]\nvnd.oma.poc.final-report+xml\tapplication/vnd.oma.poc.final-report+xml\t[OMA_Push_to_Talk_over_Cellular_POC_Working_Group]\nvnd.oma.poc.groups+xml\tapplication/vnd.oma.poc.groups+xml\t[Sean_Kelley][OMA_Push_to_Talk_over_Cellular_POC_Working_Group]\nvnd.oma.poc.invocation-descriptor+xml\tapplication/vnd.oma.poc.invocation-descriptor+xml\t[OMA_Push_to_Talk_over_Cellular_POC_Working_Group]\nvnd.oma.poc.optimized-progress-report+xml\tapplication/vnd.oma.poc.optimized-progress-report+xml\t[OMA_Push_to_Talk_over_Cellular_POC_Working_Group]\nvnd.oma.push\tapplication/vnd.oma.push\t[Bryan_Sullivan][OMA]\nvnd.oma.scidm.messages+xml\tapplication/vnd.oma.scidm.messages+xml\t[Wenjun_Zeng][Open_Mobile_Naming_Authority]\nvnd.oma.xcap-directory+xml\tapplication/vnd.oma.xcap-directory+xml\t[Sean_Kelley][OMA_Presence_and_Availability_PAG_Working_Group]\nvnd.omads-email+xml\tapplication/vnd.omads-email+xml\t[OMA_Data_Synchronization_Working_Group]\nvnd.omads-file+xml\tapplication/vnd.omads-file+xml\t[OMA_Data_Synchronization_Working_Group]\nvnd.omads-folder+xml\tapplication/vnd.omads-folder+xml\t[OMA_Data_Synchronization_Working_Group]\nvnd.omaloc-supl-init\tapplication/vnd.omaloc-supl-init\t[Julien_Grange]\nvnd.oma-scws-config\tapplication/vnd.oma-scws-config\t[Ilan_Mahalal]\nvnd.oma-scws-http-request\tapplication/vnd.oma-scws-http-request\t[Ilan_Mahalal]\nvnd.oma-scws-http-response\tapplication/vnd.oma-scws-http-response\t[Ilan_Mahalal]\nvnd.onepager\tapplication/vnd.onepager\t[Nathan_Black]\nvnd.onepagertamp\tapplication/vnd.onepagertamp\t[Nathan_Black]\nvnd.onepagertamx\tapplication/vnd.onepagertamx\t[Nathan_Black]\nvnd.onepagertat\tapplication/vnd.onepagertat\t[Nathan_Black]\nvnd.onepagertatp\tapplication/vnd.onepagertatp\t[Nathan_Black]\nvnd.onepagertatx\tapplication/vnd.onepagertatx\t[Nathan_Black]\nvnd.openblox.game-binary\tapplication/vnd.openblox.game-binary\t[Mark_Otaris]\nvnd.openblox.game+xml\tapplication/vnd.openblox.game+xml\t[Mark_Otaris]\nvnd.openeye.oeb\tapplication/vnd.openeye.oeb\t[Craig_Bruce]\nvnd.openstreetmap.data+xml\tapplication/vnd.openstreetmap.data+xml\t[Paul_Norman]\nvnd.openxmlformats-officedocument.custom-properties+xml\tapplication/vnd.openxmlformats-officedocument.custom-properties+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.customXmlProperties+xml\tapplication/vnd.openxmlformats-officedocument.customXmlProperties+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.drawing+xml\tapplication/vnd.openxmlformats-officedocument.drawing+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.drawingml.chart+xml\tapplication/vnd.openxmlformats-officedocument.drawingml.chart+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.drawingml.chartshapes+xml\tapplication/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.drawingml.diagramColors+xml\tapplication/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.drawingml.diagramData+xml\tapplication/vnd.openxmlformats-officedocument.drawingml.diagramData+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.drawingml.diagramLayout+xml\tapplication/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.drawingml.diagramStyle+xml\tapplication/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.extended-properties+xml\tapplication/vnd.openxmlformats-officedocument.extended-properties+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.commentAuthors+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.comments+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.comments+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.handoutMaster+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.notesMaster+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.notesSlide+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.presentation\tapplication/vnd.openxmlformats-officedocument.presentationml.presentation\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.presentation.main+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.presProps+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.presProps+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.slide\tapplication/vnd.openxmlformats-officedocument.presentationml.slide\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.slide+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.slide+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.slideLayout+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.slideMaster+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.slideshow\tapplication/vnd.openxmlformats-officedocument.presentationml.slideshow\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.slideshow.main+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.slideUpdateInfo+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.slideUpdateInfo+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.tableStyles+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.tags+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.tags+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.template\tapplication/vnd.openxmlformats-officedocument.presentationml.template\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.template.main+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.template.main+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.presentationml.viewProps+xml\tapplication/vnd.openxmlformats-officedocument.presentationml.viewProps+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.comments+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.connections+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.sheet\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.styles+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.table+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.table+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.template\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.template\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.template.main+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.userNames+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\tapplication/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.theme+xml\tapplication/vnd.openxmlformats-officedocument.theme+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.themeOverride+xml\tapplication/vnd.openxmlformats-officedocument.themeOverride+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.vmlDrawing\tapplication/vnd.openxmlformats-officedocument.vmlDrawing\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.comments+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.document\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.document\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.document.main+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.footer+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.numbering+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.settings+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.styles+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.template\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.template\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.template.main+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml\t[Makoto_Murata]\nvnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml\tapplication/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml\t[Makoto_Murata]\nvnd.openxmlformats-package.core-properties+xml\tapplication/vnd.openxmlformats-package.core-properties+xml\t[Makoto_Murata]\nvnd.openxmlformats-package.digital-signature-xmlsignature+xml\tapplication/vnd.openxmlformats-package.digital-signature-xmlsignature+xml\t[Makoto_Murata]\nvnd.openxmlformats-package.relationships+xml\tapplication/vnd.openxmlformats-package.relationships+xml\t[Makoto_Murata]\nvnd.oracle.resource+json\tapplication/vnd.oracle.resource+json\t[Ning_Dong]\nvnd.orange.indata\tapplication/vnd.orange.indata\t[CHATRAS_Bruno]\nvnd.osa.netdeploy\tapplication/vnd.osa.netdeploy\t[Steven_Klos]\nvnd.osgeo.mapguide.package\tapplication/vnd.osgeo.mapguide.package\t[Jason_Birch]\nvnd.osgi.bundle\tapplication/vnd.osgi.bundle\t[Peter_Kriens]\nvnd.osgi.dp\tapplication/vnd.osgi.dp\t[Peter_Kriens]\nvnd.osgi.subsystem\tapplication/vnd.osgi.subsystem\t[Peter_Kriens]\nvnd.otps.ct-kip+xml\tapplication/vnd.otps.ct-kip+xml\t[Magnus_Nystrom]\nvnd.oxli.countgraph\tapplication/vnd.oxli.countgraph\t[C._Titus_Brown]\nvnd.pagerduty+json\tapplication/vnd.pagerduty+json\t[Steve_Rice]\nvnd.palm\tapplication/vnd.palm\t[Gavin_Peacock]\nvnd.panoply\tapplication/vnd.panoply\t[Natarajan_Balasundara]\nvnd.paos.xml\tapplication/vnd.paos.xml\t[John_Kemp]\nvnd.patentdive\tapplication/vnd.patentdive\t[Christian_Trosclair]\nvnd.patientecommsdoc\tapplication/vnd.patientecommsdoc\t[Andrew_David_Kendall]\nvnd.pawaafile\tapplication/vnd.pawaafile\t[Prakash_Baskaran]\nvnd.pcos\tapplication/vnd.pcos\t[Slawomir_Lisznianski]\nvnd.pg.format\tapplication/vnd.pg.format\t[April_Gandert]\nvnd.pg.osasli\tapplication/vnd.pg.osasli\t[April_Gandert]\nvnd.piaccess.application-licence\tapplication/vnd.piaccess.application-licence\t[Lucas_Maneos]\nvnd.picsel\tapplication/vnd.picsel\t[Giuseppe_Naccarato]\nvnd.pmi.widget\tapplication/vnd.pmi.widget\t[Rhys_Lewis]\nvnd.poc.group-advertisement+xml\tapplication/vnd.poc.group-advertisement+xml\t[Sean_Kelley][OMA_Push_to_Talk_over_Cellular_POC_Working_Group]\nvnd.pocketlearn\tapplication/vnd.pocketlearn\t[Jorge_Pando]\nvnd.powerbuilder6\tapplication/vnd.powerbuilder6\t[David_Guy]\nvnd.powerbuilder6-s\tapplication/vnd.powerbuilder6-s\t[David_Guy]\nvnd.powerbuilder7\tapplication/vnd.powerbuilder7\t[Reed_Shilts]\nvnd.powerbuilder75\tapplication/vnd.powerbuilder75\t[Reed_Shilts]\nvnd.powerbuilder75-s\tapplication/vnd.powerbuilder75-s\t[Reed_Shilts]\nvnd.powerbuilder7-s\tapplication/vnd.powerbuilder7-s\t[Reed_Shilts]\nvnd.preminet\tapplication/vnd.preminet\t[Juoko_Tenhunen]\nvnd.previewsystems.box\tapplication/vnd.previewsystems.box\t[Roman_Smolgovsky]\nvnd.proteus.magazine\tapplication/vnd.proteus.magazine\t[Pete_Hoch]\nvnd.psfs\tapplication/vnd.psfs\t[Kristopher_Durski]\nvnd.publishare-delta-tree\tapplication/vnd.publishare-delta-tree\t[Oren_Ben-Kiki]\nvnd.pvi.ptid1\tapplication/vnd.pvi.ptid1\t[Charles_P._Lamb]\nvnd.pwg-multiplexed\tapplication/vnd.pwg-multiplexed\t[RFC3391]\nvnd.pwg-xhtml-print+xml\tapplication/vnd.pwg-xhtml-print+xml\t[Don_Wright]\nvnd.qualcomm.brew-app-res\tapplication/vnd.qualcomm.brew-app-res\t[Glenn_Forrester]\nvnd.quarantainenet\tapplication/vnd.quarantainenet\t[Casper_Joost_Eyckelhof]\nvnd.Quark.QuarkXPress\tapplication/vnd.Quark.QuarkXPress\t[Hannes_Scheidler]\nvnd.quobject-quoxdocument\tapplication/vnd.quobject-quoxdocument\t[Matthias_Ludwig]\nvnd.radisys.moml+xml\tapplication/vnd.radisys.moml+xml\t[RFC5707]\nvnd.radisys.msml-audit-conf+xml\tapplication/vnd.radisys.msml-audit-conf+xml\t[RFC5707]\nvnd.radisys.msml-audit-conn+xml\tapplication/vnd.radisys.msml-audit-conn+xml\t[RFC5707]\nvnd.radisys.msml-audit-dialog+xml\tapplication/vnd.radisys.msml-audit-dialog+xml\t[RFC5707]\nvnd.radisys.msml-audit-stream+xml\tapplication/vnd.radisys.msml-audit-stream+xml\t[RFC5707]\nvnd.radisys.msml-audit+xml\tapplication/vnd.radisys.msml-audit+xml\t[RFC5707]\nvnd.radisys.msml-conf+xml\tapplication/vnd.radisys.msml-conf+xml\t[RFC5707]\nvnd.radisys.msml-dialog-base+xml\tapplication/vnd.radisys.msml-dialog-base+xml\t[RFC5707]\nvnd.radisys.msml-dialog-fax-detect+xml\tapplication/vnd.radisys.msml-dialog-fax-detect+xml\t[RFC5707]\nvnd.radisys.msml-dialog-fax-sendrecv+xml\tapplication/vnd.radisys.msml-dialog-fax-sendrecv+xml\t[RFC5707]\nvnd.radisys.msml-dialog-group+xml\tapplication/vnd.radisys.msml-dialog-group+xml\t[RFC5707]\nvnd.radisys.msml-dialog-speech+xml\tapplication/vnd.radisys.msml-dialog-speech+xml\t[RFC5707]\nvnd.radisys.msml-dialog-transform+xml\tapplication/vnd.radisys.msml-dialog-transform+xml\t[RFC5707]\nvnd.radisys.msml-dialog+xml\tapplication/vnd.radisys.msml-dialog+xml\t[RFC5707]\nvnd.radisys.msml+xml\tapplication/vnd.radisys.msml+xml\t[RFC5707]\nvnd.rainstor.data\tapplication/vnd.rainstor.data\t[Kevin_Crook]\nvnd.rapid\tapplication/vnd.rapid\t[Etay_Szekely]\nvnd.rar\tapplication/vnd.rar\t[Kim_Scarborough]\nvnd.realvnc.bed\tapplication/vnd.realvnc.bed\t[Nick_Reeves]\nvnd.recordare.musicxml\tapplication/vnd.recordare.musicxml\t[W3C_Music_Notation_Community_Group]\nvnd.recordare.musicxml+xml\tapplication/vnd.recordare.musicxml+xml\t[W3C_Music_Notation_Community_Group]\nvnd.RenLearn.rlprint\tapplication/vnd.RenLearn.rlprint\t[James_Wick]\nvnd.restful+json\tapplication/vnd.restful+json\t[Stephen_Mizell]\nvnd.rig.cryptonote\tapplication/vnd.rig.cryptonote\t[Ken_Jibiki]\nvnd.route66.link66+xml\tapplication/vnd.route66.link66+xml\t[Sybren_Kikstra]\nvnd.rs-274x\tapplication/vnd.rs-274x\t[Lee_Harding]\nvnd.ruckus.download\tapplication/vnd.ruckus.download\t[Jerry_Harris]\nvnd.s3sms\tapplication/vnd.s3sms\t[Lauri_Tarkkala]\nvnd.sailingtracker.track\tapplication/vnd.sailingtracker.track\t[Heikki_Vesalainen]\nvnd.sar\tapplication/vnd.sar\t[Markus_Strehle]\nvnd.sbm.cid\tapplication/vnd.sbm.cid\t[Shinji_Kusakari]\nvnd.sbm.mid2\tapplication/vnd.sbm.mid2\t[Masanori_Murai]\nvnd.scribus\tapplication/vnd.scribus\t[Craig_Bradney]\nvnd.sealed.3df\tapplication/vnd.sealed.3df\t[John_Kwan]\nvnd.sealed.csf\tapplication/vnd.sealed.csf\t[John_Kwan]\nvnd.sealed.doc\tapplication/vnd.sealed.doc\t[David_Petersen]\nvnd.sealed.eml\tapplication/vnd.sealed.eml\t[David_Petersen]\nvnd.sealed.mht\tapplication/vnd.sealed.mht\t[David_Petersen]\nvnd.sealed.net\tapplication/vnd.sealed.net\t[Martin_Lambert]\nvnd.sealed.ppt\tapplication/vnd.sealed.ppt\t[David_Petersen]\nvnd.sealed.tiff\tapplication/vnd.sealed.tiff\t[John_Kwan][Martin_Lambert]\nvnd.sealed.xls\tapplication/vnd.sealed.xls\t[David_Petersen]\nvnd.sealedmedia.softseal.html\tapplication/vnd.sealedmedia.softseal.html\t[David_Petersen]\nvnd.sealedmedia.softseal.pdf\tapplication/vnd.sealedmedia.softseal.pdf\t[David_Petersen]\nvnd.seemail\tapplication/vnd.seemail\t[Steve_Webb]\nvnd.seis+json\tapplication/vnd.seis+json\t[ICT_Manager]\nvnd.sema\tapplication/vnd.sema\t[Anders_Hansson]\nvnd.semd\tapplication/vnd.semd\t[Anders_Hansson]\nvnd.semf\tapplication/vnd.semf\t[Anders_Hansson]\nvnd.shade-save-file\tapplication/vnd.shade-save-file\t[Connor_Horman]\nvnd.shana.informed.formdata\tapplication/vnd.shana.informed.formdata\t[Guy_Selzler]\nvnd.shana.informed.formtemplate\tapplication/vnd.shana.informed.formtemplate\t[Guy_Selzler]\nvnd.shana.informed.interchange\tapplication/vnd.shana.informed.interchange\t[Guy_Selzler]\nvnd.shana.informed.package\tapplication/vnd.shana.informed.package\t[Guy_Selzler]\nvnd.shootproof+json\tapplication/vnd.shootproof+json\t[Ben_Ramsey]\nvnd.shopkick+json\tapplication/vnd.shopkick+json\t[Ronald_Jacobs]\nvnd.shp\tapplication/vnd.shp\t[Mi_Tar]\nvnd.shx\tapplication/vnd.shx\t[Mi_Tar]\nvnd.sigrok.session\tapplication/vnd.sigrok.session\t[Uwe_Hermann]\nvnd.SimTech-MindMapper\tapplication/vnd.SimTech-MindMapper\t[Patrick_Koh]\nvnd.siren+json\tapplication/vnd.siren+json\t[Kevin_Swiber]\nvnd.smaf\tapplication/vnd.smaf\t[Hiroaki_Takahashi]\nvnd.smart.notebook\tapplication/vnd.smart.notebook\t[Jonathan_Neitz]\nvnd.smart.teacher\tapplication/vnd.smart.teacher\t[Michael_Boyle]\nvnd.snesdev-page-table\tapplication/vnd.snesdev-page-table\t[Connor_Horman]\nvnd.software602.filler.form+xml\tapplication/vnd.software602.filler.form+xml\t[Jakub_Hytka][Martin_Vondrous]\nvnd.software602.filler.form-xml-zip\tapplication/vnd.software602.filler.form-xml-zip\t[Jakub_Hytka][Martin_Vondrous]\nvnd.solent.sdkm+xml\tapplication/vnd.solent.sdkm+xml\t[Cliff_Gauntlett]\nvnd.spotfire.dxp\tapplication/vnd.spotfire.dxp\t[Stefan_Jernberg]\nvnd.spotfire.sfs\tapplication/vnd.spotfire.sfs\t[Stefan_Jernberg]\nvnd.sqlite3\tapplication/vnd.sqlite3\t[Clemens_Ladisch]\nvnd.sss-cod\tapplication/vnd.sss-cod\t[Asang_Dani]\nvnd.sss-dtf\tapplication/vnd.sss-dtf\t[Eric_Bruno]\nvnd.sss-ntf\tapplication/vnd.sss-ntf\t[Eric_Bruno]\nvnd.stepmania.package\tapplication/vnd.stepmania.package\t[Henrik_Andersson]\nvnd.stepmania.stepchart\tapplication/vnd.stepmania.stepchart\t[Henrik_Andersson]\nvnd.street-stream\tapplication/vnd.street-stream\t[Glenn_Levitt]\nvnd.sun.wadl+xml\tapplication/vnd.sun.wadl+xml\t[Marc_Hadley]\nvnd.sus-calendar\tapplication/vnd.sus-calendar\t[Jonathan_Niedfeldt]\nvnd.svd\tapplication/vnd.svd\t[Scott_Becker]\nvnd.swiftview-ics\tapplication/vnd.swiftview-ics\t[Glenn_Widener]\nvnd.sycle+xml\tapplication/vnd.sycle+xml\t[Johann_Terblanche]\nvnd.syncml.dm.notification\tapplication/vnd.syncml.dm.notification\t[Peter_Thompson][OMA-DM_Work_Group]\nvnd.syncml.dmddf+xml\tapplication/vnd.syncml.dmddf+xml\t[OMA-DM_Work_Group]\nvnd.syncml.dmtnds+wbxml\tapplication/vnd.syncml.dmtnds+wbxml\t[OMA-DM_Work_Group]\nvnd.syncml.dmtnds+xml\tapplication/vnd.syncml.dmtnds+xml\t[OMA-DM_Work_Group]\nvnd.syncml.dmddf+wbxml\tapplication/vnd.syncml.dmddf+wbxml\t[OMA-DM_Work_Group]\nvnd.syncml.dm+wbxml\tapplication/vnd.syncml.dm+wbxml\t[OMA-DM_Work_Group]\nvnd.syncml.dm+xml\tapplication/vnd.syncml.dm+xml\t[Bindu_Rama_Rao][OMA-DM_Work_Group]\nvnd.syncml.ds.notification\tapplication/vnd.syncml.ds.notification\t[OMA_Data_Synchronization_Working_Group]\nvnd.syncml+xml\tapplication/vnd.syncml+xml\t[OMA_Data_Synchronization_Working_Group]\nvnd.tableschema+json\tapplication/vnd.tableschema+json\t[Paul_Walsh]\nvnd.tao.intent-module-archive\tapplication/vnd.tao.intent-module-archive\t[Daniel_Shelton]\nvnd.tcpdump.pcap\tapplication/vnd.tcpdump.pcap\t[Guy_Harris][Glen_Turner]\nvnd.think-cell.ppttc+json\tapplication/vnd.think-cell.ppttc+json\t[Arno_Schoedl]\nvnd.tml\tapplication/vnd.tml\t[Joey_Smith]\nvnd.tmd.mediaflex.api+xml\tapplication/vnd.tmd.mediaflex.api+xml\t[Alex_Sibilev]\nvnd.tmobile-livetv\tapplication/vnd.tmobile-livetv\t[Nicolas_Helin]\nvnd.tri.onesource\tapplication/vnd.tri.onesource\t[Rick_Rupp]\nvnd.trid.tpt\tapplication/vnd.trid.tpt\t[Frank_Cusack]\nvnd.triscape.mxs\tapplication/vnd.triscape.mxs\t[Steven_Simonoff]\nvnd.trueapp\tapplication/vnd.trueapp\t[J._Scott_Hepler]\nvnd.truedoc\tapplication/vnd.truedoc\t[Brad_Chase]\nvnd.ubisoft.webplayer\tapplication/vnd.ubisoft.webplayer\t[Martin_Talbot]\nvnd.ufdl\tapplication/vnd.ufdl\t[Dave_Manning]\nvnd.uiq.theme\tapplication/vnd.uiq.theme\t[Tim_Ocock]\nvnd.umajin\tapplication/vnd.umajin\t[Jamie_Riden]\nvnd.unity\tapplication/vnd.unity\t[Unity3d]\nvnd.uoml+xml\tapplication/vnd.uoml+xml\t[Arne_Gerdes]\nvnd.uplanet.alert\tapplication/vnd.uplanet.alert\t[Bruce_Martin]\nvnd.uplanet.alert-wbxml\tapplication/vnd.uplanet.alert-wbxml\t[Bruce_Martin]\nvnd.uplanet.bearer-choice\tapplication/vnd.uplanet.bearer-choice\t[Bruce_Martin]\nvnd.uplanet.bearer-choice-wbxml\tapplication/vnd.uplanet.bearer-choice-wbxml\t[Bruce_Martin]\nvnd.uplanet.cacheop\tapplication/vnd.uplanet.cacheop\t[Bruce_Martin]\nvnd.uplanet.cacheop-wbxml\tapplication/vnd.uplanet.cacheop-wbxml\t[Bruce_Martin]\nvnd.uplanet.channel\tapplication/vnd.uplanet.channel\t[Bruce_Martin]\nvnd.uplanet.channel-wbxml\tapplication/vnd.uplanet.channel-wbxml\t[Bruce_Martin]\nvnd.uplanet.list\tapplication/vnd.uplanet.list\t[Bruce_Martin]\nvnd.uplanet.listcmd\tapplication/vnd.uplanet.listcmd\t[Bruce_Martin]\nvnd.uplanet.listcmd-wbxml\tapplication/vnd.uplanet.listcmd-wbxml\t[Bruce_Martin]\nvnd.uplanet.list-wbxml\tapplication/vnd.uplanet.list-wbxml\t[Bruce_Martin]\nvnd.uri-map\tapplication/vnd.uri-map\t[Sebastian_Baer]\nvnd.uplanet.signal\tapplication/vnd.uplanet.signal\t[Bruce_Martin]\nvnd.valve.source.material\tapplication/vnd.valve.source.material\t[Henrik_Andersson]\nvnd.vcx\tapplication/vnd.vcx\t[Taisuke_Sugimoto]\nvnd.vd-study\tapplication/vnd.vd-study\t[Luc_Rogge]\nvnd.vectorworks\tapplication/vnd.vectorworks\t[Lyndsey_Ferguson][Biplab_Sarkar]\nvnd.vel+json\tapplication/vnd.vel+json\t[James_Wigger]\nvnd.verimatrix.vcas\tapplication/vnd.verimatrix.vcas\t[Petr_Peterka]\nvnd.veryant.thin\tapplication/vnd.veryant.thin\t[Massimo_Bertoli]\nvnd.ves.encrypted\tapplication/vnd.ves.encrypted\t[Jim_Zubov]\nvnd.vidsoft.vidconference\tapplication/vnd.vidsoft.vidconference\t[Robert_Hess]\nvnd.visio\tapplication/vnd.visio\t[Troy_Sandal]\nvnd.visionary\tapplication/vnd.visionary\t[Gayatri_Aravindakumar]\nvnd.vividence.scriptfile\tapplication/vnd.vividence.scriptfile\t[Mark_Risher]\nvnd.vsf\tapplication/vnd.vsf\t[Delton_Rowe]\nvnd.wap.sic\tapplication/vnd.wap.sic\t[WAP-Forum]\nvnd.wap.slc\tapplication/vnd.wap.slc\t[WAP-Forum]\nvnd.wap.wbxml\tapplication/vnd.wap.wbxml\t[Peter_Stark]\nvnd.wap.wmlc\tapplication/vnd.wap.wmlc\t[Peter_Stark]\nvnd.wap.wmlscriptc\tapplication/vnd.wap.wmlscriptc\t[Peter_Stark]\nvnd.webturbo\tapplication/vnd.webturbo\t[Yaser_Rehem]\nvnd.wfa.dpp\tapplication/vnd.wfa.dpp\t[Wi-Fi_Alliance][Dr._Jun_Tian]\nvnd.wfa.p2p\tapplication/vnd.wfa.p2p\t[Mick_Conley]\nvnd.wfa.wsc\tapplication/vnd.wfa.wsc\t[Wi-Fi_Alliance]\nvnd.windows.devicepairing\tapplication/vnd.windows.devicepairing\t[Priya_Dandawate]\nvnd.wmc\tapplication/vnd.wmc\t[Thomas_Kjornes]\nvnd.wmf.bootstrap\tapplication/vnd.wmf.bootstrap\t[Thinh_Nguyenphu][Prakash_Iyer]\nvnd.wolfram.mathematica\tapplication/vnd.wolfram.mathematica\t[Wolfram]\nvnd.wolfram.mathematica.package\tapplication/vnd.wolfram.mathematica.package\t[Wolfram]\nvnd.wolfram.player\tapplication/vnd.wolfram.player\t[Wolfram]\nvnd.wordperfect\tapplication/vnd.wordperfect\t[Kim_Scarborough]\nvnd.wqd\tapplication/vnd.wqd\t[Jan_Bostrom]\nvnd.wrq-hp3000-labelled\tapplication/vnd.wrq-hp3000-labelled\t[Chris_Bartram]\nvnd.wt.stf\tapplication/vnd.wt.stf\t[Bill_Wohler]\nvnd.wv.csp+xml\tapplication/vnd.wv.csp+xml\t[John_Ingi_Ingimundarson]\nvnd.wv.csp+wbxml\tapplication/vnd.wv.csp+wbxml\t[Matti_Salmi]\nvnd.wv.ssp+xml\tapplication/vnd.wv.ssp+xml\t[John_Ingi_Ingimundarson]\nvnd.xacml+json\tapplication/vnd.xacml+json\t[David_Brossard]\nvnd.xara\tapplication/vnd.xara\t[David_Matthewman]\nvnd.xfdl\tapplication/vnd.xfdl\t[Dave_Manning]\nvnd.xfdl.webform\tapplication/vnd.xfdl.webform\t[Michael_Mansell]\nvnd.xmi+xml\tapplication/vnd.xmi+xml\t[Fred_Waskiewicz]\nvnd.xmpie.cpkg\tapplication/vnd.xmpie.cpkg\t[Reuven_Sherwin]\nvnd.xmpie.dpkg\tapplication/vnd.xmpie.dpkg\t[Reuven_Sherwin]\nvnd.xmpie.plan\tapplication/vnd.xmpie.plan\t[Reuven_Sherwin]\nvnd.xmpie.ppkg\tapplication/vnd.xmpie.ppkg\t[Reuven_Sherwin]\nvnd.xmpie.xlim\tapplication/vnd.xmpie.xlim\t[Reuven_Sherwin]\nvnd.yamaha.hv-dic\tapplication/vnd.yamaha.hv-dic\t[Tomohiro_Yamamoto]\nvnd.yamaha.hv-script\tapplication/vnd.yamaha.hv-script\t[Tomohiro_Yamamoto]\nvnd.yamaha.hv-voice\tapplication/vnd.yamaha.hv-voice\t[Tomohiro_Yamamoto]\nvnd.yamaha.openscoreformat.osfpvg+xml\tapplication/vnd.yamaha.openscoreformat.osfpvg+xml\t[Mark_Olleson]\nvnd.yamaha.openscoreformat\tapplication/vnd.yamaha.openscoreformat\t[Mark_Olleson]\nvnd.yamaha.remote-setup\tapplication/vnd.yamaha.remote-setup\t[Takehiro_Sukizaki]\nvnd.yamaha.smaf-audio\tapplication/vnd.yamaha.smaf-audio\t[Keiichi_Shinoda]\nvnd.yamaha.smaf-phrase\tapplication/vnd.yamaha.smaf-phrase\t[Keiichi_Shinoda]\nvnd.yamaha.through-ngn\tapplication/vnd.yamaha.through-ngn\t[Takehiro_Sukizaki]\nvnd.yamaha.tunnel-udpencap\tapplication/vnd.yamaha.tunnel-udpencap\t[Takehiro_Sukizaki]\nvnd.yaoweme\tapplication/vnd.yaoweme\t[Jens_Jorgensen]\nvnd.yellowriver-custom-menu\tapplication/vnd.yellowriver-custom-menu\t[Mr._Yellow]\nvnd.youtube.yt - OBSOLETED in favor of video/vnd.youtube.yt\tapplication/vnd.youtube.yt\t[Laura_Wood]\nvnd.zul\tapplication/vnd.zul\t[Rene_Grothmann]\nvnd.zzazz.deck+xml\tapplication/vnd.zzazz.deck+xml\t[Micheal_Hewett]\nvoicexml+xml\tapplication/voicexml+xml\t[RFC4267]\nvoucher-cms+json\tapplication/voucher-cms+json\t[RFC8366]\nvq-rtcpxr\tapplication/vq-rtcpxr\t[RFC6035]\nwasm\tapplication/wasm\t[W3C][Eric_Prudhommeaux]\nwatcherinfo+xml\tapplication/watcherinfo+xml\t[RFC3858]\nwebpush-options+json\tapplication/webpush-options+json\t[RFC8292]\nwhoispp-query\tapplication/whoispp-query\t[RFC2957]\nwhoispp-response\tapplication/whoispp-response\t[RFC2958]\nwidget\tapplication/widget\t[W3C][Steven_Pemberton][W3C-Widgets-2012]\nwita\tapplication/wita\t[Larry_Campbell]\nwordperfect5.1\tapplication/wordperfect5.1\t[Paul_Lindner]\nwsdl+xml\tapplication/wsdl+xml\t[W3C]\nwspolicy+xml\tapplication/wspolicy+xml\t[W3C]\nx-pki-message\tapplication/x-pki-message\t[RFC8894]\nx-www-form-urlencoded\tapplication/x-www-form-urlencoded\t[WHATWG][Anne_van_Kesteren]\nx-x509-ca-cert\tapplication/x-x509-ca-cert\t[RFC8894]\nx-x509-ca-ra-cert\tapplication/x-x509-ca-ra-cert\t[RFC8894]\nx-x509-next-ca-cert\tapplication/x-x509-next-ca-cert\t[RFC8894]\nx400-bp\tapplication/x400-bp\t[RFC1494]\nxacml+xml\tapplication/xacml+xml\t[RFC7061]\nxcap-att+xml\tapplication/xcap-att+xml\t[RFC4825]\nxcap-caps+xml\tapplication/xcap-caps+xml\t[RFC4825]\nxcap-diff+xml\tapplication/xcap-diff+xml\t[RFC5874]\nxcap-el+xml\tapplication/xcap-el+xml\t[RFC4825]\nxcap-error+xml\tapplication/xcap-error+xml\t[RFC4825]\nxcap-ns+xml\tapplication/xcap-ns+xml\t[RFC4825]\nxcon-conference-info-diff+xml\tapplication/xcon-conference-info-diff+xml\t[RFC6502]\nxcon-conference-info+xml\tapplication/xcon-conference-info+xml\t[RFC6502]\nxenc+xml\tapplication/xenc+xml\t[Joseph_Reagle][XENC_Working_Group]\nxhtml+xml\tapplication/xhtml+xml\t[W3C][Robin_Berjon]\nxliff+xml\tapplication/xliff+xml\t[OASIS][Chet_Ensign]\nxml\tapplication/xml\t[RFC7303]\nxml-dtd\tapplication/xml-dtd\t[RFC7303]\nxml-external-parsed-entity\tapplication/xml-external-parsed-entity\t[RFC7303]\nxml-patch+xml\tapplication/xml-patch+xml\t[RFC7351]\nxmpp+xml\tapplication/xmpp+xml\t[RFC3923]\nxop+xml\tapplication/xop+xml\t[Mark_Nottingham]\nxslt+xml\tapplication/xslt+xml\t[W3C][http://www.w3.org/TR/2007/REC-xslt20-20070123/#media-type-registration]\nxv+xml\tapplication/xv+xml\t[RFC4374]\nyang\tapplication/yang\t[RFC6020]\nyang-data+json\tapplication/yang-data+json\t[RFC8040]\nyang-data+xml\tapplication/yang-data+xml\t[RFC8040]\nyang-patch+json\tapplication/yang-patch+json\t[RFC8072]\nyang-patch+xml\tapplication/yang-patch+xml\t[RFC8072]\nyin+xml\tapplication/yin+xml\t[RFC6020]\nzip\tapplication/zip\t[Paul_Lindner]\nzlib\tapplication/zlib\t[RFC6713]\nzstd\tapplication/zstd\t[RFC8878]\n\n## 参考\n- http://www.iana.org/assignments/media-types/media-types.xhtml\n"
  },
  {
    "path": "2.1.7.2 文件上传 showOpenFilePicker.md",
    "content": "\n # input 标签，并且 type 类型为 file\n \n ```\n    <input type=\"file\" name=\"\" id=\"\">\n\n\n有几个属性\n\n\naccept\n如果没有设置的话，任何类型的文件都可以进行上传\n\n可以设置的属性有很多，查看 MDN 文档，这里有常见的 Common MIME types 的属性\n\n\n\nuser 代表前置摄像头\n\nenvironment 代表后置摄像头\n\nmultiple\n这个属性 开启的话，就可以选取多个文件\n\n\nwebkitdirectory\n添加了这个属性后，就可以用于选择 文件夹\n```\n\n\n# showOpenFilePicker: 上传选择文件\n\n- pickerOpts\n\n```\nconst pickerOpts = {\n  types: [\n    {\n      description: 'Images',\n      accept: {\n        'image/*': ['.png', '.gif', '.jpeg', '.jpg']\n      }\n    },\n  ],\n  excludeAcceptAllOption: true,\n  multiple: false\n};\n\n// create a reference for our file handle\nlet fileHandle;\n\nasync function getFile() {\n  // open file picker, destructure the one element returned array\n  [fileHandle] = await window.showOpenFilePicker(pickerOpts);\n\n  // run code with our fileHandle\n}\n\n```\n\n# showDirectoryPicker： 选择目录\n\n```\n\n```\n\n\n## 参考\n- [mdn](https://developer.mozilla.org/en-US/docs/Web/API/Window/showOpenFilePicker)\n- [caniuse](https://caniuse.com/?search=showOpenFilePicker)\n"
  },
  {
    "path": "2.1.8 innerHTML,innerText,textContent区别.md",
    "content": "# 2.1.8 innerHTML,innerText,textContent区别\n\n## innerHTML:\n- document.documentElement.innerHTML:\n- 会把<script>、<style>等标签及内容全部显示\n\n## innerText：\n- document.documentElement.innerText\n- 只显示内容 ，不会把<script>、<style>等标签及内容显示\n- 不会获取display:none 的元素\n- 返回值被格式化\n- 会触发reflow;\n\n\n\n## textContent\n- document.documentElement.textContent\n- 会把<script>、<style>等标签内的内容全部显示，标签不显示\n \n  \n## outHTML\n\n- 设置获取元素及内容的HTML\n\n\n\n## outerText\n\n- 设置获取元素及内容的文本\n\n"
  },
  {
    "path": "2.1.9 获取dom中最大的 zindex.md",
    "content": "# 2.1.9 获取dom中最大的 zindex\n\n\n```\nfunction getMaxZIndex() {\n  let arr = [...document.all].map(e => +window.getComputedStyle(e).zIndex || 0);\n  return arr.length ? Math.max(...arr) : 0;\n}\n```\n"
  },
  {
    "path": "2.2.1 重绘和回流.md",
    "content": "# 2.2.1 重绘和回流\n\n## 重排(回流)何时发生\n- 添加或者删除可见的DOM元素\n- 元素位置改变\n- 元素尺寸改变\n- 元素内容改变（例如：一个文本被另一个不同尺寸的图片替代）\n- 页面渲染初始化（这个无法避免）\n- 浏览器窗口尺寸改变\n\n\nCSS的最终表现分为以下四步：Recalculate Style -> Layout -> Paint Setup and Paint -> Composite Layers\n\n按照中文的意思大致是 查找并计算样式 -> 排布 -> 绘制 -> 组合层\n\n这上面的几个步骤有点类似于上文说到的重排必定导致重绘，而查询属性会强制发生重排。所以上文提到的重排重绘内容可以结合这里进行理解。\n\n由于transform是位于Composite Layers层，而width、left、margin等则是位于Layout层，  \n\n在Layout层发生的改变必定导致Paint Setup and Paint -> Composite Layers，所以相对而言使用transform实现的动画效果肯定比left这些更加流畅。\n\n而且就算抛开这一角度，在另一方面浏览器也会针对transform等开启GPU加速。\n\n在 CSS 中，transforms 和 opacity 这两个属性更改不会触发重排与重绘，它们是可以由合成器（composite）单独处理的属性。\n\n\n\n## 减少重绘和回流\n\n1.CSS\n\n```\n使用transform替代top\n使用visibility替换display:none,因为前者只会引起重绘，后者会引发回流（改变了布局）\n避免使用table布局，可能很小的一个改动会造成整个table的重新布局\n尽可能在DOM树的最末端改变class，回流是不可避免的，但可以减少其影响。尽可能在DOM树的最末端改变class，可以限制了回流的范围。\n避免设置多层内联样式，css选择符从右往左匹配查找，避免节点层级过多。\n将动画效果应用到position属性为absolute或者fixed的元素上，避免影响其他元素的布局，这样只是一个重绘，而不是回流。同时，控制动画速度可以选择requestAnimationFrame。\n避免使用css表达式，可能会引发回流。\n将频繁重绘或者回流的节点设置为图层，图层能够阻止该节点的渲染行为影响别的节点，例如will-change,video,iframe等标签，浏览器会自动将该节点变为图层。\nCSS3硬件加速(GPU加速)，使用CSS3硬件加速，可以让transform,opacity,filters这些动画不会引起回流重绘。\n```\n\n2.JavaScript\n\n```\n避免频繁操作样式，最好一次性重写style属性，或者将样式列表定义为class并一次性更改class属性\n避免频繁操作DOM，创建一个documentFragment，在它上面应用所有DOM操作，最后再把它添加到文档中。\n避免频繁读取会引发回流/重绘的属性，如果确实需要多次使用，就用一个变量缓存起来。\n对具有复杂动画的元素使用绝对定位，使它脱离文档流，否则会引起父元素及后续元素频繁回流。\n```\n\n\n## 参考\n- [前端性能优化 24 条建议（2020）](https://segmentfault.com/a/1190000022205291)\n"
  },
  {
    "path": "2.2.2 响应式图片 picture 使用.md",
    "content": "# 2.2.2 响应式图片 picture 使用\n\n\n```\n<picture>\n  <source media=\"(min-width: 650px)\" srcset=\"https://static.runoob.com/images/mix/html-css-js.png\">\n  <source media=\"(min-width: 465px)\" srcset=\"https://static.runoob.com/images/mix/htmlbig.png\">\n  <img src=\"https://static.runoob.com/images/mix/img_avatar.png\"  style=\"width:auto;\">\n</picture>\n```\n\n## 参考\n- []()\n"
  },
  {
    "path": "2.2.3 白屏优化.md",
    "content": "# 2.2.3 白屏优化\n\n\n\n\n## SSR\n\n\n## 预渲染\n\n- prerender-spa-plugin\n\n\n## 骨架屏\n\n- 设计图\n- css\n- page-skeleton-webpack-plugin\n\n\n\n\n>减少 http 请求，CDN 分发，压缩文件、合理的放置JS代码\n\n\n\n\n## 参考\n- [page-skeleton-webpack-plugin](https://github.com/ElemeFE/page-skeleton-webpack-plugin)\n"
  },
  {
    "path": "2.2.4 document.referrer.md",
    "content": "# 2.2.4 document.referrer\n\n>返回上一页，在PC端我们可以使用：history.go(-1)或者history.back()，可以正常返回第一层。这样，我们不需要上一页的 url 具体是什么，只要使用 history 一般都没啥问题\n\n>但是在移动端，如果想要返回上一页。比如从A页面跳到B页面，如果B页面想返回A页面，为了防止不会跳错，必须要有一个 <  按钮，给它加 history.go(-1) ，返回上一层。\n\n>那如果我们没有返回上一页的 < 的按钮，在手机上怎么操作才会返回上一页，比如：从微信分享进来的，进入的是微信内页，此时，内页就是第一页，它没有上一页，要怎么返回？  \n>这时点返回按钮是没有反应的，不是一个好的用户体验\n\n```\njavascript 有一个可以获取前一页面的URL地址的方法：document.referrer\n\ndocument.referrer 的来源\n\nreferrer 属性可返回载入当前文档的文档的 URL【摘自W3CSHCOOL】, 如果当前文档不是通过超链接访问的，那么当前文档的URL为NULL，这个属性允许客户端的 javascript 访问 HTTP 头部；\nreferrer 属性，我们可以从 http 头部获取\ndocument.referrer 的兼容性\n\ndocument.referrer IE7都支持，它的兼容性比较高，Android 5.0开始支持，iOS都支持，PC端浏览器从IE7就开始支持了，兼容性没有什么大的问题。\n\n但是有个小小的问题，就是 IE 会主动清除 referref 属性。在IE中用javascript做跳转，\n比如用window.location.href = “”; \ngoogle如果使用document.referrer无法取到浏览器请求的HTTP referrer，因为IE清空了。\n而其他主流浏览器Firefox和Chrome都会保留referrer，没办法，只好判断，如果是IE浏览器，就主动给它增加一个 referrer 。\n这样的原理就是给IE浏览器的页面偷偷加了个链接，然后自动点这个链接，于是referrer就能保留了\n```\n\n\n## 无法获取 referrer 信息的情况\n\n下面的场景无法获得 referrer 信息，以下前8条属于【张鑫旭】：\n\n```\n- 直接在浏览器中输入地址\n使用location.reload()刷新（location.href或者location.replace()刷新有信息）\n在微信对话框中，点击进入微信自身浏览器\n扫码进入微信或QQ的浏览器\n直接新窗口打开一个页面\n从https的网站直接进入一个http协议的网站（Chrome下亲测）\na标签设置rel=\"noreferrer\"（兼容IE7+）\nmeta标签来控制不让浏览器发送referer\n点击 flash 内部链接\nChrome4.0以下，IE 5.5+以下返回空的字符串\n使用 修改 Location 进行页面导航的方法，会导致在IE下丢失 referrer，这可能是IE的一个BUG\n跨域\n<meta content=\"never\" name=\"referrer\">\n```\n\n## 参考\n- https://www.cnblogs.com/baiyygynui/p/6426621.html\n"
  },
  {
    "path": "3.0 css 代码规范.md",
    "content": "# css代码规范\n\n阮一峰新增 css 教程：https://github.com/wangdoc/css-tutorial/tree/master/docs\n\n* 4 个空格\n* 在每个声明的左括号前增加一个空格，声明块的右括号应该另起一行：  .selector {\n                                                             }\n* 所有的十六进制值都应该使用小写字母，例如 #fff\n* 尽可能使用短的十六进制数值，例如使用 #fff 替代 #ffffff。\n* 为选择器中得属性取值添加引号，例如 input[type=\"text\"]\n* 不要为 0 指明单位，比如使用 margin: 0; 而不是 margin: 0px;\n\n* 相关的属性声明应该以下面的顺序分组处理\n  - Positioning\n  - Box model 盒模型\n  - Typographic 排版\n  - Visual 外观\n  \n  ```\n  /* Positioning */\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 100;\n\n  /* Box-model */\n  display: block;\n  float: right;\n  width: 100px;\n  height: 100px;\n\n  /* Typography */\n  font: normal 13px \"Helvetica Neue\", sans-serif;\n  line-height: 1.5;\n  color: #333;\n  text-align: center;\n\n  /* Visual */\n  background-color: #f5f5f5;\n  border: 1px solid #e5e5e5;\n  border-radius: 3px;\n\n  /* Misc */\n  opacity: 1;\n  ```\n* 媒体查询位置 靠近他们相关的规则\n* 不要使用 @import : 与 <link> 相比, @import 更慢，需要额外的页面请求，并且可能引发其他的意想不到的问题。\n\n* 前缀属性: 当使用厂商前缀属性时，通过缩进使取值垂直对齐以便多行编辑\n```\n.selector {\n  -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.15);\n          box-shadow: 0 1px 2px rgba(0,0,0,.15);\n}\n```\n\n* 在一个声明块中只包含一条声明的情况下，为了易读性和快速编辑可以考虑移除其中的换行。所有包含多条声明的声明块应该分为多行。\n\n* 坚持限制属性取值简写的使用，属性简写需要你必须显式设置所有取值\n```\n/* Good example */\n.element {\n  margin-bottom: 10px;\n  background-color: red;\n  background-image: url(\"image.jpg\");\n  border-top-left-radius: 3px;\n  border-top-right-radius: 3px;\n}\n```\n\n* Class 命名为全小写，可以使用短划线（不要使用下划线和 camelCase 命名）\n* Class 的命名应该尽量短，也要尽量明确,最多三个：.top-title-p\n* 以组件为单位组织代码\n* 使用一致的空白来分割代码块\n\n* 编辑器配置\n  -使用 4 个空格的 soft-tabs\n  - 在保存时删除尾部的空白字符\n  - 设置文件编码为 UTF-8\n  - 在文件结尾添加一个空白行\n"
  },
  {
    "path": "3.0.1 BEM 规范.md",
    "content": "# BEM 规范\n\n\n\n\n\n## 参考\n- https://en.bem.info/methodology/quick-start/\n- http://www.w3cplus.com/blog/tags/325.html\n"
  },
  {
    "path": "3.0.2 SMACSS 规范.md",
    "content": "# SMACSS 规范\n\n  - 基本(base)： 简单的选择器的基本样式，如 `clearfix` , `reset` 重置默认样式\n  - 布局（Layout）: 定义顶部栏、侧边栏、正文、页脚等主要部分\n  - 模块(Module)： 一群元素组成的一个模块，比如说 `header` 和  `sidebar` 等\n  - 状态（State）: 元素的不同状态。如 `hover` , `focus` ,隐藏、按住等\n  - 主题(Theme)：更多的样式,颜色、形状、边框、阴影还有其他等等\n  - 参考: https://smacss.com/book/categorizing\n\n## 参考\n- https://smacss.com/book/categorizing\n- \n"
  },
  {
    "path": "3.0.3 通用公共 css.md",
    "content": "# 公共 css 样式\n\n```\nhtml,\nbody,\np,\nol,\nul,\nli,\ndl,\ndt,\ndd,\nblockquote,\nfigure,\nfieldset,\nlegend,\ntextarea,\npre,\niframe,\nhr,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  margin: 0;\n  padding: 0;\n}\n\n* {\n  margin: 0;\n  padding: 0;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  font-size: 100%;\n  font-weight: normal;\n}\n\nul {\n  list-style: none;\n}\n\nbutton,\ninput,\nselect,\ntextarea {\n  margin: 0;\n}\n\nhtml {\n  -webkit-box-sizing: border-box;\n  box-sizing: border-box;\n}\n\n*, *:before, *:after {\n  box-sizing: inherit;\n}\n\nimg,\nembed,\niframe,\nobject,\naudio,\nvideo {\n  height: auto;\n  max-width: 100%;\n}\n\niframe {\n  border: 0;\n}\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n  text-align: left;\n}\n\nb,\nstrong {\n  font-weight: bolder;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n  -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n  border-style: none;\n  padding: 0;\n}\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n  box-sizing: border-box; /* 1 */\n  padding: 0; /* 2 */\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n[type=\"search\"] {\n  -webkit-appearance: textfield; /* 1 */\n  outline-offset: -2px; /* 2 */\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n.fl{\n  float: left;\n}\n\n.fr{\n  float: right;\n}\n\n\n\n/* 动画 */\n@-webkit-keyframes slideUp{\n  from{\n    -webkit-transform:translate3d(0, 100%, 0);\n            transform:translate3d(0, 100%, 0);\n  }\n  to{\n    -webkit-transform:translate3d(0, 0, 0);\n            transform:translate3d(0, 0, 0);\n  }\n}\n@keyframes slideUp{\n  from{\n    -webkit-transform:translate3d(0, 100%, 0);\n            transform:translate3d(0, 100%, 0);\n  }\n  to{\n    -webkit-transform:translate3d(0, 0, 0);\n            transform:translate3d(0, 0, 0);\n  }\n}\n.animate-slide-up{\n  -webkit-animation:slideUp ease .3s forwards;\n          animation:slideUp ease .3s forwards;\n}\n@-webkit-keyframes slideDown{\n  from{\n    -webkit-transform:translate3d(0, 0, 0);\n            transform:translate3d(0, 0, 0);\n  }\n  to{\n    -webkit-transform:translate3d(0, 100%, 0);\n            transform:translate3d(0, 100%, 0);\n  }\n}\n@keyframes slideDown{\n  from{\n    -webkit-transform:translate3d(0, 0, 0);\n            transform:translate3d(0, 0, 0);\n  }\n  to{\n    -webkit-transform:translate3d(0, 100%, 0);\n            transform:translate3d(0, 100%, 0);\n  }\n}\n.animate-slide-down{\n  -webkit-animation:slideDown ease .3s forwards;\n          animation:slideDown ease .3s forwards;\n}\n@-webkit-keyframes fadeIn{\n  from{\n    opacity:0;\n  }\n  to{\n    opacity:1;\n  }\n}\n@keyframes fadeIn{\n  from{\n    opacity:0;\n  }\n  to{\n    opacity:1;\n  }\n}\n.animate-fade-in{\n  -webkit-animation:fadeIn ease .3s forwards;\n          animation:fadeIn ease .3s forwards;\n}\n@-webkit-keyframes fadeOut{\n  from{\n    opacity:1;\n  }\n  to{\n    opacity:0;\n  }\n}\n@keyframes fadeOut{\n  from{\n    opacity:1;\n  }\n  to{\n    opacity:0;\n  }\n}\n.animate-fade-out{\n  -webkit-animation:fadeOut ease .3s forwards;\n          animation:fadeOut ease .3s forwards;\n}\n\n/* 滚动条 */\n::-webkit-scrollbar {\n  width: 5px;\n  height: 5px;\n  -webkit-border-radius: 6px;\n  border-radius: 6px;\n  /*background: #e4e7ed;*/\n  /*background-color: rgba(0, 0, 0, 0.1)*/\n}\n\n::-webkit-scrollbar-track {\n  /*background: #f5f7fa;*/\n  -webkit-border-radius: 6px;\n  border-radius: 6px;\n}\n\n::-webkit-scrollbar-track-piece {\n  /*background-color: rgba(0, 0, 0, 0.2);\n  -webkit-border-radius: 6px;*/\n}\n\n::-webkit-scrollbar-thumb {\n  background: #c0c4cc;\n  -webkit-border-radius: 6px;\n  border-radius: 6px;\n  background-color: rgba(0, 0, 0, 0.1)\n}\n\n/* 省略号 ...*/\n\n  .ellipsis {\n      display: block;\n      overfolw: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n  }\n\n\n/* 控制几行显示,超出显示省略号 */\n\n  .line-clam{\n    display: -webkit-box;\n    -webkit-box-orient: vertical;\n    -webkit-line-clamp: 3;\n    white-space: pre-wrap;\n    word-break: break-all;\n    overflow: hidden;\n  }\n\n\n/* 上边框(0.5px), 伪元素写法 */\n\n  .border-top {\n    position: relative;\n  }\n  .border-top::before {\n    position: absolute;\n    left: 0;\n    top: 0;\n    right: 0;\n    height: 1px;\n    border-top: 1px solid #e5e5e5;\n    color: #e5e5e5;\n    -webkit-transform-origin: 0 0;\n    transform-origin: 0 0;\n    -webkit-transform: scaleY(0.5);\n    transform: scaleY(0.5);\n    z-index: 2;\n  }\n\n  \n/* 下边距(0.5px) ,伪元素 */\n\n  .border-bottom {\n    position: relative;\n  }\n  .border-bottom:after {\n    content: \" \";\n    position: absolute;\n    left: 0;\n    bottom: 0;\n    right: 0;\n    height: 1px;\n    border-bottom: 1px solid #e5e5e5;\n    color: #e5e5e5;\n    -webkit-transform-origin: 0 100%;\n    transform-origin: 0 100%;\n    -webkit-transform: scaleY(0.5);\n    transform: scaleY(0.5);\n    z-index: 2;\n  }\n\n/* 左边距(0.5px) ,伪元素 */\n\n  .border-left::after {\n    content: \" \";\n    position: absolute;\n    left: 0;\n    bottom: 0;\n    top: 0;\n    width: 0px;\n    border-left: 1px solid #e3e5e9;\n    color: #e3e5e9;\n    -webkit-transform-origin: 0 0;\n    transform-origin: 0 0;\n    -webkit-transform: scaleX(0.5);\n    transform: scaleX(0.5);\n  }\n\n/* 右边距(0.5px) ,伪元素 */\n\n  :after {\n    content: \" \";\n    position: absolute;\n    right: 0;\n    top: 0;\n    width: 1px;\n    bottom: 0;\n    border-right: 1px solid #e5e5e5;\n    color: #e5e5e5;\n    -webkit-transform-origin: 100% 0;\n    transform-origin: 100% 0;\n    -webkit-transform: scaleX(0.5);\n    transform: scaleX(0.5);\n  }\n\n\n/* 边框 (0.5px) */\n\n  .btn:after {\n    content: \" \";\n    width: 200%;\n    height: 200%;\n    position: absolute;\n    top: 0;\n    left: 0;\n    border: 1px solid rgba(0, 0, 0, 0.2);\n    -webkit-transform: scale(0.5);\n    transform: scale(0.5);\n    -webkit-transform-origin: 0 0;\n    transform-origin: 0 0;\n    box-sizing: border-box;\n    border-radius: 10px;\n  }\n  \n/* 右箭头 > */\n  \n  :before {\n    content: \" \";\n    display: inline-block;\n    height: 6px;\n    width: 6px;\n    border-width: 2px 2px 0 0;\n    border-color: #C8C8CD;\n    border-style: solid;\n    -webkit-transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0);\n    transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0);\n    position: relative;\n    top: -2px;\n    position: absolute;\n    top: 50%;\n    right: 15px;\n    margin-top: -4px;\n  }\n  \n\n/* 三角线箭头 如 ∧∨,*/\n  \n  .vue-treeselect__control-arrow-container {\n    position: relative;\n  }\n\n  .vue-treeselect__control-arrow-container:after, .vue-treeselect__control-arrow-container:before {\n    border: 6px solid transparent;\n    border-left: 6px solid #fff;\n    width: 0;\n    height: 0;\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    margin-left: -6px;\n    content: ' ';\n    margin-top: -3px;\n    -webkit-transform: rotate(90deg);\n    -moz-transform: rotate(90deg);\n    -o-transform: rotate(90deg);\n    transform: rotate(90deg);\n    -webkit-transition: -webkit-transform .3s;\n    transition: -webkit-transform .3s;\n    transition: transform .3s;\n    transition: transform .3s, -webkit-transform .3s;\n    transition: transform .3s,-webkit-transform .3s;\n  }\n\n  .vue-treeselect__control-arrow-container:before {\n    border-left-color: #c0c4cc;\n    top: 52%;\n  }\n\n  .vue-treeselect--focused .vue-treeselect__control-arrow-container:after, .vue-treeselect--focused .vue-treeselect__control-arrow-container:before {\n    margin-top: -6px;\n    -webkit-transform: rotateZ(-90deg);\n    -moz-transform: rotateZ(-90deg);\n    -o-transform: rotateZ(-90deg);\n    transform: rotateZ(-90deg);\n    -webkit-transition: -webkit-transform .3s;\n    transition: -webkit-transform .3s;\n    transition: transform .3s;\n    transition: transform .3s, -webkit-transform .3s;\n    transition: transform .3s,-webkit-transform .3s;\n  }\n\n  .vue-treeselect--focused .vue-treeselect__control-arrow-container:before {\n    border-left-color: #c0c4cc;\n    top: 48%;\n  }\n  \n\n/* 虚线,可控制间隔 */\n.dash { \n  width: 100%;\n  height: 1px;\n  background-image: linear-gradient(to right, #ccc 0%, #ccc 50%, transparent 50%);\n  background-size: 8px 1px;\n  background-repeat: repeat-x;\n}\n\n/* 边距 */\n.ml-10 {\n  margin-left: 10px;\n}\n\n.mr-10 {\n  margin-right: 10px;\n}\n\n.mb-10 {\n  margin-bottom: 10px;\n}\n\n.mt-10 {\n  margin-top: 10px;\n}\n\n.pl-10 {\n  padding-left: 10px;\n}\n\n.pr-10 {\n  padding-right: 10px;\n}\n\n.pb-10 {\n  padding-bottom: 10px;\n}\n\n.pt-10 {\n  padding-top: 10px;\n}\n\n/* 颜色 */\n.text-white {\n  color: #fff;\n}\n\n.text-black {\n  color: #000;\n}\n\n.text-red {\n  color: #ff0000;\n}\n\n.text-green {\n  color: #00ff00;\n}\n\n.text-yellow {\n  color: #ffff00;\n}\n\n.text-blue {\n  color: #0000ff;\n}\n\n.bg-white {\n  color: #fff;\n}\n\n.bg-black {\n  color: #000;\n}\n\n.bg-red {\n  color: #ff0000;\n}\n\n.bg-green {\n  color: #00ff00;\n}\n\n.bg-yellow {\n  color: #ffff00;\n}\n\n.bg-blue {\n  color: #0000ff;\n}\n\n/* 阴影 */\n.shadow {\n   -moz-box-shadow:2px 2px 5px #333333;\n   -webkit-box-shadow:2px 2px 5px #333333;\n   box-shadow:2px 2px 5px #333333;\n}\n\n/* 圆角 */\n.radius-2 {\n  -moz-border-radius: 2px;\n  -webkit-border-radius: 2px;\n  border-radius: 2px;\n}\n\n.radius-4 {\n  -moz-border-radius: 4px;\n  -webkit-border-radius: 4px;\n  border-radius: 4px;\n}\n\n.radius-half {\n  -moz-border-radius: 50%;\n  -webkit-border-radius: 50%;\n  border-radius: 50%;\n}\n\n```\n"
  },
  {
    "path": "3.1  渲染树构建、布局及绘制.md",
    "content": "\n# 渲染树构建、布局及绘制\n\n>CSSOM 树和 DOM 树合并成渲染树，然后用于计算每个可见元素的布局，并输出给绘制流程，将像素渲染到屏幕上。优化上述每一个步骤对实现最佳渲染性能至关重要。\n\nTL;DR\n\nDOM 树与 CSSOM 树合并后形成渲染树。\n渲染树只包含渲染网页所需的节点。\n布局计算每个对象的精确位置和大小。\n最后一步是绘制，使用最终渲染树将像素渲染到屏幕上。\n\n- 第一步是让浏览器将 DOM 和 CSSOM 合并成一个“渲染树”，网罗网页上所有可见的 DOM 内容，以及每个节点的所有 CSSOM 样式信息。\n\n为构建渲染树，浏览器大体上完成了下列工作：\n\n- 1.从 DOM 树的根节点开始遍历每个可见节点。\n\n  - 某些节点不可见（例如脚本标记、元标记等），因为它们不会体现在渲染输出中，所以会被忽略。  \n  - 某些节点通过 CSS 隐藏，因此在渲染树中也会被忽略，  \n  例如，上例中的 span 节点---不会出现在渲染树中，---因为有一个显式规则在该节点上设置了“display: none”属性。  \n- 2.对于每个可见节点，为其找到适配的 CSSOM 规则并应用它们。  \n- 3.发射可见节点，连同其内容和计算的样式\n\n\n最终输出的渲染同时包含了屏幕上的所有可见内容及其样式信息。有了渲染树，我们就可以进入“布局”阶段。\n\n\n“Layout”事件在时间线中捕获渲染树构建以及位置和尺寸计算。  \n布局完成后，浏览器会立即发出“Paint Setup”和“Paint”事件，将渲染树转换成屏幕上的像素。\n\n\n## position属性\n\n```\n- static： 使用正常的布局行为，即元素在文档常规流中当前的布局位置\n- relative: 相对定位; 相对于自身\n- absolute: 绝对定位;  相对于第一个父元素\n- fixed: 固定定位：相对于屏幕视口 ; **旧版本IE不支持**\n- position: -webkit-sticky;   粘性定位，元素在跨越特定阈值前为相对定位，之后为固定定位\nposition: sticky;  **兼容性**\n```\n## position跟display、margin collapse、overflow、float这些特性相互叠加\n\n- 1.'display'、'position' 和 'float' 的相互关系\n\n![](http://www.w3help.org/zh-cn/kb/009/009/display_float_position.png)\n\n\n\n- 参考： [position跟display、margin collapse、overflow、float](https://www.cnblogs.com/jackyWHJ/p/3756087.html)\n\n## 参考\n- [render-tree-construction](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction?hl=zh-cn)\n"
  },
  {
    "path": "3.1 css到cssom 过程.md",
    "content": "# 3.1 css--->cssom 过程\n\n>cssom: CSS Object Model\n\n\n## dom 构建流程\n\n- 1.转换： 浏览器从磁盘或网络读取 HTML 的原始字节，并根据文件的指定编码（例如 UTF-8）将它们转换成各个字符。\n\n- 2.令牌化： 浏览器将字符串转换成 W3C HTML5 标准规定的各种令牌，  \n  例如，“<html>”、“<body>”，以及其他尖括号内的字符串。每个令牌都具有特殊含义和一组规则。\n- 3.词法分析： 发出的令牌转换成定义其属性和规则的“对象”。\n- 4.DOM 构建： 最后，由于 HTML 标记定义不同标记之间的关系（一些标记包含在其他标记内），  \n  创建的对象链接在一个树数据结构内，此结构也会捕获原始标记中定义的父项-子项关系：  \n  HTML 对象是 body 对象的父项，body 是 paragraph 对象的父项，依此类推。\n\n\n## cssom \n\n在浏览器构建我们这个简单页面的 DOM 时，在文档的 head 部分遇到了一个 link 标记，该标记引用一个外部 CSS 样式表：style.css。  \n由于预见到需要利用该资源来渲染页面，它会立即发出对该资源的请求，并返回以下内容：\n\n```\nbody { font-size: 16px }\np { font-weight: bold }\nspan { color: red }\np span { display: none }\nimg { float: right }\n\n```\n\n我们本可以直接在 HTML 标记内声明样式（内联），但让 CSS 独立于 HTML 有利于我们将内容和设计作为独立关注点进行处理：  \n设计人员负责处理 CSS，开发者侧重于 HTML，等等。\n\n与处理 HTML 时一样，我们需要将收到的 CSS 规则转换成某种浏览器能够理解和处理的东西。  \n因此，我们会重复 HTML 过程，不过是为 CSS 而不是 HTML：\n\n```\nCSSOM 构建步骤:\n\n Bytes  ---> characters   --->   Tokens ---> Nodes ---> cssom\n```\n\n- **CSS 字节转换成字符，接着转换成令牌和节点，最后链接到一个称为“CSS 对象模型”(CSSOM) 的树结构内**：\n\nCSSOM 树:\n\n![](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/images/cssom-tree.png?hl=zh-cn)\n\nCSSOM 为何具有树结构？为页面上的任何对象计算最后一组样式时，浏览器都会先从适用于该节点的最通用规则开始（例如，如果该节点是 body 元素的子项，则应用所有 body 样式），然后通过应用更具体的规则（即规则“向下级联”）以递归方式优化计算的样式。\n\n以上面的 CSSOM 树为例进行更具体的阐述。span 标记内包含的任何置于 body 元素内的文本都将具有 16 像素字号，并且颜色为红色 — font-size 指令从 body 向下级联至 span。不过，如果某个 span 标记是某个段落 (p) 标记的子项，则其内容将不会显示。\n\n还请注意，以上树并非完整的 CSSOM 树，它只显示了我们决定在样式表中替换的样式。每个浏览器都提供一组默认样式（也称为“User Agent 样式”），即我们不提供任何自定义样式时所看到的样式，我们的样式只是替换这些默认样式（例如默认 IE 样式）。\n\n要了解 CSS 处理所需的时间，您可以在 DevTools 中记录时间线并寻找“Recalculate Style”事件：与 DOM 解析不同，该时间线不显示单独的“Parse CSS”条目，而是在这一个事件下一同捕获解析和 CSSOM 树构建，以及计算的样式的递归计算。\n\n在 DevTools 中追踪 CSSOM 构建\n\n我们的小样式表需要大约 0.6 毫秒的处理时间，影响页面上的 8 个元素 — 虽然不多，但同样会产生开销。\n\n不过，这 8 个元素从何而来呢？CSSOM 和 DOM 是独立的数据结构！结果证明，浏览器隐藏了一个重要步骤。\n\n\n DOM 与 CSSOM 关联在一起的渲染树。\n\n## 参考\n- [/constructing-the-object-model](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/constructing-the-object-model?hl=zh-cn)\n"
  },
  {
    "path": "3.1.1 两列布局左边定宽，右边自适应.md",
    "content": "# 两列布局左边定宽，右边自适应。\n\n* 1 采用position：absollute;并设置margin-left的值\n\n```\n#left{\n    position:absolute;\n    width:300px;\n    top:0px;\n    left:0px;\n    background:#F00;\n}\n#right{\n    background:#0FC;\n    margin-left:300px;\n}\n<div id=\"left\">左边定宽</div> <div id=\"right\">右边自适应</div>\n\n\n```\n\n* 2 采用float;并设置overflow:auto;(隐藏溢出的内容)\n\n```\n#left {\n    float: left;\n    width: 300px;\n    background-color: blue;\n}\n#right {\n    overflow: auto;\n    background-color: red;\n}\n\n<div id=\"left\">左边自适应</div>\n<div id=\"right\">右边定宽</div>\n```\n\n* 3 左边和右边都用绝对定位\n\n```\ndiv.left{ position: absolute; left: 0;top: 0; width: 200px;height: 400px;background: red; word-break: break-all;}\ndiv.right{position: absolute; left: 200px;top: 0; height: 400px;  background: blue; word-break: break-all;}\n```\n"
  },
  {
    "path": "3.1.1.1 左侧自适应,右侧自适应布局.md",
    "content": "# 左侧自适应,右侧自适应布局\n\n```\n<div class=\"common-wrap__currentTag inline-block\">\n      <a :href=\"'#/'+tagLibName+'Lib'\" class=\"common-wrap__currentTag__name\">全部</a>\n      <i class=\"el-icon-arrow-right m-l-8 m-r-8\"></i>\n      <template v-for=\"(item,index) in currentTagDatas\">\n        <a :href=\"'#/'+tagLibName+'List?id='+item.id\" class=\"common-wrap__currentTag__name\">{{item.name}}</a>\n        <i class=\"el-icon-arrow-right m-l-8 m-r-8\" v-if=\"currentTagDatas.length-1 != index\"></i>\n      </template>\n    </div>\n    <div class=\"common-wrap__childTag inline-block\">\n      <template v-for=\"(item,index) in childTagDatas\">\n        <a href=\"\" class=\"common-wrap__childTag__name\">{{item.name}}</a>\n      </template>\n    </div>\n\n\n\n.common-wrap__cateTags {\n  position: relative;\n  width: 100%;\n  height: 45px;\n  margin-bottom: 20px;\n  border-bottom: 1px solid #EBEEF5;\n}\n\n.common-wrap__cateTags .common-wrap__currentTag {\n  white-space: nowrap;\n  font-size: 0;\n  color: #303133;\n}\n\n.common-wrap__currentTag .common-wrap__currentTag__name {\n  font-size: 14px;\n  color: #303133;\n  font-weight: 700;\n}\n\n.common-wrap__currentTag i {\n  font-size: 14px;\n  color: #909399;\n}\n\n.common-wrap__cateTags .common-wrap__childTag {\n  /*max-width: calc(100% - 280px);*/\n  vertical-align: middle;\n  margin-left: 6px;\n  padding-left: 10px;\n  font-size: 14px;\n  color: #606266;\n  border-left: 1px solid #979797;\n  white-space: nowrap;\n  overflow: hidden;\n  /*text-overflow: ellipsis;*/\n}\n\n.common-wrap__childTag .common-wrap__childTag__name{\n  margin-right: 10px;\n  padding: 2px 5px;\n  font-size: 14px;\n  color: #606266;\n  background: #F0F2F5;\n  border-radius: 2px;\n}\n\n```\n\n\n## 参考\n- \n"
  },
  {
    "path": "3.1.2 H5 页面在 ios 端滑动不流畅的问题.md",
    "content": "# H5页面在 ios 端滑动不流畅的问题\n\nIOS系统的惯性滑动效果非常6，但是当我们对div加overflow-y:auto;后是不会出这个效果的，滑动的时候会感觉很生涩。怎么办？\n\n方案一：\n\n在滚动容器内加-webkit-overflow-scrolling: touch\n\n但这个方案有一个问题，在页面内具有多个overflow：auto的情况下，那些具有 绝对定位（absolute, fixed） 属性的元素，也会跟着滚动。\n\n方案二：\n\nbody {overflow-x: hidden}\n\n即，给 body 元素添加overflow-x：hidden。然后在滚动容器内加overflow-y: auto\n\n"
  },
  {
    "path": "3.1.3 移动端 ios scroll 滚动穿透.md",
    "content": "# ios scroll 滚动穿透\n\n在 ios 微信浏览器或原生浏览器中，主内容容器.content在文档流内，并且overflow-y: scroll。\n\n在其之上有一个 fixed 定位的弹出层.popUp，滚动.popUp到底部，继续滚动会触发底层容器.content开始滚动。\n\n期望结果\n\n滚动弹出层.popUp,底层容器.content不会触发滚动\n\n解决方案:\n\ngoogle搜的方案基本上都不能完全解决问题......  \n经过各种尝试，下面方法可以达到预期效果：  \n1） 弹出层显示时，改变容器.content的css属性：overflow-y: hidden；  \n2） 弹出层消失时，恢复容器.content的css属性：overflow-y: scroll；  \n"
  },
  {
    "path": "3.1.4 安卓浏览器键盘输入改变弹出层的定位.md",
    "content": "# 安卓浏览器键盘输入改变弹出层的定位\n\n在安卓浏览器中，有一个在页面底部的弹出层表单，样式如下：\n\n```\n.popup {\n\n    position: absolute;\n    bottom: 0;\n}\n```\n当在这个弹出层输入内容，键盘自动弹出，弹出层的bottom值会改变成键盘的高度，有可能使输入框超出屏幕的高度。\n\nbug解决:\n\n给弹出层添加一个高度\n\n```\n.popup {\n    position: absolute;\n    bottom: 0;\n    max-height: 80%;\n}\n```\n"
  },
  {
    "path": "3.1.5 CSS中margin边界叠加问题及解决方案.md",
    "content": "# CSS中margin边界叠加问题及解决方案\n\n\n别人的博客说的情况：https://www.cnblogs.com/zhangmingze/articles/4664074.html\n- http://www.hujuntao.com/web/css/css-margin-overlap.html\n\n边界叠加的大多数问题可以通过添加透明边框或1px的补白来修复。\n```\n外层元素padding代替\n内层元素透明边框 border:1px solid transparent;\n内层元素绝对定位 postion:absolute:\n外层元素 overflow:hidden;\n内层元素 加float:left;或display:inline-block;\n内层元素padding:1px;\n```\n\n补充解决方案：\n\n1.外层padding\n\n2.透明边框border:1px solid transparent;\n\n3.绝对定位postion:absolute:\n\n4.外层DIVoverflow:hidden;\n\n5.内层DIV　加float:left;display:inline;\n\n6.外层DIV有时会用到zoom:1;\n\n7. 设置 display:inline-block的元素，垂直margin不会重叠，甚至和他们的子元素之间也是一样。\n"
  },
  {
    "path": "3.1.6 css clip-path 画聊天气泡.md",
    "content": "# css clip-path\n\n聊天气泡小角实现  \n可以由两种实现的方式  \n\n- canvas，绘制路径，然后再clip图片,本文暂不描述\n- 另一种就是利用css3的新属性clip-path属性，绘制出要切割的路径，然后再给clip-path属性赋值,火狐和IE未实现此属性\n\n\n\nCan I use: https://caniuse.com/#search=css%20clip\n\n```\n/* Keyword values */\nclip-path: none;\n\n/* Image values */ \nclip-path: url(resources.svg#c1);\n\n/* Box values */\nclip-path: fill-box;\nclip-path: stroke-box;\nclip-path: view-box;\nclip-path: margin-box;\nclip-path: border-box;\nclip-path: padding-box;\nclip-path: content-box;\n\n/* Geometry values */\nclip-path: inset(100px 50px);\nclip-path: circle(50px at 0 100px);\nclip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);\n\n/* Box and geometry values combined */\nclip-path: padding-box circle(50px at 0 100px);\n\n/* Global values */\nclip-path: inherit;\nclip-path: initial;\nclip-path: unset;\n```\n\n\n## 参考\n* https://css-tricks.com/clipping-masking-css/\n* MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path\n* 张鑫旭: http://www.zhangxinxu.com/wordpress/2014/12/css3-svg-clip-path/\n"
  },
  {
    "path": "3.1.7 css currentColor.md",
    "content": "# currentColor\n\n阮一峰： https://github.com/wangdoc/css-tutorial/blob/master/docs/color.md\n\n## currentColor\n\n`currentColor`是一个属性值，代表当前元素的`color`属性的值。\n\n```css\n.box {\n  color: red;\n  border: 1px solid currentColor;\n  box-shadow: 0 0 2px 2px currentColor;\n}\n```\n\n上面代码，`border`和`box-shadow`的颜色都与`color`保持一致。这种写法的好处是，如果要修改`color`，只要修改一处就可以了，不用修改三个地方。\n\n`currentColor`的另一个用处，是让衍生类都继承基类的颜色。\n\n```css\n.box {\n    color: red;\n}\n.box .child-1 {\n    background: currentColor;\n}\n.box .child-2 {\n    color: currentColor;\n    border 1px solid currentColor;\n}\n```\n\n伪元素也可以使用`currentColor`。\n\n```css\n.box {\n    color: red;\n}\n.box:before {\n    color: currentColor;\n    border: 1px solid currentColor;\n}\n```\n\n\n参考： http://www.qcyoung.com/2016/09/28/%E3%80%90%E8%AF%91%E3%80%91%E4%BD%BF%E7%94%A8%20currentColor%20%E5%B1%9E%E6%80%A7%E5%86%99%E5%87%BA%E6%9B%B4%E5%A5%BD%E7%9A%84%20CSS%20%E4%BB%A3%E7%A0%81/\n"
  },
  {
    "path": "3.1.8 css flexbox.md",
    "content": "# flexbox\n\n# 阮一峰\n\nhttps://github.com/wangdoc/css-tutorial/blob/master/docs/layout/flexbox.md\n\n"
  },
  {
    "path": "3.1.8.1 flex-wrap不兼容解决方案.md",
    "content": "# flex-wrap不兼容解决方案\n\n```\n-webkit-flex-wrap: wrap;\n-moz-flex-wrap: wrap;\n-ms-flex-wrap: wrap;\n-o-flex-wrap: wrap;\nflex-wrap: wrap;\n```\n\n\n目前在 `ios 10.2` 以下版本会有问题，不会换行\n\n最后使用 `float` 或者 `display: inline-block` 替代\n\n- 使用 `float`,注意清除浮动, `overflow: hidden;`\n- 使用 `display: inline-block` 注意编辑器空白 `font-size: 0;`\n\n## 参考\n- [flex-wrap不兼容解决方案](https://github.com/semi-xi/blog/issues/10)\n"
  },
  {
    "path": "3.1.9 css 属性选择器，伪类，伪元素.md",
    "content": "# css 属性选择器，伪类，伪元素\n\n\n* 属性选择器\n\n  [id*=content]: * 这里代表属性 id 的属性值包含字符 content 的元素,如：\n  ```\n    <div id=\"content\"></div>\n    <div id=\"content2\"></div>\n    <div id=\"content3\"></div>\n  ```\n  [id^=content]: * 这里代表属性 id 的属性值开头是字符 content 的元素,如：  \n  [id$=content]: * 这里代表属性 id 的属性值结尾是字符 content 的元素,如：  \n  \n* 结构性伪类选择器\n\n  - 伪元素： first-line,first-letter,before,after\n  - 伪类：  \n    root: 根元素     \n    not: 排除结构元素下的子元素    \n    empty: 指定元素空白时样式    \n    target: 页面在用户点击超链接，跳转到 target 元素后        \n    ，     \n    first-child    \n    last-child  \n    nth-child  (odd:奇数，even:偶数)  \n    nth-last-child  \n    nth-of-type  (odd:奇数，even:偶数)  \n    only-child  ： 父元素只有一个元素  \n    ，     \n* UI 元素状态伪类选择器\n  - hover\n  - active\n  - focus\n  - enabled\n  - disabled\n  - read-only\n  - read-write\n  - checked\n  - ::selection\n  - default\n  - valid\n  - invalid\n  - indeterminate\n  - required\n  - optional\n  - in-range\n  - out-of-range\n    \n"
  },
  {
    "path": "3.2.1. 使用 css3 缩小字体不偏移.md",
    "content": "# 使用 css3 缩小字体不偏移\n\n通常使用 css3 缩小字体后，文字部分会偏移位置，如，原来左对齐，缩小后发现距离左侧有一段距离，需要设置 transform-origin-x:0\n\n测试使用时对块级元素有效，行内元素无效\n\n```\n    \n    -webkit-transform-origin-x: 0;\n    transform: scale(0.5);\n    -webkit-transform: scale(0.5);\n```\n"
  },
  {
    "path": "3.2.2. css3字体缩放样式-webkit-text-size-adjust的用法.md",
    "content": "# css3字体缩放样式-webkit-text-size-adjust的用法详解\n\n就是当你放大网页时，一般情况下字体也会随着变大，而设置了以下代码后，字体只会显示你当前设置的字体大小，不会随着网页放大而变大了\n\n```\nhtml{-webkit-text-size-adjust: none;}\n```\n\n\n1、当样式表里font-size<12px时，中文版chrome浏览器里字体显示仍为12px，这时可以用 html{-webkit-text-size-adjust:none;} \n\n2、-webkit-text-size-adjust放在body上会导致页面缩放失效 \n\n3、body会继承定义在html的样式 \n\n4、用-webkit-text-size-adjust不要定义成可继承的或全局的\n"
  },
  {
    "path": "3.2.3. CSS 变量.md",
    "content": "# CSS变量\n\n* MDN: https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_variables\n* Can I USE: https://caniuse.com/#search=CSS%20Variables\n\n使用自定义属性来设置变量名，并使用特定的 var() 来访问。（比如  color: var(--main-color);）\n\n* 基本用法 Edit\n```\n声明一个局部变量：\n\nelement {\n  --main-bg-color: brown;\n} \n使用一个局部变量：\n\nelement {\n  background-color: var(--main-bg-color);\n}\n\n------------------------------------------------------\n\n声明一个 全局 CSS 变量：\n\n:root {\n  --global-color: #666;\n  --pane-padding: 5px 42px;\n}\n使用一个 全局 CSS 变量：\n\n.demo{\n   color: var(--global-color);\n}\n\n```\n\n\n1、CSS颜色属性的设置和表示方法\n\n```\n[css] view plain copy\n<style type=\"text/css\">  \n    #div{  \n        color: red;/*关键字表示法*/  \n        color: #f00;/*短-16进制表示法*/  \n        color: #ff0000;/*长-16进制表示法*/  \n        color: rgb(255, 0, 0);/*rgb基本表示法*/  \n        color: rgb(100%, 0, 0);/*rgb百分比表示法*/  \n        color: hsl(240, 0%, 50%);/*色彩三属性表示法表示法，下文具体解释hsl含义*/  \n        color: color(#29B4F0 a(50%) contrast(%10) h(10));/*颜色函数表示法，请看下文具体解释*/  \n    }  \n</style>  \n```\n2、CSS自定义变量  \n    声明语法：--*，如--color-basis: red;\n\n    调用语法：var(--color-basis)，如color: var(--color-basis);\n\n    位置：可以放在根选择器中，也可以放在常规选择器中\n\n引申：CSS3根选择器，:root选择器匹配文档根元素，在HTML中，根元素始终是html元素，语法如下：\n```\n:root{\n\n--color-basis: red;//定义一个CSS变量\n\nbackground: #f00;//设置html元素背景色\n\n}\n```\n\n\n3、CSS颜色函数\n```\n    CSS Color Module Level 4的颜色配置函数有：color()、gray()、hsl()、hwb()等，这些函数浏览器暂时不支持，实际开发需要引入第三方插件（postcss/cssnext）来做预处理。\n\n    color-mod()函数详解：\n\n    color-mod() = color(#29B4F0 a() s() h() l() tint() shade() w() b() contrast());\n\n    调用语法：\n\n    #div{background-color:color(#29B4F0 a() s() h() l() tint() shade() w() b() contrast() blend());}\n```\n第一个参数基准色值，必须设定；\n\n第二个参数及后面的参数为可选参数，这些参数的详细含义如下：\n```\n    a-alpha，透明度，值为百分比；\n\n    b-blackness，黑度，值为百分比；\n\n    blend，混合度，值为百分比;\n\n    contrast，对比度，值为百分比；    \n\n    h-hue，色相-色彩的第一属性，色彩的相貌区别；0-360deg，0和360是红色，接近120的是绿色，240是蓝色；\n\n    l-lightness，明度，亮度-色彩的第二属性，表明色彩的明暗性质； 0%是最暗，50%均值，100%最亮。\n\n    s-saturation，纯度，饱和度-色彩的第三属性，表明色彩的鲜灰程度；0%是灰度，100%饱和度最高 ;\n\n    shade，暗度，值为百分比；\n\n    tint，色调，值为百分比；\n\n    w-whiteness，白度，值为百分比；\n```\n\n\n4、综合实例\n\n```\n[css] view plain copy\n<style type=\"text/css\">  \n    :root{  \n        --color-basis: red;  \n        --mytheme-p-color: var(--color-basis);  \n    }  \n    p{  \n        color:color(var(--mytheme-p-color) a(50%) hue(+30deg));//p元素dom数组集合中，每个元素的hue递增30deg，等同于hue += 30  \n    }  \n</style>  \n```\n5、扩展\n\n    CSS函数：\n\nattr();返回选择元素的属性值\n\ncalc();计算CSS属性值\n```\nlinear-gradient();创建一个线性渐变的图像  \nradial-gradient();用径向渐变创建图像\nrepeating-linear-gradient();用重复的线性渐变创建图像\nrepeating-radial-gradient();类似 radial-gradient()，用重复的径向渐变创建图像。\n```\n6、当前文字颜色属性\n\n如：border: 1px solid currentColor;//currentColor是CSS3的一个变量\n"
  },
  {
    "path": "3.2.4 安卓 line-height 问题.md",
    "content": "## 安卓 line-height 问题\n\n```\n 如果写 height: 15px;\n       line-height: 15px;\n       font-size: 13px;\n       \n       ios 表现正常居中,android 位置偏了\n       \n 改为 width: 16px;\n      height: 16px;\n      line-height: 16px;\n     ,android 正常\n \n \n 说是 font-size 字数大小不要用奇数\n \n 有时不行,js 判断 iOS Android\n var u = navigator.userAgent;\n var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端\n var isiOS = !!u.match(/\\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端\n```\n\n### 网上说法:\n\n```\n仔细观察 这个android上line-height问题的一些看法：\n\n字体大小不要使用奇数字号，带小数点的更不要提了。也就是说被2整除的整数且不可小于12px。\n尽量不要使用rem或者em的单位，除非你能对字号的把握在每个手机上都能达到第1条的要求。\n别外，微信小程序上的rpx是动态计算的，可以单独针对这部分使用px。\n\n一般只要不是边框贴着文字，一般都还看得过去，在接受的范围之内。如果是边框贴着文字，就是居中了，也不好看啊。\n那么，对于小于12像素的居中或者是对居中要求很是严格的需求，这个问题怎么解决？\n\n把字号内外边距等设置为需求大小的2倍，使用transform进行缩放。只能针对 单个或者是一排的布局进行缩放，\n如果是父级自适应高度且可展示多行的，使用transform是有问题的。因为transform缩放是不影响页面布局的。\n把字号内外边距等设置为需求大小的2倍，使用zoom进行缩放，可以完美解决。zoom原本只有ie支持，\n但现在除了Firefox，都已经支持了。是的，Firefox不支持。\n```\n"
  },
  {
    "path": "3.2.5 css grid.md",
    "content": "# css grid: CSS 网格布局(Grid Layout)\n\n* caniuse: 浏览器支持的详细数据可在 [Caniuse查看](https://caniuse.com/#feat=css-grid)\n\ndisplay：grid把容器元素定义为一个网格  \n使用grid-template-columns和grid-template-rows设置列和行大小，  \n然后使用grid-column 和 grid-row把它的子元素放入网格  \n\n#### Grid 属性列表\n##### Grid Container 的全部属性\n\n- display\n- grid-template-columns\n- grid-template-rows\n- grid-template-areas\n- grid-template\n- grid-column-gap\n- grid-row-gap\n- grid-gap\n- justify-items\n- align-items\n- justify-content\n- align-content\n- grid-auto-columns\n- grid-auto-rows\n- grid-auto-flow\n- grid\n#### Grid Items 的全部属性\n\n- grid-column-start\n- grid-column-end\n- grid-row-start\n- grid-row-end\n- grid-column\n- grid-row\n- grid-area\n- justify-self\n- align-self\n\n### 使用\n>父容器(Grid Container)\n```\n.container {\n  display: grid | inline-grid | subgrid;\n}\n注意：column, float, clear, 以及 vertical-align 对一个 grid container 没有影响\n```\n\n>grid-template-columns / grid-template-rows\n\n- grid-template-columns: 100px 100px 100px; // 设置的列的个数为 3 ,列的宽度为 100px\n- grid-template-rows: 100px 100px 100px; // 设置的列的个数为 3 ,行的宽度为 100px\n\n使用以空格分隔的多个值来定义网格的列和行。这些值表示轨道大小(track size)，它们之间的空格代表表格线(grid line)\n```\n.container {\n  grid-template-columns: 40px 50px auto 50px 40px;\n  grid-template-rows: 25% 100px auto;\n}\n\n如果你的定义中包含重复的部分，则可以使用repeat() 符号来简化写法：\n\n.container {\n  grid-template-columns: repeat(3, 20px [col-start]) 5%;\n}\n\nfr”单位允许您将轨道大小设置为网格容器自由空间的一部分。 例如，下面的代码会将每个 grid item 为 grid container 宽度的三分之一：\n\n.container {\n  grid-template-columns: 1fr 1fr 1fr;\n}\n```\n\n>grid-template-areas\n通过引用 grid-area属性指定的网格区域的名称来定义网格模板。 重复网格区域的名称导致内容扩展到这些单元格。 点号表示一个空单元格。 语法本身提供了网格结构的可视化。\n```\n值：\n\n<grid-area-name> - 使用 grid-area 属性设置的网格区域的名称\n. - 点号代表一个空网格单元\nnone - 没有定义网格区域\n举例：\n\n.item-a {\n  grid-area: header;\n}\n.item-b {\n  grid-area: main;\n}\n.item-c {\n  grid-area: sidebar;\n}\n.item-d {\n  grid-area: footer;\n}\n\n.container {\n  grid-template-columns: 50px 50px 50px 50px;\n  grid-template-rows: auto;\n  grid-template-areas: \n    \"header header header header\"\n    \"main main . sidebar\"\n    \"footer footer footer footer\";\n}\n这将创建一个四列宽三行高的网格。 整个第一行将由 header 区域组成。 中间一行将由两个 main 区域、一个空单元格和一个 sidebar 区域组成。 最后一行是footer区域组成。\n```\n>grid-column-gap / grid-row-gap\n指定网格线的大小，你可以把它想象为设置列/行之间的间距的宽度。\n\n```\n.container {\n  grid-template-columns: 100px 50px 100px;\n  grid-template-rows: 80px auto 80px; \n  grid-column-gap: 10px;\n  grid-row-gap: 15px;\n}\n```\n>grid-gap\ngrid-row-gap 和 grid-column-gap 的缩写\n```\n.container {\n  grid-template-columns: 100px 50px 100px;\n  grid-template-rows: 80px auto 80px; \n  grid-gap: 10px 15px;\n}\n```\n>justify-items\n沿着行轴对齐网格内的内容\n```\nstart: 内容与网格区域的左端对齐\nend: 内容与网格区域的右端对齐\ncenter: 内容位于网格区域的中间位置\nstretch: 内容宽度占据整个网格区域空间(这是默认值)\n\n.container {\n  justify-items: start;\n}\n```\n>align-items\n沿着列轴对齐grid item 里的内容\n```\n.container {\n  align-items: start | end | center | stretch;\n}\n```\n\n>justify-content\n设置网格容器内的网格的对齐方式。 此属性沿着行轴对齐网格（与之对应的是 align-content, 沿着列轴对齐）\n```\nstart - 网格与网格容器的左边对齐\nend - 网格与网格容器的右边对齐\ncenter - 网格与网格容器的中间对齐\nstretch - 调整g rid item 的大小，让宽度填充整个网格容器\nspace-around - 在 grid item 之间设置均等宽度的空白间隙，其外边缘间隙大小为中间空白间隙宽度的一半\nspace-between - 在 grid item 之间设置均等宽度空白间隙，其外边缘无间隙\nspace-evenly - 在每个 grid item 之间设置均等宽度的空白间隙，包括外边缘\n.container {\n  justify-content: start | end | center | stretch | space-around | space-between | space-evenly;  \n}\n```\n\n\n\n### demo\n```\nhtml:\n<div class=\"wrapper\">\n  <div class=\"box a\">A</div>\n  <div class=\"box b\">B</div>\n  <div class=\"box c\">C</div>\n  <div class=\"box d\">D</div>\n  <div class=\"box e\">E</div>\n  <div class=\"box f\">F</div>\n</div>\n\ncss:\n.wrapper {\n  display: grid;\n  grid-template-columns: 100px 100px 100px;\n  grid-gap: 10px;\n  background-color: #fff;\n  color: #444;\n}\n\n.box {\n  background-color: #444;\n  color: #fff;\n  border-radius: 5px;\n  padding: 20px;\n  font-size: 150%;\n}\n```\n"
  },
  {
    "path": "3.2.6 css mask遮罩.md",
    "content": "# css mask遮罩\n\n兼容性有问题 [---> Can I use](https://caniuse.com/#search=mask)\n\n>偶然看到别人做镂空效果\n\n- [一行代码实现镂空效果](https://juejin.im/post/5bc8184ee51d450e81090d94)\n\n- [demo code](https://codepen.io/HelKyle/pen/yRvRyG/)\n\n```\n// 核心代码\nimg {\n  mask-image: url();\n}\n```\n\n\n## 参考\n- [CSS遮罩CSS3 mask/masks详细介绍](https://www.zhangxinxu.com/wordpress/2017/11/css-css3-mask-masks/)\n"
  },
  {
    "path": "3.2.7 flex布局当overflow时无法滑动到顶部.md",
    "content": "# flex布局当overflow时无法滑动到顶部\n\n- [js fiddle demo](https://jsfiddle.net/nickday/b8x75hso/1/)\n\n\n- 原demo: \n```\n<style type=\"text/css\">\n    .fix {\n      position: fixed;\n      top: 50%;\n      left: 50%;\n      width: 320px;\n      height: 640px;\n      transform: translate(-50%,-50%);\n    }\n    .flex {\n      display: -webkit-box;\n      display: -webkit-flex;\n      display: -ms-flexbox;\n      display: flex;\n      -webkit-box-align: center;\n      -webkit-align-items: center;\n      -ms-flex-align: center;\n      align-items: center;\n      justify-content: center;\n      height: 100%;\n      overflow-y: auto;\n      position: relative;\n    }\n    img {\n      width: 100%;\n    }\n  </style>\n<div class=\"fix\">\n   <div class=\"flex\">\n     <img src=\"https://pic01-10001430.image.myqcloud.com/a7ee5bff-d4a7-4901-96a4-3652814f27d4\">\n   </div>\n </div>\n\n```\n\n>Flexbox的对齐是一种“真正”意义上的居中， 这一点和我们使用的其他的CSS居中技术不太一样。  \n也就是说，即便当子元素超过父元素的边界时，他们在父元素中仍然是居中存在的。  \n然而这有时候会带来问题，当他们的top超过父元素的top边界，或者其left超过父元素的left边界时（当高度限定，宽度自动延伸时，  \nleft就有可能超过父元素的left边界)，其超过top的部分（或left部分），虽然确实有内容在哪里，实际上却是无法scroll过去的。 \n \n>在将来的版本中，flex的对齐属性会加入一个“安全”的选项（使得在这种情境下，top部分不至于被吞没）。  \n目前，如果这对您是一个担忧的话，您可以通过子元素的auto margins来达到这种安全的布局。  \n当子元素超过父元素时，他们会使用margin属性，而忽略居中属性。  \n不同于 justify- 的系列属性，  \n通过在flex父容器中的第一个和最后一个元素上面设置margin:auto(如果整个容器内只有一个元素，比如本例，则直接设置在该子元素上面)，  \n当有足够的空间给子元素来进行”flex”布局时，flex的布局会生效；  \n否则就会切换到一般的布局方式，也就是 margin:auto。  \n\n- 修改后 demo:\n```\n<style type=\"text/css\">\n    .fix {\n      position: fixed;\n      top: 50%;\n      left: 50%;\n      width: 320px;\n      height: 640px;\n      transform: translate(-50%,-50%);\n    }\n    .flex {\n      display: -webkit-box;\n      display: -webkit-flex;\n      display: -ms-flexbox;\n      display: flex;\n      -webkit-box-align: center;\n      -webkit-align-items: center;\n      -ms-flex-align: center;\n      align-items: center;\n      justify-content: center;\n      height: 100%;\n      overflow-y: auto;\n      position: relative;\n    }\n    img {\n      display: block;\n      width: 100%;\n      margin: auto;\n    }\n  </style>\n  \n```\n\n## 参考\n- [flex 布局的基本概念](https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox)\n- [flex布局当overflow时无法滑动到顶部](http://codingfishman.github.io/2016/05/21/flex%E5%B8%83%E5%B1%80%E5%BD%93overflow%E6%97%B6%E6%97%A0%E6%B3%95%E6%BB%91%E5%8A%A8%E5%88%B0%E9%A1%B6%E9%83%A8/)\n"
  },
  {
    "path": "3.2.8 CSS scroll-behavior.md",
    "content": "# CSS scroll-behavior\n\n>兼容性还有些问题的\n\nscroll-behavior:smooth 写在滚动容器元素上，可以让容器（非鼠标手势触发）的滚动变得平滑\n\n凡是需要滚动的地方都加一句`scroll-behavior:smooth `就好了\n\n\n\n## 参考\n\n- [CSS scroll-behavior和JS scrollIntoView让页面滚动平滑](https://www.zhangxinxu.com/wordpress/2018/10/scroll-behavior-scrollintoview-%E5%B9%B3%E6%BB%91%E6%BB%9A%E5%8A%A8/)\n"
  },
  {
    "path": "3.2.9 一道css题.md",
    "content": "# 一道css题\n\n>题目要求：  \nP标签的最大宽度不可以大于H2标签文字宽度的10%  \n这里应该是P标签的最大宽度由前面的匿名内联元素宽度（就是大字号文字宽度）决定，可参见最后期望效果GIF示意。  \nH2标签不能失去高度（h2 文字高度+p 标签高度 = h2 标签高度）  \n\n```\nHTML结构（不允许修改）\n\n<h2>\nIPHONE XR<br>\nIS THE FUCKING<br>\nBEST EVER MADE\n<p>iPhone XR has not been authorized as required by the rules of the Federal Communications Commission. iPhone XR is not, and may not be, offered for sale or lease, or sold or leased, until authorization is obtained.</p>\n</h2>\n\n\n基础CSS样式\n\nh2{\n    font-size: 52px;\n    font-weight: bold;\n    color: #000;\n}\np{\n    font-size: 12px;\n    color: #333;\n}\n```\n\n- 张鑫旭实现：\n```\nh2 {\n    width: min-content;\n    white-space: nowrap;\n}\np {\n    white-space: normal;\n}\n```\n\n- 出题人的实现：\n```\nh2 {\n    display: table;\n}\np {\n    display: table-caption;\n    caption-side: bottom;\n}\n```\n\n\n## 参考\n- [有人考了我一道CSS题目](https://www.zhangxinxu.com/wordpress/2018/10/a-css-%E9%A2%98%E7%9B%AE/)\n"
  },
  {
    "path": "3.3.1 深入浏览器理解CSS animations 和 transitions的性能问题.md",
    "content": "# 深入浏览器理解CSS animations 和 transitions的性能问题\n\n## 现代的浏览器通常会有两个重要的执行线程，这2个线程协同工作来渲染一个网页：\n\n- 主线程\n- 合成线程\n\n### 一般情况下，主线程负责：\n\n- 使用HTML创建文档对象模型（DOM）\n- 使用CSS创建CSS对象模型（CSSOM）\n- 基于DOM和CSSOM执行脚本（Scripts）\n- 合并DOM和CSSOM形成渲染树（Render Tree）\n- 使用渲染树布局（Layout）所有元素\n- 将元素绘制（Paint）到一个或多个位图中\n- 将这些位图交给合成线程\n\n### 相应地，合成线程负责：\n\n- 通过GPU将位图绘制到屏幕上\n- 通知主线程更新页面中可见或即将变成可见的部分的位图\n- 计算出页面中哪部分是可见的\n- 计算出当你在滚动页面时哪部分是即将变成可见的\n- 当你滚动页面时将相应位置的元素移动到可视区域\n- 长时间执行JavaScript或渲染一个很大的元素会阻塞主线程，在这期间，它将无法响应用户的交互。\n\n>相反，合成线程则会尽量去响应用户的交互。当一个页面发生变化时，合成线程会以每秒60 帧的间隔去不断重绘这个页面，即使这个页面不完整。\n\n>当用户滚动页面时，合成线程会通知主线程更新页面中最新可见部分的位图。  \n但是，如果主线程响应地不够快，合成线程不会保持等待，而是马上绘制已经生成的位图，还没准备好的部分用白色进行填充。\n\n### 动画卡顿现象\n那么为什么会造成动画卡顿呢？原因就是主线程和合成线程的调度不合理。\n\n### 卡顿说明\n在使用height，width，margin，padding作为transition的值时，会造成浏览器主线程的工作量较重，   \n例如从margin-left：-20px渲染到margin-left:0，主线程需要计算样式margin-left:-19px,margin-left:-18px，一直到margin-left:0，  \n而且每一次主线程计算样式后，合成进程都需要绘制到GPU然后再渲染到屏幕上，  \n前后总共进行20次主线程渲染，20次合成线程渲染，20+20次，总计40次计算。\n\n### 主线程每次都需要执行Scripts，Render Tree,Layout和Paint这四个阶段的计算。\n\n  - 基于DOM和CSSOM执行脚本（Scripts）\n  - 合并DOM和CSSOM形成渲染树（Render Tree）\n  - 使用渲染树布局（Layout）所有元素\n  - 将元素绘制（Paint）到一个或多个位图中\n\n而如果使用transform的话，例如tranform:translate(-20px,0)到transform:translate(0,0)，  \n主线程只需要进行一次tranform:translate(-20px,0)到transform:translate(0,0)，  \n然后合成线程去一次将-20px转换到0px，这样的话，总计1+20计算。\n\n## css3动画卡顿的解决方案：\n\n在使用css3 transtion做动画效果时，优先选择transform，尽量不要使用height，width，margin和padding。\n\ntransform为我们提供了丰富的api，例如scale，translate，rotate等等，但是在使用时需要考虑兼容性。  \n但其实对于大多数css3来说，mobile端支持性较好，desktop端支持性需要格外注意。  \n\n\n\n作者：小小的白菜\n链接：https://www.jianshu.com/p/72cca834384e\n\n\n\n\n## 参考\n- [深入浏览器理解CSS animations 和 transitions的性能问题](https://sy-tang.github.io/2014/05/14/CSS%20animations%20and%20transitions%20performance-%20looking%20inside%20the%20browser/)\n- [CSS3动画卡顿性能优化解决方案](https://segmentfault.com/a/1190000013045035)\n"
  },
  {
    "path": "3.3.2 CSS Paint API.md",
    "content": "# CSS Paint API\n\n- [Houdini支持吗？](https://ishoudinireadyyet.com/)\n- []()\n\n\n## 检查CSS对象，实现对JavaScript中Paint Worklet支持情况做一个检测：\n```\nif ('paintWorklet' in CSS) {\n    CSS.paintWorklet.addModule('mystuff.js');\n}\n```\n- 而在CSS通过@supports来做相应的检测：\n```\n@supports (background: paint(id)) {\n    body {\n        background-image: paint(checkerboard);\n    }\n}\n```\n- 原文: https://www.w3cplus.com/css/css-paint-api.html © w3cplus.com\n\n\n\n## 参考\n- [css-houdini](https://css-houdini.rocks/)\n- [css-paint-api](https://www.w3cplus.com/css/css-paint-api.html)\n- [CSS Houdini和CSS Paint API](https://www.w3cplus.com/css/say-hello-to-houdini-and-the-css-paint-api.html)\n- [github--css-houdini](https://github.com/iamvdo/css-houdini.rocks)\n"
  },
  {
    "path": "3.3.3 前端性能优化之 预加载 prefetch，Preload.md",
    "content": "# 前端性能优化之 预加载 prefetch，Preload\n>dns-prefetch, subresource, prefetch, preconnect, 和 prerender.\n\n## 1.dns-prefetch\n>DNS prefetching通过指定具体的URL来告知客户端未来会用到相关的资源，这样浏览器可以尽早的解析DNS。  \n比如我们需要一个在 example.com 的图片或者视频文件。在<head>就可以这么写：\n\n```\n<link rel=\"dns-prefetch\" href=\"//test.com\">\n```\n\n[front-end-performance-for-web-designers-and-front-end-developers](https://csswizardry.com/2013/01/front-end-performance-for-web-designers-and-front-end-developers/) 中建议：\n```\n简单的一行就能让支持的浏览器提前解析DNS。也就是说在浏览器请求资源时，DNS查询就已经准备好了。\n```\n \n## 2.subresource\n\n>subresource可以用来指定资源是最高优先级的。  \nChromium的文档这么解释：  \n和 \"Link rel=prefetch\"的语义不同，  \n\"Link rel=subresource\"是一种新的连接关系。  \nrel=prefetch 指定了下载后续页面用到资源的低优先级，  \n而 rel=subresource 则是指定当前页面资源的提前加载。   \n所以，如果资源是在当前页面需要，或者马上就会用到，则推荐用 subresource，否则还是用 prefetch。\n\n比如，在 Chrome 和 Opera 中我们可以加上下面的代码：  \n\n```\n<link rel=\"subresource\" href=\"base.css\">\n```\n\n## 3.prefetch\n>当能确定网页在未来一定会使用到某个资源时，开发者可以让浏览器提前请求并且缓存好以供后续使用。    \nprefetch支持预拉取图片、脚本或者任何可以被浏览器缓存的资源。  \n\n```\n<link rel=\"prefetch\" href=\"image.png\">\n```\n>不同于DNS prefetch，上面的写法可是会去请求、下载资源并且缓存起来。当然也是有一些发生条件的。  \n比如，客户端可能会在弱网络下不去请求较大的字体文件，Firefox则只会在浏览器空闲的时候prefetch资源  \n（注：[这里是MDN上对浏览器空闲的定义和一些FAQ](https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ)）。\n\n>[Bram Stein在他的文章中指出](http://www.bramstein.com/writing/preload-hints-for-web-fonts.html)，  \nprefetch很适用于优化 webfonts 的性能。  \n以前，字体文件必须等 DOM 和 CSSOM 创建好后才能下载，  \n可如果 prefetch 了字体，这个瓶颈就能轻松解决了。\n\n>注意：prefetch并没有同域的限制\n\n## 4.preconnect\n>和DNS prefetch类似，preconnect 不光会解析 DNS，还会建立TCP握手连接和TLS协议（如果需要）。用法如下：\n\n```\n<link rel=\"preconnect\" href=\"http://css-tricks.com\">\n```\n[eliminating-roundtrips-with-preconnect](https://www.igvita.com/2015/08/17/eliminating-roundtrips-with-preconnect/)篇文章详细说明了这种技术：\n\n>现代浏览器竭尽所能的尝试预测网站可能需要哪些链接。  \n通过提前连接，浏览器可以提前建立必要的通信，消除了实际请求中DNS、TCP和TLS的耗时。  \n不过，即使是只能的现代浏览器，也没办法为每个网站可靠的预测所有连接。  \n幸运的是开发者可以告诉浏览器哪些通信需要在实际请求发起前就提前建立连接。\n\n## 5.prerender\n>prerender是一个重量级的选项，它可以让浏览器提前加载指定页面的所有资源。\n\n>Steve Souders的文章详细解释了这个技术：\nprerender就像是在后台打开了一个隐藏的tab，会下载所有的资源、创建DOM、渲染页面、执行JS等等。  \n如果用户进入指定的链接，隐藏的这个页面就会进入马上进入用户的视线。  \nGoogle Search多年前就利用了这个特性实现了Instant Pages功能。\n\n```\n<link rel=\"prerender\" href=\"/nextpage.html\"/>\n```\n\n>但是要注意，一定要在十分确定用户回点某个链接时才用这个特性，否则客户端就会无端的下载很多资源和渲染这个页面。\n\n>正如任何提前的动作一样，预判总是有一定风险出错。  \n如果提前的动作是昂贵的（比如高CPU、耗电、占用带宽），就要谨慎使用了。  \n虽然不容易预判用户会点进哪个页面，但还是存在一些典型的场景：\n\n- 如果用户搜索到了一个明显正确的结果时，那么这个页面就很有可能被点入\n- 如果用户在登录页面，那么登录成功后的页面就很可能接下来会被加载了\n- 如果用户在阅读一个多页面的文章或者有页码的内容时，下一页就很可能会马上被点击了\n\n利用 [Page Visibility API](https://www.w3.org/TR/page-visibility/) 可以用来防止页面在还没真正展示给用户时就触发了JS的执行。\n\n- Page Visibility API demo: \n```\nvar videoElement = document.getElementById(\"videoElement\");\n\n// Autoplay the video if application is visible\nif (document.visibilityState == \"visible\") {\n  videoElement.play();\n}\n\n// Handle page visibility change events\nfunction handleVisibilityChange() {\n  if (document.visibilityState == \"hidden\") {\n    videoElement.pause();\n  } else {\n    videoElement.play();\n  }\n}\n\ndocument.addEventListener('visibilitychange', handleVisibilityChange, false);\n```\n\n\n## 6.Preload\n\n>允许始终预加载某些资源，不像 prefetch 有可能被浏览器忽略，浏览器必须请求 preload 标记的资源。\n\n```\n<link rel=\"preload\" href=\"image.png\">\n\n<link rel=\"preload\" href=\"late_discovered_thing.js\" as=\"script\">\n\n```\n\n`as`  属性告诉浏览器 它将要下载什么 . `as` 值包含以下:\n```\n\"script\",\n\"style\",\n\"image\",\n\"media\",\nand \"document\".\n```\n\n>大部分预加载技术移动端都不支持，PC支持有限；  \n缺点：可能会造成资源的浪费；\n\n## 参考\n- [Preconnect, prerender, prefetch](https://docs.google.com/presentation/d/18zlAdKAxnc51y_kj-6sWLmnjl6TLnaru_WH0LJTjP-o/present?slide=id.p19)\n- [MDN-/Link_prefetching_FAQ](https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ)\n- [Prebrowsing](http://www.stevesouders.com/blog/2013/11/07/prebrowsing/)\n- [preload-what-is-it-good-for](https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/#top)\n- [preload](https://w3c.github.io/preload/)\n- [前端性能优化－－预加载技术](https://blog.csdn.net/franktaoge/article/details/51473823)\n- [CAN I USE - link-rel-dns-prefetch](https://caniuse.com/#feat=link-rel-dns-prefetch)\n- [CAN I USE - link-rel-prefetch](https://caniuse.com/#feat=link-rel-prefetch)\n- [CAN I USE - link-rel-preconnect](https://caniuse.com/#feat=link-rel-preconnect)\n"
  },
  {
    "path": "3.3.4 iPhoneX的适配.md",
    "content": "# iPhoneX的适配\n\n```\nviewport-fit\n<!--默认值:可视窗口完全包含网页内容 相当于在安全区域展示-->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, viewport-fit=auto\">\n<!--或-->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, viewport-fit=contain\">\n<!--网页内容完全覆盖可视窗口-->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, viewport-fit=cover\">\n\n\nsafe-area-inset-*\n在设置viewport-fit=cover之后，Web中会新增四个常量\n\nsafe-area-inset-top\nsafe-area-inset-right\nsafe-area-inset-left\nsafe-area-inset-bottom\n分别表示safe area和可视窗口viewport顶部，右边，左边，底部的间距，可以用于设置margin和padding或者绝对定位时left和top\n\n注意：在横屏和竖屏状态下，safe-area-inset-*的值不同\n\n为了解决应用viewport-fit=cover之后，有些显示内容被裁剪的问题，我们可以通过添加边距使得网页主要内容处于safe area中不被裁剪，解决方式如下\n\npadding: constant(safe-area-inset-top) constant(safe-area-inset-right) constant(safe-area-inset-bottom) constant(safe-area-inset-left);\n```\n\n\n## 参考\n- [iPhoneX的适配](https://github.com/Wscats/iPhone-X)\n"
  },
  {
    "path": "3.3.5 CSS Modules (css 模块化).md",
    "content": "# 3.3.5 CSS Modules (css 模块化)\n\n>一个CSS模块是一个CSS文件,\n\n\n> 全局污染，命名冲突\n\n## 好处\n- 提高代码重用率\n- 提高开发效率、减少沟通成本\n- 提高页面容错\n- 降低耦合\n- 降低发布风险\n- 减少Bug定位时间和Fix成本\n- 更好的实现快速迭代\n- 便于代码维护\n\n\n## 参考\n- [css-modules](https://github.com/css-modules/css-modules)\n"
  },
  {
    "path": "3.3.6 CSS3混合模式mix-blend-mode +background-blend-mode.md",
    "content": "# CSS3混合模式mix-blend-mode + background-blend-mode\n\n>mix-blend-mode: \n```\nmix-blend-mode: normal;          //正常\nmix-blend-mode: multiply;        //正片叠底\nmix-blend-mode: screen;          //滤色\nmix-blend-mode: overlay;         //叠加\nmix-blend-mode: darken;          //变暗\nmix-blend-mode: lighten;         //变亮\nmix-blend-mode: color-dodge;     //颜色减淡\nmix-blend-mode: color-burn;      //颜色加深\nmix-blend-mode: hard-light;      //强光\nmix-blend-mode: soft-light;      //柔光\nmix-blend-mode: difference;      //差值\nmix-blend-mode: exclusion;       //排除\nmix-blend-mode: hue;             //色相\nmix-blend-mode: saturation;      //饱和度\nmix-blend-mode: color;           //颜色\nmix-blend-mode: luminosity;      //亮度\n\nmix-blend-mode: initial;         //初始\nmix-blend-mode: inherit;         //继承\nmix-blend-mode: unset;           //复原\n```\n\n- [caniuse](https://caniuse.com/css-mixblendmode/embed)\n\n## 参考\n- [张鑫旭-CSS3混合模式mix-blend-mode/background-blend-mode简介](https://www.zhangxinxu.com/wordpress/2015/05/css3-mix-blend-mode-background-blend-mode/)\n"
  },
  {
    "path": "3.3.7 img loading.md",
    "content": "# 3.3.7 img loading\n\n>兼容性依然有问题，不过已经有兼容原生的懒加载loading属性的垫片库了\n\n\n## 使用\n\n```\n<img src=\"./example.jpg\" loading=\"lazy\" alt=\"lazy loading\">\n```\n\n## 垫片库使用\n\n```\n<noscript class=\"loading-lazy\">\n\t<img\n\t\tsrc=\"simpleimage.jpg\"\n\t\tloading=\"lazy\"\n\t\talt=\"..\"\n\t\twidth=\"250\"\n\t\theight=\"150\"\n\t/>\n</noscript>\n```\n\n\n## 参考\n- [mfranzke/loading-attribute-polyfill](https://github.com/mfranzke/loading-attribute-polyfill)\n- [caniuse](https://www.caniuse.com/#search=loading)\n"
  },
  {
    "path": "3.3.8 CSS Paint API.md",
    "content": "# 3.3.8 CSS Paint API\n\n\n\nCSS Paint API可以让Canvas画布作为普通元素的background-image背景图片呈现，其作为一个CSS属性值，渲染是实时的，\n\n因此可以以一种更高效的方式丰富web页面元素的视觉展现。\n\n此API使用需要配合JavaScript使用才行，有一定门槛，包括CSS模块注册和使用canvas API语法进行图形绘制。\n\n\n\n```\n.box {\n    width: 180px; height: 180px;\n    /* someShape自己命名 */\n    background-image: paint(someShape);\n}\n\n<script>\n  if ('paintWorklet' in CSS) {\n    CSS.paintWorklet.addModule('checkerboard.js');\n  }\n</script>\n\n\n// # checkerboard.js 文件代码如下:\n// transparent-grid命名和CSS中的对应\nregisterPaint('someShape', class {\n    paint(context, size) {\n       // 这里就是绘制的代码了....\n    }\n});\n```\n\n\n\n\n\n## 参考\n- [MDN-paintWorklet](https://developer.mozilla.org/en-US/docs/Web/API/CSS/paintWorklet)\n- [CSS新特性](https://juejin.im/post/5e060792518825125c431a08#paintAPI)\n- \n"
  },
  {
    "path": "3.3.9 打印 用到 color-adjust.md",
    "content": "# 3.3.9 打印 用到 color-adjust\n\n\n```\ncolor-adjust: economy; /* 经济，省钱 */\ncolor-adjust: exact;  /* 精确 */\n```\n\n\n>打印的时候，为了省墨水，默认背景色是不打印的，纯白。可以通过设置`color-adjust: exact`实现。\n\n`Ctrl + P `或者选择浏览器“打印”菜单，此时，可以看到差异\n\n## 参考\n- [MDN-color-adjust](https://developer.mozilla.org/en-US/docs/Web/CSS/color-adjust)\n"
  },
  {
    "path": "3.4.0 scroll-snap 滚动容器中的一个临时点.md",
    "content": "# 3.4.0 scroll-snap 滚动容器中的一个临时点.md\n\n\n>可以让网页容器滚动停止的时候，自动平滑定位到指定元素的指定位置\n\n```\n/* 关键值 */\nscroll-snap-type: none;\nscroll-snap-type: x;\nscroll-snap-type: y;\nscroll-snap-type: block;\nscroll-snap-type: inline;\nscroll-snap-type: both;\n\n/* 可选 mandatory | proximity*/\nscroll-snap-type: x mandatory;\nscroll-snap-type: y proximity;\nscroll-snap-type: both mandatory;\n\n/* etc */\n\n/* 全局值 */\nscroll-snap-type: inherit;\nscroll-snap-type: initial;\nscroll-snap-type: unset;\n```\n\n## 参考\n- [scroll-snap-type](https://developer.mozilla.org/zh-CN/docs/Web/CSS/scroll-snap-type)\n"
  },
  {
    "path": "3.4.1  margin-inline和margin-block.md",
    "content": ">CSS逻辑属性往往配合direction属性或者`writing-mode`使用\n\n# margin-inline\n\n>是`margin-inline-start`和`margin-inline-end`的缩写  \n默认情况下，`margin-inline`指的是水平方向的`margin`控制，\n\n>`margin-inline-start`等同于`margin-left`，`margin-inline-end`等同于`margin-right`\n\n\n\n# margin-block\n>是`margin-block-start`和`margin-block-end`的缩写  \n`margin-block`指的是垂直方向的`margin`控制\n\n> `margin-block-start`等同于`margin-top`，`margin-block-end`等同于`margin-bottom`。\n\n\n\n\n## 参考\n- [margin-inline](https://developer.mozilla.org/en-US/docs/Web/CSS/margin-inline)\n- [margin-block](https://developer.mozilla.org/en-US/docs/Web/CSS/margin-block)\n"
  },
  {
    "path": "3.4.2 font-feature-settings.md",
    "content": "# font-feature-settings \n\n\n>CSS属性可让您控制OpenType字体中的高级印刷功能\n\n```\n/* 默认设置 */\nfont-feature-settings: normal;\n\n/* 设置OpenType功能标签的值 */\nfont-feature-settings: \"smcp\";\nfont-feature-settings: \"smcp\" on;\nfont-feature-settings: \"swsh\" 2;\nfont-feature-settings: \"smcp\", \"swsh\" 2;\n\n/* 全局值 */\nfont-feature-settings: inherit;\nfont-feature-settings: initial;\nfont-feature-settings: unset;\n```\n\n\n\n## 参考\n- [font-feature-settings](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-feature-settings)\n- [OpenType Feature Tags 列表](https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist)\n"
  },
  {
    "path": "3.4.3 CSS 伪元素 ::backdrop.md",
    "content": "# 3.4.3 CSS 伪元素 ::backdrop\n\n\n>`::backdrop CSS` 伪元素 是在任何处于全屏模式的元素下的即刻渲染的盒子（并且在所有其他在堆中的层级更低的元素之上）。\n\n```\ndialog::backdrop {\n  background: rgba(255,0,0,.25);\n}\n```\n\n\n\n\n## 参考\n- [::backdrop](https://developer.mozilla.org/zh-CN/docs/Web/CSS/::backdrop)\n"
  },
  {
    "path": "3.4.4 :placeholder-shown伪类.md",
    "content": "# 3.4.4 :placeholder-shown伪类\n\n>:placeholder-shown CSS 伪类 在 `<input>` 或 `<textarea>` 元素显示 placeholder text 时生效.\n\n```\n<div class=\"input-fill-x\">\n    <input class=\"input-fill\" placeholder=\"邮箱\">\n    <label class=\"input-label\">邮箱</label>\n</div>\n\n\n/* 默认placeholder颜色透明不可见 */\n.input-fill:placeholder-shown::placeholder {\n    color: transparent;\n}\n.input-fill-x {\n    position: relative;\n}\n.input-label {\n    position: absolute;\n    left: 16px; top: 14px;\n    pointer-events: none;\n}\n.input-fill:not(:placeholder-shown) ~ .input-label,\n.input-fill:focus ~ .input-label {\n    transform: scale(0.75) translate(0, -32px);\n}\n```\n\n\n\n## 参考\n- [:placeholder-shown伪类](https://developer.mozilla.org/zh-CN/docs/Web/CSS/:placeholder-shown)\n"
  },
  {
    "path": "3.4.5 focus-within伪类.md",
    "content": "# 3.4.5 focus-within伪类\n\n\n\n> :focus-within 是一个CSS 伪类 ，表示一个元素获得焦点，或，该元素的后代元素获得焦点。  \n换句话说，元素自身或者它的某个后代匹配:focus伪类。（在shadow trees 中的后代也在匹配序列中）\n\n\n```\n/* 匹配<div>，当它的某个后代获得焦点 */\ndiv:focus-within {\n  background: cyan;\n}\n```\n\n\n## 参考\n- [:focus-within](https://developer.mozilla.org/zh-CN/docs/Web/CSS/:focus-within)\n"
  },
  {
    "path": "3.4.6 box-decoration-break.md",
    "content": "# 3.4.6 box-decoration-break\n\n>指定元素片段在跨行、跨列或跨页（如打印）时候的样式渲染表现\n\n```\n/* Keyword values */\nbox-decoration-break: slice;\nbox-decoration-break: clone;\n\n/* Global values */\nbox-decoration-break: initial;\nbox-decoration-break: inherit;\nbox-decoration-break: unset;\n```\n\n- demo\n\n```\n.example { \n  background: linear-gradient(to bottom right, yellow, green);\n  box-shadow:\n    8px 8px 10px 0px deeppink, \n    -5px -5px 5px 0px blue, \n    5px 5px 15px 0px yellow;\n  padding: 0em 1em;\n  border-radius: 16px;\n  border-style: solid;\n  margin-left: 10px;\n  font: 24px sans-serif;\n  line-height: 2;\n  \n  // 加上 box-decoration-break 看效果\n  -webkit-box-decoration-break: clone;\n  box-decoration-break: clone;\n}\n\n\n<span class=\"example\">The<br>quick<br>orange fox</span>\n```\n\n## 参考\n-[box-decoration-break](https://developer.mozilla.org/en-US/docs/Web/CSS/box-decoration-break)\n"
  },
  {
    "path": "3.4.7 :focus-visible伪类.md",
    "content": "# 3.4.7 :focus-visible伪类\n\n\n```\n通过下面CSS代码可以实现点击无轮廓，键盘访问有轮廓：\n\n:focus:not(:focus-visible) {\n    outline: 0;\n}\n```\n\n>请注意Firefox通过较旧的前缀伪类` :-moz-focusring` 支持类似的功能。更多细节请参考:`-moz-focusring`页面。\n\n\n## 参考\n- [:focus-visible](https://developer.mozilla.org/zh-CN/docs/Web/CSS/:focus-visible)\n"
  },
  {
    "path": "3.4.8 backdrop-filter.md",
    "content": "# backdrop-filter\n\n\n>backdrop-filter CSS 属性可以让你为一个元素后面区域添加图形效果（如模糊或颜色偏移）。 \n因为它适用于 **元素背后的所有元素**，为了看到效果，必须使元素或其背景至少部分透明。\n\n\n\n```\n\n/* 关键词值 */\nbackdrop-filter: none;\n\n/* SVG 过滤器 */\nbackdrop-filter: url(commonfilters.svg#filter);\n\n/* <filter-function> 过滤器函数 */\nbackdrop-filter: blur(2px);\nbackdrop-filter: brightness(60%);\nbackdrop-filter: contrast(40%);\nbackdrop-filter: drop-shadow(4px 4px 10px blue);\nbackdrop-filter: grayscale(30%);\nbackdrop-filter: hue-rotate(120deg);\nbackdrop-filter: invert(70%);\nbackdrop-filter: opacity(20%);\nbackdrop-filter: sepia(90%);\nbackdrop-filter: saturate(80%);\n\n/* 多重过滤器 */\nbackdrop-filter: url(filters.svg#filter) blur(4px) saturate(150%);\n\n/* 全局值 */\nbackdrop-filter: inherit;\nbackdrop-filter: initial;\nbackdrop-filter: unset;\n\n\n{\n    -webkit-backdrop-filter: blur(5px);\n    backdrop-filter: blur(5px);\n}\n```\n\n- demo:\n\n```\n<div class=\"bg-red\">123455</div>\n<div class=\"mask\"></div>\n\n.bg-red {\n    width: 900px;\n    height: 900px;\n    background: #ff0000;\n    color: #0000ff;\n}\n.mask {\n    position: fixed;\n    left: 0;\n    right: 0;\n    top: 0;\n    bottom: 0;\n    background: rgba(25, 28, 34, 0.88);\n    z-index: 2;\n    // backdrop-filter: blur(10px);  \n    // -webkit-backdrop-filter: blur(10px);\n}\n```\n\n\n\n## 参考\n- [backdrop-filter](https://developer.mozilla.org/zh-CN/docs/Web/CSS/backdrop-filter)\n"
  },
  {
    "path": "3.4.9  image-set 不同屏幕显示不同图片.md",
    "content": "# 3.4.9  image-set 不同屏幕显示不同图片\n\n\n```\nbackground-image: image-set( \"cat.png\" 1x,\n                             \"cat-2x.png\" 2x,\n                             \"cat-print.png\" 600dpi);\n```\n\n\n## 参考\n- [image-set](https://developer.mozilla.org/en-US/docs/Web/CSS/image-set)\n"
  },
  {
    "path": "3.5.1 @supports使用.md",
    "content": "# 3.5.1 @supports使用\n\n\n第一步：设置网页在可视窗口的布局方式\n\n`ios11`新增 `viweport-fit` 属性，使得页面内容完全覆盖整个窗口：\n\n```\n<meta name=\"viewport\" content=\"width=device-width, viewport-fit=cover\">\n```\n\n第二步：页面主体内容限定在安全区域内\n\nenv() 和 constant()ios11新增特性\n\n● safe-area-inset-left：安全区域距离左边边界距离\n● safe-area-inset-right：安全区域距离右边边界距离\n● safe-area-inset-top：安全区域距离顶部边界距离\n● safe-area-inset-bottom：安全区域距离底部边界距离\n\n这里我们只需要关注 `safe-area-inset-bottom` 这个变量，因为它对应的就是小黑条的高度（横竖屏时值不一样）。\n\n```\nbody {\n    padding-bottom: constant(safe-area-inset-bottom);\n    padding-bottom: env(safe-area-inset-bottom);\n}\n```\n\n- 第三步：fixed 元素的适配\n  - ● 类型一：fixed 完全吸底元素（bottom = 0）\n    ```\n    {\n        padding-bottom: constant(safe-area-inset-bottom);\n        padding-bottom: env(safe-area-inset-bottom);\n    }\n    ```\n  - ● 类型二：fixed 非完全吸底元素（bottom ≠ 0），比如 “返回顶部”、“侧边广告” 等\n\n    ```\n    {\n        margin-bottom: constant(safe-area-inset-bottom);\n        margin-bottom: env(safe-area-inset-bottom);\n    }\n    ```\n\n- 第四步：如果我们只希望 `iPhoneX` 才需要新增适配样式，我们可以配合 `@supports` 来隔离兼容样式\n\n```\n@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {\n    div {\n        margin-bottom: constant(safe-area-inset-bottom);\n        margin-bottom: env(safe-area-inset-bottom);\n    }\n}\n```\n\n\n## not操作符\n\n```\n@supports not( display: flex ) {\n}\n```\n\n## and操作符\n\n```\n@supports ( display: flex ) and (display: block) {\n}\n```\n\n\n\n## or操作符\n\n```\n@supports ( display: flex ) or (display: block) {\n}\n```\n\n\n## 参考\n- [MDN-CSS/@supports](https://developer.mozilla.org/zh-CN/docs/Web/CSS/@supports)\n- [iphonex适配](https://segmentfault.com/a/1190000013299480)\n"
  },
  {
    "path": "3.5.2 overflow-y:auto 没超高度也出现了滚动条.md",
    "content": "# 3.5.2 overflow-y:auto 没超高度也出现了滚动条\n\n\n```\n<ul class=\"clerk-list-ul\">\n  <li class=\"clerk-cell\"></li>\n  <li class=\"clerk-cell\"></li>\n  <li class=\"clerk-cell\"></li>\n  <li class=\"clerk-cell\"></li>\n</ul>\n.clerk-cell {\n  box-sizing: border-box;\n}\n#overflow-y:auto 没超高度也出现了滚动条\n\nmin-height: 40px;\nmax-height: 400px;\noverflow-y: auto;\n\n可是内容没有超过 100px,出现了滚动条, \n\n# 解决\n\n最后把里面每一行的 box-sizing: border-box 去掉了\n```\n"
  },
  {
    "path": "3.5.3 :root 使用.md",
    "content": "# 3.5.3 :root 使用\n\n```\n:root {\n    --blue: #007bff;\n    --indigo: #6610f2;\n    --purple: #6f42c1;\n    --pink: #e83e8c;\n    --red: #dc3545;\n    --orange: #fd7e14;\n    --yellow: #ffc107;\n    }\n    \n    \n p {\n   color: var(--blue)\n }\n```\n\n\n\n## 参考\n- [MDN-root](https://developer.mozilla.org/en-US/docs/Web/CSS/:root)\n- [caniuse](https://www.caniuse.com/?search=%3Aroot)\n"
  },
  {
    "path": "3.5.4 新的 CSS 长宽比属性aspect-ratio.md",
    "content": "# 3.5.4 新的 CSS 长宽比属性aspect-ratio\n\n\n```\n.container {\n  width: 100%;\n  aspect-ratio: 16 / 9;\n}\n\n\n.sponsor img {\n  aspect-ratio: 1 / 1;\n  width: 100%;\n  object-fit: contain;\n}\n```\n\n\n## object-fit\n\n>object-fit CSS 属性指定可替换元素的内容应该如何适应到其使用的高度和宽度确定的框。\n\n您可以通过使用 object-position 属性来切换被替换元素的内容对象在元素框内的对齐方式。\n\n```\nobject-fit: fill;\nobject-fit: contain;\nobject-fit: cover;\nobject-fit: none;\nobject-fit: scale-down;\n```\n\n\n## 参考\n- [aspect-ratio/](https://web.dev/aspect-ratio/)\n- [CSS/object-fit](https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-fit)\n- [@media/aspect-ratio](https://developer.mozilla.org/zh-cn/docs/web/css/@media/aspect-ratio)\n"
  },
  {
    "path": "3.5.5 css 循环放大缩小波纹.md",
    "content": "# 3.5.5 css 循环放大缩小波纹\n\n\n## demo\n\n```\n\n<style>\nbody {background-color: #000;}\n\n.ripple {\n  margin: auto;\n  margin-top: 5rem;\n  background-color: #fff;\n  width: 1rem;\n  height: 1rem;\n  border-radius: 50%;\n  position:relative;\n  animation: ripple 3s linear infinite;\n}\n.ripple::before,\n.ripple::after{\n  content:\"\";\n  position:absolute;\n  top:0;\n  left:0;\n  right:0;\n  bottom:0;\n  border-radius: 50%;\n  animation: ripple 3s linear infinite 1s;\n}\n.ripple::after {\n  animation: ripple 3s linear infinite 2s;\n}\n@keyframes ripple {\n  0% {\n    box-shadow: 0 0 0 .7rem rgba(255,255,255, 0.2);\n  }\n  100% {\n    box-shadow: 0 0 0 8rem rgba(255,255,255, 0);\n  }\n}\n</style>\n<div class=\"ripple\"></div>\n```\n\n\n\n## demo\n\n```\n<div class=\"drink-btn-wrap\">\n    <div class=\"drink-btn-circle drink-btn-circle_one\"></div>\n    <div class=\"drink-btn-circle drink-btn-circle_two\"></div>\n    <div class=\"drink-btn-circle drink-btn-circle_three\" >水</div>\n  </div>\n  \n  \n  .drink-btn-wrap {\n    position: relative;\n    height: 2.32rem;\n    margin: 0.4rem 0;\n    .drink-btn-circle {\n      position: absolute;\n      left: 50%;\n      top: 50%;\n      &_one {\n        width: 2.32rem;\n        height: 2.32rem;\n        border-radius: 50%;\n        margin-left: -1.16rem;\n        margin-top: -1.16rem;\n        background:  rgba(190,241,222, 0.25);\n        -webkit-animation: living 3s 1.5s linear infinite;\n        animation: living 3s 1.5s linear infinite;\n      }\n      &_two {\n        width: 2rem;\n        height: 2rem;\n        border-radius: 50%;\n        margin-left: -1rem;\n        margin-top: -1rem;\n        background:  #BEF1DE;\n        -webkit-animation: living 3s 3s linear infinite;\n        animation: living 3s 3s linear infinite;\n      }\n      &_three {\n        width: 1.68rem;\n        height: 1.68rem;\n        border-radius: 50%;\n        margin-left: -0.84rem;\n        margin-top: -0.84rem;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        color: #fff;\n        font-size: 0.32rem;\n        background:  #30C58E;\n      }\n    }\n  }\n  @keyframes living {\n  0%{\n    transform: scale(1);\n    opacity: 0.5;  \n  }\n  100%{\n    transform: scale(1.5);  \n    opacity: 0;   /*圆形放大的同时，透明度逐渐减小为0*/\n  }\n}\n  \n```\n"
  },
  {
    "path": "3.5.6 css 获取前三个元素 nth-child.md",
    "content": "# 3.5.6 css 获取前三个元素 nth-child\n\n\n```\n.van-picker-column:nth-child(-n+3) {\n  display: none;\n}\n```\n"
  },
  {
    "path": "3.5.7 图片加载失败处理.md",
    "content": "# 3.5.7 图片加载失败处理\n\n>利用图片加载失败，触发 `<img>` 元素的 onerror 事件，给加载失败的 `<img>` 元素新增一个样式类  \n利用新增的样式类，配合 `<img>` 元素的伪元素，展示默认兜底图的同时，还能一起展示 `<img> `元素的 alt 信息\n\n```\n<img src=\"https://s2.best-wallpaper.net/waller/iphone/2005/Some-mushrooms-vegetable-table_iphone_1242x2688.jpg\" \n  alt=\"图片alt信息图片alt信息图片alt信息\" \n  onerror=\"this.classList.add('error');\">\n  \n  img.error {\n    position: relative;\n    display: inline-block;\n    width: 150px;\n    height: 100px;\n}\n\nimg.error::before {\n    content: \"\";\n    position: absolute;\n    top: 0;\n    left: 0;\n    bottom: 0;\n    right: 0;\n    background: #999;\n    background: url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBw8QEA8PERAPEA4PEA0OEA8PDw8NDw8QFREWFhURFRUYHSggGBolGxUVITEhJSkrLi4uFx8zODMsNygtLisBCgoKBQUFDgUFDisZExkrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrK//AABEIAP0AyAMBIgACEQEDEQH/xAAZAAEBAQEBAQAAAAAAAAAAAAAAAgEDBwT/xAAnEAEBAAIBBAICAgMBAQAAAAAAAQIRIQMxQVESgWFxE5EisdHhof/EABQBAQAAAAAAAAAAAAAAAAAAAAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwD2CKTFAAAAAAAAAAy2TuDRz+dvb+63p5XmXvAWAAAAyxoDn0749Jyu7+ldTjlzxxt/9BVp3/6qdP3yuQEfx3zlR0ATFJigAAAAAAGZZSMz3q6T08J3A+Vvaa/NbOnPPP7Mc5vR1M9agK0nLHmWE6k1tHq23nxAdhy5x87n+nUAAAAAAAAAAExSYoAAAAAABzw4uvuOiOpPPmA2Y+XPe7b9Lyz4/bnAVZxVXVjn8vXK50/q/gGZXjXmuqccNKAAAAAAAAAABKb1PXLcsdqkBzsy77+ovGqRhNcAsAAAAAHDLG9l49L3/ToAyTTQAAAAAAAAAE5ZyJ3b24B0HLLH46vf2wHSuc6t+m9S8ftkgKyu+Pupxxl8cxt4u/bbed/2Dene89Lc+nzbfqOgAAAAAAAAAAAAAAIy6kn7Z/lfxGY8W/23q52a4BWGEikdS6icMqDpl25HPW93+oA3Pwj5Lyx2rHGTwCMZfpU6c9LAAAAAAAAAAZaDRkrQAAAAR1J59KlKjC63PXIM6l3deiJxvn2bt7QFY3V57eGLxxvnkBsUmKAAAAAAABmWUncGst0j5W9uPzWzp++QZ87e0+z+Pffn/ToA59Pi2fcdHLLKb87npeOUoKAAAAR1MN9lgOePSn7XGgAAJikxQAAAAAACepNxQCcLuJ6dvknF17VndS0HPO7vdtt1+7pOKrNwG5TWvXZmXFlLnxq72Sb16n/0HUIAAAAAAAAAmKSy9T1yC0/Odk/G3vfqGWHHHcHQThltQAAAAI6mO5+ZyjPLep912cv4uQT8lTC3v/UdJJGgyYtAAAAAAAAAAAEXHapIjOW9jO6x/PYFzTMspHLCLx5t/AMxy14sjpKnC+KzpeZ+QdAAAAAAAAAAAAAABOWcn/EfO+tQHUAAAEued3ZPS7lrX25S+fYKb53O/mMmFv4/26Y4SAjd7/G7X08dKAAAAAAAAAAAAcst22b49ArLOfu/hlmV78Q4i/lxsGY4yNym4jHqxueXifdBvTvHKnKS95d/teGWwUACMptuOMhFAAAAAAAAAAAAAAAOfUna+nRlgMuMqepxNHT449Jyu7+gbI3Dvf7Tts/y8X9g2XVs8dzp97fdP477q5AaACYpMUAAAAACcspAUy5SI3b24jZ05+77Bnyt7T7phbuyrkTnjzLAWAAAAADn1PbnjjX0AOePTn7dIAAAAAJikxQAAAAJz7VnTxndbljxdfcBWPUlujqZ6b8Z3ct7toL/AJOL+E2dt757RtnFbvcBl487jq45dpPNdYDQAAAAAAAAAAATFOef+l43YNAAAAR1Z58xYDnllx+0TsZYXsrHp+wTMvU2uYfV/C5NNBOOEigAAAAAAAAAAAABLMOPTM52ZhhLvgHUR0r3nqrAAAAAAAAAAAAAAAGZXTlM7lfQOmWcif8AK/iKmEigcssfjq/26lm04zXAKAB//9k=);\n}\n\nimg.error::after {\n    content: attr(alt);\n    position: absolute;\n    left: 0;\n    bottom: 0;\n    right: 0;\n    padding: 5px 10px;\n    background: #000;\n    color: #eee;\n    font-size: 12px;\n    text-align: center;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    \n}\n```\n\n\n## 参考\n\n- [codepen](https://codepen.io/Chokcoco/pen/WNGgNqv?editors=1100)\n"
  },
  {
    "path": "3.5.8 object-fit.md",
    "content": "# 3.5.8 object-fit\n\n>object-fit 还有一个配套属性 object-position，它可以控制图片在其内容框中的位置。（类似于 background-position），  \n>m默认是 object-position: 50% 50%，如果你不希望图片居中展示，可以使用它去改变图片实际展示的 position 。\n\n```\nobject-fit: fill;\n\n\nfill | contain | cover | none | scale-down\n\ncontain\n被替换的内容将被缩放，以在填充元素的内容框时保持其宽高比。 整个对象在填充盒子的同时保留其长宽比，因此如果宽高比与框的宽高比不匹配，该对象将被添加“黑边”。\ncover\n被替换的内容在保持其宽高比的同时填充元素的整个内容框。如果对象的宽高比与内容框不相匹配，该对象将被剪裁以适应内容框。\nfill\n被替换的内容正好填充元素的内容框。整个对象将完全填充此框。如果对象的宽高比与内容框不相匹配，那么该对象将被拉伸以适应内容框。\nnone\n被替换的内容将保持其原有的尺寸。\nscale-down\n内容的尺寸与 none 或 contain 中的一个相同，取决于它们两个之间谁得到的对象尺寸会更小一些。\n```\n\n## 参考\n- [caniuse](https://www.caniuse.com/?search=object-fit)\n"
  },
  {
    "path": "3.5.9 scrollbar-gutter.md",
    "content": "# 3.5.9 scrollbar-gutter\n \n \n## scrollbar-color 和 scrollbar-width\n>这两个 CSS 属性只有 Firefox 浏览器支持\n \n ```\n 其中 scrollbar-color 可以定义滚动条的颜色，语法如下：\n\nscrollbar-color: auto | 滑杆颜色 轨道颜色;\nscrollbar-width 可以设置滚动条的宽度，不过这个宽度不能随意指定，是有约束的，语法如下所示：\n\nscrollbar-width: auto | thin | none;\nauto 就是默认的尺寸，在 Windows 系统下是 17px；\nthin 是窄滚动条，在 Windows 系统下是 8px；\nnone 没有滚动条，宽度为0，但是内容依然可以滚动\n ```\n \n- demo\n\n```\n<h4 class=\"fill\">窄滚动条</h4>\n<div class=\"scroll scroll-thin\">\n    <p>根据测试，窄滚动条在Windows操作系统中占据的宽度是8px。</p>\n    <p>测试方法很简单，滚动容器的宽度减去子元素的宽度即可。</p>\n    <p>打开控制台，审查对应的元素，就可以看到容器的宽度和子元素的宽度，两者相互减，就是滚动条的宽度了。</p>\n</div>\n\n<h4 class=\"fill\">无滚动条-可滚动</h4>\n<div class=\"scroll scroll-none\">\n    <p>根据测试，窄滚动条在Windows操作系统中占据的宽度是8px。</p>\n    <p>测试方法很简单，滚动容器的宽度减去子元素的宽度即可。</p>\n    <p>打开控制台，审查对应的元素，就可以看到容器的宽度和子元素的宽度，两者相互减，就是滚动条的宽度了。</p>\n</div>\nCSS：\n.scroll {\n    border: 1px solid #665;\n    height: 150px;\n    overflow: auto;\n    --thumb-color: #bbb;\n    --track-color: #ddd;\n}\n.scroll-thin {\n    scrollbar-width: thin;\n    scrollbar-color: var(--thumb-color) var(--track-color);\n}\n.scroll-none {\n    scrollbar-width: none;\n}\n.scroll-thin::-webkit-scrollbar {\n    width: 8px; height: 8px;\n}\n.scroll-thin::-webkit-scrollbar-thumb {\n    background-color: var(--thumb-color);\n}\n.scroll-thin::-webkit-scrollbar-track {\n    background-color: var(--track-color);\n}\n.scroll-none::-webkit-scrollbar {\n    width: 0; height: 0;\n}\n\n```\n \n## scrollbar-gutter\n \n>scrollbar-gutter 可以让滚动条出现的时候内容不晃动,可以提前预留滚动条位置\n \n```\n /* Initial value */\nscrollbar-gutter: auto;\n\n/* \"stable\" keyword, with optional modifier */\nscrollbar-gutter: stable;\nscrollbar-gutter: stable both-edges;\n\n/* Global values */\nscrollbar-gutter: inherit;\nscrollbar-gutter: initial;\nscrollbar-gutter: revert;\n```\n\n- auto\n  - 就是默认的表现。没有滚动条的时候，内容尽可能占据宽度，有了滚动条，可用宽度减小。\n- stable\n  - 如果 overflow 属性计算值不是 visible，则提前预留好空白区域，这样滚动条出现的时候，整个结构和布局都是稳定的。\n- both-edges\n  -这个是让左右两侧同时预留好空白区域，目的是让局部绝对居中对称\n\n\n## 参考\n- [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-gutter)\n"
  },
  {
    "path": "3.6.0. place-items.md",
    "content": "# 3.6.0. place-items\n\n>CSS 中的 place-items 是一个简写属性 ，\n它允许你在相关的布局（如 Grid 或 Flexbox）中可以同时沿着块级和内联方向对齐元素 (例如：align-items 和 justify-items 属性) 。\n如果未提供第二个值，则第一个值作为第二个值的默认值\n\n该属性是以下两个 CSS 属性的简写：\n\n- align-items\n- justify-items\n\n```\n/* Keyword values */\nplace-items: auto center;\nplace-items: normal start;\n\n/* Positional alignment */\nplace-items: center normal;\nplace-items: start auto;\nplace-items: end normal;\nplace-items: self-start auto;\nplace-items: self-end normal;\nplace-items: flex-start auto;\nplace-items: flex-end normal;\nplace-items: left auto;\nplace-items: right normal;\n\n/* Baseline alignment */\nplace-items: baseline normal;\nplace-items: first baseline auto;\nplace-items: last baseline normal;\nplace-items: stretch auto;\n\n/* Global values */\nplace-items: inherit;\nplace-items: initial;\nplace-items: unset;\n```\n\n\n\n\n## 参考\n- [MDN](https://developer.mozilla.org/zh-CN/docs/Web/CSS/place-items)\n"
  },
  {
    "path": "4.0 javascript 代码规范.md",
    "content": "# javascript 代码规范\n\n\n\n## 参考\n- Airbnb JavaScript Style Guide: https://github.com/airbnb/javascript\n"
  },
  {
    "path": "4.0.1 JavaScript 开发者应懂的 33 个概念.md",
    "content": "# JavaScript 开发者应懂的 33 个概念\n\n\n- [1.调用堆栈](#1调用堆栈)\n- [2.原始类型](#2原始类型)\n- [3.值类型和引用类型](#3值类型和引用类型)\n- [4.隐式, 显式, 名义和鸭子类型](#4隐式-显式-名义和鸭子类型)\n- [5.== 与 ===, typeof 与 instanceof](#5-vs--typeof-vs-instanceof)\n- [6.this, call, apply 和 bind](#6-this-call-apply-和-bind)\n- [7.函数作用域, 块级作用域和词法作用域](#7-函数作用域-块级作用域和词法作用域)\n- [8.闭包](#8闭包)\n- [9.map, reduce, filter 等高阶函数](#9-map-reduce-filter-等高阶函数)\n- [10.表达式和语句](#10表达式和语句)\n- [11.变量提升](#11变量提升)\n- [12.Promise](#12Promise)\n- [13.立即执行函数, 模块化, 命名空间](#13-立即执行函数-模块化-命名空间)\n- [14.递归](#14递归)\n- [15.算法](#15算法)\n- [16.数据结构](#16数据结构)\n- [17.消息队列和事件循环](#17消息队列和事件循环)\n- [18.setTimeout, setInterval 和 requestAnimationFrame](#)\n- [19.继承, 多态和代码复用](#)\n- [20.按位操作符, 类数组对象和类型化数组](#)\n- [21.DOM 树和渲染过程](#)\n- [22.new 与构造函数, instanceof 与实例](#)\n- [23.原型继承与原型链](#)\n- [24.Object.create 和 Object.assign](#)\n- [25.工厂函数和类](#)\n- [26.设计模式](#)\n- [27.Memoization](#)\n- [28.纯函数, 函数副作用和状态变化](#)\n- [29.耗性能操作和时间复杂度](#)\n- [30.JavaScript 引擎](#)\n- [32.二进制, 十进制, 十六进制, 科学记数法](#)\n- [33.偏函数, 柯里化, Compose 和 Pipe](#)\n- [34.代码整洁之道](#)\n\n## 1.调用堆栈\n- [MDN](https://developer.mozilla.org/zh-CN/docs/Glossary/Call_stack)\n>调用栈是解析器(如浏览器中的的javascript解析器)的一种机制，可以在脚本调用多个函数时，跟踪每个函数在完成执行时应该返回控制的点。（如什么函数正在执行，什么函数被这个函数调用，下一个调用的函数是谁）  \n- 当脚本要调用一个函数时，解析器把该函数添加到栈中并且执行这个函数。\n- 任何被这个函数调用的函数会进一步添加到调用栈中，并且运行到它们被上个程序调用的位置。\n- 当函数运行结束后，解释器将它从堆栈中取出，并在主代码列表中继续执行代码。\n- 如果栈占用的空间比分配给它的空间还大，那么则会导致“堆栈溢出”错误。\n\n### 1.1执行上下文的类型\nJavaScript 中有三种执行上下文类型。\n\n- 全局执行上下文 — 这是默认或者说基础的上下文，任何不在函数内部的代码都在全局上下文中。  \n  它会执行两件事：创建一个全局的 window 对象（浏览器的情况下），并且设置 this 的值等于这个全局对象。  \n  一个程序中只会有一个全局执行上下文。\n- 函数执行上下文 — 每当一个函数被调用时, 都会为该函数创建一个新的上下文。  \n  每个函数都有它自己的执行上下文，不过是在函数被调用时创建的。  \n  函数上下文可以有任意多个。  \n  每当一个新的执行上下文被创建，它会按定义的顺序（将在后文讨论）执行一系列步骤。\n- Eval 函数执行上下文 — 执行在 eval 函数内部的代码也会有它属于自己的执行上下文，但由于 JavaScript 开发者并不经常使用 eval，所以在这里我不会讨论它。\n\n链接：https://juejin.im/post/5ba32171f265da0ab719a6d7\n\n\n\n## 2.原始类型\n\n基本类型（基本数值、基本数据类型）是指非 对象 并且无方法的数据。\n\n在 JavaScript 中，共有6种基本数据类型：string，number，boolean，null，undefined，symbol\n\n- [MDN-原始数据](https://developer.mozilla.org/zh-CN/docs/Glossary/Primitive)\n\n\n## 3.值类型和引用类型\n\n- JavaScript中的变量类型有哪些？\n\n  - （1）值类型：字符串（string）、数值（number）、布尔值（boolean）、none、undefined\n\n  - （2）引用类型：对象（Object）、数组（Array）、函数（Function）\n\n- [js中的值类型和引用类型的区别](https://www.cnblogs.com/leiting/p/8081413.html)\n  - 值类型：1、占用空间固定，保存在栈中\n    - 2、保存与复制的是值本身\n    - 3、使用typeof检测数据的类型\n    - 4、基本类型数据是值类型\n  - 引用类型：1、占用空间不固定，保存在堆中\n    - 2、保存与复制的是指向对象的一个指针\n    - 3、使用instanceof检测数据类型\n    - 4、使用new()方法构造出的对象是引用型\n\n\n## 4.隐式, 显式, 名义和鸭子类型\n\n```\nvar a = 42;\nvar b = a + \"\"; // 隐式强制类型转换\nvar c = String( a ); // 显式强制类型转换\n\nconsole.log(1+ + '2'+'2')   // 32\n```\n\n- 隐式类型转换\n  - 不同的数据类型之间可以做运算，是因为JavaScript引擎在运算之前会悄悄的把他们进行了隐式类型转换的\n  - 比如，一个字符串可以和数字相加。\n  - 如果字符串和数字相加，JavaScript会自动把数字转换成字符的，不管数字在前还是字符串在前\n  - 1+null  // 1\n  - 1+undefined // NaN\n  - 1+NaN // NaN\n- 显式解析数字字符串\n  - 解析字符串中的数字和将字符串强制类型转换为数字的返回结果都是数字。但解析和转换两者之间还是有明显的差别。\n  - var a = \"42\";\n  - var b = \"42px\";\n  - Number( a ); // 42\n  - parseInt( a ); // 42\n  - Number( b ); // NaN\n  - parseInt( b ); // 42\n  - 解析允许字符串中含有非数字字符，解析按从做到右的顺序，如果遇到非数字字符就停止。而转换不允许出现非数字字符，否则会失败并返回NaN。\n\n- []+{} //\"[object Object]\"\n- {}+[] // 0\n  - 这是两个复杂数据结构相加的例子，**js的隐式类型转换**\n  - 原因是有的js解释器会将开头的 {} 看作一个代码块，而不是一个js对象，于是真正参与运算的是+[]，就是将[]转换为number，于是得出答案0\n  - 那么我们如何证明{}被看作一个代码块而不是一个js对象呢？很简单， 我们可以在{}里写上一些语句，比如:\n  ```{console.log(\"hello\")} + [] ```\n- Number(undefined) // NaN\n- Number(null)  // 0\n- Number(NaN) // NaN\n- Number(true)  // 1\n- Number(false)  // 0\n\n- ToString\n  - 它负责处理非字符串到字符串的强制类型转换。\n  - 基本类型值的字符串化规则为：null转换为“null”，undefined转换为“undefined”，true转换为“true”。\n  - 数字的字符串化则遵循通用规则。不过那些极小和极大的数字使用指数形式：\n  ```\n  // 1.07 连续乘以七个 1000\n  var a = 1.07 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000;\n  // 七个1000一共21位数字\n  a.toString(); // \"1.07e21\"\n  ```\n- JSON字符串化: JSON.stringify(..)并不是强制类型转换\n```\nJSON.stringify( 42 ); // \"42\"\nJSON.stringify( \"42\" ); // \"\"42\"\" （含有双引号的字符串）\nJSON.stringify( null ); // \"null\"\nJSON.stringify( true ); // \"true\"\n\n```\n  - 所有安全的JSON值都可以使用JSON.stringify(..)字符串化。安全的JSON值是指你能够呈现为有效JSON格式的值。\n  - JSON.stringify(..)在对象中遇到undefined、function和symbol时会自动将其忽略，在数组中则会返回null（以保证单元位置不变）。\n  ```\n  JSON.stringify(undefined); // undefined\n  JSON.stringify(function () { }); // undefined\n  JSON.stringify(\n      [1, undefined, function () { }, 4]\n  ); // \"[1,null,null,4]\"\n  JSON.stringify(\n      { a: 2, b: function () { } }\n  ); // \"{\"a\":2}\"\n  ```\n  - 向JSON.stringify(..)传递一个可选参数replacer，它可以是数组或者函数，用来指定对象序列化过程中哪些属性应该被处理，哪些应该被排除。\n    - 如果replacer是一个数组，那么它必须是一个字符串数组，其中包含序列化要处理的对象的属性名称，除此之外其他的属性则被忽略。\n    - 如果replacer是一个函数，它会对对象本身调用一次，然后对对象中的每个属性各调用一次，每次传递两个参数，键和值。如果要忽略某个键就返回undefined，否则返回指定的值。\n  ```\n  var a = {\n      b: 42,\n      c: \"42\",\n      d: [1, 2, 3]\n  };\n  JSON.stringify(a, [\"b\", \"c\"]); // \"{\"b\":42,\"c\":\"42\"}\"\n  JSON.stringify(a, function (k, v) {\n      if (k !== \"c\") return v;\n  });\n      // \"{\"b\":42,\"d\":[1,2,3]}\"\n  ```\n\n  - JSON.string还有一个可选参数space，用来指定输出的缩进格式。\n    - space为正整数时是指定每一级缩进的字符数，它还可以是字符串，此时最前面的十个字符被用于每一级的缩进：\n    ```\n    var a = {\n        b: 42,\n        c: \"42\",\n        d: [1, 2, 3]\n    };\n    JSON.stringify(a, null, 3);\n    // \"{\n    // \"b\": 42,\n    // \"c\": \"42\",\n    // \"d\": [\n    // 1,\n    // 2,\n    // 3\n    // ]\n    // }\"\n    JSON.stringify( a, null, \"-----\" );\n    // \"{\n    // -----\"b\": 42,\n    // -----\"c\": \"42\",\n    // -----\"d\": [\n    // ----------1,\n    // ----------2,\n    // ----------3\n    // -----]\n    // }\"\n    ```\n\n- **奇特的 `~` 运算符**\n\n- indexOf(..)不仅能够得到子字符串的位置，还可以用来检查字符串中是否包含指定的子字符串，相当于一个条件判断。例如：\n```\nvar a = \"Hello World\";\nif (a.indexOf(\"lo\") >= 0) { // true\n    // 找到匹配！\n}\nif (a.indexOf(\"lo\") != -1) { // true\n    // 找到匹配！\n}\nif (a.indexOf(\"ol\") < 0) { // true\n    // 没有找到匹配！\n}\nif (a.indexOf(\"ol\") == -1) { // true\n    // 没有找到匹配！\n}\n```\n- = 0 和== -1这样的写法不是很好，**称为“抽象渗漏”**，意思是在代码中暴露了底层的实现细节，这里是指用-1作为失败时的返回值，这些细节应该被屏蔽掉。\n  - 现在我们终于明白有什么用处了！和indexOf()一起可以将结果强制类型转换（实际上仅仅是转换）为真/假值：\n\n```\nvar a = \"Hello World\";\n~a.indexOf(\"lo\"); // -4 <-- 真值!\nif (~a.indexOf(\"lo\")) { // true\n    // 找到匹配！\n}\n~a.indexOf(\"ol\"); // 0 <-- 假值!\n!~a.indexOf(\"ol\"); // true\nif (!~a.indexOf(\"ol\")) { // true\n    // 没有找到匹配！\n}\n```\n\n如果indexOf(..)返回-1，`~`将其转换为假值0，其他情况一律转换为真值。\n\n\n- 假值的相等比较\n```\n\"0\" == null; // false\n\"0\" == undefined; // false\n\"0\" == false; // true -- 晕！\n\"0\" == NaN; // false\n\"0\" == 0; // true\n\"0\" == \"\"; // false\nfalse == null; // false\nfalse == undefined; // false\nfalse == NaN; // false\nfalse == 0; // true -- 晕！\nfalse == \"\"; // true -- 晕！\nfalse == []; // true -- 晕！\nfalse == {}; // false\n\"\" == null; // false\n\"\" == undefined; // false\n\"\" == NaN; // false\n\"\" == 0; // true -- 晕！\n\"\" == []; // true -- 晕！\n\"\" == {}; // false\n0 == null; // false\n0 == undefined; // false\n0 == NaN; // false\n0 == []; // true -- 晕！\n0 == {}; // false\n3、极端情况\n\n[] == ![] // true\n```\n- [你不知道的JavaScript（中卷）|强制类型转换](https://www.jianshu.com/p/777a89b4ed9a)\n_____________________________\n______________________________\n\n- 编程语言按照数据类型大体可以分为两类，一类是静态类型语言，另一类是动态类型语言\n  - 静态类型语言在编译时便已确定变量的类型，\n  - 动态类型语言的变量类型要到程序运行的时候，待变量被赋予某个值之后，才会具有某种类型。\n\n- 鸭子类型指导我们只关注对象的行为，而不关注对象本身，\n\n\n## 5.== vs ===, typeof vs instanceof\n- `===` 严格相等，会比较两个值的类型和值\n- `==`  抽象相等，比较时，会先进行类型转换，然后再比较值\n- `==` 涉及到隐式类型转换，`<`（小于号） 也涉及到隐式类型转换。\n- typeof：\n\njs 在底层存储变量的时候，会在变量的机器码的低位1-3位存储其类型信息👉\n\n000：对象  \n010：浮点数  \n100：字符串  \n110：布尔  \n1：整数  \n\nbut, 对于 undefined 和 null 来说，这两个值的信息存储是有点特殊的。  \nnull：所有机器码均为0  \nundefined：用 −2^30 整数来表示  \n所以，typeof 在判断 null 的时候就出现问题了，由于 null 的所有机器码均为0，因此直接被当做了对象来看待。  \n\n```\n// Numbers\ntypeof 37 === 'number';\ntypeof 3.14 === 'number';\ntypeof Math.LN2 === 'number';\ntypeof Infinity === 'number';\ntypeof NaN === 'number'; // 尽管NaN是\"Not-A-Number\"的缩写,意思是\"不是一个数字\"\ntypeof Number(1) === 'number'; // 不要这样使用!\n\n// Strings\ntypeof \"\" === 'string';\ntypeof \"bla\" === 'string';\ntypeof (typeof 1) === 'string'; // typeof返回的肯定是一个字符串\ntypeof String(\"abc\") === 'string'; // 不要这样使用!\n\n// Booleans\ntypeof true === 'boolean';\ntypeof false === 'boolean';\ntypeof Boolean(true) === 'boolean'; // 不要这样使用!\ntypeof Boolean(0) == 'boolean';\ntypeof new Boolean(0) == 'object';\n\n// Symbols\ntypeof Symbol() === 'symbol';\ntypeof Symbol('foo') === 'symbol';\ntypeof Symbol.iterator === 'symbol';\n\n// Undefined\ntypeof undefined === 'undefined';\ntypeof blabla === 'undefined'; // 一个未定义的变量,或者一个定义了却未赋初值的变量\n\n// Objects\ntypeof {a:1} === 'object';\n\n// 使用Array.isArray或者Object.prototype.toString.call方法可以从基本的对象中区分出数组类型\ntypeof [1, 2, 4] === 'object';\n\ntypeof new Date() === 'object';\n\n// 下面的容易令人迷惑，不要这样使用！\ntypeof new Boolean(true) === 'object';\ntypeof new Number(1) ==== 'object';\ntypeof new String(\"abc\") === 'object';\n\n// 函数\ntypeof function(){} === 'function';\ntypeof Math.sin === 'function';\n\n\ntypeof来判断数据类型其实并不准确。比如数组、正则、日期、对象的typeof返回值都是object，这就会造成一些误差。\n```\n\n-  instance_of\n\n```\nfunction new_instance_of(leftVaule, rightVaule) { \n    let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值\n    leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值\n    while (true) {\n    \tif (leftVaule === null) {\n            return false;\t\n        }\n        if (leftVaule === rightProto) {\n            return true;\t\n        } \n        leftVaule = leftVaule.__proto__ \n    }\n}\n```\n- 其实 instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。\n- 因此，instanceof 在查找的过程中会遍历左边变量的原型链，直到找到右边变量的 prototype，\n- 如果查找失败，则会返回 false，告诉我们左边变量并非是右边变量的实例。\n\n\n## 6. this, call, apply 和 bind\n\n- call与apply:\n\n```\nvar obj = {\n  name: 'linxin'\n}\n\nfunction func(age, sex) {\n  console.log(this.name,age,sex);\n}\n\nfunc.call(obj,12,'女');         // linxin 12 女\nfunc.apply(obj, [18, '女']);        //linxin 18 女\n```\n\n- 模拟实现\n\n```\nFunction.prototype.newCall = function(context) {\n  context.fn = this;  // 通过this获取call的函数\n  context.fn();\n  delete context.fn;\n}\nlet foo = {\n  value: 1\n}\nfunction bar() {\n  console.log(this.value);\n}\nbar.newCall (foo); // 1\n\n但是如果说有传参数呢？\n所以我们可以进行优化一下，因为传入的参数数量是不确定的，所以我们可以从Arguments对象中去获取，这个比较简单。\n问题是参数是不确定的，我们如何传入到我们要执行的函数中去呢 ？ 这里我们有两种选择：一种是通过eval拼接的方式，另一种就要用到es6了。\n\nFunction.prototype.newCall = function(context, ...parameter) {\n  if (typeof context === 'object') {\n    context = context || window\n  } else {\n    context = Object.create(null)\n  }\n  let fn = Symbol()\n  context[fn] = this\n  context[fn](...parameter);\n  delete context[fn]\n}\nlet person = {\n  name: 'Abiel'\n}\nfunction sayHi(age,sex) {\n  console.log(this.name, age, sex);\n}\nsayHi.newCall (person, 25, '男'); // Abiel 25 \n\n实现了call之后，apply也是同样的思路。\n===========================================\napply实现：\n\nFunction.prototype.newApply = function(context, parameter) {\n  if (typeof context === 'object') {\n    context = context || window\n  } else {\n    context = Object.create(null)\n  }\n  let fn = Symbol()\n  context[fn] = this\n  context[fn](...parameter);\n  delete context[fn]\n}\n```\n\n- bind\n```\n初体验：\n\nFunction.prototype.bind = function (context) {\n  var me = this\n  return function () { // bind之后得到的函数\n    return me.call(context)  // 执行是改变this执行\n  }\n}\n\n\n加入参数：\n\nFunction.prototype.bind = function (context,...innerArgs) {\n  var me = this\n  return function (...finnalyArgs) {\n    return me.call(context,...innerArgs,...finnalyArgs)\n  }\n}\nlet person = {\n  name: 'Abiel'\n}\nfunction sayHi(age,sex) {\n  console.log(this.name, age, sex);\n}\nlet personSayHi = sayHi.bind(person, 25)\npersonSayHi('男')\n```\n\n\n\n- [call、apply和bind的实现 ](https://github.com/Abiel1024/blog/issues/16)\n\n\n\n## 7. 函数作用域, 块级作用域和词法作用域\n>不要在块内声明一个函数（严格模式会报语法错误）。如果确实需要在块中定义函数，可以使用函数表达式来声明函数。\n\n```\n/* Recommended */\nif (x) {\n    var foo = function() {};\n}\n```\n>作用域就是一套规则，用于确定在何处以及如何查找变量（标识符）的规则\n\n>作用域有两种主要工作模型：词法作用域和动态作用域。\n\n- JavaScript采用词法作用域(lexical scoping)，也就是静态作用域。\n- 词法作用域注重的是所谓的Write-Time，即编程时的上下文，而动态作用域以及常见的this的用法，都是Run-Time，即运行时上下文。  \n  词法作用域关注的是函数在何处被定义，而动态作用域关注的是函数在何处被调用。  \n  JavaScript是典型的词法作用域的语言，即一个符号参照到语境中符号名字出现的地方，局部变量缺省有着词法作用域。\n- bash就是动态作用域\n\n- JavaScript 引擎创建了执行上下文栈（Execution context stack，ECS）来管理执行上下文\n\n```\nvar scope = \"global scope\";\nfunction checkscope(){\n    var scope = \"local scope\";\n    function f(){\n        return scope;\n    }\n    return f();\n}\ncheckscope();复制代码var scope = \"global scope\";\nfunction checkscope(){\n    var scope = \"local scope\";\n    function f(){\n        return scope;\n    }\n    return f;\n}\ncheckscope()();复制代码两段代码执行的结果一样，但是两段代码究竟有哪些不同呢？\n答案就是执行上下文栈的变化不一样。\n\n链接：https://juejin.im/post/58eaecdea0bb9f0069271861\n\n```\n- [冴羽的博客](https://github.com/mqyqingfeng/Blog)\n\n## 8.闭包\n- mdn: 闭包是函数和声明该函数的词法环境的组合。\n- \n```\nvar name = \"The Window\";\n　　var object = {\n　　　　name : \"My Object\",\n　　　　getNameFunc : function(){\n　　　　　　return function(){\n　　　　　　　　return this.name;\n　　　　　　};\n　　　　}\n　　};\n　　alert(object.getNameFunc()());\n运行结果：The Window\n\n解释：object.getNameFunc()这是属于方法调用，this指针指向的是object，可以用一个变量tmp引用它的结果，  \n实际上tmp就是这个方法返回的那个匿名函数function(){return this.name;};，此时并没有执行内部代码，执行tmp()时，  \n也就是object.getNameFunc()()时，属于函数调用（另一篇博文详解了这里，链接），this指针指向window，最终返回The Window。\n\n题目二\nvar name = \"The Window\";\n　　var object = {\n　　　　name : \"My Object\",\n　　　　getNameFunc : function(){\n　　　　　　var that = this;\n　　　　　　return function(){\n　　　　　　　　return that.name;\n　　　　　　};\n　　　　}\n　　};\n　　alert(object.getNameFunc()());\n运行结果：My Object\n\n解释：在调用getNameFunc()时，属于方法调用，那么this指针指向object，把它被that引用，那么返回的匿名函数中时刻保持对object的引用\n\n链接：https://www.jianshu.com/p/796e903754f1\n\n```\n\n## 9. map, reduce, filter 等高阶函数\n\n>高阶函数(higher-order function)指操作函数的函数，一般地，有以下两种情况  \n1、函数可以作为参数被传递  \n2、函数可以作为返回值输出\n\n```\nforEach: \n// 对于古董浏览器，如IE6-IE8\n\nif (typeof Array.prototype.forEach != \"function\") {\n  Array.prototype.forEach = function (fn, context) {\n    for (var k = 0, length = this.length; k < length; k++) {\n      if (typeof fn === \"function\" && Object.prototype.hasOwnProperty.call(this, k)) {\n        fn.call(context, this[k], k, this);\n      }\n    }\n  };\n}\n\n=============================================\nmap: \n\n让IE6-IE8浏览器也支持map方法：\n\nif (typeof Array.prototype.map != \"function\") {\n  Array.prototype.map = function (fn, context) {\n    var arr = [];\n    if (typeof fn === \"function\") {\n      for (var k = 0, length = this.length; k < length; k++) {      \n         arr.push(fn.call(context, this[k], k, this));\n      }\n    }\n    return arr;\n  };\n}\n\n=================================================\n\nfilter:\n\nif (typeof Array.prototype.filter != \"function\") {\n  Array.prototype.filter = function (fn, context) {\n    var arr = [];\n    if (typeof fn === \"function\") {\n       for (var k = 0, length = this.length; k < length; k++) {\n          fn.call(context, this[k], k, this) && arr.push(this[k]);\n       }\n    }\n    return arr;\n  };\n}\n\n==============================================\nsome:\n\nIE6-IE8扩展如下：\n\nif (typeof Array.prototype.some != \"function\") {\n  Array.prototype.some = function (fn, context) {\n\tvar passed = false;\n\tif (typeof fn === \"function\") {\n   \t  for (var k = 0, length = this.length; k < length; k++) {\n\t\t  if (passed === true) break;\n\t\t  passed = !!fn.call(context, this[k], k, this);\n\t  }\n    }\n\treturn passed;\n  };\n}\n\n=========================================\nevery: \nE6-IE8扩展（与some相比就是true和false调换一下）：\n\nif (typeof Array.prototype.every != \"function\") {\n  Array.prototype.every = function (fn, context) {\n    var passed = true;\n    if (typeof fn === \"function\") {\n       for (var k = 0, length = this.length; k < length; k++) {\n          if (passed === false) break;\n          passed = !!fn.call(context, this[k], k, this);\n      }\n    }\n    return passed;\n  };\n}\n\n==========================================\n\nindexOf:\n\nif (typeof Array.prototype.indexOf != \"function\") {\n  Array.prototype.indexOf = function (searchElement, fromIndex) {\n    var index = -1;\n    fromIndex = fromIndex * 1 || 0;\n\n    for (var k = 0, length = this.length; k < length; k++) {\n      if (k >= fromIndex && this[k] === searchElement) {\n          index = k;\n          break;\n      }\n    }\n    return index;\n  };\n}\n```\n\n\n- [ ES5 中新增的 Array 方法详细说明 —— 张鑫旭](https://www.zhangxinxu.com/wordpress/2013/04/es5新增数组方法/)\n\n## 10. 表达式和语句\n\n- 表达式在js中是短语，语句就是js的整句活命令；表达式计算出一个值，但语句用来执行以使某件事发生。《JavaScript权威指南》（第6版）\n\n## 11. 变量提升\n\n- 变量提升即: 将变量声明提升到它所在作用域的最开始的部分\n```\nfunction fn () {\n　　console.log(a); // undefined\n　　var a = 'aaa';\n　　console.log(a); // aaa\n}\n\n实际上上面的代码是按照以下来执行的\n\nfunction fn () {\n　　var a; // 变量提升，函数作用域范围内\n　　console.log(a);\n　　a = 'aaa';\n　　console.log(a);\n}\n```\n\n- 函数提升\n\n  - js中创建函数有两种方式：函数声明式和函数字面量式。只有函数声明才存在函数提升！如:\n\n```\nconsole.log(f1); // function f1() {}   \nconsole.log(f2); // undefined  \nfunction f1() {}\nvar f2 = function() {}\n```\n\n  - 只所以会有以上的打印结果，是由于js中的函数提升导致代码实际上是按照以下来执行的：\n\n```\nfunction f1() {} // 函数提升，整个代码块提升到文件的最开始<br>　　　　　console.log(f1);   \nconsole.log(f2);   \nvar f2 = function() {}\n```\n- [深入理解js的变量提升和函数提升](https://www.cnblogs.com/kawask/p/6225317.html)\n\n## 12. Promise\n\n- [Promise简单实现（正常思路版）](https://www.jianshu.com/p/473cd754311f)\n\n\n- Async - 定义异步函数(async function someName(){...})\n\n  - 自动把函数转换为 Promise\n  - 当调用异步函数时，函数返回值会被 resolve 处理\n  - 异步函数内部可以使用 await\n- Await - 暂停异步函数的执行 (var result = await someAsyncCall();)\n\n  - 当使用在 Promise 前面时，await 等待 Promise 完成，并返回 Promise 的结果\n  - await 只能和 Promise 一起使用，不能和 callback 一起使用\n  - await 只能用在 async 函数中\n- [ JavaScript Async/Await Explained in 10 Minutes](https://segmentfault.com/a/1190000011813934)\n- [原文：JavaScript Async/Await Explained in 10 Minutes](https://tutorialzine.com/2017/07/javascript-async-await-explained)\n\n\n## 13. 立即执行函数, 模块化, 命名空间\n```\n(function(){})()\n\nvar wall = {};\n(function(window, WALL, undefined){\n    \n})(window, wall);\n```\n- 1.包住function(){}的括号的意义: 是为了把function(){}转化为表达式\n```\n~function(){\n  // 代码...\n}();\n或者这种方式：\n\n+function(){\n  // 代码...\n}();\n其实，作用都一样，都是把function(){}转化成一个可执行的表达式，方便执行。\n```\n- 2.第二个括号的意义: 就是执行表达式了\n- 参数的意义:\n```\n参数分为形参和实参。\n function(window, WALL, undefined)三个参数为形参，第二个括号(window, wall)的两个参数为实参。\n 也即可以理解为 window == window，wall == WALL\n```\n\n- 参考：[javascript模块化编程-详解立即执行函数表达式IIFE](https://www.jianshu.com/p/4dbf4a4c8ebb)\n\n===============================================\n- **模块化**\n  - 命名空间：各个模块的命名空间独立。A模块的变量x不会覆盖B模块的变量x。\n  - 模块的依赖关系:通过模块管理工具如webpack/requireJS/browserify等进行管理。\n\n- 模块化的基本原理——解决命名空间冲突\n\n- [谈谈Js前端模块化规范](https://segmentfault.com/a/1190000015991869#articleHeader8)\n\n\n## 14.递归\n\n```\nfunction foo(i) {\n    if (i < 0)\n        return;\n    console.log('begin:' + i);\n    foo(i - 1);\n    console.log('end:' + i);\n}\n\n// begin:3\n// begin:2\n// begin:1\n// begin:0\n// end:0\n// end:1\n// end:2\n// end:3\n\n函数栈，想象一下，每次去把执行的函数压栈，先进后出，执行完毕再出栈\n```\n\n## 15.算法\n![](https://user-gold-cdn.xitu.io/2016/11/29/4abde1748817d7f35f2bf8b6a058aa40?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)\n\n图片名词解释： n: 数据规模 k:“桶”的个数 In-place: 占用常数内存，不占用额外内存 Out-place: 占用额外内存\n\n- **排序分类**\n  - 插入排序： 直接插入排序 和 希尔排序；\n  - 选择排序： 简单选择 和 堆排序\n  - 交换排序： 冒泡排序 和 快速排序\n  - 归并排序\n  - 基数排序\n>这些都是内部排序，要使用内存\n\n- 1.冒泡排序（Bubble Sort）\n>算法描述和实现  \n<1>.比较相邻的元素。如果第一个比第二个大，就交换它们两个；  \n<2>.对每一对相邻元素作同样的工作，从开始第一对到结尾的最后一对，这样在最后的元素应该会是最大的数；  \n<3>.针对所有的元素重复以上的步骤，除了最后一个；  \n<4>.重复步骤1~3，直到排序完成。  \n\n```\nfunction sort(arr) {\n\n  /*for(var i=0;i<arr.length-1;i++){\n    for(var j=i+1;j<arr.length;j++){\n      if(arr[i]>arr[j]){//从arr[0]开始比较大小\n        var tem=arr[i];\n        arr[i]=arr[j];\n        arr[j]=tem\n      }\n    }\n  }*/\n  var len = arr.length;\n    for (var i = 0; i < len; i++) {\n        for (var j = 0; j < len - 1 - i; j++) {\n            if (arr[j] > arr[j+1]) {        //相邻元素两两对比\n                var temp = arr[j+1];        //元素交换\n                arr[j+1] = arr[j];\n                arr[j] = temp;\n            }\n        }\n    }\n\n}\n\nvar arr=[55,88,12,36,1];\nsort(arr)\nconsole.log(arr)\n```\n\n- 2.选择排序（Selection Sort）\n>它的工作原理：首先在未排序序列中找到最小（大）元素，存放到排序序列的起始位置，   \n然后，再从剩余未排序元素中继续寻找最小（大）元素，然后放到已排序序列的末尾  ,  \n以此类推，直到所有元素均排序完毕。  \n首先找到数结构中的最小值并将其放置在第一位，然后找到第二小的值将其放置在第二位...以此类推\n\n>算法描述和实现  \nn个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。  \n<1>.初始状态：无序区为R[1..n]，有序区为空；  \n<2>.第i趟排序(i=1,2,3...n-1)开始时，当前有序区和无序区分别为R[1..i-1]和R(i..n）。  \n该趟排序从当前无序区中-选出关键字最小的记录 R[k]，将它与无序区的第1个记录R交换，  \n使R[1..i]和`R[i+1..n]`分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区；  \n<3>.n-1趟结束，数组有序化了。\n\n```\nfunction selectionSort(arr) {\n    var len = arr.length;\n    var minIndex, temp;\n    console.time('选择排序耗时');\n    for (var i = 0; i < len - 1; i++) {\n        minIndex = i;\n        for (var j = i + 1; j < len; j++) {\n            if (arr[j] < arr[minIndex]) {     //寻找最小的数\n                minIndex = j;                 //将最小数的索引保存\n            }\n        }\n        temp = arr[i];\n        arr[i] = arr[minIndex];\n        arr[minIndex] = temp;\n    }\n    console.timeEnd('选择排序耗时');\n    return arr;\n}\nvar arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];\nconsole.log(selectionSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]\n\n```\n\n- 3.插入排序（Insertion Sort）\n>工作原理是通过构建有序序列，对于未排序数据，在已排序序列中从后向前扫描，找到相应位置并插入。  \n插入排序在实现上，通常采用in-place排序（即只需用到O(1)的额外空间的排序），因而在从后向前扫描过程中，  \n需要反复把已排序元素逐步向后挪位，为最新元素提供插入空间。\n\n>具体算法描述如下：  \n<1>.从第一个元素开始，该元素可以认为已经被排序；  \n<2>.取出下一个元素，在已经排序的元素序列中从后向前扫描；  \n<3>.如果该元素（已排序）大于新元素，将该元素移到下一位置；  \n<4>.重复步骤3，直到找到已排序的元素小于或者等于新元素的位置；  \n<5>.将新元素插入到该位置后；  \n<6>.重复步骤2~5。  \n\n```\nfunction insertion_sort(arr) {\n  var temp;\n  for (var i = 1; i < arr.length; i++) {\n    for (var j = i-1; j >=0; j--) {\n      if (arr[j+1]<arr[j]) {\n        temp=arr[j+1];\n        arr[j+1]=arr[j];\n        arr[j]=temp;\n      }else if (arr[j+1]>=arr[j]) {\n        break;\n      }\n    }\n  }\n  return arr;\n}\nvar a=[11,2,3,445,7,32,71,8,94];\nconsole.log(insertion_sort(a));\n\n\n1、一旦发现arr[j+1]的值不比前面的值小，就可以结束内层循环了，break实现这一功能；\n\n2、内层循环用arr[j+1]的原因：初始时a[j]（即a[i-1]）代表a[i]前一个位置，进入循环后，a[j+1]就表示了a[i]的位置，实现了a[i]和a[i-1]的第一次比较；  \n随着j第一次自减，实际上比较了a[i-1]和a[i-2]；  \n\n依次类推。如果将arr[j+1]改成a[i]是不行的，因为没有实现位置的移动。\n```\n\n- 4.希尔排序（Shell Sort）\n>先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序，具体算法描述：  \n<1>. 选择一个增量序列t1，t2，…，tk，其中ti>tj，tk=1；  \n<2>.按增量序列个数k，对序列进行k 趟排序；  \n<3>.每趟排序，根据对应的增量ti，将待排序列分割成若干长度为m 的子序列，分别对各子表进行直接插入排序。  \n仅增量因子为1 时，整个序列作为一个表来处理，表长度即为整个序列的长度。\n\n```\nfunction shellSort(arr) {\n    var len = arr.length,\n        temp,\n        gap = 1;\n    console.time('希尔排序耗时:');\n    while(gap < len/5) {          //动态定义间隔序列\n        gap =gap*5+1;\n    }\n    for (gap; gap > 0; gap = Math.floor(gap/5)) {\n        for (var i = gap; i < len; i++) {\n            temp = arr[i];\n            for (var j = i-gap; j >= 0 && arr[j] > temp; j-=gap) {\n                arr[j+gap] = arr[j];\n            }\n            arr[j+gap] = temp;\n        }\n    }\n    console.timeEnd('希尔排序耗时:');\n    return arr;\n}\nvar arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];\nconsole.log(shellSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]\n```\n\n- 二分法查找，\n>也称折半查找，是一种在有序数组中查找特定元素的搜索算法。查找过程可以分为以下步骤：  \n（1）首先，从有序数组的中间的元素开始搜索，如果该元素正好是目标元素（即要查找的元素），则搜索过程结束，否则进行下一步。  \n（2）如果目标元素大于或者小于中间元素，则在数组大于或小于中间元素的那一半区域查找，然后重复第一步的操作。  \n（3）如果某一步数组为空，则表示找不到目标元素。\n\n- 参考代码:\n```\n     // 非递归算法\n        function binary_search(arr, key) {\n            var low = 0,\n                high = arr.length - 1;\n            while(low <= high){\n                var mid = parseInt((high + low) / 2);\n                if(key == arr[mid]){\n                    return  mid;\n                }else if(key > arr[mid]){\n                    low = mid + 1;\n                }else if(key < arr[mid]){\n                    high = mid -1;\n                }else{\n                    return -1;\n                }\n            }\n        };\n        var arr = [1,2,3,4,5,6,7,8,9,10,11,23,44,86];\n        var result = binary_search(arr,10);\n        alert(result); // 9 返回目标元素的索引值       \n\n    // 递归算法\n        function binary_search(arr,low, high, key) {\n            if (low > high){\n                return -1;\n            }\n            var mid = parseInt((high + low) / 2);\n            if(arr[mid] == key){\n                return mid;\n            }else if (arr[mid] > key){\n                high = mid - 1;\n                return binary_search(arr, low, high, key);\n            }else if (arr[mid] < key){\n                low = mid + 1;\n                return binary_search(arr, low, high, key);\n            }\n        };\n        var arr = [1,2,3,4,5,6,7,8,9,10,11,23,44,86];\n        var result = binary_search(arr, 0, 13, 10);\n        alert(result); // 9 返回目标元素的索引值  \n```\n\n- 参考：[十大经典排序算法总结（JavaScript描述）](https://juejin.im/post/57dcd394a22b9d00610c5ec8)\n\n\n\n## 16.数据结构\n\n>各种语言在处理堆栈的原理上都大同小异。堆是动态分配内存，内存大小不一，也不会自动释放。  \n栈是自动分配相对固定大小的内存空间，并由系统自动释放。\n\n\n- 参考：[JavaScript中的算法与数据结构](https://www.jianshu.com/nb/16835496)\n  - [js 数据结构](https://www.jianshu.com/p/5e0e8d183102)\n\n\n## 17.消息队列和事件循环\n>主线程运行的时候，产生堆（heap）和栈（stack），栈中的代码调用各种外部API，它们在\"任务队列\"中加入各种事件（click，load，done）。  \n只要栈中的代码执行完毕，主线程就会去读取\"任务队列\"，依次执行那些事件所对应的回调函数。  \n执行栈中的代码（同步任务），总是在读取\"任务队列\"（异步任务）之前执行\n\n- Node.js的运行机制如下。\n\n（1）V8引擎解析JavaScript脚本。\n\n（2）解析后的代码，调用Node API。\n\n（3）libuv库负责Node API的执行。它将不同的任务分配给不同的线程，形成一个Event Loop（事件循环），以异步的方式将任务的执行结果返回给V8引擎。\n\n（4）V8引擎再将结果返回给用户。\n\n\n- [js 运行机制详解-阮一峰](http://www.ruanyifeng.com/blog/2014/10/event-loop.html)\n- [EventLoop-MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop) \n\n\n## 18.setTimeout, setInterval 和 requestAnimationFrame\n\n- setTimeout()的参数：一般常用 两个 参数，如果出现第三个参数，会作为第一个 function 的参数使用\n  - 第一个参数可以是 function/code,可以使用字符串代替function \n```\nsetTimeout(function(a){\n  console.log(a);\n},1000,333)\n一秒后打印出 333\n// 333\n\n\n=======================\nsetTimeout( console.log(1),0 ); // 这种写法 谷歌下已经回提示 \nRefused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: \n\n// 字符串写法\nsetTimeout('console.log(1)',0 );\n```\n- 第三参数写法已经在总结中写过 [setTimeout、setInterval被遗忘的第三个参数](https://github.com/fairyly/front-end-summary/blob/gh-pages/4.5.6%20setTimeout、setInterval被遗忘的第三个参数.md)\n\n- window.requestAnimationFrame: 告诉浏览器您希望执行动画 并请求浏览器 在下一次重绘之前 调用指定的函数来更新动画\n  ```\n  window.requestAnimationFrame(callback);\n  \n  \n  \n  var progress = 0;\n  //回调函数\n  function render() {\n    progress += 1; //修改图像的位置\n \n    if (progress < 100) {\n           //在动画没有结束前，递归渲染\n           window.requestAnimationFrame(render);\n    }\n  }\n \n  //第一帧渲染\n  window.requestAnimationFrame(render);\n  ```\n  - 阿里前端面试题：requestAnimationFrame实现类似setInterval的计时器\n  ```\n  # 没有实现字符串功能\n      function setInterval2 (cb, delay, ...args) {\n\t  // 记录所有正在运行的 interval 用于撤销\n\t  let pool = window[Symbol.for('IntervalPool')]\n\t  if (!pool) {\n\t    pool = {}\n\t    window[Symbol.for('IntervalPool')] = pool\n\t  }\n\n\t  // interval 最低 10ms，虽然每 frame 至少得 16ms\n\t  delay = delay >= 10 ? delay : 10\n\n\t  // interval id\n\t  let ticket = Date.now()\n\t  \n\t  // 每次 interval 开始时间\n\t  let startTime = ticket\n\t  pool[ticket] = true\n\t  loop()\n\t  return ticket\n\n\t  function loop () {\n\t    if (!pool[ticket]) { return }\n\t    const now = Date.now()\n\t    if (now - startTime >= delay) {\n\t      startTime = now\n\t      cb(...args)\n\t    }\n\t    requestAnimationFrame(loop)\n\t  }\n\t}\n\n\tfunction clearInterval2 (ticket) {\n\t  let pool = window[Symbol.for('IntervalPool')]\n\t  if (pool && pool[ticket]) {\n\t    delete pool[ticket]\n\t  }\n\t}\n  ```\n  - 参考： [阿里前端面试题](https://segmentfault.com/q/1010000013909430)\n\n\n## 19.继承, 多态和代码复用\n\n- 面向对象三大特性： 封装、继承、多态\n\n- 继承：\n  - 1.类\n  - 2.类式继承---通过子类的原型prototype对象实例化来实现的\n  - 3.构造函数式继承：通过在子类的构造函数作用环境中执行一次父类的构造函数来实现的\n  - 4.组合继承就是：类式继承+构造函数继承\n  - 5.原型式继承跟类式继承一样，父类对象book中的值类型的属性被复制，引用类型的属性被共有\n  - 6.寄生式继承：通过在一个函数内的过渡对象实现继承并返回新对象的方式，称之为寄生式继承。\n  - 7.寄生组合式继承就是寄生式继承+构造函数式继承，\n\n-  现在有一个函数A和函数B，请你实现B继承A\n```\n// 方式1\nfunction B(){}\nfunction A(){}\nB.prototype = new A();\n\n// 方式2\nfunction A(){}\nfunction B(){\n  A.call(this);\n}\n\n// 方式3\nfunction B(){}\nfunction A(){}\nB.prototype = new A();\n\nfunction B(){\n  A.call(this);\n}\n\n# 说的几种继承的方式，分别说说他们的优缺点\n\n方式1：简单易懂，但是无法实现多继承，父类新增原型方法/原型属性，子类都能访问到\n方式2：可以实现多继承，但是只能继承父类的实例属性和方法，不能继承原型属性/方法\n方式3：可以继承实例属性/方法，也可以继承原型属性/方法，但是示例了两个A的构造函数\n\n作者：麦乐丶\n链接：https://juejin.im/post/5befeb5051882511a8527dbe\n```\n\n## 20.按位操作符, 类数组对象和类型化数组\n\n```\n运算符\t用法\t描述\n按位与（ AND）\ta & b\t对于每一个比特位，只有两个操作数相应的比特位都是1时，结果才为1，否则为0。\n按位或（OR）\ta | b\t对于每一个比特位，当两个操作数相应的比特位至少有一个1时，结果为1，否则为0。\n按位异或（XOR）\ta ^ b\t对于每一个比特位，当两个操作数相应的比特位有且只有一个1时，结果为1，否则为0。\n按位非（NOT）\t~ a\t反转操作数的比特位，即0变成1，1变成0。\n左移（Left shift）\ta << b\t将 a 的二进制形式向左移 b (< 32) 比特位，右边用0填充。\n有符号右移\ta >> b\t将 a 的二进制表示向右移 b (< 32) 位，丢弃被移出的位。\n无符号右移\ta >>> b\t将 a 的二进制表示向右移 b (< 32) 位，丢弃被移出的位，并使用 0 在左侧填充。\n```\n\n\n- 缓冲和视图：类型数组架构\n>JavaScript 类型数组（Typed Arrays）将实现拆分为缓冲和视图两部分。  \n一个缓冲（由 ArrayBuffer 对象实现）描述的是一个数据块。缓冲没有格式可言，并且不提供机制访问其内容。  \n为了访问在缓冲对象中包含的内存，你需要使用视图。  \n\n\n- ArrayBuffer又称类型化数组。\n\n- 参考： [MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Typed_arrays)\n\n## 21.DOM 树和渲染过程\n\n>DOM 是 JavaScript 操作网页的接口，全称为“文档对象模型”（Document Object Model）。  \n它的作用是将网页转为一个 JavaScript 对象，从而可以用脚本进行各种操作（比如增删内容）。 \n\nDOM 的最小组成单位叫做节点（node）。文档的树形结构（DOM 树），就是由各种不同类型的节点组成。\n\n- Node 常用属性主要有以下10个。\n```\nnodeType：显示节点的类型\nnodeName：显示节点的名称\nnodeValue：显示节点的值\nattributes：获取一个属性节点\nfirstChild：表示某一节点的第一个节点\nlastChild：表示某一节点的最后一个子节点\nchildNodes：表示所在节点的所有子节点\nparentNode：表示所在节点的父节点\nnextSibling：紧挨着当前节点的下一个节点\npreviousSibling：紧挨着当前节点的上一个节点\n\n作者：劼哥stone\n链接：https://juejin.im/post/583cbbfa61ff4b006ccc41fe\n```\n\n- 参考\n  - [DOM 概述](https://link.juejin.im/?target=http%3A%2F%2Fjavascript.ruanyifeng.com%2Fdom%2Fnode.html)\n\n\n## 22.new 与构造函数, instanceof 与实例\n\n- new命令的作用，就是执行构造函数，返回一个实例对象。\n\n- 构造函数： 实例化对象的函数\n\n- new 命令的原理：\n  - 创建一个空对象，作为将要返回的对象实例。\n  - 将这个空对象的原型，指向构造函数的prototype属性。\n  - 将这个空对象赋值给函数内部的this关键字。\n  - 开始执行构造函数内部的代码。\n\n```\nnew命令简化的内部流程，可以用下面的代码表示。\n\nfunction _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {\n  // 将 arguments 对象转为数组\n  var args = [].slice.call(arguments);\n  // 取出构造函数\n  var constructor = args.shift();\n  // 创建一个空对象，继承构造函数的 prototype 属性\n  var context = Object.create(constructor.prototype);\n  // 执行构造函数\n  var result = constructor.apply(context, args);\n  // 如果返回结果是对象，就直接返回，否则返回 context 对象\n  return (typeof result === 'object' && result != null) ? result : context;\n}\n\n// 实例\nvar actor = _new(Person, '张三', 28);\n```\n\n- new.target\n函数内部可以使用new.target属性。如果当前函数是new命令调用，new.target指向当前函数，否则为undefined。\n\n```\nfunction f() {\n  console.log(new.target === f);\n}\n\nf() // false\nnew f() // true\n```\n- get/set\n\n```\nvar person = {}\n\n// 通过get与set自定义访问与设置name属性的方式\nObject.defineProperty(person, 'name', {\n    get: function() {\n        // 一直返回TOM\n        return 'TOM'\n    },\n    set: function(value) {\n        // 设置name属性时，返回该字符串，value为新值\n        console.log(value + ' in set');\n    }\n})\n\n// 第一次访问name，调用get\nconsole.log(person.name)   // TOM\n\n// 尝试修改name值，此时set方法被调用\nperson.name = 'alex'   // alex in set\n\n// 第二次访问name，还是调用get\nconsole.log(person.name) // TOM\n\n作者：这波能反杀\n链接：https://www.jianshu.com/p/15ac7393bc1f\n```\n\n- 读取属性的特性值\n>我们可以使用Object.getOwnPropertyDescriptor方法读取某一个属性的特性值。\n\n```\nvar person = {}\n\nObject.defineProperty(person, 'name', {\n    value: 'alex',\n    writable: false,\n    configurable: false\n})\n\nvar descripter = Object.getOwnPropertyDescriptor(person, 'name');\n\nconsole.log(descripter);  // 返回结果如下\n\ndescripter = {\n    configurable: false,\n    enumerable: false,\n    value: 'alex',\n    writable: false\n}\n```\n- instanceof: 就是判断一个实例是否属于某种类型\n>instanceof 可以在继承关系中用来判断一个实例是否属于它的父类型\n\n```\nJavaScript instanceof 运算符代码\n\nfunction instance_of(L, R) {//L 表示左表达式，R 表示右表达式\n var O = R.prototype;// 取 R 的显示原型\n L = L.__proto__;// 取 L 的隐式原型\n while (true) { \n   if (L === null) \n     return false; \n   if (O === L)// 这里重点：当 O 严格等于 L 时，返回 true \n     return true; \n   L = L.__proto__; \n } \n}\n```\n\n- [JavaScript instanceof 运算符深入剖析](https://www.ibm.com/developerworks/cn/web/1306_jiangjj_jsinstanceof/)\n\n\n## 23.原型继承与原型链\n\n\n- 类之间的继承关系，导致了子类间的相互关联，从而形成了——基于层级的分类。”\n- “原型是工作对象的实例。对象直接从其他对象继承属性。”\n- “优先选择对象组合而不是类继承。” ~先驱四人，《设计模式：可复用面向对象软件之道》\n\n- 基于原型的面向对象设计方法总共有三种：\n  - 拼接继承： 是直接从一个对象拷贝属性到另一个对象的模式。被拷贝的原型通常被称为mixins。ES6为这个模式提供了一个方便的工具Object.assign()\n  - 原型代理：JavaScript中，一个对象可能包含一个指向原型的引用，该原型被称为代理\n  - 函数继承：在JavaScript中，任何函数都可以用来创建对象\n\n\n\n- [📖 继承与原型链 —— MDN](https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FJavaScript%2FInheritance_and_the_prototype_chain)\n- [类继承和原型继承的区别](https://juejin.im/entry/5885db221b69e600592253e7)\n- [原型与原型链](https%3A%2F%2Fgithub.com%2Fstone0090%2Fjavascript-lessons%2Ftree%2Fmaster%2F2.5-Prototype)\n\n\n\n## 33.代码整洁之道\n\n- [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript)\n\n## 参考\n- [33-js-concepts](https://github.com/leonardomso/33-js-concepts)\n- [JavaScript 开发者应懂的 33 个概念](https://juejin.im/entry/5bc9aae56fb9a05d20687bf3)\n"
  },
  {
    "path": "4.0.2 js 严格模式 'use strict'.md",
    "content": "# js 严格模式 'use strict'\n\n- 设立\"严格模式\"的目的，主要有以下几个：\n    - 消除Javascript语法的一些不合理、不严谨之处，减少一些怪异行为;\n    - 消除代码运行的一些不安全之处，保证代码运行的安全；\n    - 提高编译器效率，增加运行速度；\n    - 为未来新版本的Javascript做好铺垫。\n\n## 严格模式中的变化\n\n- 严格模式下无法再意外创建全局变量: 严格模式中意外创建全局变量被抛出错误替代\n- 严格模式会使引起静默失败(silently fail,注:不报错也没有任何效果)的赋值操抛出异常\n- 在严格模式下, 试图删除不可删除的属性时会抛出异常\n- 在Gecko版本34之前，严格模式要求一个对象内的所有属性名在对象内必须唯一\n- 严格模式要求函数的参数名唯一\n- 严格模式禁止八进制数字语法\n- ECMAScript 6中的严格模式禁止设置primitive值的属性\n-\n\n## 简化变量的使用\n- 严格模式禁用 with.\n- 严格模式下的 eval 不再为上层范围(surrounding scope,注:包围eval代码块的范围)引入新变量. \n- 严格模式禁止删除声明变量\n- 严格模式让arguments和eval少了一些奇怪的行为\n- 严格模式下，参数的值不会随 arguments 对象的值的改变而变化\n- 不再支持 arguments.callee。正常模式下，arguments.callee 指向当前正在执行的函数。（这意味着，你无法在匿名函数内部调用自身了。）\n\n\n## 参考\n- [MDN-严格模式](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode)\n- [Javascript 严格模式详解](http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html)\n"
  },
  {
    "path": "4.0.3 JavaScript 执行机制+运行原理.md",
    "content": "# JavaScript 执行机制\n\n>Event Loop是javascript的执行机制  \nJS的执行机制就是一个主线程 + 一个任务队列\n\n**事件循环流程讲的通俗易懂，对代码的执行顺序有了透彻的理解**\n\n除了广义的同步任务和异步任务，我们对任务有更精细的定义：\n\n```\nmacro-task(宏任务)：包括整体代码script，setTimeout，setInterval,UI render,postMessage\nmicro-task(微任务)：Promise，process.nextTick,MutaionObserve,Object.observe\n\nJob queue的执行顺序：\n\nscript(主程序代码)——>process.nextTick——>promise——>setTimeout\n```\n\n- 宏任务是由宿主发起的，而微任务由JavaScript自身发起\n- 宿主\n>JS运行的环境。一般为浏览器或者Node。\n\n>不同类型的任务会进入对应的Event Queue，比如setTimeout和setInterval会进入相同的Event Queue。\n事件循环的顺序，决定js代码的执行顺序。进入整体代码(宏任务)后，开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始，找到其中一个任务队列执行完毕，再执行所有的微任务\n\n遇到setTimeout，那么将其回调函数注册后分发到宏任务Event Queue。\n\n遇到了Promise，new Promise立即执行，then函数分发到微任务Event Queue。\n\n![](https://user-gold-cdn.xitu.io/2017/11/21/15fdd88994142347?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)\n\n\n\n## js运行原理\n\n- 概述\n\n>浏览器组成可分两部分：Shell+内核。浏览器内核又可以分成两部分：渲染引擎(layout engineer或者Rendering Engine)和JS引擎。  \n\n- 渲染引擎功能作用\n>渲染引擎，负责对网页语法的解释（如HTML、JavaScript）并渲染网页。  \n所以，通常所谓的浏览器内核也就是浏览器所采用的渲染引擎，渲染引擎决定了浏览器如何显示网页的内容以及页面的格式信息。  \n不同的浏览器内核对网页编写语法 的解释也有不同，因此同一网页在不同的内核的浏览器里的渲染（显示）效果也可能不同，  \n这也是网页编写者需要在不同内核的浏览器中测试网页显示效果的原因。\n\n\n- 定义\n  - 浏览器内核分成两部分渲染引擎和js引擎，由于js引擎越来越独立，内核就倾向于只指渲染引擎\n  - 渲染引擎是一种对HTML文档进行解析并将其显示在页面上的工具\n\n- 渲染引擎：\n```\n　　　　firefox使用gecko引擎\n\n　　　　IE使用Trident引擎\n\n　　　　2015年微软推出自己新的浏览器，原名叫斯巴达，后改名edge,使用edge引擎\n\n　　　　opera最早使用Presto引擎，后来弃用\n\n　　　　chrome\\safari\\opera使用webkit引擎\n\n　　　　13年chrome和opera开始使用Blink引擎\n```\n\n- js引擎：\n```\n　　　　老版本IE使用Jscript引擎\n\n　　　　IE9之后使用Chakra引擎\n\n　　　　edge浏览器仍然使用Chakra引擎\n\n　　　　firefox使用monkey系列引擎\n\n　　　　safari使用的SquirrelFish系列引擎\n\n　　　　Opera使用Carakan引擎\n\n　　　　chrome使用V8引擎。nodeJs其实就是封装了V8引擎\n```\n\n![](https://img-blog.csdn.net/20170605144024633?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvR1lfVV9ZRw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)\n\n主线程运行的时候，产生堆（heap）和栈（stack），栈中的代码调用各种外部API，它们在\"任务队列\"中加入各种事件（click，load，done）。  \n只要栈中的代码执行完毕，主线程就会去读取\"任务队列\"，依次执行那些事件所对应的回调函数。  \n\n执行栈中的代码（同步任务），总是在读取\"任务队列\"（异步任务）之前执行。  \n\n- [浏览器内核及JS引擎各有什么功能](https://blog.csdn.net/mr_gly/article/details/50393127)\n- [js运行原理](https://www.cnblogs.com/flyings/p/7058851.html)\n- [JavaScript的执行原理](https://blog.csdn.net/gy_u_yg/article/details/72869315)\n\n\n## JS中的异步运行机制\n\n- （1）所有同步任务都在主线程上执行，形成一个执行栈（execution context stack）。\n\n- （2）主线程之外，还存在一个”任务队列”（task queue）。只要异步任务有了运行结果，就在”任务队列”之中放置一个事件。\n\n- （3）一旦”执行栈”中的所有同步任务执行完毕，系统就会读取”任务队列”，看看里面有哪些事件。那些对应的异步任务，于是结束等待状态，进入执行栈，开始执行。\n\n- （4）主线程不断重复上面的第三步。\n----------------------------------------- \n\n\n## 参考\n- [这一次，彻底弄懂 JavaScript 执行机制](https://juejin.im/post/59e85eebf265da430d571f89)\n"
  },
  {
    "path": "4.0.4 session 、cookie、token的区别及联系.md",
    "content": "# session 、cookie、token的区别及联系\n\n## session\n> session机制采用的是一种在服务器端保持状态的解决方案。    \n由于采用服务器端保持状态的方案在客户端也需要保存一个标识，  \n所以session机制可能需要借助于cookie机制来达到保存标识的目的\n\n>服务器使用一种类似于散列表的结构（也可能就是使用散列表）来保存信息\n\n## cookie\n\n>cookie机制采用的是在客户端保持状态的方案。它是在用户端的会话状态的存贮机制，  \n他需要用户打开客户端的cookie支持。  \ncookie的作用就是为了解决HTTP协议无状态的缺陷所作的努力。\n\n## cookie 和session的区别\n\n1、cookie数据存放在客户的浏览器上，session数据放在服务器上。\n\n2、cookie不是很安全，别人可以分析存放在本地的COOKIE并进行COOKIE欺骗\n   考虑到安全应当使用session。\n\n3、session会在一定时间内保存在服务器上。当访问增多，会比较占用你服务器的性能\n   考虑到减轻服务器性能方面，应当使用COOKIE。\n\n4、单个cookie保存的数据不能超过4K，很多浏览器都限制一个站点最多保存20个cookie。\n\n5、将登陆信息等重要信息存放为SESSION\n\n\n## token\n\n>作为身份认证 token安全性比session好，因为每个请求都有签名还能防止监听以及重放攻击，而session就必须靠链路层来保障通讯安全了\n\n\n## 参考\n- [session与cookie的介绍和两者的区别之其相互的关系](https://blog.csdn.net/weixin_37196194/article/details/55806366)\n- [session 、cookie、token的区别](https://blog.csdn.net/jikeehuang/article/details/51488020)\n"
  },
  {
    "path": "4.0.4 禁止js访问cookie.md",
    "content": "# 禁止 js 访问 cookie\n\n\n- 使用 HttpOnly 属性\n\n```\n//设置cookie\n\nresponse.addHeader(\"Set-Cookie\", \"uid=112; Path=/; HttpOnly\");\n```\n\n\n\n- 删除某一个 cookie,\n>如果要删除某个Cookie，只需要新建一个同名的 Cookie，并将 maxAge 设置为 0，并添加到 response 中覆盖原来的 Cookie。  \n注意是 0 而不是负数。负数代表其他的意义。\n\n  - 1.设置过期时间\n  - 2.遍历删除指定名的 cookie\n\n- cookie.setMaxAge(-1);//会话级cookie，关闭浏览器失效\n- cookie.setMaxAge(0);//不记录cookie\n  \n>注意！！！修改、删除Cookie时，新建的Cookie除value、maxAge之外的所有属性，例如name、path、domain等，都要与原Cookie完全一样。  \n否则，浏览器将视为两个不同的Cookie不予覆盖，导致修改、删除失败。\n"
  },
  {
    "path": "4.0.5 js 中 callee 与 caller 的区别.md",
    "content": "#  js 中 callee 与 caller 的区别\n\n## callee是对象的一个属性，该属性是一个指针，指向参数arguments对象的函数\n\n```\n首先我们来写个阶成函数：\nfunction chen(x){\n  if (x<=1) {\n    return 1;\n  } else{\n    return x*chen(x-1);\n  };\n};\n\n从这个函数中可以看出来，用到了递归函数，要是改变了函数名，里面的函数名也要随着改变，这样很不方便所以我们用callee来试试\n\nfunction chen(x){\n  if (x<=1) {\n    return 1;\n  }else{\n    return x*arguments.callee(x-1);\n  };\n};\n```\n\n根据callee的定义，可以看出来callee是arguments对象的一个属性，指向arguments对象的函数，  \n\n这个函数就是chen（chen=arguments.callee）,这样解释应该可以理解了吧。\n\n## caller 是函数对象的一个属性，该属性保存着调用当前函数的函数的引用（指向当前函数的直接父函数）\n\n```\nfunction a(){\n  b();\n};\n\nfunction b(){\n  alert(b.caller);\n};\n\na(); //结果就是弹出函数a和内容\n\n我们来解释一下吧，首先函数 b 的属性 caller 调用当前函数b的函数引用a（就是指向当前函数b的父函数a），所以结果就是弹出 function a(){ b();};\n\n那么了解了caller和callee，那么可以不可以将二者结合在一起来使用呢\n\nfunction b(){\n  alert(b.caller);\n};\n\n从这个代码可以看出b函数中调用了b函数名，这样当函数名改变时就很不方便，我们需要替换里面的那个b\n前面我们知道用什么方法可以指向当前对象,下面我们就来修改一下：\n\n(function a(){\n  b();\n})();\n\nfunction b(){\n  alert(arguments.callee.caller);\n};\n\n从代码中可以看出我们用arguments.callee代替了b函数，\n```\n\n## 参考\n- [js中callee与caller的区别](https://www.cnblogs.com/angus-jiang/p/4434259.html)\n"
  },
  {
    "path": "4.0.6 Memoization 介绍.md",
    "content": "# Memoization \n\n>记忆化是一种典型的时间存储平衡方案。\n\n\n>英语：(memoization而非memorization）是一种提高程序运行速度的优化技术。  \n通过储存大计算量函数的返回值，当这个结果再次被需要时将其从缓存提取，而不用再次计算来节省计算时间。 \n\n\nMemoization 原理非常简单，就是把函数的每次执行结果都放入一个散列表中，  \n在接下来的执行中，在散列表中查找是否已经有相应执行过的值，如果有，直接返回该值，没有才真正执行函数体的求值部分。  \n很明显，找值，尤其是在散列中找值，比执行函数快多了\n\n\n## 参考\n- [Memoization](https://zh.wikipedia.org/wiki/%E8%AE%B0%E5%BF%86%E5%8C%96)\n- [JavaScript Memoization](https://www.cnblogs.com/rubylouvre/archive/2009/08/06/1540678.html)\n\n\n"
  },
  {
    "path": "4.0.7 getBoundingClientRect获取元素的大小及其相对于视口的位置.md",
    "content": "# getBoundingClientRect\n\n\n>返回元素的大小及其相对于视口的位置。\n\n\n```\ndocument.getElementById('round').getBoundingClientRect();\n\n// 返回 Obj \nDOMRect {x: 435.1875, y: 103.578125, width: 720.8125, height: 27, top: 103.578125, …}\nbottom: 130.578125\nheight: 27\nleft: 435.1875\nright: 1156\ntop: 103.578125\nwidth: 720.8125\nx: 435.1875\ny: 103.578125\n```\n\n\n\n## 参考\n- [MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect)\n"
  },
  {
    "path": "4.0.8IntersectionObserver实现懒加载.md",
    "content": "# IntersectionObserver实现懒加载\n\n>传统方式是监听scroll事件，再调用getBoundingClientRect()  \n方法判断元素左上角的坐标是否在视窗内，但是scroll事件触发的太密集，会造成内存不断消耗，带来性能问题。\n\n现在有一个API，IntersectionObserver可以实现监听元素是否可见。\n\n## 实现懒加载\n\ninterserverObserver对象可以通过isIntersecting或者intersectionRatio是否大于0判断元素是否在视窗内，\n\n并通过data-src来实现懒加载，以下为具体实现：\n\n- html\n\n```\n<ul class=\"box\">\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n  <li><img src=\"http://placehold.it/450x300/caaa8e/ccc.png\" data-src=\"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1549272664908&di=4bb90ffd078e31348159c07e78947f0a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F08%2F20161008151749_2VAKU.jpeg\" alt=\"\"></li>\n</ul>\n```\n\n\n- css\n\n```\nli {\n  list-style: none;\n}\n\nimg {\n  width: 450px;\n  height: 300px;\n}\n```\n\n- js\n\n```\nconst imgs = document.querySelectorAll('img')\nconst io = new IntersectionObserver(entries => {\n  entries.forEach(entry => {\n    if (entry && entry.isIntersecting) {\n      entry.target.src = entry.target.dataset.src\n      io.unobserve(entry.target)\n    }\n  })\n})\nimgs.forEach(item => {\n  io.observe(item)\n})\n```\n\n\n\n## 参考\n- [IntersectionObserver实现懒加载](https://jianshu.com/p/fd405a99423a)\n"
  },
  {
    "path": "4.0.9.1 PC端事件.md",
    "content": "\n\n# 4.0.9.1 PC端事件\n\n\n\n![](https://fairyly.github.io/image-links/20191117/PC_event.png)\n"
  },
  {
    "path": "4.0.9IntersectionObserver 触底-吸顶-动画.md",
    "content": "\n\n\n## 2. 触底\n\n我们在列表底部放一个参照元素，然后让交叉观察者去监听；\n\n假设html结构如下：\n\n```\n<!-- 数据列表 -->\n\n<ul>\n  <li>index</li> // 多个li\n</ul>\n\n<!-- 参照元素 -->\n<div class=\"reference\"></div>\n```\n\n然后监听参照元素：\n\n```\nnew IntersectionObserver(entries => {\n  let item = entries[0]; // 拿第一个就行，反正只有一个\n  if (item.isIntersecting) console.log(\"滚动到了底部，开始请求数据\");\n}).observe(document.querySelector(\".reference\")); // 监听参照元素\n```\n\n\n\n## 3. 吸顶\n\n实现元素吸顶的方式有很多种，如css的position: sticky，兼容性较差；如果用交叉观察者实现也很方便，同样也要放一个参照元素；\n\n假设html结构如下：\n\n```\n<!-- 参照元素 -->\n<div class=\"reference\"></div>\n\n<nav>我可以吸顶</nav>\n```\n\n假设scss代码如下：\n\n```\nnav {\n  &.fixed {\n    position: fixed;\n    top: 0;\n    left: 0;\n    width: 100%;\n  }\n}\n```\n\n开始监听：\n\n```\nlet nav = document.querySelector(\"nav\");\nlet reference = document.querySelector(\".reference\");\n\nnew IntersectionObserver(entries => {\n\n  let item = entries[0];\n  let top = item.boundingClientRect.top;\n\n  // 当参照元素的的top值小于0，也就是在视窗的顶部的时候，开始吸顶，否则移除吸顶\n  if (top < 0) nav.classList.add(\"fixed\");\n  else nav.classList.remove(\"fixed\");\n\n}).observe(reference);\n```\n\n>但是有个问题，当你滚动的慢的时候，会掉进一个死循环：\n当给nav增加fixed定位时，nav脱离了文档流，自然参考元素会往下掉，然后往下掉又发生了交叉，从而去除fixed定位，陷入一个死循环;\n\n思考了一会，解决办法是，让参考元素绝对定位至nav的上方：\n\n```\nlet nav = document.querySelector(\"nav\");\nlet reference = document.querySelector(\".reference\");\n\nreference.style.top = nav.offsetTop + \"px\";\n```\n\n// 以下代码不变 ...\n这样，即使nav脱离的文档流，也不会影响参考元素的位置\n\n\n\n## 动画展示\n\n假设html结构如下：\n```\n<ul>\n  <li></li> // 多个li\n</ul>\n```\n\n假设scss代码如下:\n\n```\nul {\n li {\n   &.show {\n    // 默认从左边进来\n    animation: left 1s ease;\n    \n    // 偶数从右边进来\n    &:nth-child(2n) {\n      animation: right 1s ease;\n    }\n   }\n }\n}\n\n@keyframes left {\n  from {\n    opacity: 0;\n    transform: translate(-20px, 20px); // right动画改成20px, 20px即可\n  }\n\n  to {\n    opacity: 1;\n  }\n}\n```\n\n然后开始监听：\n\n\n```\nlet list = document.querySelectorAll(\"ul li\");\n\nlet observer = new IntersectionObserver(entries => {\n  entries.forEach(item => {\n    if (item.isIntersecting) {\n      item.target.classList.add(\"show\"); // 增加show类名\n      observer.unobserve(item.target); // 移除监听\n    }\n  });\n});\n\nlist.forEach(item => observer.observe(item));\n```\n\n\n## 参考\n- [利用\"交叉观察者\"这个小宝贝儿，轻松实现懒加载、吸顶、触底](https://cloud.tencent.com/developer/article/1499647)\n"
  },
  {
    "path": "4.1.0 const .md",
    "content": "# const  \n\nthis 作用域问题\n\n```\nconst opt = {\n  name: 'Amy',\n  name2: this.name,\n  say: function() {\n    return this.name;\n  },\n  say2: function() {\n    setTimeout(function() {\n      console.log(this.name);\n    });\n  },\n  say3: function() {\n    setTimeout(() => {\n      console.log(this.name);\n    });\n  }\n};\n\nconsole.log(opt.name2); // 1. 这里打印出什么？undefined\nconsole.log(opt.say); // 2. 这里打印出什么？  function() {return this.name}\nopt.say2(); // 3. 这里打印出什么？\t\t\t undefined\nopt.say3(); // 4. 这里打印出什么？\t\t\t 'Amy'\n```\n\n## 参考\n- https://github.com/wxyyxc1992/coding-snippets/blob/master/language/js/snippets/this.js\n"
  },
  {
    "path": "4.1.0.1 +new Date使用.md",
    "content": "# 4.1.0.1 +new Date使用\n\n>最后会返回时间戳，和 `valueOf()` 一样, `+new Date()` 会调用 `Date.prototype` 上面的 `valueOf` 方法，根据\n\n```\nvar now = +new Date();\nconsole.log(now); // 1610676183172 \n\n\nvar s=new Date();\nconsole.log(s.valueOf()); // 1610676511068\n\n\nvar s=new Date();\nconsole.log(s.getTime()); // 1610676743954\n```\n\n\n## 参考\n- [MDN-valueOf](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/valueOf)\n- [MDN-getTime](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime)\n"
  },
  {
    "path": "4.1.0.2 可视区域加载.md",
    "content": "# 4.1.0.2 可视区域加载\n\n\n\n```\n# 公式: el.offsetTop - document.documentElement.scrollTop <= viewPortHeight\n\nfunction isInViewPortOfOne (el) {\n    // viewPortHeight 兼容所有浏览器写法\n    const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight \n    const offsetTop = el.offsetTop\n    const scrollTop = document.documentElement.scrollTop\n    const top = offsetTop - scrollTop\n    console.log('top', top)\n     // 这里有个+100是为了提前加载+ 100\n    return top <= viewPortHeight + 100\n}\n\n\n# 公式: el.getBoundingClientReact().top <= viewPortHeight\nfunction isInViewPortOfTwo (el) {\n    const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight \n    const top = el.getBoundingClientRect() && el.getBoundingClientRect().top\n    console.log('top', top)\n    return top  <= viewPortHeight + 100\n}\n```\n\n\n## 参考\n\n- [判断元素是否在可视区域ViewPort](https://segmentfault.com/a/1190000017150136)\n"
  },
  {
    "path": "4.1.0.2.1(标题和导航联动) 可视区域监听IntersectionObserver.md",
    "content": "# 4.1.0.2.1 可视区域监听IntersectionObserver\n\n>IntersectionObserver API 是异步的，不随着目标元素的滚动同步触发。\n\n规格写明，IntersectionObserver的实现，应该采用requestIdleCallback()，即只有线程空闲下来，才会执行观察器。\n\n这意味着，这个观察器的优先级非常低，只在其他任务执行完，浏览器有了空闲才会执行。\n\n\n## 实例：无限滚动\n>无限滚动（infinite scroll）的实现也很简单。\n\n```\nvar intersectionObserver = new IntersectionObserver(\n  function (entries) {\n    // 如果不可见，就返回\n    if (entries[0].intersectionRatio <= 0) return;\n    loadItems(10);\n    console.log('Loaded new items');\n  });\n\n// 开始观察\nintersectionObserver.observe(\n  document.querySelector('.scrollerFooter')\n);\n```\n\n## DEMO JS\n\n\n\n```\n\n使用，例如：\n\nsmartNav('article h3');\n会把符合选择器'article h3'的元素聚合成和快速定位的导航元素。\n\n语法\nsmartNav(elements, options);\n\n\n/**\n * @description 文档标题生成快捷导航，不支持IE浏览器\n * @tutorial https://www.zhangxinxu.com/wordpress/?p=9707\n * @author zhangxinxu(.com) 2020-11-23\n */\n\nvar smartNav = function (elements, options) {\n    var defaults = {\n        nav: null\n    };\n\n    var params = Object.assign({}, defaults, options || {});\n\n    if (typeof elements == 'string') {\n        elements = document.querySelectorAll(elements);\n    }\n    \n    if (!elements.forEach) {\n        return;\n    }\n\n    // 导航元素创建，如果没有\n    if (!params.nav) {\n        params.nav = document.createElement('div');\n        params.nav.className = 'title-nav-ul';\n        document.body.appendChild(params.nav);\n    }\n\n    var lastScrollTop = document.scrollingElement.scrollTop;\n\n    var zxxObserver = new IntersectionObserver(function (entries) {\n        if (params.isAvoid) {\n            return;\n        }\n        entries.reverse().forEach(function (entry) {\n            if (entry.isIntersecting) {\n                entry.target.active();  \n            } else if (entry.target.isActived) {\n                entry.target.unactive();  \n            }\n        });\n\n        lastScrollTop = document.scrollingElement.scrollTop;\n    });\n\n    elements.forEach(function (ele, index) {\n        var id = ele.id || ('smartNav' + Math.random()).replace('0.', '');\n        ele.id = id;\n        // 导航元素创建\n        var eleNav = document.createElement('a');\n        eleNav.href = '#' + id;\n        eleNav.className = 'title-nav-li';\n        eleNav.innerHTML = ele.textContent;\n        params.nav.appendChild(eleNav);\n        ele.active = function () {\n            // 对应的导航元素高亮\n            eleNav.parentElement.querySelectorAll('.active').forEach(function (eleActive) {\n                ele.isActived = false;\n                eleActive.classList.remove('active');\n            });\n            eleNav.classList.add('active');\n            ele.isActived = true;\n        };\n        ele.unactive = function () {\n            // 对应的导航元素高亮\n            if (document.scrollingElement.scrollTop > lastScrollTop) {\n                elements[index + 1] && elements[index + 1].active();\n            } else {\n                elements[index - 1] && elements[index - 1].active();\n            }\n            eleNav.classList.remove('active');\n            ele.isActived = false;\n        };\n\n        // 观察元素\n        zxxObserver.observe(ele);\n    });\n\n    params.nav.addEventListener('click', function (event) {\n        var eleLink = event.target.closest('a');\n        // 导航对应的标题元素\n        var eleTarget = eleLink && document.querySelector(eleLink.getAttribute('href'));\n        if (eleTarget) {\n            event.preventDefault();\n            // Safari不支持平滑滚动\n            eleTarget.scrollIntoView({\n                behavior: \"smooth\",\n                block: 'center'\n            });\n\n            if (CSS.supports('scroll-behavior: smooth')) {\n                params.isAvoid = true;\n                setTimeout(function () {\n                    eleTarget.active();\n                    params.isAvoid = false;\n                }, Math.abs(eleTarget.getBoundingClientRect().top  - window.innerHeight / 2) / 2);\n            } else {\n                eleTarget.active();\n            }            \n        }\n    });\n};\n```\n\n\n## 参考\n- [MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver)\n- [使用JS IntersectionObserver让标题和导航联动](https://www.zhangxinxu.com/wordpress/2020/12/js-intersectionobserver-nav/)\n- [IntersectionObserver API 使用教程](http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html)\n"
  },
  {
    "path": "4.1.0.3 缓存函数.md",
    "content": "# 4.1.0.3 缓存函数\n\n\n>将上次的计算结果缓存起来，当下次调用时，如果遇到相同的参数，就直接返回缓存中的数据。\n\n实现原理：把参数和对应的结果数据存在一个对象中，调用时判断参数对应的数据是否存在，存在就返回对应的结果数据，否则就返回计算结果。\n\n理论有了，我们来实现一个缓存函数：\n\n```\nlet memoize = function (func, content) {\n  let cache = Object.create(null)\n  content = content || this\n  return (...key) => {\n    if (!cache[key]) {\n      cache[key] = func.apply(content, key)\n    }\n    return cache[key]\n  }\n}\n\n```\n\n\n过程分析：\n\n- 在当前函数作用域定义了一个空对象，用于缓存运行结果\n- 运用柯里化返回一个函数，返回的函数因为作用域链的原因，可以访问到cache\n- 然后判断输入参数是不是在cache的中。如果已经存在，直接返回cache的内容，如果没有存在，使用函数func对输入参数求值，然后把结果存储在cache中。\n\n\n## 在Vue中也有所体现\n\n```\n/**\n * Create a cached version of a pure function.\n */\nfunction cached (fn) {\n  var cache = Object.create(null);\n  return (function cachedFn (str) {\n    var hit = cache[str];\n    return hit || (cache[str] = fn(str))\n  })\n}\n\n/**\n * Capitalize a string.\n */\nvar capitalize = cached(function (str) {\n  return str.charAt(0).toUpperCase() + str.slice(1)\n});\n\n...\n\ncapitalize(camelizedId)\n\n\n```\n## 适用场景：\n\n- 需要大量重复计算\n- 大量计算并且依赖之前的结果\n\n\n\n## 参考\n- [Javascript缓存函数&柯里化&偏函数](https://zhuanlan.zhihu.com/p/112505577)\n"
  },
  {
    "path": "4.1.0.4 长列表性能优化.md",
    "content": "# 4.1.0.4 长列表性能优化\n\n>一个针对长列表的重要优化是，只渲染页面中用户能看到的部分。  \n例如一个列表超过5000条，但是当中呈现给用户看到中的一屏显示就只有10条。  \n毕竟屏幕有限，你不可能一屏显示完所有列表条目，你需要不断翻页才能看完所有的列表条目。  \n\n>android中的ListView,oc中的TableView中都有复用控件的优化，并且在不断滚动的过程中去除不在屏幕中的元素，不再渲染，从而实现高性能的列表渲染。\n\n>当列表不断往下拉时，web中的dom元素就越多，即使这些dom元素已经离开了这个屏幕，不被用户所看到了，这些dom元素依然存在在那里。  \n导致浏览器在渲染时需要不断去考虑这些dom元素的存在，造成web浏览器的长列表渲染非常低效。\n\n>因此，实现的做法就是捕捉scroll事件，当dom离开屏幕，用户不再看到时，就将其移出dom tree。\n\n\n## jQuery demo\n\n```\n<!doctype html5>\n<html>\n\n<head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no\">\n    <title>长列表优化测试</title>\n    <script src=\"http://libs.baidu.com/jquery/2.0.0/jquery.min.js\"></script>\n    <style>\n    * {\n        padding: 0px;\n        margin: 0px;\n    }\n\n    li {\n        height: 50px;\n        line-height: 50px;\n        font-size: 20px;\n        color: red;\n        background: blue;\n        border-bottom: solid 1px yellow;\n    }\n    </style>\n</head>\n\n<body>\n    <div>长列表测试</div>\n    <ul>\n    </ul>\n    <script>\n    var ul = $('ul');\n    var newCounter = 1;\n    var prefix = $('<li></li>');\n    var liCache = []; \n    \n    // 第一步 先添加一个占位置高度的 li，和第一条数据\n    prefix.css('border', '0px');\n    prefix.css('height', '0px');\n    ul.append(prefix);\n    ul.append('<li>1</li>');\n    \n\n    function checkIsBottom(target) {\n    \t// 判断最后一个元素的位置是不是在视口底部\n        var winHeight = window.innerHeight;\n        var scrollY = window.scrollY;\n        var targetBottom = target.offset().top + target.height();\n        return targetBottom > scrollY + winHeight;\n    }\n\n    function checkIsTop(target) {\n    \t// 判断列表第一个元素的位置是不是在视口顶部\n        var scrollY = window.scrollY;\n        var targetBottom = target.offset().top + target.height();\n        return targetBottom > scrollY;\n    }\n\n    function newLi() {\n        var li;\n        if (liCache.length == 0) {\n            li = $('<li></li>');\n            console.log('new Li,counter:' + newCounter);\n            newCounter++;\n        } else {\n            li = liCache.pop();\n        }\n        return li;\n    }\n\n    function delLi(li) {\n        li.remove();\n        liCache.push(li);\n    }\n\n    function delTopData(li) {\n        while (true) {\n            //console.log(li.text());\n            var nextLi = li.next();\n            var height = li.height();\n            var prefixHeight = prefix.height();\n            delLi(li);\n            prefix.height(prefixHeight + height);\n            if (checkIsTop(nextLi)) {\n                break;\n            }\n            li = nextLi;\n        }\n    }\n\n    function addBottomData() {\n        var lastLi = $('li:last');\n        var counter = parseInt(lastLi.text());\n        while (true) {\n            var li = newLi();\n            li.text(++counter);\n            ul.append(li);\n            if (checkIsBottom(li)) {\n                break;\n            }\n        }\n    }\n\n    function addTopData() {\n        var prefixLi = $('li:first');\n        var firstLi = $('li:first').next();\n        while (true) {\n            var newFirstLi = newLi();\n            var prefixHeight = prefix.height();\n            newFirstLi.text(parseInt(firstLi.text()) - 1);\n            firstLi.before(newFirstLi);\n            prefix.height(prefixHeight - newFirstLi.height());\n            if (prefixLi.height() == 0 || checkIsTop(prefixLi) == false) {\n                break;\n            }\n            firstLi = newFirstLi;\n        }\n    }\n\n    function delBottomData() {\n        var prefixLi = $('li:first');\n        var li = $('li:last');\n        var prevLi = li.prev();\n        while (true) {\n            delLi(li);\n            li = prevLi;\n            prevLi = li.prev();\n            if (prevLi.prev()[0] == prefixLi[0] || checkIsBottom(prevLi) == false) {\n                break;\n            }\n        }\n    }\n    // 第二步，添加一条数据后，依次添加数据，知道最后一条数据触底了\n    addBottomData();\n    \n    // 第三步 监听滚动（向下滚，向上滚），\n    $(window).scroll(function() {\n        //顶部移除节点\n        var firstLi = $('li:first').next();\n        // 滚动条向下滑动，页面向上滚动，先把列表上面的数据删掉，缓存下来，\n        if (checkIsTop(firstLi) == false) {\n            delTopData(firstLi);\n        }\n\n        //尾部移除节点\n        var suffixLi = $('li:last').prev();\n        // 滚动条像上滑动，从列表最后一条前删除数据\n        if (checkIsBottom(suffixLi)) {\n            delBottomData();\n        }\n\n        //尾部添加节点\n        var lastLi = $('li:last');\n        // 滚动到底部，添加底部数据\n        if (checkIsBottom(lastLi) == false) {\n            addBottomData();\n        }\n\n        //顶部添加节点\n        var prerfixLi = $('li:first');\n\n        if (prerfixLi.height() != 0 && checkIsTop(prerfixLi) == true) {\n            addTopData();\n        }\n\n    });\n    </script>\n</body>\n\n</html>\n```\n\n\n- 这个demo也存在一些没有实现的地方:\n  - 往上拉了以后，底部的列表条目没有留下来，全部都删了，导致下一次往下拉时又要重复拉数据。\n  - 列表条目的高度都是固定的\n  - 列表条目的数据没有存下来，重建数据时是用附近条目的数据来补充过来的\n  \n  \n  \n## vue demo\n\n```\n<template>\n  <div class=\"virtual-list\" ref=\"virtualList\" @scroll.passive=\"getScrollFn\">\n    <div class=\"scroll-bar\" ref=\"scrollBar\"></div>\n\n    <div class=\"scroll-list\" ref=\"scrollList\" :class=\"{ 'waterfall-list': isWaterfall }\" :style=\"transform\">\n      <template v-if=\"isWaterfall\">\n        <div class=\"left-wrap\" :style=\"{ transform: `translate3d(0,${leftOffset}px,0)` }\">\n          <div v-for=\"item in renderLeft\" class=\"left-item\" :vid=\"item.id\" :style=\"setStyle(item)\" :key=\"item.id\">\n            <slot :item=\"item\"></slot>\n          </div>\n        </div>\n\n        <div class=\"right-wrap\" :style=\"{ transform: `translate3d(0,${rightOffset}px,0)` }\">\n          <div v-for=\"item in renderRight\" class=\"right-item\" :vid=\"item.id\" :style=\"setStyle(item)\" :key=\"item.id\">\n            <slot :item=\"item\"></slot>\n          </div>\n        </div>\n      </template>\n      <template v-else>\n        <div v-for=\"item in renderData\" :key=\"item.id\" :vIndex=\"item.index\" ref=\"nodes\">\n          <slot :item=\"item\"></slot>\n        </div>\n      </template>\n    </div>\n    <slot name=\"bottom\"></slot>\n  </div>\n</template>\n<script>\nimport throttle from 'lodash/throttle'\nimport { getCurrentIndex, virtualType } from './../utils'\nexport default {\n  name: 'lite-virtual-list',\n  props: {\n    data: {\n      type: Array,\n      required: true,\n      default: () => [],\n    },\n    // fixed variable waterfall\n    type: {\n      type: String,\n      required: true,\n    },\n    // 每屏可见的数据条数\n    remain: {\n      type: [Number, String],\n      required: true,\n    },\n    size: {\n      type: [Number, String],\n      required: false,\n    },\n    // 前后各渲染几屏\n    screen: {\n      type: Array,\n      required: false,\n      default: () => [1, 1],\n    },\n    //加载更多 触底距离\n    distance: {\n      type: [Number, String],\n      required: false,\n      default: 50,\n    },\n    // 截流函数触发间隔\n    interval: {\n      type: Number,\n      required: false,\n      default: 0,\n    },\n    virtualHieght: {\n      type: String,\n      required: false,\n      default: '100%',\n    },\n    unit: {\n      type: String,\n      required: false,\n      default: 'px',\n    },\n    marginBottom: {\n      type: [Number, String],\n      required: false,\n      default: 0,\n    },\n    deleteId: {\n      type: [Number, String],\n      required: false,\n    },\n  },\n  data() {\n    return {\n      start: 0,\n      end: 0,\n      offset: 0,\n      leftOffset: 0,\n      rightOffset: 0,\n      leftStart: 0,\n      rightStart: 0,\n      leftEnd: 0,\n      rightEnd: 0,\n      leftHeight: 0,\n      rightHieght: 0,\n      scrollTop: 0,\n    }\n  },\n  computed: {\n    isWaterfall() {\n      return this.type === virtualType.WATERFALL\n    },\n    transform() {\n      if (!this.isWaterfall) {\n        return {\n          transform: `translate3d(0,${this.offset}px,0)`,\n        }\n      }\n      return null\n    },\n    renderLeft() {\n      return this.waterfallInfo ? this.waterfallInfo.left.slice(this.leftStart - this.leftPrevCount, this.leftEnd + this.leftNextCount) : 0\n    },\n    renderRight() {\n      return this.waterfallInfo ? this.waterfallInfo.right.slice(this.rightStart - this.rightPrevCount, this.rightEnd + this.rightNextCount) : 0\n    },\n    renderData() {\n      return this.formatData.slice(this.start - this.prevCount, this.end + this.nextCount)\n    },\n    formatData() {\n      return this.data.map((item, index) => ({ ...item, index }))\n    },\n    prevScreen() {\n      return this.remain * this.screen[0]\n    },\n    nextScreen() {\n      return this.remain * this.screen[1]\n    },\n    prevCount() {\n      return Math.min(this.start, this.prevScreen)\n    },\n    leftPrevCount() {\n      return Math.min(this.leftStart, this.prevScreen)\n    },\n    rightPrevCount() {\n      return Math.min(this.rightStart, this.prevScreen)\n    },\n    nextCount() {\n      return Math.min(this.data.length - this.end, this.nextScreen)\n    },\n    leftNextCount() {\n      return this.waterfallInfo ? Math.min(this.waterfallInfo.left.length - this.leftEnd, this.nextScreen) : 0\n    },\n    rightNextCount() {\n      return this.waterfallInfo ? Math.min(this.waterfallInfo.right.length - this.rightEnd, this.nextScreen) : 0\n    },\n  },\n  watch: {\n    deleteId(id) {\n      this.deleteItem(id)\n    },\n    data(newData) {\n      // 防止组件外部单个item更改状态后触发watch 例\n      // item.visible = true\n      // this.$set(this.items, item.id, item)\n      // if (newData.length === oldData.length) return\n      let prevLength, newLoadData\n      switch (this.type) {\n        case virtualType.FIXED:\n          this.$refs.scrollBar.style.height = newData.length * this.size + this.unit\n          break\n        case virtualType.VARIABLE:\n          prevLength = this.variableData.length\n          newLoadData = newData.slice(prevLength)\n          // pushData 此数据并没有偏移量  页面通过renderData更新之后 会触发update 在updated中通过渲染的节点会主动更新后面数据的偏移量\n          this.variableData = [...this.variableData, ...this.getVisiblePosition(newLoadData)]\n          break\n        case virtualType.WATERFALL:\n          prevLength = this.waterfallInfo.left.length + this.waterfallInfo.right.length\n          newLoadData = newData.slice(prevLength)\n          this.handleWaterfallData(newLoadData)\n          break\n      }\n      // 当滚动时未触发回弹时会造成$emit('scroll')事件不执行 会导致懒加载处理的dom时获取到的是老数据\n      this.$nextTick(() => {\n        this.$emit('domUpdateSuccess', this.type === virtualType.WATERFALL ? { renderLeftData: this.renderLeft, renderRightData: this.renderRight } : this.renderData)\n        this.emitScrollEvent()\n      })\n    },\n  },\n  created() {\n    if (this.type === virtualType.WATERFALL) {\n      this.waterfallInfo = { left: [], right: [] }\n    }\n    this.getScrollFn = throttle(this.handleScroll, this.interval, { leading: false })\n  },\n  mounted() {\n    if (this.type !== virtualType.WATERFALL) {\n      if (!this.size) {\n        throw new Error(`type为${this.type}时，请传入size属性`)\n      }\n      this.$refs.scrollBar.style.height = this.data.length * this.size + this.unit\n      this.end = this.start + this.remain\n    }\n    switch (this.type) {\n      case virtualType.FIXED:\n        this.$refs.virtualList.style.height = this.remain * this.size + this.unit\n        break\n      case virtualType.VARIABLE:\n        this.variableData = []\n        this.$refs.virtualList.style.height = this.virtualHieght\n        this.variableData = this.getVisiblePosition(this.data)\n        break\n      case virtualType.WATERFALL:\n        // 必须设置scrollList高度否则scrollList的高度会被 容器内容撑起造成高度与virtualList不同 则会出现滚动条偏移问题\n        this.$refs.virtualList.style.height = this.$refs.scrollList.style.height = this.virtualHieght\n        this.leftEnd = this.leftStart + this.remain\n        this.rightEnd = this.rightStart + this.remain\n        if (this.data && this.data.length) {\n          this.handleWaterfallData(this.data)\n        }\n        break\n    }\n    this.deleteId && this.deleteItem(this.deleteId)\n  },\n  updated() {\n    this.$nextTick(() => {\n      const nodes = this.$refs.nodes\n      if (this.type === virtualType.VARIABLE && nodes && nodes.length) {\n        //用节点更新缓存\n        nodes.forEach((node) => {\n          let { height } = node.getBoundingClientRect()\n          let index = +node.getAttribute('vIndex')\n          let oldHeight = this.variableData[index].height\n          let difference = oldHeight - height\n          if (difference) {\n            this.variableData[index].height = height\n            this.variableData[index].bottom = this.variableData[index].bottom - difference\n            for (let i = index + 1; i < this.variableData.length; i++) {\n              this.variableData[i].top = this.variableData[i - 1].bottom\n              this.variableData[i].bottom = this.variableData[i].bottom - difference\n            }\n          }\n        })\n        this.$refs.scrollBar.style.height = this.variableData[this.variableData.length - 1].bottom + this.unit\n      }\n    })\n  },\n  methods: {\n    deleteItem(id) {\n      let deleteIndex = this.data.findIndex((item) => item.id == id)\n      if (deleteIndex > -1) {\n        let deleteItem\n        this.data.splice(deleteIndex, 1)\n        if (this.type === virtualType.FIXED) {\n          deleteItem = this.data[deleteIndex]\n        }\n        if (this.type === virtualType.VARIABLE) {\n          deleteItem = this.variableData[deleteIndex]\n          this.variableData.splice(deleteIndex, 1)\n          // 更新位置坐标\n          for (let i = deleteItem.index; i < this.variableData.length; i++) {\n            this.variableData[i].index = this.variableData[i].index - 1\n            this.variableData[i].top = this.variableData[i].top - deleteItem.height\n            this.variableData[i].bottom = this.variableData[i].bottom - deleteItem.height\n          }\n        }\n        if (this.type === virtualType.WATERFALL) {\n          if (this.waterfallInfo) {\n            deleteItem = [...this.waterfallInfo.left, ...this.waterfallInfo.right].find((item) => item.id == id)\n          }\n          this.waterfallInfo = { left: [], right: [] }\n          this.leftHeight = this.rightHieght = 0\n          this.handleWaterfallData(this.data)\n        }\n        this.$emit('deleteSuccess', deleteItem)\n      }\n    },\n    setStyle(item) {\n      if (this.marginBottom === 0) {\n        return { height: item.height + this.unit }\n      } else {\n        return { marginBottom: item.marginBottom + this.unit, height: item.height + this.unit }\n      }\n    },\n    handleWaterfallData(data) {\n      if (!data.length) return\n      if (this.waterfallInfo.left.length) {\n        this.waterfallInfo.left[this.waterfallInfo.left.length - 1].marginBottom = this.marginBottom\n      }\n      if (this.waterfallInfo.right.length) {\n        this.waterfallInfo.right[this.waterfallInfo.right.length - 1].marginBottom = this.marginBottom\n      }\n      const { left: leftData, right: rightData } = this.waterfallGroup(data)\n      const left = [...this.waterfallInfo.left, ...this.getWaterfallPosition(leftData, 'left')]\n      const right = [...this.waterfallInfo.right, ...this.getWaterfallPosition(rightData, 'right')]\n      left[left.length - 1].marginBottom = 0\n      right[right.length - 1].marginBottom = 0\n      this.waterfallInfo = { left, right }\n      this.setWaterfallBarHeight()\n    },\n    // 分组\n    waterfallGroup(data) {\n      const left = []\n      const right = []\n      for (let i = 0, item; (item = data[i++]); ) {\n        item.marginBottom = this.marginBottom\n        const size = Number(item.height) + Number(item.marginBottom)\n        if (this.leftHeight <= this.rightHieght) {\n          this.leftHeight += size\n          left.push(item)\n        } else {\n          right.push(item)\n          this.rightHieght += size\n        }\n      }\n      return { left, right }\n    },\n    setWaterfallBarHeight() {\n      if (this.leftHeight >= this.rightHieght) {\n        this.$refs.scrollBar.style.height = this.leftHeight - this.marginBottom + this.unit\n      } else {\n        this.$refs.scrollBar.style.height = this.rightHieght - this.marginBottom + this.unit\n      }\n    },\n    getVisiblePosition(data) {\n      if (!data.length) return []\n      const prevLength = this.variableData ? this.variableData.length : 0\n      let result = data.map((item, index) => ({\n        id: item.id,\n        index: prevLength + index,\n        height: this.size,\n        top: (prevLength + index) * this.size,\n        bottom: (prevLength + index + 1) * this.size,\n      }))\n      return result\n    },\n    getWaterfallPosition(data, type) {\n      if (!data.length) return []\n      const prevData = this.waterfallInfo[type]\n      for (let i = 0; i < data.length; i++) {\n        data[i].index = prevData.length + i\n        const size = Number(data[i].height) + Number(this.marginBottom)\n        if (i == 0) {\n          if (prevData.length) {\n            data[i].top = prevData[prevData.length - 1].bottom\n            data[i].bottom = prevData[prevData.length - 1].bottom + size\n          } else {\n            data[i].top = 0\n            data[i].bottom = size\n          }\n        } else {\n          data[i].top = data[i - 1].bottom\n          data[i].bottom = data[i - 1].bottom + size\n        }\n      }\n      return data\n    },\n    emitScrollEvent() {\n      if (this.type === virtualType.WATERFALL) {\n        this.$emit('scroll', {\n          scrollTop: this.scrollTop,\n          renderLeftScrollTop: this.scrollTop - this.leftOffset,\n          renderRightScrollTop: this.scrollTop - this.rightOffset,\n          renderLeftData: this.renderLeft,\n          renderRightData: this.renderRight,\n        })\n      } else {\n        this.$emit('scroll', {\n          scrollTop: this.scrollTop,\n          renderScrollTop: this.scrollTop - this.offset,\n          renderData: this.renderData,\n        })\n      }\n    },\n    handleScroll() {\n      // this.$parent.$emit.apply(this.$parent, ['abc', '触发'])\n      const scrollTop = (this.scrollTop = this.$refs.virtualList.scrollTop)\n      switch (this.type) {\n        case virtualType.FIXED:\n          this.start = Math.floor(scrollTop / this.size)\n          this.end = this.start + this.remain\n          this.offset = this.start * this.size - this.prevCount * this.size\n          break\n        case virtualType.VARIABLE:\n          this.start = getCurrentIndex(scrollTop, this.variableData)\n          this.end = this.start + this.remain\n          this.offset = this.variableData[this.start - this.prevCount].top || 0\n          break\n        case virtualType.WATERFALL:\n          this.leftStart = getCurrentIndex(scrollTop, this.waterfallInfo.left)\n          this.rightStart = getCurrentIndex(scrollTop, this.waterfallInfo.right)\n          // 渲染的屏幕数量\n          this.leftEnd = this.leftStart + this.remain\n          this.rightEnd = this.rightStart + this.remain\n          this.leftOffset = this.waterfallInfo.left[this.leftStart - this.leftPrevCount].top || 0\n          this.rightOffset = this.waterfallInfo.right[this.rightStart - this.rightPrevCount].top || 0\n          break\n      }\n      this.$nextTick(() => {\n        this.emitScrollEvent()\n        if (scrollTop + this.$refs.virtualList.clientHeight >= this.$refs.virtualList.scrollHeight - this.distance) {\n          this.$emit('loadMore')\n        }\n      })\n    },\n  },\n}\n</script>\n<style>\n.virtual-list {\n  overflow-y: auto;\n  position: relative;\n  -webkit-overflow-scrolling: touch;\n}\n.virtual-list * {\n  box-sizing: border-box;\n}\n.scroll-list {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n}\n.waterfall-list {\n  display: flex;\n  justify-content: space-between;\n}\n.left-wrap,\n.right-wrap {\n  width: 50%;\n}\n.left-item,\n.right-item {\n  overflow: hidden;\n}\n</style>\n```\n\n\n## 参考\n- [tangbc/vue-virtual-scroll-list](https://github.com/tangbc/vue-virtual-scroll-list)\n- [再谈前端虚拟列表的实现](https://juejin.cn/post/6844903577631227912)\n- [前端的长列表性能优化](https://segmentfault.com/p/1210000011237223/read)\n- [lite-virtual-list](https://github.com/wensiyuanseven/lite-virtual-list)\n"
  },
  {
    "path": "4.1.0.6 长属性菜单优化.md",
    "content": "# 4.1.0.6 长属性菜单优化\n\n\n>就是大数据量的树形菜单，加载优化（非懒加载，非逐级加载），待解决\n\n"
  },
  {
    "path": "4.1.1 移动端 js 拖拽实现.md",
    "content": "# 移动端 js 拖拽实现\n\n```\n// 获取节点\n      var block = document.getElementById(\"editmove\");\n      var oW,oH;\n      // 绑定touchstart事件\n      block.addEventListener(\"touchstart\", function(e) {\n         console.log(e);\n         var touches = e.touches[0];\n         oW = touches.clientX - block.offsetLeft;\n         oH = touches.clientY - block.offsetTop;\n         //阻止页面的滑动默认事件\n         document.addEventListener(\"touchmove\",defaultEvent,false);\n      },false)\n     \n      block.addEventListener(\"touchmove\", function(e) {\n         var touches = e.touches[0];\n         var oLeft = touches.clientX - oW;\n         var oTop = touches.clientY - oH;\n         // if(oLeft < 0) {//去掉这部分可以让自由拖放不限制\n         //  oLeft = 0;//改过-150\n         // }else if(oLeft > document.documentElement.clientWidth - block.offsetWidth) {\n         //  oLeft = (document.documentElement.clientWidth - block.offsetWidth);\n         // }\n         block.style.left = oLeft + \"px\";\n         block.style.top = oTop + \"px\";\n      },false);\n       \n      block.addEventListener(\"touchend\",function() {\n         document.removeEventListener(\"touchmove\",defaultEvent,false);\n      },false);\n      \n      function defaultEvent(e) {\n         e.preventDefault();\n      }\n```\n"
  },
  {
    "path": "4.1.1.1 前端性能优化总结.md",
    "content": "# 4.1.1.1 前端性能优化总结.\n\n\n## \n\n- 减少cookie传输\n\n- cdn：\n\n- 减少http请求\n\n- 减少重绘和回流\n\n- 多进程压缩\n\n- 使用缓存\n\n- 区分环境\n\n- 按需加载\n\n- 提取公共代码\n\n- Tree Shaking\n\n- 预加载\n\n- ssr\n\n- 骨架屏\n\n\n\n## 参考\n- []()\n"
  },
  {
    "path": "4.1.2.  call 和 apply .md",
    "content": "# 调用一个对象的一个方法，以另一个对象替换当前对象\n\napply(func,数组参数)：\n\n```\nfunction add(a,b){\n  return a+b;  \n}\nfunction sub(a,b){\n  return a-b;  \n}\nvar a1 = add.apply(sub,[4,2]);　　//sub调用add的方法\nvar a2 = sub.apply(add,[4,2]);\nalert(a1);  //6     \nalert(a2);  //2\n```\n\n\n\n## 模拟实现\n```\nFunction.prototype.newCall = function(context) {\n  context.fn = this;  // 通过this获取call的函数\n  context.fn();\n  delete context.fn;\n}\nlet foo = {\n  value: 1\n}\nfunction bar() {\n  console.log(this.value);\n}\nbar.newCall (foo); // 1\n```\n\n>但是如果说有传参数呢？  \n所以我们可以进行优化一下，因为传入的参数数量是不确定的，所以我们可以从Arguments对象中去获取，这个比较简单。  \n问题是参数是不确定的，我们如何传入到我们要执行的函数中去呢 ？ 这里我们有两种选择：一种是通过eval拼接的方式，另一种就要用到es6了。\n\n```\nFunction.prototype.newCall = function(context, ...parameter) {\n  if (typeof context === 'object') {\n    context = context || window\n  } else {\n    context = Object.create(null)\n  }\n  let fn = Symbol()\n  context[fn] = this\n  context[fn](...parameter);\n  delete context[fn]\n}\nlet person = {\n  name: 'Abiel'\n}\nfunction sayHi(age,sex) {\n  console.log(this.name, age, sex);\n}\nsayHi.newCall (person, 25, '男'); // Abiel 25 \n\n```\n\n实现了call之后，apply也是同样的思路。\n\n===========================================\n\n## apply实现：\n```\nFunction.prototype.newApply = function(context, parameter) {\n  if (typeof context === 'object') {\n    context = context || window\n  } else {\n    context = Object.create(null)\n  }\n  let fn = Symbol()\n  context[fn] = this\n  context[fn](...parameter);\n  delete context[fn]\n}\n\n```\n## bind\n初体验：\n\n```\nFunction.prototype.bind = function (context) {\n  var me = this\n  return function () { // bind之后得到的函数\n    return me.call(context)  // 执行是改变this执行\n  }\n}\n\n\n加入参数：\n\nFunction.prototype.bind = function (context,...innerArgs) {\n  var me = this\n  return function (...finnalyArgs) {\n    return me.call(context,...innerArgs,...finnalyArgs)\n  }\n}\nlet person = {\n  name: 'Abiel'\n}\nfunction sayHi(age,sex) {\n  console.log(this.name, age, sex);\n}\nlet personSayHi = sayHi.bind(person, 25)\npersonSayHi('男')\n```\n"
  },
  {
    "path": "4.1.3. this 在不同环境中指向.md",
    "content": "# this\n\n\n## js中this在不同环境的指向\n\nthis的指向在函数定义的时候是确定不了的，只有函数执行的时候才能确定this到底指向谁，\n实际上this的最终指向的是那个调用它的对象；\n\n1.函数调用模式\n\n当作函数调用，这时函数内的this指向全局对象（大对数情况下是window）\n```\nfunction a(){    \n   var num= 233;    \n   console.log(this.num); //undefined    \n   console.log(this); //Window\n}\na();\n```\n按照我们上面说的this最终指向的是调用它的对象，  \n这里的函数a实际是被Window对象所点出来的，  \n相当于window.a();  \na() 是 一个函数也可以说是方法 ，那方法肯定是由对象来调用的。  \n所以当执行 a(); 时 就有一个隐式对象调用了a() ，这个隐式对象就是 window;  \n\n2.方法调用模式\n\n当一个函数是一个对象的属性时，我们称它为该对象的一个方法，当一个方法被调用时，this指向该对象;\n```\nvar o = {\n    t:\"test\",\n    fn:function(){\n        console.log(this.t);  //test\n    }\n}\no.fn();\n```\n这里的this指向的是对象o，因为调用这个fn是通过o.fn()执行的，那自然指向就是对象o，  \n这里再次强调一点，this的指向在函数创建的时候是决定不了的，在调用的时候才能决定，谁调用的就指向谁;  \n如果一个函数中有this，这个函数中包含多个对象，尽管这个函数是被最外层的对象所调用，this指向的也只是它上一级的对象;  \n```\nvar o = {\n    a:10,\n    b:{        // a:12,\n        fn:function(){\n            console.log(this.a); //undefined\n        }\n    }\n}\n```\no.b.fn();//尽管对象b中没有属性a，这个this指向的也是对象b，因为this只会指向它的上一级对象，    \n不管这个对象中有没有this要的东西  \n\n还有一种比较特殊的情况:\n```\nvar o = {\n    a:10,\n    b:{\n        a:12,\n        fn:function(){\n            console.log(this.a); //undefined\n            console.log(this); //window\n        }\n    }\n}\nvar j = o.b.fn;\nj();\n```\n这里this指向的是window,this永远指向的是最后调用它的对象，也就是看它执行的时候是谁调用的;    \n虽然函数fn是被对象b所引用，但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window;\n\n3.构造器调用模式 使用new调用的函数称为构造器函数，此时的this指向该构造器函数实例出来的对象；\n```\nfunction Fn(){    \n    this.user = \"test\";\n}\n var a = new Fn();\nconsole.log(a.user); //test\n```\n这个this指向对象a,这里之所以对象a可以点出函数Fn里面的user是因为new关键字可以改变this的指向，将这个this指向对象a;\n\n这里用变量a创建了一个Fn的实例（相当于复制了一份Fn到对象a里面），此时仅仅只是创建，并没有执行， \n而调用这个函数Fn的是对象a，那么this指向的自然是对象a，那么为什么对象Fn中会有user， \n因为你已经复制了一份Fn函数到对象a中，用了new关键字就等同于复制了一份。 　　\n除了上面的这些以外，还可以自行改变this的指向，用JavaScript中call,apply,bind方法更改this的指向。 　　\n一个小问题当this碰到return时\n```\nfunction fn()  \n{  \n    this.user = 'test';  \n    return {};  \n}\nvar a = new fn;  \nconsole.log(a.user); //undefined\n\n```\n再看一个\n```\nfunction fn()  \n{  \n    this.user = 'test';  \n    return function(){};\n}\nvar a = new fn;  \nconsole.log(a.user); //undefined\n```\n再来\n```\nfunction fn()  \n{  \n    this.user = 'test';  \n    return 1;\n}\nvar a = new fn;  \nconsole.log(a.user); //test\n```\n```\nfunction fn()  \n{  \n    this.user = 'test';  \n    return undefined;\n}\nvar a = new fn;  \nconsole.log(a.user); //test\n```\n什么意思呢？ 　　如果返回值是一个对象，那么this指向的就是那个返回的对象，如果返回值不是一个对象那么this还是指向函数的实例。\n```\nfunction fn()  \n{  \n    this.user = 'test';  \n    return undefined;\n}\nvar a = new fn;  \nconsole.log(a); //fn {user: \"test\"}\n　　还有一点就是虽然null也是对象，但是在这里this还是指向那个函数的实例，因为null比较特殊。\n\nfunction fn()  \n{  \n    this.user = 'test';  \n    return null;\n}\nvar a = new fn;  \nconsole.log(a.user); //test\n```\n\n4.apply/call调用模式以及bind\n\napply、call、bind方法可以让我们设定调用者中的this指向谁\n```\nfunction showValue(){\n  console.log(this.value);\n}\nvar obj={\n  value:4\n}\nshowValue.call(obj)//输出4，this指向了obj对象\ncall()：\n\nvar a = {\n    user:\"test\",\n    fn:function(){\n        console.log(this.user); //test\n    }\n}\nvar b = a.fn;\nb.call(a);\n```\n通过在call方法，给第一个参数添加要把b添加到哪个环境中，简单来说，this就会指向那个对象，这里指向a ;\n\napply();\n\napply方法和call方法有些相似，它也可以改变this的指向;\n```\nvar a = {\n    user:\"test\",\n    fn:function(){\n        console.log(this.user); //test\n    }\n}\nvar b = a.fn;\nb.apply(a);\n//注意如果call和apply的第一个参数写的是null，那么this指向的是window对象\n```\n5.箭头函数中调用：\nes6里面this指向固定化，始终指向外部对象，因为箭头函数没有this,因此它自身不能进行new实例化,\n同时也不能使用call, apply, bind等方法来改变this的指向;\n\nfunction Timer() {        \n    this.seconds = 0;\n    setInterval( () => this.seconds ++, 1000);\n } \n    var timer = new Timer();\n    \n setTimeout( () => console.log(timer.seconds), 3100);    \n    // 3\n在构造函数内部的setInterval()内的回调函数， this始终指向实例化的对象， 并获取实例化对象的seconds的属性, 每1s这个属性的值都会增加1。 \n否则最后在3s后执行setTimeOut()函数执行后输出的是0；\n\n6.Eval函数\n\n该函数执行的时候，this绑定到当前作用域的对象上\n\n```\n    var name=\"XL\";    \n    var person={\n        name:\"xl\",\n        showName:function(){            \n            eval(\"console.log(this.name)\");\n        }\n    }\n    \n    person.showName();  //输出  \"xl\"\n    \n    var a=person.showName;\n    a();  //输出  \"XL\"\n```\n## 严格模式 ‘use strict’;\n\n如果在严格模式的情况下执行纯粹的函数调用，那么这里的的 \nthis 并不会指向全局，而是 undefined，这样的做法是为了消除 js 中一些不严谨的行为；\n\n```\n'use strict';\nfunction test() {\n  console.log(this);\n};\n\ntest();\n\n// undefined\n```\n\n部分参考：追梦子http://www.cnblogs.com/pssp/p/5216085.html\n\n- [this指向面试题](https://juejin.cn/post/6844903493845647367)\n"
  },
  {
    "path": "4.1.3.1 JavaScript 的 this 原理.md",
    "content": "# JavaScript 的 this 原理\n\n\n>JavaScript 语言之所以有this的设计，跟内存里面的数据结构有关系\n\n如果要读取obj.foo，引擎先从obj拿到内存地址，然后再从该地址读出原始的对象，返回它的foo属性。\n\n原始的对象以字典结构保存，每一个属性名都对应一个属性描述对象。举例来说，上面例子的foo属性，\n\n```\nvar obj = { p: 'a' };\nObject.getOwnPropertyDescriptor(obj, 'p')\n// Object { value: \"a\",\n// writable: true,\n// enumerable: true,\n// configurable: true\n// }\n```\n\n\n\n\n- 面试题\n\n```\nvar name = 'window'\n\nvar person1 = {\n  name: 'person1',\n  show1: function () {\n    console.log(this.name)\n  },\n  show2: () => console.log(this.name),\n  show3: function () {\n    return function () {\n      console.log(this.name)\n    }\n  },\n  show4: function () {\n    return () => console.log(this.name)\n  }\n}\nvar person2 = { name: 'person2' }\n\nperson1.show1(); // this指向最后调用的， person1\nperson1.show1.call(person2) //call改变this指向 ，person2\n\nperson1.show2()// 箭头函数this指向固定，指向定义函数时所在上下文，window\nperson1.show2.call(person2)， window\n\nperson1.show3()() // 相当于 var a = person1.show3(); a(); 指向 window\nperson1.show3().call(person2) // 相当于 var a = person1.show3(); a().call(person2); call改变this指向，指向 person2\nperson1.show3.call(person2)() // 相当于 var a = person1.show3.call(person2); a(); call改变this指向，最后才是调用函数，指向 window\n\nperson1.show4()()// 箭头函数体内的this对象，就是定义时所在的对象，而不是使用时所在的对象;    // person1\nperson1.show4().call(person2);   //箭头函数 person1\nperson1.show4.call(person2)();// person1.show4.call(person2)，先改变了this指向，最后调用， person2\n\n //谁调用箭头函数的外层function，箭头函数的this就是指向该对象，如果箭头函数没有外层函数，则指向window\n\n\n```\n\n## 参考\n- [JavaScript 的 this 原理](http://www.ruanyifeng.com/blog/2018/06/javascript-this.html)\n"
  },
  {
    "path": "4.1.4. 事件委托.md",
    "content": "# 事件委托\n\n事件委托:就是利用事件冒泡，只指定一个事件处理程序，就可以管理某一类型的所有事件\n\n>适合用事件委托的事件：click，mousedown，mouseup，keydown，keyup，keypress。\n\n比如我们有100个li，每个li都有相同的click点击事件，可能我们会用for循环的方法，来遍历所有的li，然后给它们添加事件，那这么做会存在什么影响呢？\n\n在JavaScript中，添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能，\n\n因为需要不断的与dom节点进行交互，访问dom的次数越多，\n\n引起浏览器重绘与重排的次数也就越多，就会延长整个页面的交互就绪时间，\n\n这就是为什么性能优化的主要思想之一就是减少DOM操作的原因；\n\n如果要用事件委托，就会将所有的操作放到js程序里面，与dom的操作就只需要交互一次，这样就能大大的减少与dom的交互次数，提高性能；\n\n**只要在父级绑定事件**\n\n## 事件委托影响性能的因素：\n\n- 1：元素中，绑定事件委托的次数；\n\n- 2：点击的最底层元素，到绑定事件元素之间的DOM层数；\n\n结合这三点，在必须使用事件委托的地方，可以如下的处理：\n\n- 1：只在必须的地方，使用事件委托，比如：ajax的局部刷新区域\n\n- 2：尽量的减少绑定的层级，不在body元素上，进行绑定\n\n- 3：减少绑定的次数，如果可以，那么把多个事件的绑定，合并到一次事件委托中去，由这个事件委托的回调，来进行分发。\n\n## 解决的方案：\n\n- 1：降低层级，这个比较好实现，在开发中，直接把事件绑定在低层级的元素上即可，这个无法继续优化；\n\n- 2：减少绑定的次数，现在只能在这个点上继续优化了。\n\n## 参考\n- [理解-事件委托](http://www.zhangyunling.com/564.html)\n"
  },
  {
    "path": "4.1.5. 浅拷贝和深拷贝.md",
    "content": "# 浅拷贝和深拷贝\n\nJS 中的浅拷贝与深拷贝，只是针对复杂数据类型（Object，Array）的复制问题。\n\n浅拷贝与深拷贝都可以实现在已有对象上再生出一份的作用。但是对象的实例是存储\n\n在堆内存中然后通过一个引用值去操作对象，由此拷贝的时候就存在两种情况了：拷贝引用和拷贝实例，这也是浅拷贝和深拷贝的区别。\n\n* 浅拷贝：浅拷贝是拷贝引用，拷贝后的引用都是指向同一个对象的实例，彼此之间的操作会互相影响\n\n* 深拷贝：在堆中重新分配内存，并且把源对象所有属性都进行新建拷贝，\n      以保证深拷贝的对象的引用图不包含任何原有对象或对象图上的任何对象，拷贝后的对象与原 来的对象是完全隔离，互不影响\n\n- 浅拷贝\n\n浅拷贝分两种情况，拷贝直接拷贝源对象的引用 和 源对象拷贝实例，但其属性（类型为Object，Array的属性）拷贝引用。\n\n拷贝原对象的引用\n\n这是最简单的浅拷贝。例：\n\n```\nvar a = {c:1};\nvar b = a;\nconsole.log(a === b); // 输出true。\na.c = 2;\nconsole.log(b.c); // 输出 2\n```\n源对象拷贝实例，其属性对象拷贝引用。\n\n这种情况，外层源对象是拷贝实例，如果其属性元素为复杂杂数据类型时，内层元素拷贝引用。\n对源对象直接操作，不影响两外一个对象，但是对其属性操作时候，会改变两外一个对象的属性的只。\n常用方法为：Array.prototype.slice(), Array.prototype.concat(), jQury的$.extend({},obj)，例：\n\n```\nvar a = [{c:1}, {d:2}];\nvar b = a.slice();\nconsole.log(a === b); // 输出false，说明外层数组拷贝的是实例\na[0].c = 3;\nconsole.log(b[0].c); // 输出 3，说明其元素拷贝的是引用\n```\n\n- 深拷贝\n\n深拷贝后，两个对象，包括其内部的元素互不干扰。  \n第一种用JSON.parse(JSON.stringify(obj))，  \n第二种可以使用for...in加递归完成\n\n递归\n```\nfunction isObj(obj) {\n    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null\n}\nfunction deepCopy(obj) {\n    let tempObj = Array.isArray(obj) ? [] : {}\n    for(let key in obj) {\n        tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]\n    }\n    return tempObj\n}\n\n作者：YDJFE\n链接：https://juejin.im/post/5b235b726fb9a00e8a3e4e88\n```\n\n常见方法有JSON.parse(),JSON.stringify()，jQury的$.extend(true,{},obj)，lodash的_.cloneDeep和_.clone(value, true)。例：\n\n```\nvar a = {c: {d: 1}};\nvar b = $.extend(true, {}, a);\nconsole.log(a === b); // 输出false\na.c.d = 3;\nconsole.log(b.c.d); // 输出 1，没有改变。\n\n\n// JSON.parse(),JSON.stringify()\nconst oldObj = {\n  a: 1,\n  b: [ 'e', 'f', 'g' ],\n  c: { h: { i: 2 } }\n};\n\nconst newObj = JSON.parse(JSON.stringify(oldObj));\nconsole.log(newObj.c.h, oldObj.c.h); // { i: 2 } { i: 2 }\nconsole.log(oldObj.c.h === newObj.c.h); // false\nnewObj.c.h.i = 'change';\nconsole.log(newObj.c.h, oldObj.c.h); // { i: 'change' } { i: 2 }\n\n作者：寻找海蓝96\n链接：https://juejin.im/post/5abb55ee6fb9a028e33b7e0a\n\n确实,这个方法虽然可以解决绝大部分是使用场景,但是却有很多坑.\n\n1.他无法实现对函数 、RegExp等特殊对象的克隆\n\n2.会抛弃对象的constructor,所有的构造函数会指向Object\n\n3.对象有循环引用,会报错\n```\n\n- 序列化函数对象\n\n>序列化就是将对象的内容进行流化\n\n```\nvar a = {\n true: function() {console.log(1)}\n}\n\nvar b = JSON.stringify(a)\n// \"{}\"\n\n----------------------------\nvar a = {\n true: /^[0-9]+(\\.[0-9]{1,3})?$/\n}\n\nvar b = JSON.stringify(a)\n\n//\"{\"true\":{}}\"\n```\n\n## 原始类型对象的克隆\n```\n1.字符串的克隆\nvar x=\"1\";\nvar y=x;\ny=\"2\";\nconsole.log(x) //\"1\"  源不变\nconsole.log(y) //\"2\"\n\n2.数值的克隆\nvar x=1;\nvar y=x;\ny=2;\nconsole.log(x) //1  源不变\nconsole.log(y) //2\n\n3.布尔值的克隆\nvar x=true;\nvar y=x;\ny=false;\nconsole.log(x) //true  源不变\nconsole.log(y) //false\n\n4.数组的克隆\nvar x=[1,2];\nvar y=x;\ny[3] = 8;\n\nconsole.log(x) //[1, 2, empty, 8]  源跟着变化\nconsole.log(y) //[1, 2, empty, 8]\n原始数组x，克隆数组y，修改了克隆数组y，但也同时修改了原始数组x，这就是引用对象的特点。\n=====================================\nvar x=[1,2];\nvar y=[];\nvar i=0;\nvar j=x.length;\nfor(;i<j;i++)\n{\n\ty[i]=x[i];\n}\ny[3] = 8;\n\nconsole.log(x) //[1, 2]  源不变\nconsole.log(y) //[1, 2, empty, 8]\n克隆数组y，原始数组x，两个数组互补干扰，实现了完整的数组克隆\n--------------------------------------\nvar _test = [1,2,3];//原数组\nvar _testCopy = [].concat(_test);//拷贝数组\n_testCopy[0]=4;\nconsole.log(_test);// [1,2,3]\nconsole.log(_testCopy);//[4,2,3]\n-----------------------------------\n\n\n5. 对象的克隆\n和数组的克隆同理，\nvar x={1:2,3:4};\nvar y=x;\ny[4] =6\nconsole.log(x) //{1: 2, 3: 4, 4: 6}  源变\nconsole.log(y) //{1: 2, 3: 4, 4: 6}\n\n======================================\n完整的克隆\nvar x={1:2,3:4};\nvar y={};\nvar i;\nfor(i in x)\n{\n  y[i]=x[i];\n}\ny[5]=6;\n \nconsole.log(x); // Object {1: 2, 3: 4} \nconsole.log(y);// Object {1: 2, 3: 4, 5: 6} \n\n------------------------------------------\nvar _test = [{\"name\":\"weifeng\"},{\"name\":\"boy\"}];//原数组\nvar _testCopy = [].concat(JSON.parse(JSON.stringify(_test)));//拷贝数组,注意这行的拷贝方法\n_testCopy[1].name=\"girl\";\nconsole.log(_test);// [{\"name\":\"weifeng\"},{\"name\":\"boy\"}]\nconsole.log(_testCopy);//[{\"name\":\"weifeng\"},{\"name\":\"girl\"}]\n------------------------------------------\n\n```\n\n## 完整的对象克隆demo,在生产环境中最好用`lodash`的深克隆实现.\n```\nfunction clone(obj)\n{\n\tvar o,i,j,k;\n\tif(typeof(obj)!=\"object\" || obj===null)return obj;\n\tif(obj instanceof(Array))\n\t{\n\t\to=[];\n\t\ti=0;j=obj.length;\n\t\tfor(;i<j;i++)\n\t\t{\n\t\t\tif(typeof(obj[i])==\"object\" && obj[i]!=null)\n\t\t\t{\n\t\t\t\to[i]=arguments.callee(obj[i]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\to[i]=obj[i];\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\to={};\n\t\tfor(i in obj)\n\t\t{\n\t\t\tif(typeof(obj[i])==\"object\" && obj[i]!=null)\n\t\t\t{\n\t\t\t\to[i]=arguments.callee(obj[i]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\to[i]=obj[i];\n\t\t\t}\n\t\t}\n\t}\n\treturn o;\n}\n```\n\n\n// 只解决date，reg类型，其他的可以自己添加\n```\nfunction deepCopy(obj, hash = new WeakMap()) {\n    let cloneObj\n    let Constructor = obj.constructor\n    switch(Constructor){\n        case RegExp:\n            cloneObj = new Constructor(obj)\n            break\n        case Date:\n            cloneObj = new Constructor(obj.getTime())\n            break\n        default:\n            if(hash.has(obj)) return hash.get(obj)\n            cloneObj = new Constructor()\n            hash.set(obj, cloneObj)\n    }\n    for (let key in obj) {\n        cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];\n    }\n    return cloneObj\n}\n\n\n作者：YDJFE\n链接：https://juejin.im/post/5b235b726fb9a00e8a3e4e88\n```\n\n\n## 递归去复制所有层级属性。\n\n深拷贝的函数\n\n```\nfunction deepClone(obj){\n    let objClone = Array.isArray(obj)?[]:{};\n    if(obj && typeof obj===\"object\"){\n        for(key in obj){\n            if(obj.hasOwnProperty(key)){\n                //判断ojb子元素是否为对象，如果是，递归复制\n                if(obj[key]&&typeof obj[key] ===\"object\"){\n                    objClone[key] = deepClone(obj[key]);\n                }else{\n                    //如果不是，简单复制\n                    objClone[key] = obj[key];\n                }\n            }\n        }\n    }\n    return objClone;\n}    \nlet a=[1,2,3,4],\n    b=deepClone(a);\na[0]=2;\nconsole.log(a,b);\n```\n\n\n## es6 当对象或者数组内部的都是基本数据类型的话，以下的方式可以实现深拷贝。但是如果出现了引用类型嵌套引用类型的话。以下方法将不可用\n\n- `[...arr]`\n\n```\n# 数组深拷贝\nvar arr = [{a:1}];\nvar b= [...arr];\nb[1] = 2;\n\n---------\n\nvar arr = [{a:1,b:{c:1}}]\nvar b= [...arr];\nb[1] = 2;  // arr 不变\nb[0].b.b = 2; // arr 变了\n\n# 对象深拷贝\n\nvar obj = {a:1};\nvar b= {...obj};\nb.concat = 2;\n\n---------------\n\n# 对象浅拷贝\n\nvar obj = {a:1,b:{f:2}};\nvar b= {...obj};\nb.b.c = 2;  // obj 也变了\nb.c = 3; // obj没变\n\n```\n\n- Object.assign()\n\n\n```\n# 也是如此\n\nvar cc= {a:1,b:{c:1}};\n\nvar dd = Object.assign(cc)\n\ndd.n = 1\n\n// cc 变了\n```\n"
  },
  {
    "path": "4.1.5.1 深拷贝 structuredClone().md",
    "content": "# 4.1.5.1 深拷贝 structuredClone().md\n\n\n>structuredClone\n\n\n## 使用\n\n```\nlet a = [{a:1}]\n\nlet b = structuredClone(a)\n\nconsole.log(b) // [{a:1}]\n\n\n```\n\n## 兼容性\n![image](https://user-images.githubusercontent.com/17672815/199702482-5ff1cb3d-edc5-4829-83c2-79809a571bc0.png)\n\n\n\n\n## 参考\n-  [structuredClone](https://developer.mozilla.org/zh-CN/docs/Web/API/structuredClone)\n-  [caniuse](https://caniuse.com/?search=structuredClone)\n"
  },
  {
    "path": "4.1.6 适用于 vue.js 和原生 js 的渐进式图片加载.md",
    "content": "# 适用于 vue.js 和原生 js 的渐进式图片加载 \n\n* [适用于 vue.js 和原生 js 的渐进式图片加载](https://github.com/ccforward/cc/issues/64)\n\n* js demo : https://ccforward.github.io/progressive-image/index.html\n\n* 版本库 : https://github.com/ccforward/progressive-image\n\n* cc 代码库: https://github.com/ccforward/Catalogue\n\n## 目录\n\n# cc Github目录\n\n## 1、技术博客\n  \n[http://blog.ccforward.net](https://github.com/ccforward/cc/issues)\n\n## 2、仓库\n* Material Design 风格的 Vue.js UI 组件库 [Rubik UI](https://github.com/ccforward/rubik)  \n  * [npm地址](https://www.npmjs.com/package/i-rubik)  \n  * [Demo 地址](https://ccforward.github.io/rubik/)\n* 知乎日报爬虫Node.js+Vue.js [zhihu-daily](https://github.com/ccforward/zhihu)  \n  [线上地址](http://zhihu.ccforward.net)\n* 渐进式图片加载 [progressive-image](https://github.com/ccforward/progressive-image)  \n  * [NPM](https://www.npmjs.com/package/progressive-image)  \n  * [详细解释](https://github.com/ccforward/cc/issues/64)  \n  * [线上地址](https://ccforward.github.io/progressive-image/index.html)\n* vue.js 服务端渲染模板 [SSR](https://github.com/ccforward/vue-ssr)   \n  [线上demo地址](http://ssr.ccforward.net/)  \n* 日期计算模块，主要用于知乎日报爬虫项目 [date-calc](https://github.com/ccforward/date-calc)  \n  [npm地址](https://www.npmjs.com/package/date-calc)\n* 自定义事件库 for Broswer & Node.js [EventFire](https://github.com/ccforward/EventFire)  \n  [npm地址](https://www.npmjs.com/package/EventFire)\n* vue.js 懒加载组件 [vue-lazy-image](https://github.com/ccforward/vue-lazy-image)  \n  [npm地址](https://www.npmjs.com/package/vue-lazy-image)\n* vue.js 滑块组件 [vue-range](https://github.com/ccforward/cc/tree/master/vue-range)  \n  [npm地址](https://www.npmjs.com/package/vue-range)\n* vue.js 分页组件[vue-pages](https://github.com/ccforward/cc/tree/master/vue-pages)  \n  [npm地址](https://www.npmjs.com/package/vue-pages)\n* 上传图片到七牛的chrome扩展和 node.js 接口 [chrome-qiniu](https://github.com/ccforward/qiniu)\n* 自动刷新的chrome扩展 [Auto-Refresh](https://github.com/ccforward/Auto-Refresh)  \n  [chrome 市场地址](https://chrome.google.com/webstore/detail/auto-fresh/kpmibidobilopnejmgmlihijhlmdacmc)\n* 修改请求HTTP头的chrome扩展 [C-Header](https://github.com/ccforward/C-Header)  \n  [chrome 市场地址](https://chrome.google.com/webstore/detail/c-header/cpkhilpjaiopicjdglhldbgamilgegnd)\n* KISSY 组件(支持到KISSY1.3的版本)\n\t* 模拟PC的console进行debug [KMobileConsole](https://github.com/ccforward/KMobileConsole)\n\t* 快捷键组件 [KissKey](https://github.com/ccforward/KissKey)\n\t* 表格排序组件(不再维护) [KSortTable](https://github.com/ccforward/KSortTable)\n\t* input输入过滤组件(不再维护) [inputFilter](https://github.com/ccforward/inputFilter)\n\n## 3、代码库\n### JS\n* [可联机玩的五子棋](https://github.com/ccforward/cc/tree/master/chess)\n* [Vue.js 五子棋(没有AI)](http://ccforward.github.io/game/chess/chess.html)\n* [URL分析](https://github.com/ccforward/cc/tree/master/URLParse)\n* [RGB颜色转换为HEX颜色](https://github.com/ccforward/cc/tree/master/RGB2HEX)\n* [promise规范简单实现](https://github.com/ccforward/cc/tree/master/promise)\n* [移动端模拟console的js工具](https://github.com/ccforward/cc/tree/master/mobileConsole)\n* [获取服务器时间 & 倒计时](https://github.com/ccforward/cc/tree/master/countdown)\n* [简单前后日期计算类](https://github.com/ccforward/cc/tree/master/date)\n* [requestAnimationFrame](https://github.com/ccforward/cc/blob/master/css3/requestAnimationFrame.js)\n* [20行代码的简单模板引擎](https://github.com/ccforward/cc/blob/master/simple-template/index.js)\n* [Node.js批量下载文件](https://github.com/ccforward/cc/blob/master/download/down.js)\n* [simple jQuery-bootstrap分页组件](https://github.com/ccforward/cc/blob/master/pagination/pagination.js)\n* [HTTP/2 + Node.js](https://github.com/ccforward/cc/blob/master/h2-node)\n\n\n### CSS\n* [一行代码调试CSS Layout Debug](https://github.com/ccforward/cc/issues/3)\n* [sticky](http://ccforward.github.io/demos/css/sticky/index.html)\n* [CSS Reset](https://github.com/ccforward/cc/blob/master/cssreset/index.css)\n* [文字下划线](http://ccforward.github.io/css-secrets/underline/index.html)\n* [画个梯形](http://ccforward.github.io/css-secrets/trapezoid/index.html)\n* [毛玻璃](http://ccforward.github.io/css-secrets/frosted-glass/index.html)\n\n### HTML5\n* [封装shake事件](https://github.com/ccforward/cc/tree/master/shake)\n* [devicemotion和deviceorientation事件](https://github.com/ccforward/ccforward.github.io/tree/master/demos/ios)\n  [网页指南针demo(手机打开)](http://ccforward.github.io/demos/ios/compass.html)\n* [模拟ios7的视差](https://github.com/ccforward/ccforward.github.io/blob/master/demos/ios/parallax.html)\n  [视差demo(手机打开)](http://ccforward.github.io/demos/ios/parallax.html)\n* [canvas七巧板](http://ccforward.github.io/demos/canvas/tangram.html)\n* [HTML5调用摄像头拍照](https://github.com/ccforward/cc/tree/master/HTML5_camera)  \n  [demo](http://ccforward.github.io/demos/webrtc/camera.html)\n* [WebRTC的demo](http://ccforward.github.io/demos/webrtc/index.html)\n\n### 其他\n* [Git自动推送shell脚本](https://github.com/ccforward/cc/blob/master/git/autoPush.sh)\n* [Git笔记](https://github.com/ccforward/cc/tree/master/git)\n* [常用bash](https://github.com/ccforward/cc/blob/master/bash/bash.sh)\n* [php-fpm脚本](https://github.com/ccforward/cc/blob/master/php-fpm/php-fpm.sh)\n* [ruby批量下载文件](https://github.com/ccforward/cc/blob/master/download/down.rb)\n\n\n"
  },
  {
    "path": "4.1.7 原型链和数据类型.md",
    "content": "# 数据类型和原型链\n\n* 基本数据类型\n  - string\n  - number\n  - boolean\n  - undefined\n  - null\n  - symbol\n  - 区别 null 和 undefined 最简单方法 Number(null)--->0 ; Number(undefined)--->NAN\n  \n* 特别注意的是，空数组（[]）和空对象（{}）对应的布尔值，都是true。\n  ```\n  if ([]) {\n    console.log(true);\n  }\n  // true\n\n  if ({}) {\n    console.log(true);\n  }\n  // true\n  ```\n  \n* 引用数据类型 （object 细分）\n  - object\n  - array\n  - function\n\n* 创建对象的方法\n  - 通过”字面量“方式创建      var person = {} //创建空对象 \n  - 通过”构造函数“方式创建。   var obj = new 函数名(); \n  - 通过object方式创建。      var obj = new Object(); \n  - Object.create方法        var obj = Object.create({});\n\n* 清空数组的方法\n  - 赋值为[]，\n  - length 赋值为 0\n  - splice() \n\n* 类型检测\n  - typeof()\n      typeof window // \"object\"  \n      typeof {} // \"object\"  \n      typeof [] // \"object\"  \n      typeof null // \"object\"  \n  - instanceof()\n      o instanceof Array\n  - Object.prototype.toString  \n      Object.prototype.toString.apply(null)  ---> \"[object Null]\"  \n      Object.prototype.toString.apply([]) ---> \"[object Array]\"  \n      Object.prototype.toString.apply({}) ---> \"[object Object]\"  \n   - \n    ```\n     1、最常见的判断方法：typeof\n\n     alert(typeof a)   ------------> string\n     alert(typeof b)   ------------> number\n     alert(typeof c)   ------------> object\n     alert(typeof d)   ------------> object\n     alert(typeof e)   ------------> function\n     alert(typeof f)   ------------> function\n     其中typeof返回的类型都是字符串形式，需注意，例如：\n     alert(typeof a == \"string\") -------------> true\n     alert(typeof a == String) ---------------> false\n     另外typeof 可以判断function的类型；在判断除Object类型的对象时比较方便。\n     2、判断已知对象类型的方法： instanceof\n     \n     alert(c instanceof Array) ---------------> true\n     alert(d instanceof Date) \n     alert(f instanceof Function) ------------> true\n     alert(f instanceof function) ------------> false\n     注意：instanceof 后面一定要是对象类型，并且大小写不能错，该方法适合一些条件选择或分支。\n     3、根据对象的constructor判断： constructor\n     \n     alert(c.constructor === Array) ----------> true\n     alert(d.constructor === Date) -----------> true\n     alert(e.constructor === Function) -------> true\n     注意： constructor 在类继承时会出错\n     eg：\n             function A(){};\n             function B(){};\n        A.prototype = new B(); //A继承自B\n         var aObj = new A();\n         alert(aobj.constructor === B) -----------> true;\n                alert(aobj.constructor === A) -----------> false;\n        而instanceof方法不会出现该问题，对象直接继承和间接继承的都会报true：\n          alert(aobj instanceof B) ----------------> true;\n          alert(aobj instanceof B) ----------------> true;\n        言归正传，解决construtor的问题通常是让对象的constructor手动指向自己：\n        aobj.constructor = A; //将自己的类赋值给对象的constructor属性\n        alert(aobj.constructor === A) -----------> true;\n        alert(aobj.constructor === B) -----------> false; //基类不会报true了;\n       4、通用但很繁琐的方法： prototype\n       \n       alert(Object.prototype.toString.call(a) === ‘[object String]’) -------> true;\n       alert(Object.prototype.toString.call(b) === ‘[object Number]’) -------> true;\n       alert(Object.prototype.toString.call(c) === ‘[object Array]’) -------> true;\n       alert(Object.prototype.toString.call(d) === ‘[object Date]’) -------> true;\n       alert(Object.prototype.toString.call(e) === ‘[object Function]’) -------> true;\n       alert(Object.prototype.toString.call(f) === ‘[object Function]’) -------> true;\n       大小写不能写错，比较麻烦，但胜在通用。\n       5、无敌万能的方法：jquery.type()\n\n       如果对象是undefined或null，则返回相应的“undefined”或“null”。\n       jQuery.type( undefined ) === \"undefined\"\n       jQuery.type() === \"undefined\"\n       jQuery.type( window.notDefined ) === \"undefined\"\n       jQuery.type( null ) === \"null\"\n       如果对象有一个内部的[[Class]]和一个浏览器的内置对象的 [[Class]] 相同，我们返回相应的 [[Class]] 名字。 (有关此技术的更多细节。 )\n       jQuery.type( true ) === \"boolean\"\n       jQuery.type( 3 ) === \"number\"\n       jQuery.type( \"test\" ) === \"string\"\n       jQuery.type( function(){} ) === \"function\"\n       jQuery.type( [] ) === \"array\"\n       jQuery.type( new Date() ) === \"date\"\n       jQuery.type( new Error() ) === \"error\" // as of jQuery 1.9\n       jQuery.type( /test/ ) === \"regexp\"\n       其他一切都将返回它的类型“object”。\n       通常情况下用typeof 判断就可以了，遇到预知Object类型的情况可以选用instanceof或constructor方法,实在没辙就使用$.type()方法。\n    ```\n* 判断某个属性定义在对象自身，还是定义在原型链上\n  - hasOwnProperty('length')\n  \n* in 运算符返回一个布尔值，表示一个对象是否具有某个属性 \n  - 'length' in Date // true\n  - 'toString' in Date // true\n  - 获得对象的所有可枚举属性（不管是自身的还是继承的），可以使用 for...in 循环\n\n* Object.create() 方法会使用指定的原型对象及其属性去创建一个新的对象\n  - Object.getOwnPropertyNames 方法返回 Date 所有自身的属性名  \n    Object.getOwnPropertyNames 方法返回一个数组，成员是对象本身的所有属性的键名，不包含继承的属性键名。\n\n* 对象实例的 isPrototypeOf 方法，用来判断一个对象是否是另一个对象的原型。\n  ```\n  var o1 = {};\n  var o2 = Object.create(o1);\n  var o3 = Object.create(o2);\n\n  o2.isPrototypeOf(o3) // true\n  o1.isPrototypeOf(o3) // true\n  ```\n\n\n\n* 原型链\n  \n  [一张图让你秒懂原型链](http://flypursue.com/jekyll/update/2015/06/03/proto.html)\n  \n  ![](http://flypursue.com/img/yuanxinglian.jpg)\n  ![](http://upload-images.jianshu.io/upload_images/574093-c03529e3f0943633.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)\n\n## 参考\n- https://www.cnblogs.com/libin-1/p/5820550.html\n- [MDN](https://developer.mozilla.org/zh-CN/docs/Glossary/Primitive)\n"
  },
  {
    "path": "4.1.7.1 作用域链.md",
    "content": "# 作用域\n\n作用域是程序源代码中定义变量的区域。\n\n作用域规定了如何查找变量，也就是确定当前执行代码对变量的访问权限。\n\n>说到作用域，js 分为词法作用域（静态作用域）和动态作用域  \n- 词法作用域：函数的作用域在函数定义的时候就决定了。  \n- 动态作用域，函数的作用域是在函数调用的时候才决定的。  \n\n\n## 作用域链\n>当查找变量的时候，会先从当前上下文的变量对象中查找，  \n如果没有找到，就会从父级(词法层面上的父级)执行上下文的变量对象中查找，一直找到全局上下文的变量对象，也就是全局对象。  \n这样由多个执行上下文的变量对象构成的链表就叫做作用域链。  \n\n对于每个执行上下文，都有三个重要属性：\n\n- 变量对象(Variable object，VO)\n- 作用域链(Scope chain)\n- this\n\n\n## 块级作用域\n\n- 任何一对花括号（｛和｝）中的语句集都属于一个块，在这之中定义的所有变量在代码块外都是不可见的，我们称之为块级作用域。\n\n\n## 参考\n- [JavaScript深入之词法作用域和动态作用域](https://github.com/mqyqingfeng/Blog/issues/3)\n- [JavaScript深入之作用域链](https://github.com/mqyqingfeng/Blog/issues/6)\n"
  },
  {
    "path": "4.1.8 DOM 节点.md",
    "content": "# dom\n\n- [原生 js dom 操作 api ](https://github.com/fairyly/html-demo/blob/gh-pages/%E5%8E%9F%E7%94%9F%20js%20dom%20%E6%93%8D%E4%BD%9C.md)\n\n* 内部节点属性\n```\ndocument.doctype，document.documentElement，document.defaultView\ndocument.body，document.head\ndocument.activeElement\n```\n* 节点集合属性\n```\ndocument.links，document.forms，document.images，document.embeds\ndocument.scripts，document.styleSheets\n```\n\n* 文档信息属性\n```\ndocument.documentURI，document.URL\ndocument.domain\ndocument.lastModified\ndocument.location\ndocument.referrer，document.title，document.characterSet\ndocument.readyState\ndocument.designMode\ndocument.implementation\ndocument.compatMode\ndocument.cookie\n```\n\n* 读写相关的方法\n```\ndocument.open()，document.close()\ndocument.write()，document.writeln()\n```\n\n* 查找节点的方法\n```\ndocument.querySelector()，document.querySelectorAll()\ndocument.getElementsByTagName()\ndocument.getElementsByClassName()\ndocument.getElementsByName()\ngetElementById()\ndocument.elementFromPoint()\n```\n* 生成节点的方法\n```\ndocument.createElement()\ndocument.createTextNode()\ndocument.createAttribute()\ndocument.createDocumentFragment()\n```\n* 事件相关的方法\n```\ndocument.createEvent()\ndocument.addEventListener()，document.removeEventListener()，document.dispatchEvent()\n```\n* 其他方法\n```\ndocument.hasFocus()\ndocument.createNodeIterator()，document.createTreeWalker()\ndocument.adoptNode()\ndocument.importNode()\ndocument.getSelection()\n```\n\n\n* 节点的创建、添加、删除、克隆\n  ```\n  创建节点、追加节点\n  1、createElement（标签名）创建一个元素节点（具体的一个元素）。\n  2、appendChild（节点）追加一个节点。\n  3、createTextNode（节点文本内容）创建一个文本节点\n  4、insertBefore（a,b）是参照节点，意思是a节点会插入b节点的前面。\n  5、removeChild(节点) 删除一个节点，用于移除删除一个参数（节点）\n  6、replaceChild(插入的节点，被替换的节点) ，用于替换节点，接受两个参数，第一参数是要插入的节点，第二个是要被替换的节点。返回的是被替换的节点。\n  7、oldEle.cloneNode(true); 深度复制，复制节点下面所有的子节点  \n  ```\n"
  },
  {
    "path": "4.1.9 清空对象数组的方法.md",
    "content": "var有三种声明的情形：\n\n    var声明的全局变量\n\n    var在函数范围内声明的局部变量\n\n    eval中声明的全局变量。\n\n首先，1、2种情形var声明的变量是无法删除的。\n\n尽管var声明的全局变量是属于window对象的属性（在浏览器中），但依然是无法删除的，因为这种属性的configurable=false，因此不能delete掉\n\n而eval('var a = 1')看似和直接var是一样的效果（当然是在全局作用执行eval），执行完也会在window对象上创建一个a属性，但是这个属性的configurable=true，因此可以删除。\n\n通过查看属性可以看到\n```\nvar x = 4;\nObject.getOwnPropertyDescriptor(window, 'x') //函数来获取描述属性特性的描述符对象\n\nconfigurable:false\nenumerable:true\nvalue:1\nwritable:true\n```\n\n## 清空对象的方法\n\n* 定义对象\n  - var student = {}\n  - var student = new Object()\n  - var student = Object.create({})\n* 删除对象属性（清空对象）\n  - for(var key in student){\n      delete student[key];\n    }\n* 设置对象属性\n  ```\n    var obj = {\"name\":\"Poly\", \"career\":\"it\"}\n    Object.defineProperty(obj, \"age\", {value:\"forever 18\", enumerable:false});\n  ```\n  - Object.getOwnPropertyNames(obj) //返回对象的自有属性，包括可枚举和不可枚举的\n  - hasOwnProperty() 函数用于指示一个对象自身(不包括原型链)是否具有指定名称的属性。如果有，返回true，否则返回false。\n  - Object.prototype.isPrototypeOf()  isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。  \n    ```\n      function Foo() {}\n      function Bar() {}\n      function Baz() {}\n\n      Bar.prototype = Object.create(Foo.prototype);\n      Baz.prototype = Object.create(Bar.prototype);\n\n     var baz = new Baz();\n\n     console.log(Baz.prototype.isPrototypeOf(baz)); // true\n     console.log(Bar.prototype.isPrototypeOf(baz)); // true\n     console.log(Foo.prototype.isPrototypeOf(baz)); // true\n     console.log(Object.prototype.isPrototypeOf(baz)); // true\n    ```\n  - Object.prototype.toString()  检测对象类型\n    ```\n      var o = new Object();\n      o.toString(); // returns [object Object]\n      \n      var toString = Object.prototype.toString;\n      toString.call(new Date);\n    ```\n  - Number.prototype.toString() 返回指定 Number 对象的字符串表示形式。 用于进制转换\n    numObj.toString([radix])  radix 指定要用于数字到字符串的转换的基数(从2到36)。如果未指定 radix 参数，则默认值为 10\n    ```\n      var num = 255; \n      num.toString(8)\n      //\"377\"\n    ```\n\n## 清空数组的方法\n\n* 定义数组\n  - var arr = []\n  - var arr = new Array()\n  \n* 清空数组\n  - splice\n    ```\n     var ary = [1,2,3,4];\n      ary.splice(0,ary.length);\n      console.log(ary); // 输出 []，空数组，即被清空了\n    ```\n  - 赋值 []  \n    var arr = []\n    \n  - length 赋值为 0\n    ```\n      var ary = [1,2,3,4];\n      ary.length = 0;\n      console.log(ary); // 输出 []，空数组，即被清空了\n    ```\n"
  },
  {
    "path": "4.2.0 Thunk 函数.md",
    "content": "# Thunk 函数\n\n>临时函数传入函数体。这个临时函数就叫做 Thunk 函数\n\n```\nlet x = 1;\n\nfunction fn(thunk){\n    return thunk()*2;\n}\n\nfunction thunk(){\n    return x+1;\n}\n\nfn(thunk);\n```\n\n>Thunk 函数现在可以用于 Generator 函数的自动流程管理\n\n\n>co 模块用于 Generator 函数的自动执行。  \nco 模块其实就是将两种自动执行器（Thunk 函数和 Promise 对象），包装成一个模块\n\n```\nGenerator 函数只要传入co函数，就会自动执行。\n\nco函数返回一个Promise对象，因此可以用then方法添加回调函数。\n\nco(gen).then(function (){\n  console.log('Generator 函数执行完成');\n});\n```\n\n\n## 参考\n- [Thunk函数的含义和用法](http://www.ruanyifeng.com/blog/2015/05/thunk.html)\n"
  },
  {
    "path": "4.2.1 在总的列表数组对象中查找返回的对象中存在的字段.md",
    "content": "# 在总的列表数组对象中查找返回的对象中存在的字段\n\n* 一种方法在总数组中遍历，存在值的数组中查找\n  以此类推， 不管遇到是在对象中找都转化成这种数组中\n\n  ```\n    var list1 = ['游泳',\"跳舞\"];\n    var list2 = [{name:\"游泳\"},{name:\"跳舞\"},{name:\"舞\"},{name:\"跳\"}];\n\n    list2.forEach(function(el,key){\n        if(list1.indexOf(el.name) != -1){\n          el.select = true;\n        }\n    })\n  ```\n\n* 一种方法 引入 Underscore.js\n```\ns:function(){\n    var list1 = ['游泳',\"跳舞\"];\n    var list2 = [{name:\"游泳\"},{name:\"跳舞\"},{name:\"舞\"},{name:\"跳\"}];\n    list1.map(function(json) {\n      var item = _.find(list2, function(json2) {\n          return json2.name == json;\n      });\n      if (item) {\n          item.checked = true;\n      }\n    })\n  console.log(list2)\n  },\n  ```\n"
  },
  {
    "path": "4.2.2 数组分组小算法.md",
    "content": "# 数组分组\n\n* 把一个数组中的一个属性值分成若干组， 可以配置多少组\n  加入分成 9 组,采用二位数组方法\n\n```\nvar arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,47,50]\nvar group = 9;//总的小组数\nvar arrgroup = [];//总小组数组\nfor(var i=0; i <group; i++){\n  arrgroup[i] = []; //在总小组数组中添加九个组\n}\n\narr.forEach(function(el,index){\n  arrgroup[index%group][index/group] = el\n})\n\n```\n"
  },
  {
    "path": "4.2.3 数字的千位分割.md",
    "content": "# 千位分割\n\n将普通的数字转换为带千位分隔符格式的数字字符串是一个非常常见的问题，千位分隔符格式的规则是数字的整数部分每三位一组，以“，”分节。小数部分不分节 。\n示例：19,351,235.235767\n\n**先分离出小数部分；\n对整数部分逆序为数组；**\n\n\n.replace(/(\\d)(?=(\\d{3})+\\.)/g, '$1,')\n\n```\n$1、$2、...、$99\t与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。\n$&\t与 regexp 相匹配的子串。\n$`\t位于匹配子串左侧的文本。\n$'\t位于匹配子串右侧的文本。\n$$\t直接量符号。\nfunction format (num) {  \n    var reg=/\\d{1,3}(?=(\\d{3})+$)/g;   \n    return (num + '').replace(reg, '$&,');  \n}\n\n正则表达式 \\d{1,3}(?=(\\d{3})+$)  表示前面有1~3个数字，后面的至少由一组3个数字结尾。\n?=表示正向引用，可以作为匹配的条件，但匹配到的内容不获取，并且作为下一次查询的开始。\n $& 表示与正则表达式相匹配的内容，具体的使用可以查看字符串replace()方法的\n\n```\n\n![](http://img.blog.csdn.net/20161003155012112)\n\n1.非标准$1, $2, $3, $4, $5, $6, $7, $8, $9 属性是包含括号子串匹配的正则表达式的静态和只读属性。该特性是非标准的，请尽量不要在生产环境中使用它！\n\n```\nfunction cc(s){\n  if(/[^0-9\\.]/.test(s)) return \"invalid value\";\n  s = s.replace(/^(\\d*)$/,\"$1.\");\n  s = (s+\"00\").replace(/(\\d*\\.\\d\\d)\\d*/,\"$1\");\n  s = s.replace(\".\",\",\");\n  var re = /(\\d)(\\d{3},)/;\n  while(re.test(s))\n    s = s.replace(re,\"$1,$2\");\n    s = s.replace(/,(\\d\\d)$/,\".$1\");\n    return \"￥\" + s.replace(/^\\./,\"0.\")\n}\n\ncc('34444444')  //\"￥34,444,444.00\"\n```\n2.toLocaleString() 方法返回这个数字在特定语言环境下的表示字符串。\n\n```\nvar a=1234567894532;\nvar b=673439.4542;\n\nconsole.log(a.toLocaleString());  // \"1,234,567,894,532\"\nconsole.log(b.toLocaleString());  // \"673,439.454\"  （小数部分四舍五入了）\n\n```\n\n3.实现思路是将数字转换为字符数组，再循环整个数组， 每三位添加一个分隔逗号，最后再合并成字符串。因为分隔符在顺序上是从后往前添加的：比如 1234567添加后是1,234,567 而不是 123,456,7 ，所以方便起见可以先把数组倒序，添加完之后再倒序回来，就是正常的顺序了。要注意的是如果数字带小数的话，要把小数部分分开处理。\n\n```\nfunction numFormat(num){\n    num=num.toString().split(\".\");  // 分隔小数点\n    var arr=num[0].split(\"\").reverse();  // 转换成字符数组并且倒序排列\n    var res=[];\n    for(var i=0,len=arr.length;i<len;i++){\n      if(i%3===0&&i!==0){\n         res.push(\",\");   // 添加分隔符\n      }\n      res.push(arr[i]);\n    }\n    res.reverse(); // 再次倒序成为正确的顺序\n    if(num[1]){  // 如果有小数的话添加小数部分\n      res=res.join(\"\").concat(\".\"+num[1]);\n    }else{\n      res=res.join(\"\");\n    }\n    return res;\n}\n\nvar a=1234567894532;\nvar b=673439.4542;\nconsole.log(numFormat(a)); // \"1,234,567,894,532\"\nconsole.log(numFormat(b)); // \"673,439.4542\"\n\n```\n"
  },
  {
    "path": "4.2.3.1 判断一个字符串是数值.md",
    "content": "# 4.2.3.1 判断一个字符串是数值\n\n- parseFloat 会尝试转部分数值，而忽略掉不能转数值的部分\n\n- Number.isNaN，该方法不会对参数做类型转换，只要参数不是NaN，不管是什么类型，Number.isNaN一律返回false\n- Number中的±Infinite和NaN的isFinite结果都返回false\n\n```\n# 月影\n\nfunction isNumeric(obj) {\n  return !Number.isNaN(parseFloat(obj)) && Number.isFinite(Number(obj));\n}\n\n\n\n# 贺老\n\nfunction isNumeric(n) {\n    return /^[0-9.eE+-]+$/.test(n) && isFinite(n)\n}\n\n// 非常严谨的显式转型版本：\nfunction isNumeric(n) {\n    const s = String(n)\n    if (!/^[0-9.eE+-]+$/.test(s)) return false\n    const v = Number(s)\n    return Number.isFinite(v)\n}\n```\n\n\n## 参考\n-[如何正确判断一个字符串是数值](https://github.com/akira-cn/FE_You_dont_know/issues/7)\n"
  },
  {
    "path": "4.2.4 数字，字符串，数组，对象的方法.md",
    "content": "# 数字，字符串，数组，对象的方法\n\nES6 引入了一种新的原始数据类型Symbol，表示独一无二的值。它是 JavaScript 语言的第七种数据类型，前六种是：undefined、null、布尔值（Boolean）、字符串（String）、数值（Number）、对象（Object）\n\n* JavaScript 函数式编程:http://taobaofed.org/blog/2017/03/16/javascript-functional-programing/\n* 参考资料:\n  https://lodash.com/\n  http://reactivex.io/\n\n## 数字 number math\n  - toExponential(x)\t把对象的值转换为指数计数法。\n  - toFixed(x)\t把数字转换为字符串，结果的小数点后有指定位数的数字。\n  - toPrecision(x)\t把数字格式化为指定的长度。\n  - toString()\t把数字转换为字符串，使用指定的基数。\n  - valueOf()\t返回一个 Number 对象的基本数字值。\n  - ES6 在Number对象上，新提供了Number.isFinite()和Number.isNaN()两个方法\n  - Number.isFinite()用来检查一个数值是否为有限的（finite）。\n  - Number.isNaN()用来检查一个值是否为NaN。\n  - // ES5的写法\n    parseInt('12.34') // 12\n    parseFloat('123.45#') // 123.45\n    \n    // ES6的写法\n    Number.parseInt('12.34') // 12\n    Number.parseFloat('123.45#') // 123.45\n  - Number.isInteger()用来判断一个值是否为整数\n  - Number.isSafeInteger()则是用来判断一个整数是否落在这个范围之内\n  - Math.trunc方法用于去除一个数的小数部分，返回整数部分\n  - Math.sign方法用来判断一个数到底是正数、负数、还是零  \n      参数为正数，返回+1；  \n      参数为负数，返回-1；  \n      参数为0，返回0；  \n      参数为-0，返回-0;  \n      其他值，返回NaN。  \n  - Math.cbrt方法用于计算一个数的立方根。\n  - Math.clz32方法返回一个数的32位无符号整数形式有多少个前导0\n  - Math.imul方法返回两个数以32位带符号整数形式相乘的结果，返回的也是一个32位的带符号整数。\n  - Math.fround方法返回一个数的单精度浮点数形式。\n  - Math.hypot方法返回所有参数的平方和的平方根\n  - Math.abs(x)\t返回 x 的绝对值。\n  - Math.acos(x)\t返回 x 的反余弦值。\n  - Math.asin(x)\t返回 x 的反正弦值。\n  - Math.atan(x)\t以介于 -PI/2 与 PI/2 弧度之间的数值来返回 x 的反正切值。\n  - Math.atan2(y,x)\t返回从 x 轴到点 (x,y) 的角度（介于 -PI/2 与 PI/2 弧度之间）。\n  - Math.ceil(x)\t对数进行上舍入。\n  - Math.cos(x)\t返回数的余弦。\n  - Math.exp(x)\t返回 Ex 的指数。\n  - Math.floor(x)\t对 x 进行下舍入。\n  - Math.log(x)\t返回数的自然对数（底为e）。\n  - Math.max(x,y,z,...,n)\t返回 x,y,z,...,n 中的最高值。\n  - Math.min(x,y,z,...,n)\t返回 x,y,z,...,n中的最低值。\n  - Math.pow(x,y)\t返回 x 的 y 次幂。\n  - Math.random()\t返回 0 ~ 1 之间的随机数。\n  - Math.round(x)\t把数四舍五入为最接近的整数。\n  - Math.sin(x)\t返回数的正弦。\n  - Math.sqrt(x)\t返回数的平方根。\n  - Math.tan(x)\t返回角的正切。\n   \n## 字符串\n   var str = \"test\"  \n  - str.charAt()\t返回在指定位置的字符。\n  - str.charCodeAt()\t返回在指定的位置的字符的 Unicode 编码。\n  - str.concat()\t连接两个或更多字符串，并返回新的字符串。\n  - str.fromCharCode()\t将 Unicode 编码转为字符。\n  - str.indexOf()\t返回某个指定的字符串值在字符串中首次出现的位置。\n  - str.lastIndexOf()\t从后向前搜索字符串，并从起始位置（0）开始计算返回字符串最后出现的位置。\n  - str.match()\t查找找到一个或多个正则表达式的匹配。\n  - str.replace()\t在字符串中查找匹配的子串， 并替换与正则表达式匹配的子串。\n  - str.search()\t查找与正则表达式相匹配的值。\n  - str.slice()\t提取字符串的片断，并在新的字符串中返回被提取的部分。\n  - str.split()\t把字符串分割为字符串数组。\n  - str.substr()\t从起始索引号提取字符串中指定数目的字符。\n  - str.substring()\t提取字符串中两个指定的索引号之间的字符。\n  - str.toLowerCase()\t把字符串转换为小写。\n  - str.toUpperCase()\t把字符串转换为大写。\n  - str.trim()\t去除字符串两边的空白\n  - str.valueOf()\t返回某个字符串对象的原始值。\n\n\n\n## 数组\n   var arr = [\"test\"]\n\n  - arr.concat()\t连接两个或更多的数组，并返回结果。\n  - arr.copyWithin()\t从数组的指定位置拷贝元素到数组的另一个指定位置中。\n    ```\n      Array.prototype.copyWithin(target, start = 0, end = this.length)\n        target（必需）：从该位置开始替换数据。\n        start（可选）：从该位置开始读取数据，默认为0。如果为负值，表示倒数。\n        end（可选）：到该位置前停止读取数据，默认等于数组长度。如果为负值，表示倒数。\n      [1, 2, 3, 4, 5].copyWithin(0, 3) //[4, 5, 3, 4, 5]\n      表示将从3号位直到数组结束的成员（4和5），复制到从0号位开始的位置，结果覆盖了原来的1和2。\n    ```\n  - arr.every()\t检测数值元素的每个元素是否都符合条件。\n  - arr.fill()\t使用一个固定值来填充数组。\n  - arr.filter()\t检测数值元素，并返回符合条件所有元素的数组。\n  - arr.find()\t返回符合传入测试（函数）条件的数组元素。\n    用于找出第一个符合条件的数组成员。它的参数是一个回调函数，所有数组成员依次执行该回调函数，直到找出第一个返回值为true的成员，然后返回该成员。如果     没有符合条件的成员，则返回undefined。\n  - arr.findIndex()\t返回符合传入测试（函数）条件的数组元素索引。\n  - arr.forEach()\t数组每个元素都执行一次回调函数。\n  - arr.indexOf()\t搜索数组中的元素，并返回它所在的位置。\n  - Array.prototype.includes方法返回一个布尔值，表示某个数组是否包含给定的值，与字符串的includes方法类似。ES2016 引入了该方法。\n    ```\n      [1, 2, 3].includes(2)     // true\n      [1, 2, 3].includes(4)     // false\n      [1, 2, NaN].includes(NaN) // true\n    ```\n  - arr.join()\t把数组的所有元素放入一个字符串。\n  - arr.lastIndexOf()\t返回一个指定的字符串值最后出现的位置，在一个字符串中的指定位置从后向前搜索。\n  - arr.map()\t通过指定函数处理数组的每个元素，并返回处理后的数组。\n  - arr.pop()\t删除数组的最后一个元素并返回删除的元素。\n  - arr.push()\t向数组的末尾添加一个或更多元素，并返回新的长度。\n  - arr.reduce()\t将数组元素计算为一个值（从左到右）。\n  - arr.reduceRight()\t将数组元素计算为一个值（从右到左）。\n  - arr.reverse()\t反转数组的元素顺序。\n  - arr.shift()\t删除并返回数组的第一个元素。\n  - arr.slice()\t选取数组的的一部分，并返回一个新数组。\n  - arr.some()\t检测数组元素中是否有元素符合指定条件。\n  - arr.sort()\t对数组的元素进行排序。\n  - arr.splice()\t从数组中添加或删除元素。\n  - arr.toString()\t把数组转换为字符串，并返回结果。\n  - arr.unshift()\t向数组的开头添加一个或更多元素，并返回新的长度。\n  - arr.valueOf()\t返回数组对象的原始值。\n  - ...: 扩展运算符（spread）是三个点（...）。它好比 rest 参数的逆运算，将一个数组转为用逗号分隔的参数序列。任何 Iterator 接口的对象都可以用扩展运     算符转为真正的数组。\n    console.log(...[1, 2, 3])  \n    扩展运算符还可以将字符串转为真正的数组,  \n    [...'hello']  \n    // [ \"h\", \"e\", \"l\", \"l\", \"o\" ]  \n    + Map 和 Set 结构，Generator 函数\n      ```\n        let map = new Map([\n           [1, 'one'],\n           [2, 'two'],\n           [3, 'three'],\n        ]);\n\n        let arr = [...map.keys()]; // [1, 2, 3]\n        \n        const go = function*(){\n          yield 1;\n          yield 2;\n          yield 3;\n        };\n\n        [...go()] // [1, 2, 3]\n      ```\n  - Array.from方法用于将两类对象转为真正的数组：类似数组的对象（array-like object）和可遍历（iterable）的对象（包括ES6新增的数据结构Set和Map）\n  - Array.of方法用于将一组值，转换为数组。\n  - ES6 提供三个新的方法——entries()，keys()和values()——用于遍历数组。\n    ```\n      for (let index of ['a', 'b'].keys()) {\n        console.log(index);\n      }\n      // 0\n      // 1\n\n      for (let elem of ['a', 'b'].values()) {\n        console.log(elem);\n      }\n      // 'a'\n      // 'b'\n\n      for (let [index, elem] of ['a', 'b'].entries()) {\n        console.log(index, elem);\n      }\n      // 0 \"a\"\n      // 1 \"b\"\n      \n      如果不使用for...of循环，可以手动调用遍历器对象的next方法，进行遍历。\n\n      let letter = ['a', 'b', 'c'];\n      let entries = letter.entries();\n      console.log(entries.next().value); // [0, 'a']\n      console.log(entries.next().value); // [1, 'b']\n      console.log(entries.next().value); // [2, 'c']\n    ```\n\n## 对象\n  - 如果使用字面量方式定义对象\n    ```\n      var obj = {\n        foo: true,\n        abc: 123\n      }\n    ```\n  - 函数的name属性，返回函数名。对象方法也是函数，因此也有name属性。\n  - Object.is() 用来比较两个值是否严格相等，与严格比较运算符（===）的行为基本一致。\n  - Object.assign方法用于对象的合并，将源对象（source）的所有可枚举属性，复制到目标对象（target）。\n    Object.assign方法实行的是浅拷贝，而不是深拷贝。也就是说，如果源对象某个属性的值是对象，那么目标对象拷贝得到的是这个对象的引用。\n  - Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象\n    ```\n      let obj = { foo: 123 };\n      Object.getOwnPropertyDescriptor(obj, 'foo')\n      描述对象的enumerable属性，称为”可枚举性“，如果该属性为false，就表示某些操作会忽略当前属性。\n      //  {\n      //    value: 123,\n      //    writable: true,\n      //    enumerable: true,\n      //    configurable: true\n      //  }\n      \n      有四个操作会忽略enumerable为false的属性。\n      for...in循环：只遍历对象自身的和继承的可枚举的属性。\n      Object.keys()：返回对象自身的所有可枚举的属性的键名。\n      JSON.stringify()：只串行化对象自身的可枚举的属性。\n      Object.assign()： 忽略enumerable为false的属性，只拷贝对象自身的可枚举的属性。\n    ```\n  - ES6 一共有5种方法可以遍历对象的属性。\n    + for...in循环遍历对象自身的和继承的可枚举属性（不含 Symbol 属性）。\n    + Object.keys返回一个数组，包括对象自身的（不含继承的）所有可枚举属性（不含 Symbol 属性）的键名\n    + Object.getOwnPropertyNames返回一个数组，包含对象自身的所有属性（不含 Symbol 属性，但是包括不可枚举属性）的键名。\n    + Object.getOwnPropertySymbols返回一个数组，包含对象自身的所有 Symbol 属性的键名。\n    + Reflect.ownKeys返回一个数组，包含对象自身的所有键名，不管键名是 Symbol 或字符串，也不管是否可枚举。\n  - ES2017 引入了Object.getOwnPropertyDescriptors方法，返回指定对象所有自身属性（非继承属性）的描述对象。  \n    Object.getOwnPropertyDescriptors(obj)\n  - __proto__属性（前后各两个下划线），用来读取或设置当前对象的prototype对象。目前，所有浏览器（包括 IE11）都部署了这个属性\n  - Object.setPrototypeOf方法的作用与__proto__相同，用来设置一个对象的prototype对象，返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。\n  - Object.setPrototypeOf方法配套，用于读取一个对象的原型对象。\n  - ES6 又新增了另一个类似的关键字super，指向当前对象的原型对象。\n  - Object.keys方法，返回一个数组，成员是参数对象自身的（不含继承的）所有可遍历（enumerable）属性的键名。\n  - Object.values方法返回一个数组，成员是参数对象自身的（不含继承的）所有可遍历（enumerable）属性的键值。\n  - Object.entries方法返回一个数组，成员是参数对象自身的（不含继承的）所有可遍历（enumerable）属性的键值对数组。\n  - 扩展运算符（...）用于取出参数对象的所有可遍历属性，拷贝到当前对象之中\n    ```\n      let z = { a: 3, b: 4 };\n      let n = { ...z };\n    ```\n  - Object.keys()，Object.values()，Object.entries() 对象遍历\n  - Object.preventExtensions(对象名)：阻止对象可扩展\n  - Object.freeze(对象名）：冻结对象，阻止修改\n\n## date\n  var date = new Date()  \n  - date.getDate()\t从 Date 对象返回一个月中的某一天 (1 ~ 31)。\n  - date.getDay()\t从 Date 对象返回一周中的某一天 (0 ~ 6)。\n  - date.getFullYear()\t从 Date 对象以四位数字返回年份。\n  - date.getHours()\t返回 Date 对象的小时 (0 ~ 23)。\n  - date.getMilliseconds()\t返回 Date 对象的毫秒(0 ~ 999)。\n  - date.getMinutes()\t返回 Date 对象的分钟 (0 ~ 59)。\n  - date.getMonth()\t从 Date 对象返回月份 (0 ~ 11)。\n  - date.getSeconds()\t返回 Date 对象的秒数 (0 ~ 59)。\n  - date.getTime()\t返回 1970 年 1 月 1 日至今的毫秒数。\n  - date.getTimezoneOffset()\t返回本地时间与格林威治标准时间 (GMT) 的分钟差。\n  - date.getUTCDate()\t根据世界时从 Date 对象返回月中的一天 (1 ~ 31)。\n  - date.getUTCDay()\t根据世界时从 Date 对象返回周中的一天 (0 ~ 6)。\n  - date.getUTCFullYear()\t根据世界时从 Date 对象返回四位数的年份。\n  - date.getUTCHours()\t根据世界时返回 Date 对象的小时 (0 ~ 23)。\n  - date.getUTCMilliseconds()\t根据世界时返回 Date 对象的毫秒(0 ~ 999)。\n  - date.getUTCMinutes()\t根据世界时返回 Date 对象的分钟 (0 ~ 59)。\n  - date.getUTCMonth()\t根据世界时从 Date 对象返回月份 (0 ~ 11)。\n  - date.getUTCSeconds()\t根据世界时返回 Date 对象的秒钟 (0 ~ 59)。\n  - date.getYear()\t已废弃。 请使用 getFullYear() 方法代替。\n  - date.parse()\t返回1970年1月1日午夜到指定日期（字符串）的毫秒数。\n  - date.setDate()\t设置 Date 对象中月的某一天 (1 ~ 31)。\n  - date.setFullYear()\t设置 Date 对象中的年份（四位数字）。\n  - date.setHours()\t设置 Date 对象中的小时 (0 ~ 23)。\n  - date.setMilliseconds()\t设置 Date 对象中的毫秒 (0 ~ 999)。\n  - date.setMinutes()\t设置 Date 对象中的分钟 (0 ~ 59)。\n  - date.setMonth()\t设置 Date 对象中月份 (0 ~ 11)。\n  - date.setSeconds()\t设置 Date 对象中的秒钟 (0 ~ 59)。\n  - date.setTime()\tsetTime() 方法以毫秒设置 Date 对象。\n  - date.setUTCDate()\t根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。\n  - date.setUTCFullYear()\t根据世界时设置 Date 对象中的年份（四位数字）。\n  - date.setUTCHours()\t根据世界时设置 Date 对象中的小时 (0 ~ 23)。\n  - date.setUTCMilliseconds()\t根据世界时设置 Date 对象中的毫秒 (0 ~ 999)。\n  - date.setUTCMinutes()\t根据世界时设置 Date 对象中的分钟 (0 ~ 59)。\n  - date.setUTCMonth()\t根据世界时设置 Date 对象中的月份 (0 ~ 11)。\n  - date.setUTCSeconds()\tsetUTCSeconds() 方法用于根据世界时 (UTC) 设置指定时间的秒字段。\n  - date.setYear()\t已废弃。请使用 setFullYear() 方法代替。\n  - date.toDateString()\t把 Date 对象的日期部分转换为字符串。\n  - date.toGMTString()\t已废弃。请使用 toUTCString() 方法代替。\n  - date.toISOString()\t使用 ISO 标准返回字符串的日期格式。\n  - date.toJSON()\t以 JSON 数据格式返回日期字符串。\n  - date.toLocaleDateString()\t根据本地时间格式，把 Date 对象的日期部分转换为字符串。\n  - date.toLocaleTimeString()\t根据本地时间格式，把 Date 对象的时间部分转换为字符串。\n  - date.toLocaleString()\t据本地时间格式，把 Date 对象转换为字符串。\n  - date.toString()\t把 Date 对象转换为字符串。\n  - date.toTimeString()\t把 Date 对象的时间部分转换为字符串。\n  - date.toUTCString()\t根据世界时，把 Date 对象转换为字符串。\n  - date.UTC()\t根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。\n  - date.valueOf()\t返回 Date 对象的原始值。\n\n\n## 函数\n\n  - 函数绑定运算符是并排的两个冒号（::），双冒号左边是一个对象，右边是一个函数。该运算符会自动将左边的对象，作为上下文环境（即this对象），绑定到右边的函数上面。\n  - 尾调用（Tail Call）是函数式编程的一个重要概念，本身非常简单，一句话就能说清楚，就是指某个函数的最后一步是调用另一个函数。\n  - ES2017 允许函数的最后一个参数有尾逗号（trailing comma）。\n  - ES6 允许使用“箭头”（=>）定义函数。var f = v => v;\n    ```\n      （1）函数体内的this对象，就是定义时所在的对象，而不是使用时所在的对象。\n\n      （2）不可以当作构造函数，也就是说，不可以使用new命令，否则会抛出一个错误。\n\n      （3）不可以使用arguments对象，该对象在函数体内不存在。如果要用，可以用 rest 参数代替。\n\n      （4）不可以使用yield命令，因此箭头函数不能用作 Generator 函数。\n\n       上面四点中，第一点尤其值得注意。this对象的指向是可变的，但是在箭头函数中，它是固定的。\n    ```\n  - 创建能够记住其状态的函数\n    ```\n    function a(t) {\n\t     function b(i){\n\t     \treturn i+t;\n\t     }\n\t     return b;\n     }\n     var c= a(2);\n    c(4);\n    或者\n    function a(t) {\n\t     return function (i){\n\t     \treturn i+t;\n\t     }\n     }\n    ```\n"
  },
  {
    "path": "4.2.4.1 正则匹配相关.md",
    "content": "#  正则匹配相关\n\n## JavaScript RegExp 对象\n\n```\n直接量语法\n  /pattern/attributes\n创建 RegExp 对象的语法：\n  new RegExp(pattern, attributes);\n  \n参数 pattern 是一个字符串，指定了正则表达式的模式或其他正则表达式。\n参数 attributes 是一个可选的字符串，包含属性 \"g\"、\"i\" 和 \"m\"，分别用于指定全局匹配、区分大小写的匹配和多行匹配 \nECMAScript 标准化之前，不支持 m 属性。如果 pattern 是正则表达式，而不是字符串，则必须省略该参数。\n  \ndemo:\nnew RegExp(/\\w/, \"g\");\n```\n\n- 修饰符\n修饰符\t描述\n- i\t执行对大小写不敏感的匹配。\n- g\t执行全局匹配（查找所有匹配而非在找到第一个匹配后停止）。\n- m\t执行多行匹配。\n\n\n- 方括号\n方括号用于查找某个范围内的字符：\n```\n[abc]\t查找方括号之间的任何字符。\n[^abc]\t查找任何不在方括号之间的字符。\n[0-9]\t查找任何从 0 至 9 的数字。\n[a-z]\t查找任何从小写 a 到小写 z 的字符。\n[A-Z]\t查找任何从大写 A 到大写 Z 的字符。\n[A-z]\t查找任何从大写 A 到小写 z 的字符。\n[adgk]\t查找给定集合内的任何字符。\n[^adgk]\t查找给定集合外的任何字符。\n(red|blue|green)\t查找任何指定的选项。\n```\n- 元字符\n元字符（Metacharacter）是拥有特殊含义的字符：\n\n```\n.\t查找单个字符，除了换行和行结束符。\n\\w\t查找单词字符。\n\\W\t查找非单词字符。\n\\d\t查找数字。\n\\D\t查找非数字字符。\n\\s\t查找空白字符。\n\\S\t查找非空白字符。\n\\b\t匹配单词边界。\n\\B\t匹配非单词边界。\n\\0\t查找 NUL 字符。\n\\n\t查找换行符。\n\\f\t查找换页符。\n\\r\t查找回车符。\n\\t\t查找制表符。\n\\v\t查找垂直制表符。\n\\xxx\t查找以八进制数 xxx 规定的字符。\n\\xdd\t查找以十六进制数 dd 规定的字符。\n\\uxxxx\t查找以十六进制数 xxxx 规定的 Unicode 字符。\n```\n\n- 量词\n```\nn+\t匹配任何包含至少一个 n 的字符串。\nn*\t匹配任何包含零个或多个 n 的字符串。\nn?\t匹配任何包含零个或一个 n 的字符串。\nn{X}\t匹配包含 X 个 n 的序列的字符串。\nn{X,Y}\t匹配包含 X 至 Y 个 n 的序列的字符串。\nn{X,}\t匹配包含至少 X 个 n 的序列的字符串。\nn$\t匹配任何结尾为 n 的字符串。\n^n\t匹配任何开头为 n 的字符串。\n?=n\t匹配任何其后紧接指定字符串 n 的字符串。\n?!n\t匹配任何其后没有紧接指定字符串 n 的字符串。\n```\n\n- RegExp 对象属性\n```\n\n属性\t描述\tFF\tIE\nglobal\tRegExp 对象是否具有标志 g。\t1\t4\nignoreCase\tRegExp 对象是否具有标志 i。\t1\t4\nlastIndex\t一个整数，标示开始下一次匹配的字符位置。\t1\t4\nmultiline\tRegExp 对象是否具有标志 m。\t1\t4\nsource\t正则表达式的源文本。\n```\n\n- RegExp 对象方法\n```\ncompile\t编译正则表达式。 \nexec\t检索字符串中指定的值。返回找到的值，并确定其位置。\t \ntest\t检索字符串中指定的值。返回 true 或 false。\t \n```\n\n- 支持正则表达式的 String 对象的方法\n```\nsearch\t检索与正则表达式相匹配的值。 \nmatch\t找到一个或多个正则表达式的匹配。\t \nreplace\t替换与正则表达式匹配的子串。 \nsplit\t把字符串分割为字符串数组。\n```\n\n## repalce() demo \n- www.w3school.com.cn/jsref/jsref_replace.asp\n\n```\nstringObject.replace(regexp/substr,replacement)\n\nregexp/substr\t:\n必需。规定子字符串或要替换的模式的 RegExp 对象。\n\n请注意，如果该值是一个字符串，则将它作为要检索的直接量文本模式，而不是首先被转换为 RegExp 对象。\n\nreplacement:\t必需。一个字符串值。规定了替换文本或生成替换文本的函数。\n```\n-  replacement 中的 $ 字符具有特定的含义\n```\n$1、$2、...、$99\t与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。\n$&\t与 regexp 相匹配的子串。\n$`\t位于匹配子串左侧的文本。\n$'\t位于匹配子串右侧的文本。\n$$\t直接量符号。\n```\n- 1\n```\nvar str=\"Visit Microsoft!\"\ndocument.write(str.replace(/Microsoft/, \"W3School\"))\n\n```\n\n- 2 全局替换\n```\ndocument.write(str.replace(/Microsoft/g, \"W3School\"))\n```\n- 3 匹配字符串大写字符\n```\ntext = \"javascript Tutorial\";\ntext.replace(/javascript/i, \"JavaScript\");\n```\n- 4 将把 \"Doe, John\" 转换为 \"John Doe\" 的形式：\n```\n将把 \"Doe, John\" 转换为 \"John Doe\" 的形式：\n\nname = \"Doe, John\";\nname.replace(/(\\w+)\\s*, \\s*(\\w+)/, \"$2 $1\");\n```\n\n- 5 所有的双引号替换为单引号：\n```\n\n\nname = '\"a\", \"b\"';\nname.replace(/\"([^\"]*)\"/g, \"'$1'\");\n```\n- 6 把字符串中所有单词的首字母都转换为大写：\n```\nname = 'aaa bbb ccc';\nuw=name.replace(/\\b\\w+\\b/g, function(word){\n  return word.substring(0,1).toUpperCase()+word.substring(1);}\n  );\n```\n\n- 7 只能输入汉字，数字，字母，下划线，中划线\n\n```\n- 匹配汉字：\\u4e00-\\u9fa5\n- 匹配大小写英文字母和数字： \\w\n\nlet reg = /[^\\w_-\\u4e00-\\u9fa5]/gi;\nlet filterValue = value.replace(reg, '');\n```\n\n\n\n\n\n\n## 参考资料\n- MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions\n"
  },
  {
    "path": "4.2.4.2 常用正则匹配.md",
    "content": "# 4.2.4.2 常用正则匹配\n\n## 1.匹配中文\n\n```\n[\\u4e00-\\u9fa5]\n```\n\n## 2.匹配双子节（包括汉字在内）\n\n```\n[^\\x00-\\xff]\n\n那么单字节就是\n\n[\\x00-\\xff]\n```\n\n## 3.匹配非负数字\n\n```\n/^\\d+(\\.\\d+)?$/\n```\n\n## 正则匹配替换特定字符\n\n```\n'2333[微笑][撇嘴][尴尬][惊讶]'.replace(/\\[.*?]/g, callback);\n```\n\n## 使用正则匹配以什么开头什么结尾的字符串\n\n```\nvar str = \"fsadjfsfopendfassfoverfsadfopendfadsfsoverfsadfsfopendfasdfsfsover\"\nvar res = str.match(/\\open.*?\\over/g)   //  \\s 会被识别成正则表达式   要进行转义\nconsole.log(res) //[ 'opendfassfover', 'opendfadsfsover', 'opendfasdfsfsover' ]\n\n\n# 匹配以 #/ 开头， ? 结尾 ，不包括（ #/  和 ?）\nconst hashName = window.location.hash;\nhashName.match(/(?<=\\#\\/).*?(?=\\?)/)[0]；\n\n\n(?<=CN_)表示获取CN_之后的字符串。不包含CN_\n\n \n\n5.同理，获得CN_之后（不包括CN_）, .mtl之前的。(包括.mtl)的字符串方法\n\nvar matchReg = /(?<=CN_).*?\\.mtl/;\nconsole.log(strMatch.match(matchReg));//输出 CM_SHANGHAI_24709063_M.mtl\n6.输出以CN_开头，.mtl或者.obj结尾的字符串\n\nvar matchReg = /CN_.*?(.mtl|.obj)/;\nconsole.log(strMatch.match(matchReg));//输出 CN_CM_SHANGHAI_24709063_M.mtl和CN_CM_SHANGHAI_2470190623_Q.obj\n7.输出以CN_开头，以.mtl或者.obj结尾之前的内容\n\nvar matchReg = /CN_.*?(?=(.mtl|.obj))/;\nconsole.log(strMatch.match(matchReg));//输出 CN_CM_SHANGHAI_24709063_M和CN_CM_SHANGHAI_2470190623_Q\n```\n\n## 参考\n\n- []()\n"
  },
  {
    "path": "4.2.4.2 数字数组判断是否是连续.md",
    "content": "# 4.2.4.2 数字数组判断是否是连续\n\n\n\n\n```\nfunction isContinuityNum(numArr){\n    let array=[];\n    if(numArr instanceof Array){\n        array = [...numArr];\n    }else{\n        array = Array.from(numArr.toString());//转换为数组\n    }            \n\n    let childNum = array[0];\n    let flag= true;\n    for(let e in array){\n        if(array[e] != childNum){\n            flag = false;\n            break;\n        }\n        childNum++;\n    }\n    return flag;\n}\n```\n"
  },
  {
    "path": "4.2.4.3 计算一个字符串的宽度.md",
    "content": "# 4.2.4.3 计算一个字符串的宽度\n\n\n```\nfunction textWidth(fontSize, text) {\n    let span = document.createElement(\"span\");\n    let result = {};\n    result.width = span.offsetWidth;\n    result.height = span.offsetWidth;\n    span.style.visibility = \"hidden\";\n    document.body.appendChild(span);\n    if (typeof span.textContent != \"undefined\"){\n        span.textContent = text;\n    }else {\n        span.innerText = text;\n    }\n    result.width = span.offsetWidth - result.width;\n    result.height = span.offsetHeight - result.height;\n    span.parentNode.removeChild(span);\n    return result;\n}\n\nconst size = textWidth(\"12px\", \"应用描述\");\nconsole.log(size);\n```\n"
  },
  {
    "path": "4.2.4.4 ==和===.md",
    "content": "# 4.2.4.4 ==和===\n\n>简单说，它们的区别是相等运算符（==）比较两个值是否相等，  \n严格相等运算符（===）比较它们是否为“同一个值”。  \n如果两个值不是同一类型，严格相等运算符（===）直接返回false，  \n而相等运算符（==）会将它们转换成同一个类型，再用严格相等运算符进行比较。\n\n## ==：相等运算符\n\n- （1）原始类型的值\n\n原始类型的数据会转换成数值类型再进行比较。\n\n```\n1 == true // true\n// 等同于 1 === Number(true)\n\n0 == false // true\n// 等同于 0 === Number(false)\n\n2 == true // false\n// 等同于 2 === Number(true)\n\n2 == false // false\n// 等同于 2 === Number(false)\n\n'true' == true // false\n// 等同于 Number('true') === Number(true)\n// 等同于 NaN === 1\n\n'' == 0 // true\n// 等同于 Number('') === 0\n// 等同于 0 === 0\n\n'' == false  // true\n// 等同于 Number('') === Number(false)\n// 等同于 0 === 0\n\n'1' == true  // true\n// 等同于 Number('1') === Number(true)\n// 等同于 1 === 1\n\n'\\n  123  \\t' == 123 // true\n\n```\n// 因为字符串转为数字时，省略前置和后置的空格\n上面代码将字符串和布尔值都转为数值，然后再进行比较。具体的字符串与布尔值的类型转换规则，参见《数据类型转换》一章。\n\n- （2）对象与原始类型值比较\n\n对象（这里指广义的对象，包括数组和函数）与原始类型的值比较时，对象转化成原始类型的值，再进行比较。\n\n```\n[1] == 1 // true\n// 等同于 Number([1]) == 1\n\n[1] == '1' // true\n// 等同于 Number([1]) == Number('1')\n\n[1] == true // true\n// 等同于 Number([1]) == Number(true)\n\n```\n\n上面代码中，数组[1]与数值进行比较，会先转成数值，再进行比较；与字符串进行比较，会先转成数值，然后再与字符串进行比较，这时字符串也会转成数值；\n\n与布尔值进行比较，两个运算子都会先转成数值，然后再进行比较。\n\n- （3）undefined 和 null\n\nundefined和null与其他类型的值比较时，结果都为false，它们互相比较时结果为true。\n\n```\nfalse == null // false\nfalse == undefined // false\n\n0 == null // false\n0 == undefined // false\n\nundefined == null // true\n```\n\n绝大多数情况下，对象与undefined和null比较，都返回false。只有在对象转为原始值得到undefined时，才会返回true，这种情况是非常罕见的。\n\n- （4）相等运算符的缺点\n\n相等运算符隐藏的类型转换，会带来一些违反直觉的结果。\n\n```\n0 == ''             // true\n0 == '0'            // true\n\n2 == true           // false\n2 == false          // false\n\nfalse == 'false'    // false\nfalse == '0'        // true\n\nfalse == undefined  // false\nfalse == null       // false\nnull == undefined   // true\n\n' \\t\\r\\n ' == 0     // true\n```\n\n上面这些表达式都很容易出错，因此不要使用相等运算符（==），最好只使用严格相等运算符（===）。\n\n- （5）不相等运算符\n\n相等运算符有一个对应的“不相等运算符”（!=），两者的运算结果正好相反。\n\n```\n1 != '1' // false\n```\n\n----------------------------------\n\n\n## ===： 严格相等运算符\n\n- （1）不同类型的值\n\n如果两个值的类型不同，直接返回false。\n\n```\n1 === \"1\" // false\ntrue === \"true\" // false\n\n```\n上面代码比较数值的1与字符串的“1”、布尔值的true与字符串\"true\"，因为类型不同，结果都是false。\n\n- （2）同一类的原始类型值\n\n同一类型的原始类型的值（数值、字符串、布尔值）比较时，值相同就返回true，值不同就返回false。\n\n```\n1 === 0x1 // true\n```\n\n上面代码比较十进制的1与十六进制的1，因为类型和值都相同，返回true。\n\n需要注意的是，NaN与任何值都不相等（包括自身）。另外，正0等于负0。\n\n```\nNaN === NaN  // false\n+0 === -0 // true\n```\n\n- （3）复合类型值\n\n两个复合类型（对象、数组、函数）的数据比较时，不是比较它们的值是否相等，而是比较它们是否指向同一个地址。\n\n```\n{} === {} // false\n[] === [] // false\n(function () {} === function () {}) // false\n```\n\n上面代码分别比较两个空对象、两个空数组、两个空函数，结果都是不相等。原因是对于复合类型的值，严格相等运算比较的是，它们是否引用同一个内存地址，\n\n而运算符两边的空对象、空数组、空函数的值，都存放在不同的内存地址，结果当然是false。\n\n如果两个变量引用同一个对象，则它们相等。\n\n```\nvar v1 = {};\nvar v2 = v1;\nv1 === v2 // true\n```\n\n注意，对于两个对象的比较，严格相等运算符比较的是地址，而大于或小于运算符比较的是值。\n\n```\nnew Date() > new Date() // false\nnew Date() < new Date() // false\nnew Date() === new Date() // false\n```\n\n上面的三个表达式，前两个比较的是值，最后一个比较的是地址，所以都返回false。\n\n- （4）undefined 和 null\n\n```\nundefined和null与自身严格相等。\n\nundefined === undefined // true\nnull === null // true\n```\n\n由于变量声明后默认值是undefined，因此两个只声明未赋值的变量是相等的。\n\n```\nvar v1;\nvar v2;\nv1 === v2 // true\n```\n\n- （5）严格不相等运算符\n\n严格相等运算符有一个对应的“严格不相等运算符”（!==），它的算法就是先求严格相等运算符的结果，然后返回相反值。\n\n```\n1 !== '1' // true\n```\n\n\n>>>\n\n>==先类型转换，再严格相等运算符比较值\n=== 比较值\n\n\n## 参考\n- [运算符](https://javascript.ruanyifeng.com/grammar/operator.html#toc6)\n"
  },
  {
    "path": "4.2.4.5 requestAnimationFrame 使用.md",
    "content": "# 4.2.4.5 requestAnimationFrame 使用\n\n>`window.requestAnimationFrame() `告诉浏览器——你希望执行一个动画，并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。\n\n该方法需要传入一个回调函数作为参数，该回调函数会在浏览器下一次重绘之前执行\n\n\n`requestAnimationFrame` 的用法与 `settimeout` 很相似，只是不需要设置时间间隔而已。\n\n`requestAnimationFrame` 使用一个回调函数作为参数，这个回调函数会在浏览器重绘之前调用。\n\n它返回一个整数，表示定时器的编号，这个值可以传递给 `cancelAnimationFrame` 用于取消这个函数的执行\n\n```\nrequestID = requestAnimationFrame(callback); \n//控制台输出1和0 \n\nvar timer = requestAnimationFrame(function(){\n    console.log(0);\n}); \nconsole.log(timer);//1\n\n\n\n也可以直接使用返回值进行取消\n\nvar timer = requestAnimationFrame(function(){\n    console.log(0);\n}); \ncancelAnimationFrame(1);\n```\n\n\n## 参考\n- [MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame)\n"
  },
  {
    "path": "4.2.4.6 时分比较大小排序.md",
    "content": "# 4.2.4.6 时分比较大小排序\n\n\n\n```\n+new Date(2021,02,21,10,10);\n1616292600000\n\n\nvar arr = ['10:00','16:00','12:00','20:05','23:05'];\nvar b=arr.sort((a,b)=>{return Number(a.replace(/:/g,''))-Number(b.replace(/:/g,''));})\n```\n"
  },
  {
    "path": "4.2.5 JS 中的柯里化(currying).md",
    "content": "# JS中的柯里化(currying)\n\n* 张鑫旭:http://www.zhangxinxu.com/wordpress/2013/02/js-currying/\n\n① 柯里化（Currying），又称部分求值（Partial Evaluation），函数闭包的一种特殊形式  \n  是把接受多个参数的函数变换成接受一个单一参数（最初函数的第一个参数）的函数，  \n  并且返回接受余下的参数而且返回结果的新函数的技术  \n\n下面这个是通用的柯里化函数\n```\nvar currying = function (fn) {\n    var _args = [];\n    return function () {\n        if (arguments.length === 0) {\n            return fn.apply(this, _args);\n        }\n        Array.prototype.push.apply(_args, [].slice.call(arguments));\n        return arguments.callee;\n    }\n};\n```\n\n\n## 柯里化有3个常见作用：1. 参数复用；2. 提前返回；3. 延迟计算/运行\n\n* 把函数参数转换到一个数组中\n  - var args = Array.prototype.slice.call(arguments)或者\n  - var args = [].slice.call(arguments)\n\n\n1.参数复用实例\n```\nvar currying = function(fn) {\n    // fn 指官员消化老婆的手段\n    var args = [].slice.call(arguments, 1);\n    // args 指的是那个合法老婆\n    return function() {\n        // 已经有的老婆和新搞定的老婆们合成一体，方便控制\n        var newArgs = args.concat([].slice.call(arguments));\n        // 这些老婆们用 fn 这个手段消化利用，完成韦小宝前辈的壮举并返回\n        return fn.apply(null, newArgs);\n    };\n};\n\n// 下为官员如何搞定7个老婆的测试\n// 获得合法老婆\nvar getWife = currying(function() {\n    var allWife = [].slice.call(arguments);\n    // allwife 就是所有的老婆的，包括暗渡陈仓进来的老婆\n    console.log(allWife.join(\";\"));\n}, \"合法老婆\");\n\n// 获得其他6个老婆\ngetWife(\"大老婆\",\"小老婆\",\"俏老婆\",\"刁蛮老婆\",\"乖老婆\",\"送上门老婆\");\n\n// 换一批老婆\ngetWife(\"超越韦小宝的老婆\");\n\n===========================================\n\nvar curryWeight = function(fn) {\n    var _fishWeight = [];\n    return function() {\n        if (arguments.length === 0) {\n            return fn.apply(null, _fishWeight);\n        } else {\n            _fishWeight = _fishWeight.concat([].slice.call(arguments));\n        }\n    }\n};\nvar fishWeight = 0;\nvar addWeight = curryWeight(function() {\n    var i=0; len = arguments.length;\n    for (i; i<len; i+=1) {\n        fishWeight += arguments[i];\n    }\n});\n\naddWeight(2.3);\naddWeight(6.5);\naddWeight(1.2);\naddWeight(2.5);\naddWeight();    //  这里才计算\n\nconsole.log(fishWeight);    // 12.5\n```\n2. “提前返回”\n```\nvar addEvent = (function(){\n    if (window.addEventListener) {\n        return function(el, sType, fn, capture) {\n            el.addEventListener(sType, function(e) {\n                fn.call(el, e);\n            }, (capture));\n        };\n    } else if (window.attachEvent) {\n        return function(el, sType, fn, capture) {\n            el.attachEvent(\"on\" + sType, function(e) {\n                fn.call(el, e);\n            });\n        };\n    }\n})();\n```\n\n3. “延迟计算”\n\n```\nvar averageWeight = 0;\nvar addWeight = curryWeight(function() {\n    var i=0; len = arguments.length;\n    for (i; i<len; i+=1) {\n        averageWeight += arguments[i]/len;\n    }\n});\n\naddWeight(2.3);\naddWeight(6.5);\naddWeight(1.2);\naddWeight(2.5);\naddWeight();    //  这里才计算\n\nconsole.log(averageWeight);    // 3.125\n```\n\n## uncurry(反柯里化)\n\n- uncurrying 函数\n```\nvar obj = {\n    \"length\":1,\n    \"0\":1\n}\nFunction.prototype.uncurrying = function() {\n    var self = this;\n    return function() {\n        return Function.prototype.call.apply(self, arguments);\n}\n}\n\nvar push = Array.prototype.push.uncurrying()\n\npush(obj, 2) //{0: 1, 1: 2, length: 2}\n\n过程解析：\nFunction.prototype.call.apply(Array.prototype.push, [obj, 2])\n\napply替换的执行函数的对象并扁平化了数组内容，\n\nArray.prototype.push.call(obj, 2)\n\ncall函数将数组内容的第一个参数替换执行函数对象\n\nobj.push(2)\n\n结果便是\n\n{0: 1, 1: 2, length: 2}\n```\n\n我们平时借用Math.Max时求数组中最大值是这样的\n```\nMath.Max.apply([], [1,2,3])\n```\n\n>先执行apply将Math替换为[]   \n然后再执行Max并传入[1,2,3] 因为apply的关系 参数[1,2,3]已经扁平化成1,2,3  \n所以相当于[].Max(1,2,3)   \ncall也是一样的道理：  \n\n之所以要和大家讲这个例子是想说明先调用apply再调用排在apply之前的函数max\n\n\n\n## 参考\n- [关于Function.prototype.apply.call的一些补充](https://www.cnblogs.com/qianlegeqian/p/4786766.html)\n- [关于Function.prototype.call.apply理解](https://www.cnblogs.com/chaky/articles/9059207.html)\n- [javascript中有趣的反柯里化](http://www.cnblogs.com/hustskyking/archive/2013/04/09/uncurrying.html)\n"
  },
  {
    "path": "4.2.6 js 面向对象之公有、私有、静态属性和方法详解.md",
    "content": "# js面向对象之公有、私有、静态属性和方法详解\n\n1.公有属性和公有方法\n```\n  function User(name,age){\n    this.name = name;//公有属性\n    this.age = age;\n  }\n  User.prototype.getName = function(){//公有方法\n    return this.name;\n  }\n  var user = new User('fire子海',26);\n  console.log(user.getName());//output:fire子海\n```\n2.私有属性和方法\n```\n  function User(name,age){\n    var name = name;//私有属性\n    var age = age;\n    function alertAge(){//私有方法\n       alert(age);\n    }\n    alertAge(age); //弹出26\n  }\n  var user = new User('fire子海',26);\n```\n3.静态属性和方法\n\n在php中，无需实例化就可以调用的方法就叫静态方法，js也一样，无需实例化，即用new操作符实化对象，就可调用对象的方法和属性。\n```\n  function User(){}\n  User.age = 26;//静态属性\n  User.myname = 'fire子海';\n  User.getName =function(){//静态方法\n    \n    return this.myname;//如果这里使用this.name，返回的将是User，所有改用了myname，\n  }\n  console.log(User.getName());//output:fire子海\n```\n4.特权方法\n```\n  function User(name,age){\n    var name = name;//私有属性\n    var age = age;\n    this.getName = function(){ //特权方法\n       return name;//私有属性和方法不能使用this调用\n    }\n  }\n  var user = new User('fire子海',26);\n  console.log(user.getName());//output:fire子海\n```\n5.静态类\n\n对于静态方法和静态属性，我们无需像第三步中那样去创建，如果网友看过我那篇“js如何制作图片轮播”，就知道可以使用字面量的方式来创建。\n```\n  var user = {\n    init:function(name,age){\n     this.name = name;\n     this.age = age;\n    },\n    getName:function(){\n     return this.name;\n   }\n  }\n  user.init('fire子海',26);\n  console.log(user.getName());//output:fire子海\n```\n6.公有方法的调用规则\n\n调用公有方法，我们必需先实例化对象  \n公有方法中通过不this调用公有属性和特权方法，不能使用this调用静态方法和属性，必需裁通过对象本身调用，即对象名。公有方法也不能调用私有方法  \n```\n  function User(){\n    this.myname = 'fire子海';//公有属性\n    this.age = 26;\n    this.do = function(){//特权方法\n      return this.myname+'学习js';\n    }\n  }\n  User.eat = function(food){\n   return '晚餐只有'+food;\n  }\n  User.prototype.alertAge = function(){\n    alert(this.age);\n  }\n  User.prototype.alertDo = function(){\n    alert(this.do());//调用特权方法\n  }\n  User.prototype.alertEat = function(food){\n    alert(User.eat(food));//只能通过对象本身调用静态方法\n    //alert(this.ear(food))这样调用将出错:this.eat is not a function\n  }\n  var user = new User();\n  user.alertAge();//alert:26\n  user.alertDo();//alert:fire子海学习js\n  user.alertEat('方便面')//alert:晚餐只有方便面\n```\n7.静态方法的调用规则\n使用静态方法时，无需实例化对象，便可以调用，对象实例不能调用对象的静态方法，只能调用实例自身的静态属性和方法\n```\nfunction User(){}\nUser.age = 26;//静态属性\nUser.myname = 'fire子海';\nUser.getName =function(){//静态方法\n  \n  return this.myname;\n}\nvar user = new User();\nconsole.log(user.getName);//TypeError: user.getName is not a function\nuser.supper = '方便面';\nuser.eat = function(){\n return '晚餐只有'+this.supper;\n}\nuser.eat();//晚餐只有方便面\n静态方法无法调用公有属性、公有方法、私有方法、私有属性、特权方法和原型属性\n\nfunction User(){\n    this.myname = 'fire子海';//公有属性\n    this.age = 26;\n    this.do = function(){//特权方法\n      return this.myname+'学习js';\n    }\n}\nUser.prototype.alertAge = function(){//公共方法，也叫原型方法\n  alert(this.age);\n}\nUser.prototype.sex = '男';//原型属性\nUser.getName= function(){//静态方法\n  return this.myname;\n}\nUser.getAge = function(){\n   this.alertAge();\n  \n}\nUser.getDo = function(){\n  return this.do();\n}\n//console.log(User.getName())//undefined\n//console.log(User.getDo());//TypeError: this.do is not a function\n//console.log(User.getAge())//TypeError: this.alertAge is not a function\n```\n8.特权方法的调用规则\n特权方法通过this调用公有方法、公有属性，通过对象本身调用静态方法和属性，在方法体内直接调用私有属性和私有方法\n```\nfunction User(girlfriend){\n   var girlfriend = girlfriend;\n   function getGirlFriend(){ \n     return '我女朋友'+girlfriend+'是美女！';\n   }\n  this.myname = 'fire子海';//公有属性\n  this.age = 26;\n  this.do = function(){//特权方法\n    return this.myname+'学习js';\n  }\n  this.alertAge = function(){\n   this.changeAge();//特权方法调用公有方法\n    alert(this.age);\n  }\n  this.alertGirlFriend = function(){\n   alert(getGirlFriend());//调用私有方法\n  }\n}\nUser.prototype.changeAge = function(){\n  this.age = 29;\n}\nvar user = new User('某某');\nuser.alertAge();//alert:29\nuser.alertGirlFriend();//alert:我的女朋友某某是美女！\n```\n9.私有方法\n\n对象的私有方法和属性,外部是不可以访问的,在方法的内部不是能this调用对象的公有方法、公有属性、特权方法的\n```\nfunction User(girlfriend){\n   var girlfriend = girlfriend;\n  this.myname = 'fire子海';//公有属性\n  this.age = 26;\n  function getGirlFriend(){ \n    //this.myname ;//此时的this指向的window对象，并非User对象，\n    // this.myname = 'fire子海',此时的this指向的是getGirFriend对象了。\n    //如果通过this调用了getGirFriend中不存在的方法呀属性，this便会指向window 对象，\n    只有this调用了getGirlFriend存在的方法和属性，this才会指定getGirlFriend;\n     alert(User.eat('泡面'));//alert：晚餐只有方便面\n  }\n  this.do = function(){//特权方法\n    return this.myname+'学习js';\n  }\n  this.alertAge = function(){\n   this.changeAge();//特权方法调用公有方法\n    alert(this.age);\n  }\n  this.alertGirlFriend = function(){\n   getGirlFriend();//调用私有方法\n  }\n}\nUser.eat = function(supper){\n return '晚餐只有'+supper;\n}\nvar user = new User('某某');\nuser.alertGirlFriend();\n```\n"
  },
  {
    "path": "4.2.7 定时器.md",
    "content": "# setTimeout \n\n\nsetTimeout还有一个需要注意的地方：如果被setTimeout推迟执行的回调函数是某个对象的方法，  \n那么该方法中的this关键字将指向全局环境，而不是定义时所在的那个对象。\n```\nvar x = 1;\n\nvar o = {\n  x: 2,\n  y: function(){\n    console.log(this.x);\n  }\n};\n\nsetTimeout(o.y,1000);\n// 1\n```\n上面代码输出的是1，而不是2，这表示o.y的this所指向的已经不是o，而是全局环境了。\n\n再看一个不容易发现错误的例子。\n```\nfunction User(login) {\n  this.login = login;\n  this.sayHi = function() {\n    console.log(this.login);\n  }\n}\n\nvar user = new User('John');\n\nsetTimeout(user.sayHi, 1000);\n```\n上面代码只会显示undefined，因为等到user.sayHi执行时，它是在全局对象中执行，所以this.login取不到值。\n\n为了防止出现这个问题，一种解决方法是将user.sayHi放在函数中执行。\n```\nsetTimeout(function() {\n  user.sayHi();\n}, 1000);\n```\n上面代码中，sayHi是在user作用域内执行，而不是在全局作用域内执行，所以能够显示正确的值。\n\n另一种解决方法是，使用bind方法，将绑定sayHi绑定在user上面。\n```\nsetTimeout(user.sayHi.bind(user), 1000);\n```\nHTML 5标准规定，setTimeout的最短时间间隔是4毫秒。   \n为了节电，对于那些不处于当前窗口的页面，浏览器会将时间间隔扩大到1000毫秒。  \n另外，如果笔记本电脑处于电池供电状态，Chrome和IE 9以上的版本，会将时间间隔切换到系统定时器，大约是15.6毫秒。  \n"
  },
  {
    "path": "4.2.8 for 循环中 onclick 问题.md",
    "content": "\n# for循环中onclick问题\n\nhttp://blog.csdn.net/k358971707/article/details/52968378\n\n```\n<script> \n        var btn=document.getElementsByTagName('button');\n        for(var i=0;i<btn.length;i++){ \n            (function(i){ //这个是function里i，即function的形参，也可以换成j，换成什么变量名都无所谓\n                btn[i].onclick=function(){ \n                    console.log(i);\n                }\n            })(i);//这是循环中的i,被作为参数传入\n        }\n    </script>\n\n再运行这段代码，就可以得到你想要的效果，但是是为什么呢？ \n知道了原因就好办了，利用闭包把每个function里的i都变成不同的i就行了，当时作为一名初学者还不懂闭包，也是后来才理解的。 \n循环中的function自调用，将循环中的i作为参数传入function中，\n此时，function中的i已经不是循环中的i了（这里有点绕，其实形参i，即function里的i换成什么变量名都行），\n而是在内存中开辟了一个内存空间存储了作为参数传进来的i的值，\n这样function中的就不会随着循环中的i的值的改变而改变了,就可以打印出你要的结果了。\n```\n"
  },
  {
    "path": "4.2.9 js 短路求值.md",
    "content": "# 短路求值 &&  ||\n```\n几乎所有语言中||和&&都遵循“短路”原理，如&&中第一个表达式为假就不会去处理第二个表达式，而||正好相反。 \n\njs也遵循上述原则。但是比较有意思的是它们返回的值。 \n\n代码：var attr = true && 4 && “aaa”; \n\n那么运行的结果attr就不是简单的true或这false，而是”aaa” \n\n再来看看||： \n\n代码：var attr = attr || “”;这个运算经常用来判断一个变量是否已定义，如果没有定义就给他一个初始值，这在给函数的参数定义一个默认值的时候比较有用。\n因为js不像php可以直接在型参数上定义func($attr=5)。\n再次提醒你记住上面的原则：如果实参需要是0、\"\"、null、false、undefined、NaN的时候也会当false来处理。 \n\nif(a >=5){ \nalert(\"你好\"); \n} \n可以写成： \n\na >= 5 && alert(\"你好\");\n\n这样只需一行代码就搞定。但是需要注意的一点就是：js中||和&&的特性帮我们精简了代码的同时，也带来了代码可读性的降低。\n这就需要我们自己来权衡了。 \n一方面精简js代码，能实质性的减少网络流量，尤其是大量应用的js公用库。\n个人比较推荐的做法是：如果是相对复杂的应用，请适当地写一些注释。\n这个和正则表达式一样，能够精简代码，但是可读性会降低，对读代码的人要求会高些，最好的办法就是写注释。 \n```\n"
  },
  {
    "path": "4.3.0 执行 js 代码片段.md",
    "content": "# 4.3.0 执行 js 代码片段\n\n\n## eval(`code`)\n\n>执行其中的的 JavaScript 代码,应该尽可能地避免使用。\n\n```\neval(`console.log(1)`)\n```\n\n\n## `new Function()`\n\n```\nvar func = new Function(code)\nfunc.call(this)\n```\n"
  },
  {
    "path": "4.3.1 判断为 true 严谨写法.md",
    "content": "# 判断 true\n\n```\nif(foo)      //不够严谨\n \nif(!!foo)    //更为严谨，!!可将其他类型的值转换为boolean类型\n\ntypeof(5)  //返回number\ntypeof(!!5)  // 返回boolean\n\n```\n"
  },
  {
    "path": "4.3.2 多级菜单遍历判断子节点.md",
    "content": "# demo\n\n```\nvar nature = [\n                    {\n                        name: \"华东区\",\n                        check: false,\n                        children: [\n                            {\n                                name: \"华东1区\",\n                                check: false,\n                                children: [\n                                    {\n                                        name: \"华东11区\",\n                                        check: false,\n                                        children: [\n                                            {\n                                                name: \"华东111区\",\n                                                check: false,\n                                                children: [\n                                                    {\n                                                        name: \"华东1111区\",\n                                                        check: false,\n                                                    },\n                                                    {\n                                                        name: \"华东1112区\",\n                                                        check: false,\n                                                    },\n                                                ]\n                                            },\n                                            {\n                                                name: \"华东112区\",\n                                                check: false,\n                                            },\n                                        ]\n                                    },\n                                    {\n                                        name: \"华东12区\",\n                                        check: false,\n                                        children: [\n                                            {\n                                                name: \"华东121区\",\n                                                check: false,\n                                            },\n                                            {\n                                                name: \"华东122区\",\n                                                check: false,\n                                            },\n                                        ]\n                                    },\n                                ]\n                            },\n                            {\n                                name: \"华东2区\",\n                                check: false,\n                                children: [\n                                    {\n                                        name: \"华东21区\",\n                                        check: false,\n                                    },\n                                    {\n                                        name: \"华东22区\",\n                                        check: false,\n                                    },\n                                ]\n                            },\n                            {\n                                name: \"华东3区\",\n                                check: false,\n                                children: [\n                                    {\n                                        name: \"华东31区\",\n                                        check: false,\n                                    },\n                                    {\n                                        name: \"华东32区\",\n                                        check: false,\n                                    },\n                                ]\n                            },\n                            {\n                                name: \"华东4区\",\n                                check: false,\n                                children: [\n                                    {\n                                        name: \"华东41区\",\n                                        check: false,\n                                    },\n                                    {\n                                        name: \"华东42区\",\n                                        check: false,\n                                    },\n                                ]\n                            }\n                        ]\n                    },\n                    {\n                        name: \"华西区\",\n                        check: false,\n                        children: [\n                            {\n                                name: \"华西1区\",\n                                check: false,\n                            },\n                            {\n                                name: \"华西2区\",\n                                check: false,\n                            }\n                        ]\n                    },\n                    {\n                        name: \"华南区\",\n                        check: false,\n                        children: [\n                            {\n                                name: \"华南1区\",\n                                check: false,\n                            },\n                            {\n                                name: \"华南2区\",\n                                check: false,\n                            }\n                        ]\n                    },\n                    {\n                        name: \"华北区\",\n                        check: true,\n                        children: [\n                            {\n                                name: \"华北1区\",\n                                check: true,\n                            },\n                            {\n                                name: \"华北2区\",\n                                check: true,\n                            }\n                        ]\n                    },\n                    {\n                        name: \"华中区\",\n                        check: false,\n                        children: [\n                            {\n                                name: \"华中1区\",\n                                check: true,\n                            },\n                            {\n                                name: \"华中2区\",\n                                check: false,\n                            }\n                        ]\n                    },\n                    {\n                        name: \"华区\",\n                        check: true\n                    }\n                ];\n                \n                append()//追加数据\n                function append() {\n                    var checklist = [];\n                    nature.forEach(function(el,index) {\n                        var show = '';\n                        var label = '';\n                        if (el.check == true) {\n                            show = 'checked=\"checked\"';\n                            checklist.push(true)\n                        }else {\n                            show = '';\n                        }\n                        if (el.children) {// 判断是否有孩子节点\n                          label = '<div class=\"area-cell list-cell'+index+' label-list'+index+'\"><label class=\"area-cell-label weui_cell weui_check_label weui_cells_access s'+index+'\" for=\"s'+index+'\"><div class=\"weui_cell_hd\"><input type=\"checkbox\" class=\"weui_check\" name=\"checkbox1\" id=\"s'+index+'\"'+show+'><i class=\"weui_icon_checked\"></i></div><div class=\"weui_cell_bd weui_cell_primary\"><p>'+el.name+'</p></div><div class=\"weui_cell_ft\" data-role=\"0\"></div></label></div>';\n                          $('.nature-list').append(label)//第一层数据\n                        \n                          children(el,''+index)//检查子节点\n                        }else {\n                          label = '<div class=\"area-cell list-cell'+index+'\"><label class=\"area-cell-label weui_cell weui_check_label weui_cells_access s'+index+'\" for=\"s'+index+'\"><div class=\"weui_cell_hd\"><input type=\"checkbox\" class=\"weui_check\" name=\"checkbox1\" id=\"s'+index+'\"'+show+'><i class=\"weui_icon_checked\"></i></div><div class=\"weui_cell_bd weui_cell_primary\"><p>'+el.name+'</p></div></label></div>';\n                          $('.nature-list').append(label)//第一层数据\n                        }\n                    })\n                    checknum()//检查选择数量\n                }\n\n                function children(item,index) {\n                  if (item.children) {\n                    item.children.forEach(function(e1,k1){\n                      var showchild = '';\n                      var labelchild = ''; //要插入的标签\n                      if (e1.check == true) {\n                          showchild = 'checked=\"checked\"';\n                      }else {\n                          showchild = '';\n                      }\n                      if (e1.children) {\n                        labelchild = '<div class=\"label-list label-list'+index+k1+'\"><label class=\"weui_cell weui_check_label weui_cells_access\" for=\"s'+index+k1+'\"><div class=\"weui_cell_hd\"><input type=\"checkbox\" class=\"weui_check\" name=\"checkbox1\" id=\"s'+index+k1+'\"'+showchild+'><i class=\"weui_icon_checked\"></i></div><div class=\"weui_cell_bd weui_cell_primary\"><p>'+e1.name+'</p></div><div class=\"weui_cell_ft\" data-role=\"0\"></div></label></div>';\n\n                        $('.label-list'+index).append(labelchild); // 第二层数据列表\n                        children(e1,''+index+k1); // 检查子节点\n                      }else {\n                        labelchild = '<div class=\"label-list label-list'+index+k1+'\"><label class=\"weui_cell weui_check_label weui_cells_access\" for=\"s'+index+k1+'\"><div class=\"weui_cell_hd\"><input type=\"checkbox\" class=\"weui_check\" name=\"checkbox1\" id=\"s'+index+k1+'\"'+showchild+'><i class=\"weui_icon_checked\"></i></div><div class=\"weui_cell_bd weui_cell_primary\"><p>'+e1.name+'</p></div></label></div>';\n\n                        $('.label-list'+index).append(labelchild); // 第二层数据列表\n                      }\n                    })\n                  }else {\n\n                  }\n                }\n```\n"
  },
  {
    "path": "4.3.3 JavaScript 性能优化的小知识.md",
    "content": "# JavaScript 性能优化的小知识\n\n* 静逸博客： http://www.cnblogs.com/liyunhua/p/4529086.html\n\n- [前言](#前言)\n- [避免全局查找](#避免全局查找)\n- [定时器](#定时器)\n- [字符串连接](#字符串连接)\n- [避免with语句](#避免with语句)\n- [数字转换成字符串](#避免with语句)\n- [浮点数转换成整型](#浮点数转换成整型)\n- [各种类型转换](#各种类型转换)\n- [多个类型声明](#多个类型声明)\n- [插入迭代器](#插入迭代器)\n- [使用直接量](#使用直接量)\n- [使用DocumentFragment优化多次append](#使用DocumentFragment优化多次append)\n- [使用一次innerHTML赋值代替构建dom元素](#使用一次innerHTML赋值代替构建dom元素)\n- [通过模板元素clone，替代createElement](#通过模板元素clone，替代createElement)\n- [使用firstChild和nextSibling代替childNodes遍历dom元素](#使用firstChild和nextSibling代替childNodes遍历dom元素)\n- [删除DOM节点](#删除DOM节点)\n- [使用事件代理](#使用事件代理)\n- [重复使用的调用结果，事先保存到局部变量](#[重复使用的调用结果，事先保存到局部变量)\n- [注意NodeList](#注意NodeList)\n- [优化循环](#优化循环)\n- [展开循环](#展开循环)\n- [避免双重解释](#避免双重解释)\n- [缩短否定检测](#[缩短否定检测)\n- [条件分支](#条件分支)\n- [使用常量](#使用常量)\n- [避免与null进行比较](#避免与null进行比较)\n- [避免全局量](#避免全局量)\n- [尊重对象的所有权](#尊重对象的所有权)\n- [循环引用](#循环引用)\n- [通过javascript创建的dom对象，必须append到页面中](#通过javascript创建的dom对象，必须append到页面中)\n- [释放dom元素占用的内存](#释放dom元素占用的内存)\n- [释放javascript对象](#释放javascript对象)\n- [避免string的隐式装箱](#避免string的隐式装箱)\n- [松散耦合](#松散耦合)\n- [性能方面的注意事项](#性能方面的注意事项)\n- [避免错误应注意的地方](#避免错误应注意的地方)\n- [==和===的区别](#==和===的区别)\n- [不要使用生偏语法](#不要使用生偏语法)\n- [函数返回统一类型](#函数返回统一类型)\n- [总是检查数据类型](#总是检查数据类型)\n- [何时用单引号，何时用双引号](#何时用单引号，何时用双引号)\n- [部署](#部署)\n\n\n### 前言\n\n一直在学习javascript，也有看过《犀利开发Jquery内核详解与实践》，对这本书的评价只有两个字犀利，可能是对javascript理解的还不够透彻异或是自己太笨，更多的是自己不擅于思考懒得思考以至于里面说的一些精髓都没有太深入的理解。\n\n鉴于想让自己有一个提升，进不了一个更加广阔的天地，总得找一个属于自己的居所好好生存，所以平时会有意无意的去积累一些使用jQuerry的常用知识，特别是对于性能要求这一块，总是会想是不是有更好的方式来实现。\n\n\n\n\n### 避免全局查找\n\n在一个函数中会用到全局对象存储为局部变量来减少全局查找，因为访问局部变量的速度要比访问全局变量的速度更快些\n\n    ```\n        function search() {\n            //当我要使用当前页面地址和主机域名\n            alert(window.location.href + window.location.host);\n        }\n        //最好的方式是如下这样  先用一个简单变量保存起来\n        function search() {\n            var location = window.location;\n            alert(location.href + location.host);\n        }    \n    ```\n \n\n\n### 定时器\n\n 如果针对的是不断运行的代码，不应该使用setTimeout，而应该是用setInterval，因为setTimeout每一次都会初始化一个定时器，而setInterval只会在开始的时候初始化一个定时器\n\n    ```\n        var timeoutTimes = 0;\n        function timeout() {\n            timeoutTimes++;\n            if (timeoutTimes < 10) {\n                setTimeout(timeout, 10);\n            }\n        }\n        timeout();\n        //可以替换为：\n        var intervalTimes = 0;\n        function interval() {\n            intervalTimes++;\n            if (intervalTimes >= 10) {\n                clearInterval(interv);\n            }\n        }\n        var interv = setInterval(interval, 10);        \n    ```\n \n\n\n### 字符串连接\n\n如果要连接多个字符串，应该少使用+=，如\n\ns+=a;\n\ns+=b;\n\ns+=c;\n\n应该写成s+=a + b + c；\n\n而如果是收集字符串，比如多次对同一个字符串进行+=操作的话，最好使用一个缓存，使用JavaScript数组来收集，最后使用join方法连接起来\n    ```\n        var buf = [];\n        for (var i = 0; i < 100; i++) {\n            buf.push(i.toString());\n        }\n        var all = buf.join(\"\");\n     ```\n\n\n### 避免with语句\n\n和函数类似 ，with语句会创建自己的作用域，因此会增加其中执行的代码的作用域链的长度，由于额外的作用域链的查找，在with语句中执行的代码肯定会比外面执行的代码要慢，在能不使用with语句的时候尽量不要使用with语句。\n\n```\n with (a.b.c.d) {\n            property1 = 1;\n            property2 = 2;\n        }\n        //可以替换为：\n        var obj = a.b.c.d;\n        obj.property1 = 1;\n        obj.property2 = 2;\n```\n\n### 数字转换成字符串\n\n最好用\"\" + 1来将数字转换成字符串，虽然看起来比较丑一点，但事实上这个效率是最高的，性能上来说：\n\n(\"\" +) > String() > .toString() > new String()\n\n\n### 浮点数转换成整型\n\n很多人喜欢使用parseInt()，其实parseInt()是用于将字符串转换成数字，而不是浮点数和整型之间的转换，我们应该使用Math.floor()或者Math.round()\n\n\n### 各种类型转换\n\n```\nvar myVar = \"3.14159\",\n        str = \"\" + myVar, //  to string  \n        i_int = ~ ~myVar,  //  to integer  \n        f_float = 1 * myVar,  //  to float  \n        b_bool = !!myVar,  /*  to boolean - any string with length \n                                and any number except 0 are true */\n        array = [myVar];  //  to array  \n```\n如果定义了toString()方法来进行类型转换的话，推荐显式调用toString()，因为内部的操作在尝试所有可能性之后，会尝试对象的toString()方法尝试能否转化为String，所以直接调用这个方法效率会更高\n\n\n ###多个类型声明\n\n在JavaScript中所有变量都可以使用单个var语句来声明，这样就是组合在一起的语句，以减少整个脚本的执行时间，就如上面代码一样，上面代码格式也挺规范，让人一看就明了。\n\n\n### 插入迭代器\n\n如var name=values[i]; i++;前面两条语句可以写成var name=values[i++]\n\n\n### 使用直接量\n\n```\nvar aTest = new Array(); //替换为\n        var aTest = [];\n        var aTest = new Object; //替换为\n        var aTest = {};\n        var reg = new RegExp(); //替换为\n        var reg = /../;\n        //如果要创建具有一些特性的一般对象，也可以使用字面量，如下：\n        var oFruit = new O;\n        oFruit.color = \"red\";\n        oFruit.name = \"apple\";\n        //前面的代码可用对象字面量来改写成这样：\n        var oFruit = { color: \"red\", name: \"apple\" };\n```\n\n### 使用DocumentFragment优化多次append\n\n一旦需要更新DOM,请考虑使用文档碎片来构建DOM结构，然后再将其添加到现存的文档中。\n\n```\nfor (var i = 0; i < 1000; i++) {\n            var el = document.createElement('p');\n            el.innerHTML = i;\n            document.body.appendChild(el);\n        }\n        //可以替换为：\n        var frag = document.createDocumentFragment();\n        for (var i = 0; i < 1000; i++) {\n            var el = document.createElement('p');\n            el.innerHTML = i;\n            frag.appendChild(el);\n        }\n        document.body.appendChild(frag);\n```\n\n### 使用一次innerHTML赋值代替构建dom元素\n\n对于大的DOM更改，使用innerHTML要比使用标准的DOM方法创建同样的DOM结构快得多。\n\n    ```\n        var frag = document.createDocumentFragment();\n        for (var i = 0; i < 1000; i++) {\n            var el = document.createElement('p');\n            el.innerHTML = i;\n            frag.appendChild(el);\n        }\n        document.body.appendChild(frag);\n        //可以替换为：\n        var html = [];\n        for (var i = 0; i < 1000; i++) {\n            html.push('<p>' + i + '</p>');\n        }\n        document.body.innerHTML = html.join('');\n    ```\n\n### 通过模板元素clone，替代createElement\n\n很多人喜欢在JavaScript中使用document.write来给页面生成内容。  \n事实上这样的效率较低，如果需要直接插入HTML，可以找一个容器元素，比如指定一个div或者span，并设置他们的innerHTML来将自己的HTML代码插入到页面中。  \n通常我们可能会使用字符串直接写HTML来创建节点，其实这样做，1无法保证代码的有效性2字符串操作效率低，所以应该是用document.createElement()方法，  \n而如果文档中存在现成的样板节点，应该是用cloneNode()方法，因为使用createElement()方法之后，你需要设置多次元素的属性，  \n使用cloneNode()则可以减少属性的设置次数——同样如果需要创建很多元素，应该先准备一个样板节点\n\n    ```\n        var frag = document.createDocumentFragment();\n        for (var i = 0; i < 1000; i++) {\n            var el = document.createElement('p');\n            el.innerHTML = i;\n            frag.appendChild(el);\n        }\n        document.body.appendChild(frag);\n        //替换为：\n        var frag = document.createDocumentFragment();\n        var pEl = document.getElementsByTagName('p')[0];\n        for (var i = 0; i < 1000; i++) {\n            var el = pEl.cloneNode(false);\n            el.innerHTML = i;\n            frag.appendChild(el);\n        }\n        document.body.appendChild(frag);\n    ```\n\n### 使用firstChild和nextSibling代替childNodes遍历dom元素\n\n \n\n    ```\n        var nodes = element.childNodes;\n        for (var i = 0, l = nodes.length; i < l; i++) {\n            var node = nodes[i];\n            //……\n        }\n        //可以替换为：\n        var node = element.firstChild;\n        while (node) {\n            //……\n            node = node.nextSibling;\n    ```\n\n### 删除DOM节点\n\n删除dom节点之前,一定要删除注册在该节点上的事件,不管是用observe方式还是用attachEvent方式注册的事件,否则将会产生无法回收的内存。   \n另外，在removeChild和innerHTML=’’二者之间,尽量选择后者. 因为在sIEve(内存泄露监测工具)中监测的结果是用removeChild无法有效地释放dom节点\n\n\n### 使用事件代理\n\n任何可以冒泡的事件都不仅仅可以在事件目标上进行处理，目标的任何祖先节点上也能处理，   \n使用这个知识就可以将事件处理程序附加到更高的地方负责多个目标的事件处理，同样，对于内容动态增加并且子节点都需要相同的事件处理函数的情况，   \n可以把事件注册提到父节点上，这样就不需要为每个子节点注册事件监听了。另外，现有的js库都采用observe方式来创建事件监听,   \n其实现上隔离了dom对象和事件处理函数之间的循环引用,所以应该尽量采用这种方式来创建事件监听\n\n\n### 重复使用的调用结果，事先保存到局部变量\n\n    ```\n        //避免多次取值的调用开销\n        var h1 = element1.clientHeight + num1;\n        var h4 = element1.clientHeight + num2;\n        //可以替换为：\n        var eleHeight = element1.clientHeight;\n        var h1 = eleHeight + num1;\n        var h4 = eleHeight + num2;\n    ```\n\n### 注意NodeList\n\n最小化访问NodeList的次数可以极大的改进脚本的性能\n\n    ```\n      var images = document.getElementsByTagName('img');\n        for (var i = 0, len = images.length; i < len; i++) {\n\n        }\n    ```\n    \n编写JavaScript的时候一定要知道何时返回NodeList对象，这样可以最小化对它们的访问\n\n进行了对getElementsByTagName()的调用\n获取了元素的childNodes属性\n获取了元素的attributes属性\n访问了特殊的集合，如document.forms、document.images等等\n要了解了当使用NodeList对象时，合理使用会极大的提升代码执行速度\n\n\n### 优化循环\n\n可以使用下面几种方式来优化循环\n\n* 减值迭代\n大多数循环使用一个从0开始、增加到某个特定值的迭代器，在很多情况下，从最大值开始，在循环中不断减值的迭代器更加高效\n\n* 简化终止条件\n由于每次循环过程都会计算终止条件，所以必须保证它尽可能快，也就是说避免属性查找或者其它的操作，最好是将循环控制量保存到局部变量中，也就是说对数组或列表对象的遍历时，提前将length保存到局部变量中，避免在循环的每一步重复取值。\n\n    ```\n        var list = document.getElementsByTagName('p');\n        for (var i = 0; i < list.length; i++) {\n            //……\n        }\n\n        //替换为：\n        var list = document.getElementsByTagName('p');\n        for (var i = 0, l = list.length; i < l; i++) {\n            //……\n        }\n    ```\n* 简化循环体\n循环体是执行最多的，所以要确保其被最大限度的优化\n\n* 使用后测试循环\n在JavaScript中，我们可以使用for(;;),while(),for(in)三种循环，事实上，这三种循环中for(in)的效率极差，因为他需要查询散列键，只要可以，就应该尽量少用。for(;;)和while循环，while循环的效率要优于for(;;)，可能是因为for(;;)结构的问题，需要经常跳转回去。\n\n    ```\n        var arr = [1, 2, 3, 4, 5, 6, 7];\n        var sum = 0;\n        for (var i = 0, l = arr.length; i < l; i++) {\n            sum += arr[i];\n        }\n\n        //可以考虑替换为：\n\n        var arr = [1, 2, 3, 4, 5, 6, 7];\n        var sum = 0, l = arr.length;\n        while (l--) {\n            sum += arr[l];\n        }\n    ```\n    \n最常用的for循环和while循环都是前测试循环，而如do-while这种后测试循环，可以避免最初终止条件的计算，因此运行更快。\n\n\n* 展开循环\n\n当循环次数是确定的，消除循环并使用多次函数调用往往会更快。\n\n\n### 避免双重解释\n\n如果要提高代码性能，尽可能避免出现需要按照JavaScript解释的字符串，也就是\n\n尽量少使用eval函数  \n使用eval相当于在运行时再次调用解释引擎对内容进行运行，需要消耗大量时间，而且使用Eval带来的安全性问题也是不容忽视的。\n\n不要使用Function构造器  \n不要给setTimeout或者setInterval传递字符串参数\n\n    ```\n        var num = 0;\n        setTimeout('num++', 10);\n        //可以替换为：\n        var num = 0;\n        function addNum() {\n            num++;\n        }\n        setTimeout(addNum, 10);\n    ```\n\n### 缩短否定检测\n\n    ```\n       if (oTest != '#ff0000') {\n            //do something\n        }\n        if (oTest != null) {\n            //do something\n        }\n        if (oTest != false) {\n            //do something\n        }\n        //虽然这些都正确，但用逻辑非操作符来操作也有同样的效果：\n        if (!oTest) {\n            //do something\n        }\n    ```\n    \n### 条件分支\n\n将条件分支，按可能性顺序从高到低排列：可以减少解释器对条件的探测次数  \n在同一条件子的多（>2）条件分支时，使用switch优于if：switch分支选择的效率高于if，在IE下尤为明显。4分支的测试，IE下switch的执行时间约为if的一半。  \n使用三目运算符替代条件分支  \n    ```\n        if (a > b) {\n            num = a;\n        } else {\n            num = b;\n        }\n        //可以替换为：\n        num = a > b ? a : b;\n    ```\n\n### 使用常量\n\n重复值:任何在多处用到的值都应该抽取为一个常量  \n用户界面字符串:任何用于显示给用户的字符串，都应该抽取出来以方便国际化  \nURLs:在Web应用中，资源位置很容易变更，所以推荐用一个公共地方存放所有的URL  \n任意可能会更改的值:每当你用到字面量值的时候，你都要问一下自己这个值在未来是不是会变化，如果答案是“是”，那么这个值就应该被提取出来作为一个常量。  \n\n### 避免与null进行比较\n\n由于JavaScript是弱类型的，所以它不会做任何的自动类型检查，所以如果看到与null进行比较的代码，尝试使用以下技术替换\n\n如果值应为一个引用类型，使用instanceof操作符检查其构造函数  \n如果值应为一个基本类型，作用typeof检查其类型  \n如果是希望对象包含某个特定的方法名，则使用typeof操作符确保指定名字的方法存在于对象上  \n\n### 避免全局量\n\n全局变量应该全部字母大写，各单词之间用_下划线来连接。尽可能避免全局变量和函数, 尽量减少全局变量的使用，因为在一个页面中包含的所有JavaScript都在同一个域中运行。所以如果你的代码中声明了全局变量或者全局函数的话，后面的代码中载入的脚本文件中的同名变量和函数会覆盖掉（overwrite）你的。\n\n```\n//糟糕的全局变量和全局函数\nvar current = null;\nfunction init(){\n//...\n}\nfunction change() {\n    //...\n}\nfunction verify() {\n    //...\n}\n//解决办法有很多，Christian Heilmann建议的方法是：\n//如果变量和函数不需要在“外面”引用，那么就可以使用一个没有名字的方法将他们全都包起来。\n(function(){\nvar current = null;\nfunction init() {\n    //...\n}\nfunction change() {\n    //...\n}\nfunction verify() {\n    //...\n}\n})();\n//如果变量和函数需要在“外面”引用，需要把你的变量和函数放在一个“命名空间”中\n//我们这里用一个function做命名空间而不是一个var，因为在前者中声明function更简单，而且能保护隐私数据\nmyNameSpace = function() {\n    var current = null;\n\n    function init() {\n        //...\n    }\n\n    function change() {\n        //...\n    }\n\n    function verify() {\n        //...\n    }\n\n//所有需要在命名空间外调用的函数和属性都要写在return里面\n    return {\n        init: init,\n        //甚至你可以为函数和属性命名一个别名\n        set: change\n    };\n};\n```\n\n### 尊重对象的所有权\n\n \n\n因为JavaScript可以在任何时候修改任意对象，这样就可以以不可预计的方式覆写默认的行为，所以如果你不负责维护某个对象，它的对象或者它的方法，那么你就不要对它进行修改，具体一点就是说：\n\n不要为实例或原型添加属性  \n不要为实例或者原型添加方法  \n不要重定义已经存在的方法   \n不要重复定义其它团队成员已经实现的方法，永远不要修改不是由你所有的对象，你可以通过以下方式为对象创建新的功能:  \n创建包含所需功能的新对象，并用它与相关对象进行交互  \n创建自定义类型，继承需要进行修改的类型，然后可以为自定义类型添加额外功能  \n\n### 循环引用\n\n如果循环引用中包含DOM对象或者ActiveX对象，那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前，即使是刷新页面，这部分内存不会被浏览器释放。\n\n简单的循环引用：\n    ```\n        var el = document.getElementById('MyElement');\n        var func = function () {\n            //…\n        }\n        el.func = func;\n        func.element = el;\n    ```\n但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。\n\n    ```\n        function init() {\n            var el = document.getElementById('MyElement');\n            el.onclick = function () {\n                //……\n            }\n        }\n        init();\n    ```\ninit在执行的时候，当前上下文我们叫做context。这个时候，context引用了el，el引用了function，function引用了context。这时候形成了一个循环引用。\n\n下面2种方法可以解决循环引用：\n\n1)  置空dom对象\n\n    ```\n       function init() {\n            var el = document.getElementById('MyElement');\n            el.onclick = function () {\n                //……\n            }\n        }\n        init();\n        //可以替换为：\n        function init() {\n            var el = document.getElementById('MyElement');\n            el.onclick = function () {\n                //……\n            }\n            el = null;\n        }\n        init();\n    ```\n将el置空，context中不包含对dom对象的引用，从而打断循环应用。\n\n如果我们需要将dom对象返回，可以用如下方法：\n\n    ```\n        function init() {\n            var el = document.getElementById('MyElement');\n            el.onclick = function () {\n                //……\n            }\n            return el;\n        }\n        init();\n        //可以替换为：\n        function init() {\n            var el = document.getElementById('MyElement');\n            el.onclick = function () {\n                //……\n            }\n            try {\n                return el;\n            } finally {\n                el = null;\n            }\n        }\n        init();\n    ```\n2)  构造新的context\n\n    ```\n        function init() {\n            var el = document.getElementById('MyElement');\n            el.onclick = function () {\n                //……\n            }\n        }\n        init();\n        //可以替换为：\n        function elClickHandler() {\n            //……\n        }\n        function init() {\n            var el = document.getElementById('MyElement');\n            el.onclick = elClickHandler;\n        }\n        init();\n    ```\n把function抽到新的context中，这样，function的context就不包含对el的引用，从而打断循环引用。\n\n\n### 通过javascript创建的dom对象，必须append到页面中\n\nIE下，脚本创建的dom对象，如果没有append到页面中，刷新页面，这部分内存是不会回收的！\n\n    ```\n        function create() {\n            var gc = document.getElementById('GC');\n            for (var i = 0; i < 5000; i++) {\n                var el = document.createElement('div');\n                el.innerHTML = \"test\";\n                //下面这句可以注释掉，看看浏览器在任务管理器中，点击按钮然后刷新后的内存变化\n                gc.appendChild(el);\n            }\n        }\n    ```\n### 释放dom元素占用的内存\n\n将dom元素的innerHTML设置为空字符串，可以释放其子元素占用的内存。\n\n在rich应用中，用户也许会在一个页面上停留很长时间，可以使用该方法释放积累得越来越多的dom元素使用的内存。\n\n\n### 释放javascript对象\n\n在rich应用中，随着实例化对象数量的增加，内存消耗会越来越大。所以应当及时释放对对象的引用，让GC能够回收这些内存控件。\n\n对象：obj = null\n\n对象属性：delete obj.myproperty\n\n数组item：使用数组的splice方法释放数组中不用的item\n\n\n### 避免string的隐式装箱\n\n对string的方法调用，比如'xxx'.length，浏览器会进行一个隐式的装箱操作，将字符串先转换成一个String对象。推荐对声明有可能使用String实例方法的字符串时，采用如下写法：\n\nvar myString = new String('Hello World');\n\n\n### 松散耦合\n\n1、解耦HTML/JavaScript\n\nJavaScript和HTML的紧密耦合：直接写在HTML中的JavaScript、使用包含内联代码的<script>元素、使用HTML属性来分配事件处理程序等\n\nHTML和JavaScript的紧密耦合：JavaScript中包含HTML，然后使用innerHTML来插入一段html文本到页面\n\n其实应该是保持层次的分离，这样可以很容易的确定错误的来源，所以我们应确保HTML呈现应该尽可能与JavaScript保持分离\n\n2、解耦CSS/JavaScript\n\n显示问题的唯一来源应该是CSS，行为问题的唯一来源应该是JavaScript，层次之间保持松散耦合才可以让你的应用程序更加易于维护，所以像以下的代码element.style.color=”red”尽量改为element.className=”edit”，而且不要在css中通过表达式嵌入JavaScript\n\n3、解耦应用程序/事件处理程序\n\n将应用逻辑和事件处理程序相分离：一个事件处理程序应该从事件对象中提取，并将这些信息传送给处理应用逻辑的某个方法中。这样做的好处首先可以让你更容易更改触发特定过程的事件，其次可以在不附加事件的情况下测试代码，使其更易创建单元测试\n\n\n### 性能方面的注意事项\n\n1、尽量使用原生方法\n\n2、switch语句相对if较快\n\n通过将case语句按照最可能到最不可能的顺序进行组织\n\n3、位运算较快\n\n当进行数字运算时，位运算操作要比任何布尔运算或者算数运算快\n\n4、巧用||和&&布尔运算符\n\n    ```\n        function eventHandler(e) {\n            if (!e) e = window.event;\n        }\n        //可以替换为：\n        function eventHandler(e) {\n            e = e || window.event;\n        }\n   \n        if (myobj) {\n            doSomething(myobj);\n        }\n        //可以替换为：\n        myobj && doSomething(myobj);\n    ```\n### 避免错误应注意的地方\n\n1、每条语句末尾须加分号\n\n在if语句中，即使条件表达式只有一条语句也要用{}把它括起来，以免后续如果添加了语句之后造成逻辑错误\n\n2、使用+号时需谨慎\n\nJavaScript 和其他编程语言不同的是，在 JavaScript 中，'+'除了表示数字值相加，字符串相连接以外，还可以作一元运算符用，把字符串转换为数字。因而如果使用不当，则可能与自增符'++'混淆而引起计算错误\n    ```\n        var valueA = 20;\n        var valueB = \"10\";\n        alert(valueA + valueB);     //ouput: 2010 \n        alert(valueA + (+valueB));  //output: 30 \n        alert(valueA + +valueB);    //output:30 \n        alert(valueA ++ valueB);     //Compile error\n    ```\n3、使用return语句需要注意\n\n一条有返回值的return语句不要用()括号来括住返回值，如果返回表达式，则表达式应与return关键字在同一行，以避免压缩时，压缩工具自动加分号而造成返回与开发人员不一致的结果\n\n    ```\n        function F1() {\n            var valueA = 1;\n            var valueB = 2;\n            return valueA + valueB;\n        }\n        function F2() {\n            var valueA = 1;\n            var valueB = 2;\n            return\n            valueA + valueB;\n        }\n        alert(F1());  //output: 3 \n        alert(F2());  //ouput: undefined\n    ```\n\n### ==和===的区别\n\n避免在if和while语句的条件部分进行赋值，如if (a = b)，应该写成if (a == b)，但是在比较是否相等的情况下，最好使用全等运行符，也就是使用===和!==操作符会相对于==和!=会好点。==和!=操作符会进行类型强制转换\n\n    ```\n        var valueA = \"1\";\n        var valueB = 1;\n        if (valueA == valueB) {\n            alert(\"Equal\");\n        }\n        else {\n            alert(\"Not equal\");\n        }\n        //output: \"Equal\"\n        if (valueA === valueB) {\n            alert(\"Equal\");\n        }\n        else {\n            alert(\"Not equal\");\n        }\n        //output: \"Not equal\"\n    ```\n### 不要使用生偏语法\n\n不要使用生偏语法，写让人迷惑的代码，虽然计算机能够正确识别并运行，但是晦涩难懂的代码不方便以后维护\n\n\n### 函数返回统一类型\n\n虽然JavaScript是弱类型的，对于函数来说，前面返回整数型数据，后面返回布尔值在编译和运行都可以正常通过，但为了规范和以后维护时容易理解，应保证函数应返回统一的数据类型\n\n\n### 总是检查数据类型\n\n要检查你的方法输入的所有数据，一方面是为了安全性，另一方面也是为了可用性。用户随时随地都会输入错误的数据。这不是因为他们蠢，而是因为他们很忙，并且思考的方式跟你不同。用typeof方法来检测你的function接受的输入是否合法\n\n\n### 何时用单引号，何时用双引号\n\n虽然在JavaScript当中，双引号和单引号都可以表示字符串, 为了避免混乱，我们建议在HTML中使用双引号，在JavaScript中使用单引号，但为了兼容各个浏览器，也为了解析时不会出错，定义JSON对象时，最好使用双引号\n\n\n用JSLint运行JavaScript验证器来确保没有语法错误或者是代码没有潜在的问\n\n部署之前推荐使用压缩工具将JS文件压缩\n\n文件编码统一用UTF-8\n\nJavaScript 程序应该尽量放在 .js 的文件中，需要调用的时候在 HTML 中以 <script src=\"filename.js\"> 的形式包含进来。JavaScript 代码若不是该 HTML 文件所专用的，则应尽量避免在 HTML 文件中直接编写 JavaScript 代码。因为这样会大大增加 HTML 文件的大小，无益于代码的压缩和缓存的使用。另外，<script src=\"filename.js\"> 标签应尽量放在文件的后面,最好是放在</body>标签前。这样会降低因加载 JavaScript 代码而影响页面中其它组件的加载时间。\n\n永远不要忽略代码优化工作，重构是一项从项目开始到结束需要持续的工作，只有不断的优化代码才能让代码的执行效率越来越好\n\n"
  },
  {
    "path": "4.3.4 使用数组 splice 删除元素时的坑.md",
    "content": "# 使用数组splice删除元素时，你应该知道这个小坑\n\n* https://juejin.im/entry/5a1ecfef6fb9a044fe462143\n\n从数组[1,2,5,6,8,9]中保留小于等于4的元素。思路，循环数组，然后从循环中调判断如果元素大于4，那么就使用splice剔除掉。\n\n代码如下：\n```\nvar arr = [1,2,5,6,8,9];\nfor(var i=0;i<arr.length;i++){\n  if(arr[i]>4)\n  arr.splice(i,1)\n}\nconsole.log(arr) //[1, 2, 6, 9]\n```\n\n这时我们发现，奇怪了，为什么 6和9被保存了下来呢？这是因为我们每次找到大于4的元素之后，就直接splice剔除掉了，需要注意的是，splice是一个会变异方法，  \n也就是会改变原数组的方法，所以当i等于2的时候，会找到元素‘5’，然后splice的时候，由于元素‘5’剔除之后，有空位，后面的元素‘6’ ‘8’ ‘9’就都往前补一位，  \n但是此时i的值在结束此次循环之后，经过了i++语句，  \n就是直接跳到元素‘8’的位置了，元素‘6’因为补位 躲过了一劫！ 再次循环9又因为补位也躲过了一劫，所以结果就是1269  \n\n当 i 等于4 的时候 情况也是一样的，元素‘9’补位，躲过一劫。\n\n就是使用splice的时候，要注意数组的变化以及索引的位置\n\n```\n所以使用 i-- 或者 --i\nvar arr = [1,2,5,6,8,9];\nfor(var i=0;i<arr.length;i++){\n  if(arr[i]>4){\n    arr.splice(i,1)\n    --i\n  }\n}\nconsole.log(arr) \n```\n\n当然 我们也可以用一个新数组来保存，符合条件的元素，或者使用数组的filter方法来筛选元素\n\n```\nvar arr = [1,2,5,6,8,9];\nvar filt = arr.filter(function(ele,index,array){\n  return ele < 4;\n})\nconsole.log(arr); // [1, 2, 5, 6, 8, 9]\nconsole.log(filt); // [1, 2]\n```\n\n"
  },
  {
    "path": "4.3.5 JS中document对象和window对象的区别.md",
    "content": "# JS中document对象和window对象的区别\n\n简单来说，document是window的一个对象属性。  \nWindow 对象表示浏览器中打开的窗口。  \n如果文档包含框架（frame 或 iframe 标签），浏览器会为 HTML 文档创建一个 window 对象，并为每个框架创建一个额外的 window 对象。  \n所有的全局函数和对象都属于Window 对象的属性和方法。  \ndocument   对 Document 对象的只读引用。  \n\n区别:1、window 指窗体。document指页面。document是window的一个子对象。\n\n```\n　document 当前显示的文档(该属性本身也是一个对象)\n\n　　frame 窗口里的一个框架((FRAME>)(该属性本身也是一个对象)\n\n　　frames array 列举窗口的框架对象的数组,按照这些对象在文档中出现的顺序列出(该属性本身也是一个\n\n对象)\n\n　　history 窗口的历史列表(该属性本身也是一个对象)\n\n　　length 窗口内的框架数\n\n　　 location 窗口所显示文档的完整(绝对)URL(该属性本身也是一个对象)不要把它与如document.location\n\n混淆,后者是当前显示文档的URL。用户可以改变window.location(用另一个文档取代当前文档),但却不能改变\n\ndocument.location (因为这是当前显示文档的位置)\n\n　　name 窗口打开时,赋予该窗口的名字\n\n　　opener 代表使用window.open打开当前窗口的脚本所在的窗口(这是Netscape Navigator 3.0beta 3所引\n\n入的一个新属性)\n\n　　parent 包含当前框架的窗口的同义词。frame和window对象的一个属性\n\n　　self 当前窗口或框架的同义词\n\n　　status 状态条中的消息\n\n　　top 包含当前框架的最顶层浏览器窗口的同义词\n\n　　window 当前窗口或框架的同义词,与self相同\n\n　　方法\n\n　　alert() 打开一个Alert消息框\n\n　　clearTimeout() 用来终止setTimeout方法的工作\n\n　　close() 关闭窗口\n\n　　 confirm() 打开一个Confirm消息框,用户可以选择OK或Cancel,如果用户单击OK,该方法返回true,单击\n\nCancel返回false\n\n　　 blur() 把焦点从指定窗口移开(这是Netscape Navigator 3.0 beta 3引入的新方法)\n\n　　focus() 把指定的窗口带到前台(另一个新方法)\n\n　　open() 打开一个新窗口\n\n　　 prompt() 打开一个Prompt对话框,用户可向该框键入文本,并把键入的文本返回到脚本\n\n　　setTimeout() 等待一段指定的毫秒数时间,然后运行指令事件处理程序事件处理程序\n\n　　Onload() 页面载入时触发\n\n　　Onunload() 页面关闭时触发\n\n[document 对象]\n\n　　该对象是window和frames对象的一个属性,是显示于窗口或框架内的一个文档。\n\n　　属性\n\n　　alinkColor 活动链接的颜色(ALINK)\n\n　　anchor 一个HTMI锚点,使用<A NAME=>标记创建(该属性本身也是一个对象)\n\n　　anchors array 列出文档锚点对象的数组(<A NAME=>)(该属性本身也是一个对象)\n\n　　bgColor 文档的背景颜色(BGCOLOR)\n\n　　cookie 存储于cookie.txt文件内的一段信息,它是该文档对象的一个属性\n\n　　fgColor 文档的文本颜色(<BODY>标记里的TEXT特性)\n\n　　form 文档中的一个窗体(<FORM>)(该属性本身也是一个对象)\n\n　　forms anay 按照其出现在文档中的顺序列出窗体对象的一个数组(该属性本身也是一个对象)\n\n　　lastModified 文档最后的修改日期\n\n　　linkColor 文档的链接的颜色,即<BODY>标记中的LINK特性(链接到用户没有观察到的文档)\n\n　　link 文档中的一个<A HREF=>标记(该属性本身也是一个对象)\n\n　　links array 文档中link对象的一个数组,按照它们出现在文档中的顺序排列(该属性本身也是一个对象)\n\n　　location 当前显示文档的URL。用户不能改变document.location(因为这是当前显示文档的位置)。但是,\n\n可以改变 window.location (用其它文档取代当前文档)window.location本身也是一个对象,而\n\ndocument.location不是对象\n\n　　referrer 包含链接的文档的URL,用户单击该链接可到达当前文档\n\n　　title 文档的标题((TITLE>)\n\n　　vlinkColor 指向用户已观察过的文档的链接文本颜色,即<BODY>标记的VLINK特性\n\n　　方法\n\n　　clear 清除指定文档的内容\n\n　　close 关闭文档流\n\n　　open 打开文档流\n\n　　 write 把文本写入文档\n\n　　writeln 把文本写入文档,并以换行符结尾\n```\n"
  },
  {
    "path": "4.3.6 自执行函数中暴露方法调用.md",
    "content": "# 自执行函数中暴露方法调用\n\n```\nvar demo = (function(){\n  function test(){\n  }\n  return {\n    test:test\n  }\n})();\n\ndemo.test();\n```\n\nJavascript中匿名函数的多种调用方式\n\nJavascript中定义函数的方式有多种，函数直接量就是其中一种。如var fun = function(){},这里function如果不赋值给fun那么它就是一个匿名函数。好，看看匿名函数的如何被调用。\n\n \n\n方式1，调用函数，得到返回值。强制运算符使函数调用执行\n```\n(function(x,y){\n    alert(x+y);\n    return x+y;\n}(3,4));\n```\n方式2，调用函数，得到返回值。强制函数直接量执行再返回一个引用，引用再去调用执行\n\n```\n(function(x,y){\n    alert(x+y);\n    return x+y;\n})(3,4);\n这种方式也是很多库爱用的调用方式，如jQuery，Mootools。\n```\n \n\n方式3，使用void\n\n```\nvoid function(x) {\n      x = x-1;\n      alert(x);\n}(9);\n```\n\n方式4，使用-/+运算符\n\n```\n-function(x,y){\n    alert(x+y);\n    return x+y;\n}(3,4);\n \n+function(x,y){\n    alert(x+y);\n    return x+y;\n}(3,4);\n \n--function(x,y){\n    alert(x+y);\n    return x+y;\n}(3,4);\n \n++function(x,y){\n    alert(x+y);\n    return x+y;\n}(3,4);\n``` \n\n方式5，使用波浪符(~)\n\n```\n~function(x, y) {\n    alert(x+y);\n   return x+y;\n}(3, 4);\n```\n\n方式6，匿名函数执行放在中括号内\n\n```\n[function(){\n   console.log(this) // 浏览器得控制台输出window\n}(this)]\n``` \n\n 方式7，匿名函数前加typeof\n\n```\ntypeof function(){\n   console.log(this) // 浏览器得控制台输出window\n}(this)\n```\n\n方式8，匿名函数前加delete\n\n```\ndelete function(){\n   console.log(this) // 浏览器得控制台输出window\n}(this)\n```\n\n方式9，匿名函数前加void\n\n```\nvoid function(){\n   console.log(this) // 浏览器得控制台输出window\n}(this)\n```\n\n方式10，使用new方式，传参\n\n```\nnew function(win){\n   console.log(win) // window\n}(this)\n```\n\n方式11，使用new，不传参\n\n```\nnew function(){\n    console.log(this) // 这里的this就不是window了\n}\n```\n\n方式12，逗号运算符\n\n```\n1, function(){\n    console.log(this) // window\n}();\n```\n\n方式13，按位异或运算符\n\n```\n1^function(){\n    console.log(this) // window\n}();\n```\n\n方式14，比较运算符\n\n```\n1>function(){\n    console.log(this) // window\n}();\n```\n\n \n"
  },
  {
    "path": "4.3.7 前端数组对象排序算法.md",
    "content": "# 前端数组对象排序算法\n\n主要用 Arrays.sort(func);  //进行排序   \n\nMDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort\n\n```\n// 根据字段排序\nvar by = function(name){ \n  return function(o, p){ \n    var a, b; \n    if (typeof o === \"object\" && typeof p === \"object\" && o && p) { \n      a = o[name]; b = p[name]; \n      if (a === b) { \n        return 0; \n      } \n      if (typeof a === typeof b) { \n        return a < b ? -1 : 1; \n      } \n      return typeof a < typeof b ? -1 : 1; \n    } else { \n      throw ( \"error\"); \n    } \n  } \n}\nvar employees=[\n    {name:\"George\", age:32, retiredate:\"March 12, 2014\"},\n    {name:\"Edward\", age:17, retiredate:\"June 2, 2023\"},\n    {name:\"Christine\", age:58, retiredate:\"December 20, 2036\"},\n    {name:\"Sarah\", age:62, retiredate:\"April 30, 2020\"}\n]\n\nemployees.sort(by(\"age\"));\nconsole.log(employees);\n\n// 根据多个字段排序\nvar by = function(name,minor){\n  return function(o,p){\n    var a,b;\n    if(o && p && typeof o === 'object' && typeof p ==='object'){\n      a = o[name];\n      b = p[name];\n      if(a === b){\n        return typeof minor === 'function' ? minor(o,p):0;\n      }\n      if(typeof a === typeof b){\n        return a < b ? -1:1;\n      }\n      return typeof a < typeof b ? -1 : 1;\n    }else{\n      throw(\"error\");\n    }\n  }\n}\n\nemployees.sort(by('age',by('name')));\nconsole.log(employees);\n```\n"
  },
  {
    "path": "4.3.8 ios 需要点击2次才能跳转.md",
    "content": "# 需要点击2次才能跳转\n\n\n```\n（1）网页头部添加meta\n\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n（2）使用js 添加 click touchend\n\n $('a').on('click touchend', function(e) {\n      \n   });\n 亲自试了一下，这种方案是可行的。\n \n I had this same issue. The simplest solution is not to bind the 'mouseenter' event on iOS (or any touch enabled target platform). If that is not bound the hover event won't get triggered and click is triggered on the first tap.\n \n 就是不要绑定 mouseenter 事件\n```\n"
  },
  {
    "path": "4.3.9 2017-12-11 遇到 ios bug.md",
    "content": "# 4-27 2017-12-11 遇到 ios bug \n\n起因  -webkit-overflow-scroll: touch\n\n表格左侧单行固定,头部和内容分离\n\n```\n//单列固定\n111111    2222  333 444 555 //头部固定(我的做法是复制一个表格头出来固定)\n111111    5     6     7\n111111\n111111\n111111\n\n如果加了 -webkit-overflow-scroll: touch ios 机子出现如果右侧内容部分很多, `单列固定`这一列无法显示出来了\n\n排查一个一个修改\n\n回退 svn\n\nsvn update\n\nsvn merge -r 最新版本号:回退到的版本号 \"\"   //(\"\" 指的是全部目录回退)\n\nsvn commit -m '回退到版本号多少'\n\n```\n"
  },
  {
    "path": "4.4.1 新增元素使用 jQuery on()方法重复绑定的问题.md",
    "content": "# 新增元素使用jQuery on()方法重复绑定的问题\n\n\n最近写ajax新增元素button绑定click事件的时候发现元素重新添加进来的时候会多执行一次事件函数，  \n找了半天，怀疑是on()的问题，于是测试了一下，果然是因为on()的使用方式造成了有的新增元素会执行多次绑定事件函数。\n\n\n\n当使用$(document).find('target-selector').on(event,function);时，必须在元素每次添加进来之后重新绑定，否则会无效。  \n\n而使用$(document).on(event,selector,function);时，只需执行一次绑定即可，可以在开头就写好绑定，对后面添加进来的元素都有效，  \n如果在元素每次添加进来之后都绑定，则绑定了几次，触发事件的时候就会执行几次事件函数。  \n"
  },
  {
    "path": "4.4.2 一年中的 周 范围计算.md",
    "content": "#  一年中的 周 范围计算\n\n```\nfunction formatDig(num){\n        return num>9?''+num:'0'+num;\n    }\n    function formatDate(mill){\n        var y=new Date(mill);\n        let raws= [\n            y.getFullYear(),\n            formatDig(y.getMonth()+1),\n            formatDig(y.getDate()),\n            y.getDay()||7\n        ];\n        let format=['年','月','日 星期'];\n        return String.raw({raw:raws},...format);\n    }\n    function* createWeeks(year){\n        const ONE_DAY=24*3600*1000;\n        let start=new Date(year,0,1),\n               end=new Date(year,11,31);\n        let firstDay=start.getDay()|| 7,\n                lastDay=end.getDay()||7;\n        let startTime=+start,\n                endTime=startTime+(7-firstDay)*ONE_DAY,\n                _endTime=end-(7-lastDay)*ONE_DAY;\n        yield [startTime,endTime];\n        startTime=endTime+ONE_DAY;\n        endTime=endTime+7*ONE_DAY;\n        while(endTime<_endTime){\n            yield [startTime,endTime];\n            startTime=endTime+ONE_DAY;\n            endTime=endTime+7*ONE_DAY;\n        }\n        yield [startTime,+end];\n    }\n    let index=1;\n    for(let i of createWeeks(2016)){\n        let start=i[0],\n                end=i[1];\n        console.log(`第${formatDig(index++)}周 ${formatDate(start)}-${formatDate(end)}`);\n    }\n```\n\n\n不使用 generate 函数的方法\n\nString.raw方法也可以作为正常的函数使用。这时，它的第一个参数，应该是一个具有raw属性的对象，且raw属性的值应该是一个数组。\n```\nString.raw({ raw: 'test' }, 0, 1, 2);\n// 't0e1s2t'\n\n// 等同于\nString.raw({ raw: ['t','e','s','t'] }, 0, 1, 2);\n```\n具体代码:\n```\nfunction formatDig(num){\n        return num>9?''+num:'0'+num;\n    }\n    function formatDate(mill){\n        var y=new Date(mill);\n        var raws= [\n            y.getFullYear(),\n            formatDig(y.getMonth()+1),\n            formatDig(y.getDate()),\n            y.getDay()||7\n        ];\n        var format=['年','月','日 星期'];\n        return String.raw({raw:raws},...format);\n    }\n    function createWeeks(year){\n        const ONE_DAY=24*3600*1000;\n\t\tvar re = [];\n\t\t\n        \tvar start=new Date(year,0,1),\n               end=new Date(year,11,31);\n        \tvar firstDay=start.getDay()|| 7,\n                lastDay=end.getDay()||7;\n       \t\t var startTime=+start,\n                endTime=startTime+(7-firstDay)*ONE_DAY,\n                _endTime=end-(7-lastDay)*ONE_DAY;\n       \t\tre.push([startTime,endTime]);\n        \tstartTime=endTime+ONE_DAY;\n            endTime=endTime+7*ONE_DAY;\n\n          while(endTime<= _endTime){\n           re.push([startTime,endTime]);\n            startTime=endTime+ONE_DAY;\n            endTime=endTime+7*ONE_DAY;\n          }\n\t\t  console.log(re);\n\t\t\n        return re;\n    }\n    var index=1;\n    for(var i of createWeeks(2017)){\n        var start=i[0],\n                end=i[1];\n        console.log(`第${formatDig(index++)}周 ${formatDate(start)}-${formatDate(end)}`);\n    }\n    \n    多个年份\n    var year = [2014,2015,2016,2017];\n  year.forEach(function(ele,ind){\n    var index=1;\n    for(var i of createWeeks(ele)){\n        var start=i[0],\n                end=i[1];\n        console.log(`第${formatDig(index++)}周 ${formatDate(start)}-${formatDate(end)}`);\n    }\n  })\n```\n"
  },
  {
    "path": "4.4.3 BFC-块格式化上下文（block formatting context）.md",
    "content": "# 块格式化上下文（block formatting context）\n\n* MDN 介绍：https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Block_formatting_context\n\n一个块格式化上下文由以下之一创建：\n\n- 根元素或其它包含它的元素\n- 浮动元素 (元素的 float 不是 none)\n- 绝对定位元素 (元素的 position 为 absolute 或 fixed)\n- 内联块 (元素具有 display: inline-block)\n- 表格单元格 (元素具有 display: table-cell，HTML表格单元格默认属性)\n- 表格标题 (元素具有 display: table-caption, HTML表格标题默认属性)\n- 具有overflow 且值不是 visible 的块元素，\n- display: flow-root\n- column-span: all 应当总是会创建一个新的格式化上下文，即便具有 column-span: all 的元素并不被包裹在一个多列容器中。\n\n一个块格式化上下文包括创建它的元素内部所有内容，除了被包含于创建新的块级格式化上下文的后代元素内的元素。\n\n>BFC就是页面上一个隔离的独立容器，容器内的子元素不会影响外部的元素  \n- Box: CSS布局的基本单位  \n　　Box 是 CSS 布局的对象和基本单位， 直观点来说，就是一个页面是由很多个 Box 组成的。元素的类型和 display 属性，决定了这个 Box 的类型。   \n-\n- Formatting context  \n　　Formatting context 是 W3C CSS2.1 规范中的一个概念。它是页面中的一块渲染区域，并且有一套渲染规则，它决定了其子元素将如何定位，以及和其他元素的关系和相互作用。最常见的 Formatting context 有 Block fomatting context (简称BFC)和 Inline formatting context (简称IFC)。\n\n## BFC布局规则：\n- 内部的Box会在垂直方向，一个接一个地放置。\n- Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠\n- 每个元素的margin box的左边， 与包含块border box的左边相接触(对于从左往右的格式化，否则相反)。即使存在浮动也是如此。\n- BFC的区域不会与float box重叠。\n- BFC就是页面上的一个隔离的独立容器，容器里面的子元素不会影响到外面的元素。反之也如此。\n- 计算BFC的高度时，浮动元素也参与计算\n\n\n## BFC的用途\n- 自适应两栏布局\n- 清除内部浮动\n- 防止marin重叠\n\n\n\n## 3、GFC（css3）\n>GFC(GridLayout Formatting Contexts)直译为\"网格布局格式化上下文\"，  \n当为一个元素设置display值为grid的时候，此元素将会获得一个独立的渲染区域，    \n我们可以通过在网格容器（grid container）上定义网格定义行（grid definition rows）和网格定义列（grid definition columns）属性各在网格项目（grid item）上定义网格行（grid row）和网格列（grid columns）为每一个网格项目（grid item）定义位置和空间。   \n那么GFC有什么用呢，和table又有什么区别呢？  \n首先同样是一个二维的表格，但GridLayout会有更加丰富的属性来控制行列，控制对齐以及更为精细的渲染语义和控制。\n\n## 4、FFC（css3）\n>FFC(Flex Formatting Contexts)直译为\"自适应格式化上下文\"，  \ndisplay值为flex或者inline-flex的元素将会生成自适应容器（flex container），  \n可惜这个牛逼的属性只有谷歌和火狐支持，不过在移动端也足够了，  \n至少safari和chrome还是OK的，毕竟这俩在移动端才是王道。  \nFlex Box 由伸缩容器和伸缩项目组成。通过设置元素的 display 属性为 flex 或 inline-flex 可以得到一个伸缩容器。设置为 flex 的容器被渲染为一个块级元素，而设置为 inline-flex 的容器则渲染为一个行内元素。\n伸缩容器中的每一个子元素都是一个伸缩项目。伸缩项目可以是任意数量的。伸缩容器外和伸缩项目内的一切元素都不受影响。简单地说，Flexbox 定义了伸缩容器内伸缩项目该如何布局。\n\n## 参考\n- [理解BFC、IFC、GFC、FFC](https://blog.csdn.net/u011472830/article/details/73010596)\n- [前端精选文摘：BFC 神奇背后的原理](http://www.cnblogs.com/lhb25/p/inside-block-formatting-ontext.html)\n- [前端的BFC、IFC、GFC和FFC](https://blog.csdn.net/qq_22855325/article/details/76153101)\n"
  },
  {
    "path": "4.4.4 函数封装写法.md",
    "content": "#  函数封装写法\n\n* 1\n```\n(function(){\n  \n  var datepicker = {};\n  datepicker.getMonthData = function (year, month) {\n    if (!year || !month) { \n      var today =new Date();\n      year=today.getFullYear(); \n      month = today.getMonth() + 1;\n    }\n    \n    return {\n      year: year,\n      month: month,\n      days: ret\n    };\n  }\n  \n  window.datepicker = datepicker; //方法暴露出去，供外部调用\n})();\n\n\n// 带参数写法\n( function(str){\n    alert(str);\n})(\"你好！\");\n\n\n/ ------------------------ 工具包---------------------------------//\nvar utils = {\n            center : function(dom){\n                dom.style.position = 'absolute';\n                dom.style.top = '50%';\n                dom.style.left = '50%';\n                dom.style['margin-top'] = - dom.offsetHeight / 2 + 'px';\n                dom.style['margin-left'] = - dom.offsetWidth / 2 + 'px';\n            },\n        \n            /** dom相关 * */\n            isDom : ( typeof HTMLElement === 'object' ) ?\n                function(obj){\n                    return obj instanceof HTMLElement;\n                } :\n                function(obj){\n                    return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string';\n                } ,\n             \n            /** 数组相关 * */\n            isArray : function(obj){\n                return obj.constructor.toString().indexOf('Array') != -1;\n            }\n            \n        }\n\n```\n"
  },
  {
    "path": "4.4.5 字符串数组常用方法.md",
    "content": "# 字符串数组常用方法\n\n```\nvar a = 0;\nfunction test(){\n    alert(a);\n    var a = 100;\n}\ntest();\n// undefined\n先创建了空间,再赋值，都是从上到下的。还有一点就是现在自己的空间里面找是否有，没有去父级找。要是还没有就再往上一层。简单的说就是作用域链\n\n```\n\n* 1. 字符串 format方法\n\n```\nif(!String.prototype.format ){\n    String.prototype.format = function() {\n        var e = arguments;\n        return this.replace(/{(\\d+)}/g,function(t, n) {\n            return typeof e[n] != \"undefined\" ? e[n] : t;\n        })\n    };\n}\n\nvar template = \"今天是{0},马上就是{1}！\";\nconsole.log(template.format(\"2017\",\"2018\"));\n```\n\n* 2. 数组 forEach(callback,context) 操作数组中的每一个元素\n\n```\n/**\n    forEach除了接受一个必须的回调函数参数，还可以接受一个可选的上下文参数（改变回调函数里面的this指向）（第2个参数）。\n*/\nif (!Array.prototype.forEach && typeof Array.prototype.forEach !== \"function\") {\n    Array.prototype.forEach = function(callback, context) {\n       // 遍历数组,在每一项上调用回调函数，这里使用原生方法验证数组。\n       if (Object.prototype.toString.call(this) === \"[object Array]\") {\n           var i,len;\n           //遍历该数组所有的元素\n           for (i = 0, len = this.length; i < len; i++) {\n               if (typeof callback === \"function\"  && Object.prototype.hasOwnProperty.call(this, i)) {\n                   if (callback.call(context, this[i], i, this) === false) {\n                       break; // or return;\n                   }\n               }\n           }\n       }\n    };\n}\n\nvar drinks = ['雪碧','可乐','脉动','红牛','农夫山泉'];\n        \nvar context = {\n    str1 : '【',\n    str2 : '】'\n};\n        \ndrinks.forEach(function(item){\n    console.log(this.str1 + item + this.str2);\n},context);\n\n\n```\n\n* indexOf():indexOf(searchvalue,fromindex) 查询数组中某一个值的下标\n```\n//获取某元素在数组中第一次出现的下标\nif (!Array.prototype.indexOf) {\n  Array.prototype.indexOf = function(searchElement, fromIndex) {\n    var k;\n    // 1. Let O be the result of calling ToObject passing\n    //    the this value as the argument.\n    if (this == null) {\n      throw new TypeError('\"this\" is null or not defined');\n    }\n\n    var O = Object(this);\n\n    // 2. Let lenValue be the result of calling the Get\n    //    internal method of O with the argument \"length\".\n    // 3. Let len be ToUint32(lenValue).\n    var len = O.length >>> 0;\n\n    // 4. If len is 0, return -1.\n    if (len === 0) {\n      return -1;\n    }\n\n    // 5. If argument fromIndex was passed let n be\n    //    ToInteger(fromIndex); else let n be 0.\n    var n = +fromIndex || 0;\n\n    if (Math.abs(n) === Infinity) {\n      n = 0;\n    }\n\n    // 6. If n >= len, return -1.\n    if (n >= len) {\n      return -1;\n    }\n\n    // 7. If n >= 0, then Let k be n.\n    // 8. Else, n<0, Let k be len - abs(n).\n    //    If k is less than 0, then let k be 0.\n    k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);\n\n    // 9. Repeat, while k < len\n    while (k < len) {\n      // a. Let Pk be ToString(k).\n      //   This is implicit for LHS operands of the in operator\n      // b. Let kPresent be the result of calling the\n      //    HasProperty internal method of O with argument Pk.\n      //   This step can be combined with c\n      // c. If kPresent is true, then\n      //    i.  Let elementK be the result of calling the Get\n      //        internal method of O with the argument ToString(k).\n      //   ii.  Let same be the result of applying the\n      //        Strict Equality Comparison Algorithm to\n      //        searchElement and elementK.\n      //  iii.  If same is true, return k.\n          if (k in O && O[k] === searchElement) {\n            return k;\n          }\n          k++;\n        }\n        return -1;\n      };\n    }\n\nvar index = drinks.indexOf('雪碧');\nalert(index);//0\n```\n\n* 3 数组封装\n\n```\n//模拟ArrayList\nfunction ArrayList(){\n    var arr = []; //用于保存数据的数组\n    var length = 0; //数组的长度，默认为0\n    \n    /**\n     * 判断是否为空\n     */\n    this.isEmpty = function(){\n        return length == 0;\n    }\n    \n    /**\n     * 获取列表长度\n     */\n    \n    this.size = function(){\n        return length;\n    }\n    \n    /** \n    * 判断对象中是否包含给定对象\n    */ \n    this.contains = function(obj){\n        if(arr.indexOf(obj) != -1){\n            return true;\n        }\n        return false;\n    }\n    \n    /** \n    * 新增\n    */\n   this.add = function(obj){\n    length = length + 1;\n    arr.push(obj);\n   }\n   \n   /**\n    * 删除\n    * 参数1  obj ： 需要删除的元素\n    * 参数2  deleteAll： 是否全部删除，否则默认删除第一个匹配项\n    */\n   this.remove = function(obj,deleteAll){\n        var len = arr.length;\n        for(var i = 0 ;i < len ;i++){\n            if(arr[i] == obj){\n                arr.splice(i,1);\n                length = length - 1;\n                if(!deleteAll){\n                    break;\n                }\n            }\n        }\n   }\n   \n   \n   /**\n    * 根据索引获取对应的元素\n    */\n   this.get = function(index){\n    if(index > length - 1){\n        return null;\n    }\n    return arr[index];\n   }\n   \n   /**\n    * 获取列表数组\n    */\n   this.toArray = function(){\n     return arr;\n   }\n   \n   /**\n    * 获取某一个元素的角标\n    * 如果只出现一次，就返回一个数字，如果大于一次，就返回数组\n    */\n   this.indexOf = function(obj){\n     var rstArr = [];\n     var count = 0;\n     for(var i = 0 ;i < length ;i++){\n        if(obj == arr[i]){\n            rstArr[count++] = i;\n        }\n     }\n     if(count == 1){\n        return rstArr[0];\n     }\n     return rstArr;\n   }\n   \n   this.toString = function(){\n    return arr.toString();  \n   }\n}\n\n//测试代码\nvar list = new ArrayList();\nlist.add('张三');\nlist.add('李四');\nlist.add('王五');\nlist.add('赵六');\nlist.add('王五');\nconsole.log(list.size());\nconsole.log(list.toString());\nconsole.log(list.contains('张三'));\nlist.remove('王五',true); //null,undefined,''\nconsole.log(list.toString());\nconsole.log(list.get(0));\nconsole.log(list.get(1));\nconsole.log(list.get(2));\nconsole.log(list.size());\n\nconsole.log(list.toArray());\nlist.add('张三');\nlist.add('张三');\nconsole.log(list.toArray());\nconsole.log(list.indexOf('张三'));\nconsole.log(list.indexOf('赵六'));\n\n```\n\n\n\n```\n作者：剽悍一小兔\n链接：https://www.jianshu.com/p/3c37e186d41c\n```\n"
  },
  {
    "path": "4.4.6 本地缓存与浏览器缓存区别.md",
    "content": "# 本地缓存与浏览器缓存区别\n\n* 本地缓存\n  - 为整个 web 应用程序服务，\n  - 只缓存指定的网页\n  - 可靠的，\n  - 控制对哪些内容缓存\n* 浏览器网页缓存\n  - 服务单个网页\n  - 不安全，不可靠\n  - 任何网页都具有缓存\n\n\n\n\n## 强缓存\n用户发送的请求，直接从客户端缓存中获取，不发送请求到服务器，不与服务器发生交互行为。\n\n不需要发送请求到服务端，直接读取浏览器本地缓存，在 Chrome 的 Network 中显示的 HTTP 状态码是 200 ，在 Chrome 中，\n\n强缓存又分为 Disk Cache (存放在硬盘中)和 Memory Cache (存放在内存中)，存放的位置是由浏览器控制的。\n\n是否强缓存由 Expires、Cache-Control 和 Pragma 3 个 Header 属性共同来控制。\n\n- Expires\nExpires 的值是一个 HTTP 日期，在浏览器发起请求时，会根据系统时间和 Expires 的值进行比较，如果系统时间超过了 Expires 的值，缓存失效。\n\n由于和系统时间进行比较，所以当系统时间和服务器时间不一致的时候，会有缓存有效期不准的问题。Expires 的优先级在三个 Header 属性中是最低的。\n\n- Cache-Control\n\nCache-Control 是 HTTP/1.1 中新增的属性，在请求头和响应头中都可以使用，常用的属性值如有：\n\n  - max-age：单位是秒，缓存时间计算的方式是距离发起的时间的秒数，超过间隔的秒数缓存失效\n  - no-cache：不使用强缓存，需要与服务器验证缓存是否新鲜\n  - no-store：禁止使用缓存（包括协商缓存），每次都向服务器请求最新的资源\n  - private：专用于个人的缓存，中间代理、CDN 等不能缓存此响应\n  - public：响应可以被中间代理、CDN 等缓存\n  - must-revalidate：在缓存过期前可以使用，过期后必须向服务器验证\n\n## 协商缓存\n用户发送的请求，发送到服务器后，由服务器判定是否从缓存中获取资源。\n\n两者共同点：客户端获得的数据最后都是从客户端缓存中获得。\n\n两者的区别：从名字就可以看出，强缓存不与服务器交互，而协商缓存则需要与服务器交互。\n\n\n>from memory cache代表使用内存中的缓存，from disk cache则代表使用的是硬盘中的缓存，浏览器读取缓存的顺序为memory –> disk。  \n在浏览器中，浏览器会在js和图片等文件解析执行后直接存入内存缓存中，  \n那么当刷新页面时只需直接从内存缓存中读取(from memory cache)；  \n而css文件则会存入硬盘文件中，所以每次渲染页面都需要从硬盘读取缓存(from disk cache)。\n\n>Etag是上一次加载资源时，服务器返回的response header，是对该资源的一种唯一标识，只要资源有变化，Etag就会重新生成。  \n浏览器在下一次加载资源向服务器发送请求时，会将上一次返回的Etag值放到request header里的If-None-Match里，  \n服务器只需要比较客户端传来的If-None-Match跟自己服务器上该资源的ETag是否一致，就能很好地判断资源相对客户端而言是否被修改过了\n\n\n- 两者之间对比：\n  - 首先在精确度上，Etag要优于Last-Modified。Last-Modified的时间单位是秒，如果某个文件在1秒内改变了多次，那么他们的Last-Modified其实并没有体现出来修改，但是Etag每次都会改变确保了精度；如果是负载均衡的服务器，各个服务器生成的Last-Modified也有可能不一致。\n  - 第二在性能上，Etag要逊于Last-Modified，毕竟Last-Modified只需要记录时间，而Etag需要服务器通过算法来计算出一个hash值。\n  - 第三在优先级上，服务器校验优先考虑Etag\n\n\n\n\n## 参考\n- [彻底理解浏览器缓存机制](https://www.cnblogs.com/shixiaomiao1122/p/7591556.html)\n- [深入理解浏览器的缓存机制](https://www.jianshu.com/p/54cc04190252)\n- [图解 HTTP 缓存](https://juejin.im/post/5eb7f811f265da7bbc7cc5bd)\n"
  },
  {
    "path": "4.4.7. 判断 js 中的数据类型的几种方法.md",
    "content": "# 判断js中的数据类型的几种方法\n\n\n判断js中的数据类型的几种方法\n\n判断js中的数据类型有一下几种方法：typeof、instanceof、 constructor、 prototype、 $.type()/jquery.type(),接下来主要比较一下这几种方法的异同。\n\n先举几个例子：\n```\nvar a = \"iamstring.\";\nvar b = 222;\nvar c= [1,2,3];\nvar d = new Date();\nvar e = function(){alert(111);};\nvar f = function(){this.name=\"22\";};\n```\n\n1、最常见的判断方法：typeof\n\n```\nalert(typeof a)   ------------> string\nalert(typeof b)   ------------> number\nalert(typeof c)   ------------> object\nalert(typeof d)   ------------> object\nalert(typeof e)   ------------> function\nalert(typeof f)   ------------> function\n\n其中typeof返回的类型都是字符串形式，需注意，例如：\nalert(typeof a == \"string\") -------------> true\nalert(typeof a == String) ---------------> false\n另外typeof 可以判断function的类型；在判断除Object类型的对象时比较方便。\n\n```\n\n2、判断已知对象类型的方法： instanceof\n\n```\nalert(c instanceof Array) ---------------> true\nalert(d instanceof Date) \nalert(f instanceof Function) ------------> true\nalert(f instanceof function) ------------> false\n\n注意：instanceof 后面一定要是对象类型，并且大小写不能错，该方法适合一些条件选择或分支。\n```\n\n3、根据对象的constructor判断： constructor\n```\nalert(c.constructor === Array) ----------> true\nalert(d.constructor === Date) -----------> true\nalert(e.constructor === Function) -------> true\n注意： constructor 在类继承时会出错\neg：\n      function A(){};\n      function B(){};\n      A.prototype = new B(); //A继承自B\n      var aObj = new A();\n      alert(aobj.constructor === B) -----------> true;\n      alert(aobj.constructor === A) -----------> false;\n而instanceof方法不会出现该问题，对象直接继承和间接继承的都会报true：\n      alert(aobj instanceof B) ----------------> true;\n      alert(aobj instanceof B) ----------------> true;\n言归正传，解决construtor的问题通常是让对象的constructor手动指向自己：\n      aobj.constructor = A; //将自己的类赋值给对象的constructor属性\n      alert(aobj.constructor === A) -----------> true;\n      alert(aobj.constructor === B) -----------> false; //基类不会报true了;\n\n```\n4、通用但很繁琐的方法： prototype\n```\nalert(Object.prototype.toString.call(a) === ‘[object String]’) -------> true;\nalert(Object.prototype.toString.call(b) === ‘[object Number]’) -------> true;\nalert(Object.prototype.toString.call(c) === ‘[object Array]’) -------> true;\nalert(Object.prototype.toString.call(d) === ‘[object Date]’) -------> true;\nalert(Object.prototype.toString.call(e) === ‘[object Function]’) -------> true;\nalert(Object.prototype.toString.call(f) === ‘[object Function]’) -------> true;\n\n大小写不能写错，比较麻烦，但胜在通用。\n```\n5、无敌万能的方法：jquery.type()\n```\n如果对象是undefined或null，则返回相应的“undefined”或“null”。\n\njQuery.type( undefined ) === \"undefined\"\njQuery.type() === \"undefined\"\njQuery.type( window.notDefined ) === \"undefined\"\njQuery.type( null ) === \"null\"\n\n如果对象有一个内部的[[Class]]和一个浏览器的内置对象的 [[Class]] 相同，我们返回相应的 [[Class]] 名字。 (有关此技术的更多细节。 )\n\njQuery.type( true ) === \"boolean\"\njQuery.type( 3 ) === \"number\"\njQuery.type( \"test\" ) === \"string\"\njQuery.type( function(){} ) === \"function\"\njQuery.type( [] ) === \"array\"\njQuery.type( new Date() ) === \"date\"\njQuery.type( new Error() ) === \"error\" // as of jQuery 1.9\njQuery.type( /test/ ) === \"regexp\"\n\n其他一切都将返回它的类型“object”。\n通常情况下用typeof 判断就可以了，遇到预知Object类型的情况可以选用instanceof或constructor方法,实在没辙就使用$.type()方法。\n```\n"
  },
  {
    "path": "4.4.8 获取字符串长度（汉字算两个字符，字母数字算一个.md",
    "content": "* 获取字符串长度（汉字算两个字符，字母数字算一个\n\n```\n//获取字符串长度（汉字算两个字符，字母数字算一个）\n    function getByteLen(val) {\n      var len = 0;\n      for (var i = 0; i < val.length; i++) {\n        var a = val.charAt(i);\n        if (a.match(/[^\\x00-\\xff]/ig) != null) {\n          len += 2;\n        }\n        else {\n          len += 1;\n        }\n      }\n      return len;\n    }\n    \n    \n    \n  vue demo \n  // 输入\n      toInput: function(value) {\n        var that = this;\n        console.log(value,that.floorName)\n        that.inputLength = 4;\n        var l = strLength.getByteLen(that.floorName);\n        console.log(l)\n        if (l <= 8) {\n          that.inputLength = that.floorName.length\n          console.log(\"len:\",that.inputLength)\n\n        }\n        that.floorName = value.target.value.substr(0,that.inputLength)\n\n        console.log(that.floorName)\n\n      },\n```\n\n### 截取制定长度的字符串（一个汉字算两个字符，英文或字母算一个）\n```\nfunction func(s, n) {\n　　　　return s.slice(0, n).replace(/([^x00-xff])/g, \"$1a\").slice(0, n).replace(/([^x00-xff])a/g, \"$1\");\n}\n\nfunc('我的jdfhjfdsgf', 5)\n// 有点问题\n\n----------------------------------\n测试可用：\nfunction getByteVal(val, max) {\n    var returnValue = '';\n    var byteValLen = 0;\n    for (var i = 0; i < val.length; i++) {\n        if (val[i].match(/[^\\x00-\\xff]/ig) != null)\n        byteValLen += 2;\n        else\n        byteValLen += 1;\n        if (byteValLen > max)\n        break;\n        returnValue += val[i];\n    }\n    return returnValue;\n}\n\ngetByteVal('我的合肥决定是否看见',10)\n\n```\n\n\n### 截取字符串（一个字算一个，两个英文字母或数字算一个字）\n\n```\nfunction getByteVal(val, max) {\n    var returnValue = '';\n    var byteValLen = 0;\n    for (var i = 0; i < val.length; i++) {\n        if (val[i].match(/[^\\x00-\\xff]/ig) != null)\n        byteValLen += 1;\n        else\n        byteValLen += 0.5;\n        if (byteValLen > max)\n        break;\n        returnValue += val[i];\n    }\n    return returnValue;\n}\nundefined\ngetByteVal('我的合肥决定是否看见',10)\n//\"我的合肥决定是否看见\"\n```\n\n###  判断是否为纯汉字\n```\nfunction load(str){  \n    var regex =/^[\\u4E00-\\u9FA5]+$/;  \n    if(!regex.test(str)){  \n    return false;  \n    }else{  \n    return true;  \n    }  \n   }  \n```\n\n### 判断字符串是否为数字和字母的组合\n\n```\nfunction checkRate(nubmer)  \n{  \n     var re =  /^[0-9a-zA-Z]*$/g;  //判断字符串是否为数字和字母组合     //判断正整数 /^[1-9]+[0-9]*]*$/    \n     if (!re.test(nubmer))  \n    {  \n        return false;  \n     }else{  \n    return true;  \n     }  \n}  \n```\n\n\n### 两个字母数字算一个字，一个字算一个字\n\n```\nfunction getByteLen(val) {\n      var len = 0;\n      for (var i = 0; i < val.length; i++) {\n        var a = val.charAt(i);\n        if (a.match(/[^\\x00-\\xff]/ig) != null) {\n          len += 1;\n        }\n        else {\n          len += 0.5;\n        }\n      }\n      return Math.ceil(len);\n    }\n```\n\n\n## vue 中用到的\n\n```\n\n\n/*\n * 判断字符长度\n * @param: str\n */\n\nexport default {\n  /*\n  * 一个汉字算两个字符,一个英文字母或数字算一个字符\n  */\n getByteLen: function(val) {\n      var len = 0;\n      for (var i = 0; i < val.length; i++) {\n        var a = val.charAt(i);\n        if (a.match(/[^\\x00-\\xff]/ig) != null) {\n          len += 2;\n        }\n        else {\n          len += 1;\n        }\n      }\n      return len;\n  },\n   /*\n  * 一个汉字算一个字,一个英文字母或数字算半个字\n  */\n  getZhLen: function (val) {\n      var len = 0;\n      for (var i = 0; i < val.length; i++) {\n        var a = val.charAt(i);\n        if (a.match(/[^\\x00-\\xff]/ig) != null) {\n          len += 1;\n        }\n        else {\n          len += 0.5;\n        }\n      }\n      return Math.ceil(len);\n  },\n\n  /*暂无用*/\n  cutStr: function(str, len,type){\n    var char_length = 0;\n    for (var i = 0; i < str.length; i++){\n        var son_str = str.charAt(i);\n        if(type==1) {\n          encodeURI(son_str).length > 2 ? char_length += 1 : char_length += 0.5;\n        }\n        if(type==2) {\n           char_length += 1 ;\n        }\n        if (char_length >= len){\n            var sub_len = char_length == len ? i+1 : i;\n            return str.substr(0, sub_len);\n\n        }\n    }\n  },\n\n  /*\n  * 限制字数用, 一个汉字算一个字,两个英文/字母算一个字\n  */\n  getByteVal: function(val, max) {\n    var returnValue = '';\n    var byteValLen = 0;\n    for (var i = 0; i < val.length; i++) {\n        if (val[i].match(/[^\\x00-\\xff]/ig) != null)\n        byteValLen += 1;\n        else\n        byteValLen += 0.5;\n        if (byteValLen > max)\n        break;\n        returnValue += val[i];\n    }\n    return returnValue;\n  },\n\n  /*\n  * 限制字符数用, 一个汉字算两个字符,一个英文/字母算一个字符\n  */\n  getCharVal: function (val, max) {\n    var returnValue = '';\n    var byteValLen = 0;\n    for (var i = 0; i < val.length; i++) {\n        if (val[i].match(/[^\\x00-\\xff]/ig) != null)\n        byteValLen += 2;\n        else\n        byteValLen += 1;\n        if (byteValLen > max)\n        break;\n        returnValue += val[i];\n    }\n    return returnValue;\n  },\n\n  /*\n  * 正则校验,校验非负数字\n  */\n  regPos: function(v) {\n    var regTest = /^\\d+(\\.\\d+)?$/;\n    return regTest.test(v);\n  }\n}\n```\n\n## 其他\n\n```\n匹配中文字符的正则表达式：\n\n[\\u4e00-\\u9fa5]匹配双字节字符(包括汉字在内)：\n\n[^\\x00-\\xff]应用：计算字符串的长度（一个双字节字符长度计2，ASCII字符计1）\n\nString.prototype.len=function(){ return this.replace([^\\x00-\\xff]/g,\"aa\").length; }匹配空行的正则表达式：\n\n\\n[\\s|]*\\r匹配HTML标记的正则表达式：\n\n/<(.*)>.*<\\/\\1>|<(.*) \\/>/匹配首尾空格的正则表达式：\n\n(^\\s*)|(\\s*$)应用：j avascript中没有像v bscript那样的trim函数，我们就可以利用这个表达式来实现，如下：\n\nString.prototype.trim = function(){ return this.replace(/(^\\s*)|(\\s*$)/g, \"\");}利用正则表达式分解和转换IP地址下面是利用正则表达式匹配IP地址，并将IP地址转换成对应数值的Javascript程序：\n\nfunction IP2V(ip){ re=/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)/g  //匹配IP地址的正则表达式 if(re.test(ip)) { return RegExp.$1*Math.pow(255,3))+RegExp.$2*Math.pow(255,2))+RegExp.$3*255+RegExp.$4*1 } else { throw new Error(\"Not a valid IP address!\") }}不过上面的程序如果不用正则表达式，而直接用split函数来分解可能更简单，程序如下：\n\nvar ip=\"10.100.20.168\"ip=ip.split(\".\")alert(\"IP值是：\"+(ip[0]*255*255*255+ip[1]*255*255+ip[2]*255+ip[3]*1))匹配Email地址的正则表达式：\n\n\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*匹配网址URL的正则表达式\n\n\n用正则表达式限制只能输入中文：\n\nonkeyup=\"value=value.replace(/[^\\u4E00-\\u9FA5]/g,'')\" onbeforepaste=\"clipboardData.setData('text',clipboardData.getData('text').replace(/[^\\u4E00-\\u9FA5]/g,''))\"用正则表达式限制只能输入全角字符：\n\nonkeyup=\"value=value.replace(/[^\\uFF00-\\uFFFF]/g,'')\" onbeforepaste=\"clipboardData.setData('text',clipboardData.getData('text').replace(/[^\\uFF00-\\uFFFF]/g,''))\"用正则表达式限制只能输入数字：\n\nonkeyup=\"value=value.replace(/[^\\d]/g,'') \"onbeforepaste=\"clipboardData.setData('text',clipboardData.getData('text').replace(/[^\\d]/g,''))\"用正则表达式限制只能输入数字和英文：\n\nonkeyup=\"value=value.replace(/[\\W]/g,'') \"onbeforepaste=\"clipboardData.setData('text',clipboardData.getData('text').replace(/[^\\d]/g,''))\"匹配非负整数（正整数 + 0）\n\n^\\d+$匹配正整数\n\n^[0-9]*[1-9][0-9]*$匹配非正整数（负整数 + 0）\n\n^((-\\d+)|(0+))$匹配负整数\n\n^-[0-9]*[1-9][0-9]*$匹配整数\n\n^-?\\d+$匹配非负浮点数（正浮点数 + 0）\n\n^\\d+(\\.\\d+)?$匹配正浮点数\n\n^(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*))$匹配非正浮点数（负浮点数 + 0）\n\n^((-\\d+(\\.\\d+)?)|(0+(\\.0+)?))$匹配负浮点数\n\n^(-(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*)))$匹配浮点数\n\n^(-?\\d+)(\\.\\d+)?$匹配由26个英文字母组成的字符串\n\n^[A-Za-z]+$匹配由26个英文字母的大写组成的字符串\n\n^[A-Z]+$匹配由26个英文字母的小写组成的字符串\n\n^[a-z]+$匹配由数字和26个英文字母组成的字符串\n\n^[A-Za-z0-9]+$匹配由数字、26个英文字母或者下划线组成的字符串\n\n^\\w+$匹配email地址\n\n^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$匹配url\n\n^[a-zA-z]+://匹配(\\w+(-\\w+)*)(\\.(\\w+(-\\w+)*))*(\\?\\S*)?$匹配html tag\n```\n"
  },
  {
    "path": "4.4.9-函数式编程.md",
    "content": "# 函数式编程\n\n- 纯函数（Pure functions）\n- 函数复合（Function composition）\n- 避免共享状态（Avoid shared state）\n- 避免改变状态（Avoid mutating state）\n- 避免副作用（Avoid side effects）\n\n* 函数式编程初探: http://www.ruanyifeng.com/blog/2012/04/functional_programming.html\n* 使用 JavaScript 进行函数式编程 : http://www.codeceo.com/javascript-functional-programming-1.html\n\n```\n函数式编程要求使用函数，我们可以把运算过程定义为不同的函数，然后写成下面这样：\n\n　　var result = subtract(multiply(add(1,2), 3), 4);\n\n这就是函数式编程。\n```\n\n\n### 下面是写出正确函数的几个原则：\n\n- （1）直接把函数赋值给变量\n\n记住：凡是使用return返回函数调用的，都可以去掉这个间接包裹层，最终连参数和括号一起去掉！\n\n以下代码都来自 npm 上的模块包：\n```\n// 太傻了\nvar getServerStuff = function(callback){\n  return ajaxCall(function(json){\n    return callback(json);\n  });\n};\n\n// 这才像样\nvar getServerStuff = ajaxCall;\n世界上到处都充斥着这样的垃圾 ajax 代码。以下是上述两种写法等价的原因：\n\n// 这行\nreturn ajaxCall(function(json){\n  return callback(json);\n});\n\n// 等价于这行\nreturn ajaxCall(callback);\n\n// 那么，重构下 getServerStuff\nvar getServerStuff = function(callback){\n  return ajaxCall(callback);\n};\n\n// ...就等于\nvar getServerStuff = ajaxCall; // <-- 看，没有括号哦\n```\n- （2）使用最普适的方式命名\n\n函数属于操作，命名最好简单直白体现功能性，比如add等。参数是数据，最好不要限定在特定的数据上，比如articles，就能让写出来的函数更加通用，避免重复造轮子。例如：\n\n```\n// 只针对当前的博客\nvar validArticles = function(articles) {\n  return articles.filter(function(article){\n    return article !== null && article !== undefined;\n  });\n};\n\n// 对未来的项目友好太多\nvar compact = function(xs) {\n  return xs.filter(function(x) {\n    return x !== null && x !== undefined;\n  });\n};\n```\n\n- （3）避免依赖外部变量\n\n不依赖外部变量和环境，就能确保写出的函数是纯函数，即：相同输入得到相同输出的函数。 比如 slice 和 splice，这两个函数的作用一样，但方式不同， slice 符合纯函数的定义是因为对相同的输入它保证能返回相同的输出。而 splice 却会嚼烂调用它的那个数组，然后再吐出来，即这个数组永久地改变了。\n\n再如：\n\n```\n// 不纯的\nvar minimum = 21;\n\nvar checkAge = function(age) {\n  return age >= minimum;\n};\n\n// 纯的\nvar checkAge = function(age) {\n  var minimum = 21;\n  return age >= minimum;\n};\n```\n\n\n## 一道著名的坑人题，可能一些同学见过：\n\n```JavaScript\n\n['2', '3', '4'].map(parseInt);\n```\n\n说出上面代码的执行结果。  \n如果不过脑子的说是 [2, 3, 4]，那么肯定是错了。实际上，真正的执行结果是 [2, NaN, NaN]。\n\n为什么会这样呢？是因为 map 的算子是有两个参数的，第一个参数是被迭代数组的元素，第二个参数是该元素的下标。  \n所以 ['2', '3', '4'].map(parseInt) 实际上相当于执行了 [parseInt('2', 0), parseInt('3', 1), parseInt('4', 2)]，  \n结果就变成了 [2, NaN, NaN] 了。\n\n今天我们在这里主要不是讨论为什么 parseInt('2', 0) 是 2，  \n而 parseInt('3', 1) 是 NaN，实际上我们期望的结果应该是 parseInt('2', 10) 和  \nparseInt('3', 10)，把字符串 ‘2’、’3′ 转成十进制 number。\n\n所以，正确的写法应该是：\n\n```JavaScript\n\n['2', '3', '4'].map(num => parseInt(num, 10));\n```\n\n\n## 副作用（Side Effects）\n副作用是指除了函数返回值以外，任何在函数调用之外观察到的应用程序状态改变。副作用包括：\n\n```\n改变了任何外部变量或对象属性（例如，全局变量，或者一个在父级函数作用域链上的变量）\n写日志\n在屏幕输出\n写文件\n发网络请求\n触发任何外部进程\n调用另一个有副作用的函数\n```\n\n\n## 函数式编程偏好：\n\n```\n使用纯函数而不是使用共享状态和副作用\n让可变数据成为不可变的\n用函数复合替代命令控制流\n使用高阶函数来操作许多数据类型，创建通用、可复用功能取代只是操作集中的数据的方法。\n使用声明式而不是命令式代码（关注做什么，而不是如何做）\n使用表达式替代语句\n使用容器与高阶函数替代多态\n```\n\n## 参考\n- [从一道坑人的面试题说函数式编程](http://web.jobbole.com/90916/)\n- [征服 JavaScript 面试：什么是函数式编程](http://web.jobbole.com/90116/)\n"
  },
  {
    "path": "4.5.0 易混淆的判断.md",
    "content": "# 4.5.0 易混淆的判断\n\n\n\n```\n# 空值\n\n'' == false          ---> true\n'' == 0              ---> true\n/^\\d*$/.test('')     ---> true\n\n\n'' === false         ---> false\n\n0.1 + 0.2 == 0.3     ---> false\n0.1 + 0.2 - 0.3 == 0 ---> false \n\n\nnull == undefined    ---> true\n\n```\n"
  },
  {
    "path": "4.5.1 JavaScript 内存机制（堆栈池）.md",
    "content": "# JavaScript 内存机制\n\n### #内存模型\n\nJS内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)。 其中栈存放变量，堆存放复杂对象，池存放常量。\n\n\n为了更好的搞懂栈内存与堆内存，我们可以结合以下例子与图解进行理解。\n```\nvar a1 = 0; // 栈 \nvar a2 = 'this is string'; // 栈\nvar a3 = null; // 栈 \nvar b = { m: 20 }; // 变量b存在于栈中，{m: 20} 作为对象存在于堆内存中\nvar c = [1, 2, 3]; // 变量c存在于栈中，[1, 2, 3] 作为对象存在于堆内存中\n\n作者：梁音\n链接：https://juejin.im/post/5b10ba336fb9a01e66164346\n```\n\n### #内存的生命周期\n\nJS环境中分配的内存一般有如下生命周期：\n\n- 内存分配：当我们申明变量、函数、对象的时候，系统会自动为他 们分配内存\n- 内存使用：即读写内存，也就是使用变量、函数等\n- 内存回收：使用完毕，由垃圾回收机制自动回收不再使用的内存\n\n为了便于理解，我们使用一个简单的例子来解释这个周期。\n```\nvar a = 20;  // 在内存中给数值变量分配空间\nalert(a + 100);  // 使用内存\nvar a = null; // 使用完毕之后，释放内存空间\n```\n\n### #内存回收\n\n\n### #垃圾回收算法\n\n- 引用计数算法\n  - 引用计数算法定义“内存不再使用”的标准很简单，就是看一个对象是否有指向它的引用。如果没有其他对象指向它了，说明该对象已经不再需了。\n- 标记清除算法\n  - 现代的浏览器已经不再使用引用计数算法了。现代浏览器通用的大多是基于标记清除算法的某些改进算法，总体思想都是一致的\n  - 标记清除算法将“不再使用的对象”定义为“无法达到的对象”。简单来说，就是从根部（在JS中就是全局对象）出发定时扫描内存中的对象。\n  - 凡是能从根部到达的对象，都是还需要使用的。那些无法由根部出发触及到的对象被标记为不再使用，稍后进行回收。\n\n\n\n### #内存泄露\n\n- 不再用到的内存，没有及时释放，就叫做内存泄漏（memory leak）。\n\n#### 内存泄漏的识别方法\n- 浏览器方法\n\n打开开发者工具，选择 Timeline 面板  \n在顶部的Capture字段里面勾选 Memory  \n点击左上角的录制按钮。  \n在页面上进行各种操作，模拟用户的使用情况。  \n一段时间后，点击对话框的 stop 按钮，面板上就会显示这段时间的内存占用情况。  \n\n如果内存占用基本平稳，接近水平，就说明不存在内存泄漏。  \n反之，就是内存泄漏了。  \n\n- 命令行方法\n\n命令行可以使用 Node 提供的 process.memoryUsage 方法。\n```\nconsole.log(process.memoryUsage());\n// { rss: 27709440,\n//  heapTotal: 5685248,\n//  heapUsed: 3449392,\n//  external: 8772 }\n```\n\nprocess.memoryUsage返回一个对象，包含了 Node 进程的内存占用信息。该对象包含四个字段，单位是字节，含义如下。\n\n```\nResident Set(常驻内存)\n\nCode Segment(代码区)\n\nStack(Local Variables, Pointers)\n\nHeap(Objects, Closures)\n\nUsed Heap\n\n\nrss（resident set size）：所有内存占用，包括指令区和堆栈。\nheapTotal：\"堆\"占用的内存，包括用到的和没用到的。\nheapUsed：用到的堆的部分。\nexternal： V8 引擎内部的 C++ 对象占用的内存。\n```\n判断内存泄漏，以heapUsed字段为准。\n\n\n## 哪些场景会造成内存泄漏\n\n\n- 意外的全局变量\n- 遗忘的定时器\n- 使用不当的闭包\n- 遗漏的 DOM 元素\n- 网络回调\n\n> 检测网页脚本是否有内存泄露的工具，原理是自动打开网页，依次点击每个链接，然后按一下浏览器的\"后退\"按钮，不断重复这个过程，看内存占用是否有变化。\n- https://nolanlawson.com/2021/12/17/introducing-fuite-a-tool-for-finding-memory-leaks-in-web-apps/\n\n\n## #参考资料\n\n- http://www.ruanyifeng.com/blog/2017/04/memory-leak.html\n- https://juejin.im/post/5b10ba336fb9a01e66164346\n- https://www.jianshu.com/p/84a8fd5fa0ee\n- https://www.cnblogs.com/dasusu/p/12200176.html\n"
  },
  {
    "path": "4.5.2 forEach和map和for方法的区别.md",
    "content": "## forEach和map和for方法的区别\n\nJS中的forEach、$.each、map方法\n\n \n- forEach 是 ECMA5 中 Array 新方法中最基本的一个，就是遍历，循环。例如下面这个例子：\n\n[1, 2 ,3, 4].forEach(alert);\n\n等同于下面这个for循环\n\n```\nvar array = [1, 2, 3, 4];\nfor (var k = 0, length = array.length; k < length; k++) {\n alert(array[k]);\n}\n```\nArray在ES5新增的方法中，参数都是function类型，默认有传参，forEach方法中的function回调支持3个参数，第1个是遍历的数组内容；第2个是对应的数组索引，第3个是数组本身。\n\n```\n[].forEach(function(value, index, array) {\n  // ...\n});\n\n```\n对比jQuery中的$.each方法：\n\n```\n$.each([], function(index, value, array) {\n  // ...\n});\n```\n\n会发现，第1个和第2个参数正好是相反的，大家要注意了，不要记错了。后面类似的方法，例如$.map也是如此。\n\n```\nvar data=[1,3,4] ; \nvar sum=0 ;\ndata.forEach(function(val,index,arr){\n  console.log(arr[index]==val);  // ==> true\n  sum+=val            \n})\nconsole.log(sum);          // ==> 8\n\n```\n\n##  foreach跳出循环\n\n- 1.不能使用 break; break 使用于 for 循环；\n- 2.可以通过 抛出异常来跳出循环；\n\n### #map\n\n这里的map不是“地图”的意思，而是指“映射”。[].map(); 基本用法跟forEach方法类似：\n```\narray.map(callback,[ thisObject]);\ncallback的参数也类似：\n\n[].map(function(value, index, array) {\n  // ...\n});\n\n```\n\nmap方法的作用不难理解，“映射”嘛，也就是原数组被“映射”成对应新数组。下面这个例子是数值项求平方：\n\n```\nvar data=[1,3,4]\n \nvar Squares=data.map(function(val,index,arr){\n  console.log(arr[index]==val);  // ==> true\n  return val*val           \n})\nconsole.log(Squares);        // ==> [1, 9, 16]\n注意：由于forEach、map都是ECMA5新增数组的方法，所以ie9以下的浏览器还不支持（万恶的IE啊），不过呢，可以从Array原型扩展可以实现以上全部功能，例如forEach方法:\n\n\nif (typeof Array.prototype.forEach != \"function\") {\n  Array.prototype.forEach = function() {\n    /* 实现 */\n  };\n}\n```\n\n\n\n \n\n \n\n## #总结：\n\n1、map速度比foreach快\n\n2、map会返回一个新数组，不对原数组产生影响,foreach不会产生新数组，foreach返回undefined\n\n3、map因为返回数组所以可以链式操作，foreach不能\n\n4, map里可以用return ,而foreach里用return不起作用，foreach不能用break，会直接报错\n\n \n\n那么接下来，继续做分析，为什么更推荐用.map()，而不是.forEach()？\n\n首先，.map()要比.forEach()执行速度更快。虽然我也说过执行速度不是我们需要考虑的主要因素，但是他们都比for()要更好用，那肯定要选更优化的一个。\n\n第二，.forEach()的返回值并不是array。如果你想用函数式编程写个链式表达式来装个逼，.map()将会是你不二的选择。\n\n来看下面这个例子：\n```\nvar arr = [1, 2, 3];\n\nconsole.log(\n    arr.map(function(i){\n        return i+i;\n    })\n    //链式风格\n    .sort()\n);// [2,4,6]\n\nconsole.log(\n    arr.forEach(function(i){\n        return i+i;\n    })\n    //接不起来，断了\n    .sort()\n);//TypeError: Cannot read property 'sort' of undefined\n```\n\n最后，感谢大家耐心的阅读，排个序\n```\n.map() > .forEach() > for()\n```\n\n高级浏览器(包括ie9以上)支持map和forEach方法对数组循环遍历，用法基本相同，但有些区别必须知道，才能在项目中正确选择\n\n* 原理：\n\n高级浏览器支持forEach方法  \n语法：forEach和map都支持2个参数：一个是回调函数（item,index,list）和上下文；  \nforEach:用来遍历数组中的每一项；这个方法执行是没有返回值的，对原来数组也没有影响；  \n数组中有几项，那么传递进去的匿名回调函数就需要执行几次；  \n每一次执行匿名函数的时候，还给其传递了三个参数值：数组中的当前项item,当前项的索引index,原始数组input；  \n理论上这个方法是没有返回值的，仅仅是遍历数组中的每一项，不对原来数组进行修改；但是我们可以自己通过数组的索引来修改原来的数组；  \nforEach方法中的this是ary,匿名回调函数中的this默认是window；  \n \n```\nvar ary = [12,23,24,42,1];\nvar res = ary.forEach(function (item,index,input) {\n     input[index] = item*10;\n})\nconsole.log(res);//-->undefined;\n```\n\nmap:和forEach非常相似，都是用来遍历数组中的每一项值的，用来遍历数组中的每一项；  \n区别：map的回调函数中支持return返回值；return的是啥，相当于把数组中的这一项变为啥（并不影响原来的数组，  \n只是相当于把原数组克隆一份，把克隆的这一份的数组中的对应项改变了）；  \n不管是forEach还是map 都支持第二个参数值，第二个参数的意思是把匿名回调函数中的this进行修改。  \n\n```\nvar ary = [12,23,24,42,1];\nvar res = ary.map(function (item,index,input) {\n     return item*10;\n})\nconsole.log(res);//-->[120,230,240,420,10];\nconsole.log(ary);//-->[12,23,24,42,1]；\n```\n\n## #兼容写法：\n不管是forEach还是map在IE6-8下都不兼容（不兼容的情况下在Array.prototype上没有这两个方法）,那么需要我们自己封装一个都兼容的方法，代码如下：\n\n```\n/**\n* forEach遍历数组\n* @param callback [function] 回调函数；\n* @param context [object] 上下文；\n*/\nArray.prototype.myForEach = function myForEach(callback,context){\n    context = context || window;\n    if('forEach' in Array.prototpye) {\n        this.forEach(callback,context);\n        return;\n    }\n    //IE6-8下自己编写回调函数执行的逻辑for(var i = 0,len = this.length; i < len;i++) {\n        callback && callback.call(context,this[i],i,this);\n    }\n}\n/**\n* map遍历数组\n* @param callback [function] 回调函数；\n* @param context [object] 上下文；\n*/\nArray.prototype.myMap = function myMap(callback,context){\n    context = context || window;\n    if('map' in Array.prototype) {\n        return this.map(callback,context);\n    }\n    //IE6-8下自己编写回调函数执行的逻辑var newAry = [];\n    for(var i = 0,len = this.length; i < len;i++) {\n        if(typeof  callback === 'function') {\n            var val = callback.call(context,this[i],i,this);\n            newAry[newAry.length] = val;\n        }\n    }\n    return newAry;\n}\n```\n\n\n- forEach和map循环的区别：forEach没有返回值，即使你给出return也不管用，map会返回一个新数组给你，原数组不会发生改变\n- filter：返回一个新的对象数组，不会将原有的数组进行改变\n- some:用于检测数组中的元素是否满足条件\n- find：返回通过测试（函数内判断）的数组的第一个元素的值，为数组中的每个元素都调用一次函数执行。\n  -  find() 对于空数组，函数是不会执行的。\n  - find() 并没有改变数组的原始值。\n\n\n## 参考\n* https://www.cnblogs.com/chaoyuehedy/p/6905422.html\n"
  },
  {
    "path": "4.5.3 json 数组对象中键值重复判断.md",
    "content": "# 对象中键值重复判断\n\n```\nvar arr = [\n  {'type':'checkbox','name':'选项1'},\n  {'type':'checkbox','name':'选项1'},\n  {'type':'checkbox','name':'选项3'}\n]\n\n//判断 type 的值 和 name 值 是否有重复\n\nvar arrValues = arr.map(item=>item.name) // 返回对象中键值集合的数组\n\n// 判断数组中是否有重复值\nfunction isRepeat(arr){\n  var hash = {};\n  for(var i in arr) {\n    if(hash[arr[i]]){\n       return true;\n     }\n     hash[arr[i]] = true;\n  }\n  return false;\n}\n```\n"
  },
  {
    "path": "4.5.3.1 数组对象去重.md",
    "content": "# 数组对象去重\n\n\ndemo:\n\n```\nvar arr = [{\n    \"name\": \"ZYTX\",\n    \"age\": \"Y13xG_4wQnOWK1QwJLgg11d0pS4hewePU95UHtpMl3eE81uS74NC-6zu-Rtnw4Ix\",\n    \"gender\": \"AAAAAA.doc\"\n}, {\n    \"name\": \"ZYTA\",\n    \"age\": \"Y13xG_4wQnOWK1QwJLgg11d0pS4hewePU95UHtpMl3eE81uS74NC-6zu-Rtnw4Ix\",\n    \"gender\": \"BBBBBB.doc\"\n}, {\n    \"name\": \"ZDTX\",\n    \"age\": \"Y13xG_4wQnOWK1QwJLgg11d0pS4hewePU95UHtpMl3eE81uS74NC-6zu-Rtnw4Ix\",\n    \"gender\": \"CCCCCC.doc\"\n}, {\n    \"name\": \"ZYTX\",\n    \"age\": \"Y13xG_4wQnOWK1QwJLgg11d0pS4hewePU95UHtpMl3eE81uS74NC-6zu-Rtnw4Ix\",\n    \"gender\": \"AAAAAA.doc\"\n}];\nvar hash = {};\narr = arr.reduce(function(item, next) {\n    hash[next.name] ? '' : hash[next.name] = true && item.push(next);\n    return item\n}, [])\nconsole.log(arr);\n```\n\n\n- ES6的SetSet还是适合对基本类型的去重，如果Set中的每一项是对象的话，是不会去重的，即使有的对象一模一样\n\n\n```\nlet arr = new Set([\n    {id: 0, name: \"小明\"},\n    {id: 0, name: \"小明\"},\n    {id: 0, name: \"小明\"},\n    {id: 0, name: \"小明\"}      \n]);\nconsole.log([...arr]); //依旧是这4个对象\n\n\n\n[...new Set(n)];\n```\n"
  },
  {
    "path": "4.5.3.2 JS取出两个数组的不同或相同元素.md",
    "content": "# 4.5.3.2 JS取出两个数组的不同或相同元素\n\n\n```\ngetArrDifference(arr1, arr2) {  \n  return arr1.concat(arr2).filter(function(v, i, arr) {\n    return arr.indexOf(v) === arr.lastIndexOf(v);\n  });\n},\n```\n\n## 取出两个数组的相同元素\n\n```\ngetArrEqual(arr1, arr2) {\n   let newArr = [];\n   for (let i = 0; i < arr2.length; i++) {\n       for (let j = 0; j < arr1.length; j++) {\n           if(arr1[j] === arr2[i]){\n               newArr.push(arr1[j]);\n          }\n      }\n    }\n   return newArr;\n},\n```\n"
  },
  {
    "path": "4.5.3.3 表格table json数据行到列的转换.md",
    "content": "# 4.5.3.3 表格 table json数据行到列的转换\n\n\n```\n\n      name    role    sex    age  ...\n\ndate1\ndate2  \n...\n//           转换后\n// =========================================\n      date1   date2  ...\nname\nrole\nsex\nage    \n...  \n\n\n  \n  [{ id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, date: '2021.09.01' },\n  { id: 10002, name: 'Test2', role: 'Test', sex: 'Women', age: 22, date: '2021.09.02' },\n  { id: 10003, name: 'Test3', role: 'PM', sex: 'Man', age: 32, date: '2021.09.03' },\n  { id: 10004, name: 'Test4', role: 'Designer', sex: 'Women', age: 23, date: '2021.09.04' },]\n\n======>\n\n[\n   {key: 'name',  date1Value: 'Test1',date2Value: 'Test2',date3Value: 'Test3',date4Value: 'Test4',},\n   {key: 'role',  date1Value: 'Develop',date2Value: 'Test',date3Value: 'PM',date4Value: 'Designer'},\n   {key: 'sex', keyValue: 'Test3',...},\n   {key: 'age', keyValue: 'Test4',.....}\n  ]\n  \n```\n\n## 实现\n\n```\n\n\n\nlet arr = [{ id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, date: '2021.09.01' },\n  { id: 10002, name: 'Test2', role: 'Test', sex: 'Women', age: 22, date: '2021.09.02' },\n  { id: 10003, name: 'Test3', role: 'PM', sex: 'Man', age: 32, date: '2021.09.03' },\n  { id: 10004, name: 'Test4', role: 'Designer', sex: 'Women', age: 23, date: '2021.09.04' },]\n\nlet columnsData = arr.map(ele=>ele.date);\nlet rowsData = Object.keys(arr[0]).filter(ele=>ele!='id'&&ele!='date');\n\nlet newArr=[];\nrowsData.forEach((row,rowIndex) =>{\n   let obj = {};\n   let key = '';\n   obj['rowName'] = row;\n   columnsData.forEach((column,columnIndex ) =>{\n    key = column+row;\n    obj[key] = arr[columnIndex][row];\n   })\n   newArr.push(obj)\n})\n\n\nconsole.log(newArr):\n\n\n\n\n```\n\n![image](https://user-images.githubusercontent.com/17672815/133609807-e7430822-af63-4bf3-9ba4-539178d6b1ea.png)\n"
  },
  {
    "path": "4.5.4 时间戳日期转换.md",
    "content": "# 时间戳日期转换\n\n```\n/*\n * 时间日期转换\n * @param: \"10:00-22:00\"/ new Date()\n */\nexport default {\n  /*\n  * var storeBusinessTime=\"10:00-22:00\"  to\n  */\n  timeToDate: function(val) {\n    var date = new Date()\n    var y = date.getFullYear();\n    var m = date.getMonth() +1;\n    var day = date.getDate();\n    var d = [],newArr = [];\n    var dArr = val.split('-');\n    dArr.forEach(function(ele,index){\n      newArr.push(ele.split(':'))\n    })\n    d = [new Date(y,m,day,newArr[0][0],newArr[0][1]),new Date(y,m,day,newArr[1][0],newArr[1][1])]\n    return d;\n  },\n  dateToTime(val) {\n    console.log(val)\n    //  (0-9)年月数字的显示\n    function formatDig(num) {\n      return num > 9 ? '' + num : '0' + num;\n    }\n    var t;\n    var t1 = formatDig(new Date(val[0]).getHours())+':'+formatDig(new Date(val[0]).getMinutes())\n    var t2 = formatDig(new Date(val[1]).getHours())+':'+formatDig(new Date(val[1]).getMinutes())\n    t= t1+'-'+t2\n    return t;\n  },\n  /**\n   * 时间戳---> *天*小时*分\n   * @param timestamp\n   */\n  timeStampSpace(date) {\n    if (!date) {\n      return;\n    }\n    let date2 = new Date();\n    let date3 = new Date(Number(date)).getTime() - date2.getTime(); //时间差的毫秒数\n    console.log(date3);\n    if (date3 < 0) {\n      return '';\n    }\n    //计算出相差天数\n    let days = Math.floor(date3 / (24 * 3600 * 1000));\n    console.log(date3, days);\n    //计算出小时数\n    let leave1 = date3 % (24 * 3600 * 1000); //计算天数后剩余的毫秒数\n    let hours = Math.floor(leave1 / (3600 * 1000));\n    //计算相差分钟数\n    let leave2 = leave1 % (3600 * 1000); //计算小时数后剩余的毫秒数\n    let minutes = Math.floor(leave2 / (60 * 1000));\n    //计算相差秒数\n    // let leave3 = leave2 % (60 * 1000); //计算分钟数后剩余的毫秒数\n    // let seconds = Math.round(leave3 / 1000);\n    return `${days}天${hours}小时${minutes}分`;\n  }\n}\n```\n"
  },
  {
    "path": "4.5.5 常用JS方法.md",
    "content": "# 常用JS方法整理\n\n## 目录\n- [1.截取指定字节数的字符串](#1.截取指定字节数的字符串)\n- [2.判断是否微信](#2.判断是否微信)\n- [3.获取时间格式的几个举例](#3.获取时间格式的几个举例)\n- [4.获取字符串字节长度](#4.获取字符串字节长度)\n- [5.对象克隆、深拷贝](#5.对象克隆、深拷贝)\n- [6.组织结构代码证验证](#6.组织结构代码证验证)\n- [7.身份证号验证](#7.身份证号验证)\n- [8.js正则为url添加http标识](#8.js正则为url添加http标识)\n- [9.URL有效性校验方法](#9.URL有效性校验方法)\n- [10.自定义jsonp方法](#10.自定义jsonp方法)\n- [11.cookie操作](#11.cookie操作)\n- [12.生成随机字符串 (可指定长度)](#12.生成随机字符串 (可指定长度))\n- [13.浏览器判断](#13.浏览器判断])\n- [14.Rem移动端适配](#14.Rem移动端适配)\n- [15.获取url后参数](#15.获取url后参数)\n- [16.动态加载JS](#16.动态加载JS)\n- [17.生成随机颜色值](#17.生成随机颜色值)\n\n## 1.截取指定字节数的字符串\n```\n/**\n * 截取指定字节的字符串\n * @param str 要截取的字符穿\n * @param len 要截取的长度，根据字节计算\n * @param suffix 截取前len个后，其余的字符的替换字符，一般用“…”\n * @returns {*}\n */\nfunction cutString(str, len, suffix) {\n  if (!str) return \"\";\n  if (len <= 0) return \"\";\n  if (!suffix) suffix = \"\";\n  var templen = 0;\n  for (var i = 0; i < str.length; i++) {\n    if (str.charCodeAt(i) > 255) {\n      templen += 2;\n    } else {\n      templen++\n    }\n    if (templen == len) {\n      return str.substring(0, i + 1) + suffix;\n    } else if (templen > len) {\n      return str.substring(0, i) + suffix;\n    }\n  }\n  return str;\n}\n```\n## 2.判断是否微信\n```\n/**\n * 判断微信浏览器\n * @returns {Boolean}\n */\nfunction isWeiXin() {\n  var ua = window.navigator.userAgent.toLowerCase();\n  if (ua.match(/MicroMessenger/i) == 'micromessenger') {\n    return true;\n  } else {\n    return false;\n  }\n}\n```\n## 3.获取时间格式的几个举例\n```\nfunction getTimeFormat(time) {\n  var date = new Date(parseInt(time) * 1000);\n  var month, day, hour, min;\n  parseInt(date.getMonth()) + 1 < 10 ? month = '0' + (parseInt(date.getMonth()) + 1) : month = parseInt(date.getMonth()) + 1;\n  date.getDate() < 10 ? day = '0' + date.getDate() : day = date.getDate();\n  date.getHours() < 10 ? hour = '0' + date.getHours() : hour = date.getHours();\n  date.getMinutes() < 10 ? min = '0' + date.getMinutes() : min = date.getMinutes();\n  return [month, day].join('-') + '  ' + hour + ':' + min\n}\n\nfunction getTimeFormatYMD(time) {\n  var date = new Date(parseInt(time) * 1000);\n  var year, month, day;\n  year = date.getFullYear();\n  parseInt(date.getMonth()) + 1 < 10 ? month = '0' + (parseInt(date.getMonth()) + 1) : month = parseInt(date.getMonth()) + 1;\n  date.getDate() < 10 ? day = '0' + date.getDate() : day = date.getDate();\n  return [year, month, day].join('-')\n}\n\nfunction getTimeFormatAll(time) {\n  var date = new Date(parseInt(time) * 1000);\n  var year, month, day, hour, min, second;\n  year = date.getFullYear();\n  parseInt(date.getMonth()) + 1 < 10 ? month = '0' + (parseInt(date.getMonth()) + 1) : month = parseInt(date.getMonth()) + 1;\n  date.getDate() < 10 ? day = '0' + date.getDate() : day = date.getDate();\n  date.getHours() < 10 ? hour = '0' + date.getHours() : hour = date.getHours();\n  date.getMinutes() < 10 ? min = '0' + date.getMinutes() : min = date.getMinutes();\n  date.getSeconds() < 10 ? second = '0' + date.getSeconds() : second = date.getSeconds();\n\n  return [year, month, day].join('-') + '  ' + hour + ':' + min + ':' + second\n}\n```\n## 4.获取字符串字节长度\n```\n/**\n * 获取字符串字节长度\n * @param {String}\n * @returns {Boolean}\n */\nfunction checkLength(v) {\n  var realLength = 0;\n  var len = v.length;\n  for (var i = 0; i < len; i++) {\n    var charCode = v.charCodeAt(i);\n    if (charCode >= 0 && charCode <= 128) realLength += 1;\n    else realLength += 2;\n  }\n  return realLength;\n}\n```\n## 5.对象克隆、深拷贝\n```\n/**\n * 对象克隆&深拷贝\n * @param obj\n * @returns {{}}\n */\nfunction cloneObj(obj) {\n  var newO = {};\n  if (obj instanceof Array) {\n    newO = [];\n  }\n  for (var key in obj) {\n    var val = obj[key];\n    newO[key] = typeof val === 'object' ? arguments.callee(val) : val;\n  }\n  return newO;\n};\n\n克隆拷贝增强版\n/**\n * 对象克隆&深拷贝\n * @param obj\n * @returns {{}}\n */\nfunction clone(obj) {\n  // Handle the 3 simple types, and null or undefined\n  if (null == obj || \"object\" != typeof obj) return obj;\n  // Handle Date\n  if (obj instanceof Date) {\n    var copy = new Date();\n    copy.setTime(obj.getTime());\n    return copy;\n  }\n  // Handle Array\n  if (obj instanceof Array) {\n    var copy = [];\n    for (var i = 0,\n    len = obj.length; i < len; ++i) {\n      copy[i] = clone(obj[i]);\n    }\n    return copy;\n  }\n  // Handle Object\n  if (obj instanceof Object) {\n    var copy = {};\n    for (var attr in obj) {\n      if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);\n    }\n    return copy;\n  }\n  throw new Error(\"Unable to copy obj! Its type isn't supported.\");\n}\n测试用例：\nvar origin = {\n  a: \"text\",\n  b: null,\n  c: undefined,\n  e: {\n    f: [1, 2]\n  }\n}\n```\n## 6.组织结构代码证验证\n```\n验证规则：\n组织机构代码是每一个机关、社会团体、企事业单位在全国范围内唯一的、始终不变的法定代码标识。最新使用的组织机构代码在1997年颁布实施，由8位数字（或大写拉丁字母）本体代码和1位数字（或大写拉丁字母）校验码组成。本体代码采用系列（即分区段）顺序编码方法。校验码按下列公式计算：8 C9 = 11 - MOD(∑Ci * Wi，11)… (2) i = 1其中：MOD——表示求余函数；i——表示代码字符从左到右位置序号；Ci——表示第i位置上的代码字符的值，采用附录A“代码字符集”所列字符；C9——表示校验码；Wi——表示第i位置上的加权因子，其数值如下表：i 1 2 3 4 5 6 7 8 Wi 3 7 9 10 5 8 4 2当MOD函数值为1（即C9 = 10）时，校验码用字母X表示。\n验证方法：\ncheckOrgCodeValid: function(el) {\n  var txtval = el.value;\n  var values = txtval.split(\"-\");\n  var ws = [3, 7, 9, 10, 5, 8, 4, 2];\n  var str = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n  var reg = /^([0-9A-Z]){8}$/;\n  if (!reg.test(values[0])) {\n    return false\n  }\n  var sum = 0;\n  for (var i = 0; i < 8; i++) {\n    sum += str.indexOf(values[0].charAt(i)) * ws[i];\n  }\n  var C9 = 11 - (sum % 11);\n  var YC9 = values[1] + '';\n  if (C9 == 11) {\n    C9 = '0';\n  } else if (C9 == 10) {\n    C9 = 'X';\n  } else {\n    C9 = C9 + '';\n  }\n  return YC9 == C9;\n}\n```\n##7.身份证号验证\n```\n/**\n * 验证身份证号\n * @param el 号码输入input\n * @returns {boolean}\n */\nfunction checkCardNo(el) {\n  var txtval = el.value;\n  var reg = /(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)/;\n  return reg.test(txtval)\n}\n```\n## 8.js正则为url添加http标识\n```\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"renderer\" content=\"webkit\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\">  \n  <title></title>\n  <script>\n        var html = 'http:/ / www.google.com ';\n        html += '\\rwww.google.com ';\n        html += '\\rcode.google.com ';\n        html += '\\rhttp: //code.google.com/hosting/search?q=label%3aPython';\n        var regex = /(https?:\\/\\/)?(\\w+\\.?)+(\\/[a-zA-Z0-9\\?%=_\\-\\+\\/]+)?/gi;\n        alert('before replace:');\n        alert(html);\n        html = html.replace(regex,\n            function(match, capture) {\n                if (capture) {\n                    return match\n                } else {\n                    return 'http://' + match;\n                }\n            });\n        alert('after replace:');\n        alert(html); \n    </script>\n</head>\n<body>  \n</body>\n</html>\n\n```\n## 9.URL有效性校验方法\n```\n/**\n * URL有效性校验\n * @param str_url\n * @returns {boolean}\n */\nfunction isURL(str_url) { \n  // 验证url\n  var strRegex = \"^((https|http|ftp|rtsp|mms)?://)\" + \"?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?\" //           \n  ftp的user@ + \"(([0-9]{1,3}\\.){3}[0-9]{1,3}\" // IP形式的URL- 199.194.52.184\n  + \"|\" // 允许IP和DOMAIN（域名）\n  + \"([0-9a-z_!~*'()-]+\\.)*\" // 域名- www.\n  + \"([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\.\" // 二级域名\n  + \"[a-z]{2,6})\" // first level domain- .com or .museum\n  + \"(:[0-9]{1,4})?\" // 端口- :80\n  + \"((/?)|\" // a slash isn't required if there is no file name\n  + \"(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$\";\n  var re = new RegExp(strRegex);\n  return re.test(str_url);\n}\n// 建议的正则\nfunctionisURL(str) {\n  return !! str.match(/(((^https?:(?:\\/\\/)?)(?:[-;:&=\\+\\$,\\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\\+\\$,\\w]+@)[A-Za-z0-9.-]+)((?:\\/[\\+~%\\/.\\w-_]*)?\\??(?:[-\\+=&;%@.\\w_]*)#?(?:[\\w]*))?)$/g);\n}\n```\n## 10.自定义jsonp方法\n```\n/**\n * 自定义封装jsonp方法\n * @param options\n */\njsonp = function(options) {\n  options = options || {};\n  if (!options.url || !options.callback) {\n    throw new Error(\"参数不合法\");\n  }\n  //创建 script 标签并加入到页面中\n  var callbackName = ('jsonp_' + Math.random()).replace(\".\", \"\");\n  var oHead = document.getElementsByTagName('head')[0];\n  options.data[options.callback] = callbackName;\n  var params = formatParams(options.data);\n  var oS = document.createElement('script');\n  oHead.appendChild(oS);\n  //创建jsonp回调函数\n  window[callbackName] = function(json) {\n    oHead.removeChild(oS);\n    clearTimeout(oS.timer);\n    window[callbackName] = null;\n    options.success && options.success(json);\n  };\n  //发送请求\n  oS.src = options.url + '?' + params;\n  //超时处理\n  if (options.time) {\n    oS.timer = setTimeout(function() {\n      window[callbackName] = null;\n      oHead.removeChild(oS);\n      options.fail && options.fail({\n        message: \"超时\"\n      });\n    },\n    time);\n  }\n};\n/**\n * 格式化参数\n * @param data\n * @returns {string}\n */\nformatParams = function(data) {\n  var arr = [];\n  for (var name in data) {\n    arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));\n  }\n  return arr.join('&');\n}\n```\n## 11.cookie操作\n```\n//写cookies\nsetCookie = function(name, value, time) {\n  var strsec = getsec(time);\n  var exp = new Date();\n  exp.setTime(exp.getTime() + strsec * 1);\n  document.cookie = name + \"=\" + escape(value) + \";expires=\" + exp.toGMTString();\n}\n//cookie操作辅助函数\ngetsec = function(str) {\n  var str1 = str.substring(1, str.length) * 1;\n  var str2 = str.substring(0, 1);\n  if (str2 == \"s\") {\n    return str1 * 1000;\n  } else if (str2 == \"h\") {\n    return str1 * 60 * 60 * 1000;\n  } else if (str2 == \"d\") {\n    return str1 * 24 * 60 * 60 * 1000;\n  }\n}\n//读取cookies\ngetCookie = function(name) {\n  var arr, reg = new RegExp(\"(^| )\" + name + \"=([^;]*)(;|$)\");\n  if (arr = document.cookie.match(reg)) return (arr[2]);\n  else return null;\n}\n\n//删除cookies\ndelCookie = function(name) {\n  var exp = new Date();\n  exp.setTime(exp.getTime() - 1);\n  var cval = getCookie(name);\n  if (cval != null) document.cookie = name + \"=\" + cval + \";expires=\" + exp.toGMTString();\n}\n```\n## 12.生成随机字符串 (可指定长度)\n```\n/**\n * 生成随机字符串(可指定长度)\n * @param len\n * @returns {string}\n */\nrandomString = function(len) {\n  len = len || 8;\n  var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';\n  /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/\n  var maxPos = $chars.length;\n  var pwd = '';\n  for (var i = 0; i < len; i++) {\n    pwd += $chars.charAt(Math.floor(Math.random() * maxPos));\n  }\n  return pwd;\n}\n```\n## 13.浏览器判断\n```\nfunction parseUA() {\n  var u = navigator.userAgent;\n  var u2 = navigator.userAgent.toLowerCase();\n  return { //移动终端浏览器版本信息\n    trident: u.indexOf('Trident') > -1,\n    //IE内核\n    presto: u.indexOf('Presto') > -1,\n    //opera内核\n    webKit: u.indexOf('AppleWebKit') > -1,\n    //苹果、谷歌内核\n    gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1,\n    //火狐内核\n    mobile: !!u.match(/AppleWebKit.*Mobile.*/),\n    //是否为移动终端\n    ios: !!u.match(/\\(i[^;]+;( U;)? CPU.+Mac OS X/),\n    //ios终端\n    android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1,\n    //android终端或uc浏览器\n    iPhone: u.indexOf('iPhone') > -1,\n    //是否为iPhone或者QQHD浏览器\n    iPad: u.indexOf('iPad') > -1,\n    //是否iPad\n    webApp: u.indexOf('Safari') == -1,\n    //是否web应该程序，没有头部与底部\n    iosv: u.substr(u.indexOf('iPhone OS') + 9, 3),\n    weixin: u2.match(/MicroMessenger/i) == \"micromessenger\",\n    ali: u.indexOf('AliApp') > -1,\n  };\n}\nvar ua = parseUA();\nif (!ua.mobile) {\n  location.href = './pc.html';\n}\n\n```\n## 14.Rem移动端适配\n```\nvar rem = {\n  baseRem: 40,\n  // 基准字号，按照iphone6应该为20，此处扩大2倍，便于计算\n  baseWidth: 750,\n  // 基准尺寸宽，此处是按照ihpone6的尺寸\n  rootEle: document.getElementsByTagName(\"html\")[0],\n  initHandle: function() {\n    this.setRemHandle();\n    this.resizeHandle();\n  },\n  setRemHandle: function() {\n    var clientWidth = document.documentElement.clientWidth || document.body.clientWidth;\n    this.rootEle.style.fontSize = clientWidth * this.baseRem / this.baseWidth + \"px\";\n  },\n  resizeHandle: function() {\n    var that = this;\n    window.addEventListener(\"resize\",\n    function() {\n      setTimeout(function() {\n        that.setRemHandle();\n      },\n      300);\n    });\n  }\n};\nrem.initHandle();\n```\n\n## 15.获取url后参数\n```\nfunction GetRequest() {\n  var url = location.search; //获取url中\"?\"符后的字串\n  var theRequest = new Object();\n  if (url.indexOf(\"?\") != -1) {\n    var str = url.substr(1);\n    strs = str.split(\"&\");\n    for (var i = 0; i < strs.length; i++) {\n      theRequest[strs[i].split(\"=\")[0]] = (strs[i].split(\"=\")[1]);\n    }\n  }\n  return theRequest;\n}\n```\n## 16.动态加载JS\n```\nfunction loadScript(url, callback) {\n  var script = document.createElement(\"script\");\n  script.type = \"text/\";\n  if (typeof(callback) != \"undefined\") {\n    if (script.readyState) {\n      script.onreadystatechange = function() {\n        if (script.readyState == \"loaded\" || script.readyState == \"complete\") {\n          script.onreadystatechange = null;\n          callback();\n        }\n      };\n    } else {\n      script.onload = function() {\n        callback();\n      };\n    }\n  }\n  script.src = url;\n  document.body.appendChild(script);\n}\n```\n\n## 17.生成随机颜色值\n```\nfunction getRandomColor () {\n  const rgb = []\n  for (let i = 0 ; i < 3; ++i){\n    let color = Math.floor(Math.random() * 256).toString(16)\n    color = color.length == 1 ? '0' + color : color\n    rgb.push(color)\n  }\n  return '#' + rgb.join('')\n}\n\n```\n\n## 参考\n- https://juejin.im/post/5b62d02ee51d453467552dc9\n"
  },
  {
    "path": "4.5.6 setTimeout、setInterval被遗忘的第三个参数.md",
    "content": "# setTimeout、setInterval被遗忘的第三个参数\n\n第三个以后的参数是作为第一个func()的参数传进去。\n```\nvar timeoutID = scope.setTimeout(function[, delay,param1, ..., paramN]); \nvar timeoutID = scope.setTimeout(code[, delay]);\n```\ncode\n\n这是一个替代语法，你可以使用字符串代替function ，在delay毫秒之后执行字符串 (使用该语法是不推荐的, 原因和使用 eval()一样，有安全风险)。\n\nparam1, ..., paramN 可选\n\n附加参数，一旦定时器到期，它们会作为参数传递给function 或 执行字符串（setTimeout参数中的code）\n\n\n```\nfor(var i=0;i<5;i++){\nsetTimeout(\n console.log(i),1000  // 这种写法 和直接写 console.log(i) 执行结果一样，这时候没有异步，\n);\n}\n-----------------------------------\n\nfor(var i=0;i<5;i++){\nsetTimeout(\n 'console.log(i)',1000  // 变成字符串就是异步的\n);\n}\n\n\n```\n\n## \n\n## demo\n\n```\nasync function example() {\n  const r1 = await new Promise(resolve =>\n    setTimeout(resolve, 500, 'slowest')\n  )\n  const r2 = await new Promise(resolve =>\n    setTimeout(resolve, 200, 'slow')\n  )\n  return [r1, r2]\n}\n\nexample().then(result => console.log(result))\n```\n\n## 参考\n- https://www.cnblogs.com/leaf930814/p/6828588.html\n- MDN: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout\n"
  },
  {
    "path": "4.5.6.1 关于setTimeout和Promise执行顺序问题.md",
    "content": "# 关于setTimeout和Promise执行顺序问题\n\n执行顺序：Promise——>其后的.then()——>setTimeout（异步）\n\nsetTimeout函数用来指定某个函数或某段代码，在多少毫秒之后执行。它返回一个整数，表示定时器的编号，以后可以用来取消这个定时器。\n\n\nvar timerId = setTimeout(func|code, delay,hideparam)\n\n上面代码中，setTimeout函数接受两个参数，第一个参数func|code是将要推迟执行的函数名或者一段代码，第二个参数delay是推迟执行的毫秒数。\n\n第三个参数如果存在将作为 func 的参数\n\n\n- this 指向全局\n```\nvar a=1;\nvar obj={\na:2,\nb:function(){\nsetTimeout(function(){\nconsole.log(this.a);//这里返回的是：1；\n},2000);\n}\n};\nobj.b();\n```\n\n- 通过使用bind()方法来改变这个情况：\n\n```\nvar a=1;\nvar obj={\na:2,\nb:function(){\nsetTimeout(function(){\nconsole.log(this.a);//这里返回的是：2；\n}.bind(this),2000);//注意这行\n}\n};\nobj.b();\n```\n\n\n## 参考\n- [你所不知道的setTimeout](https://www.jeffjade.com/2016/01/10/2016-01-10-javacript-setTimeout/?utm_source=caibaojian.com)\n- [setTimeout和Promise执行顺序问题](http://www.cnblogs.com/sunmarvell/p/9564815.html)\n- [setTimeout与console.log、promise.then之间执行先后顺序](https://blog.csdn.net/judy_qiudie/article/details/82768243)\n"
  },
  {
    "path": "4.5.6.2JS 异步解决方案的发展历程以及优缺点.md",
    "content": "# 4.5.6.2JS 异步解决方案的发展历程以及优缺点\n\n## 1. 回调函数（callback）\n```\nsetTimeout(() => {\n    // callback 函数体\n}, 1000)\n```\n- 缺点：回调地狱，不能用 try catch 捕获错误，不能 return\n\n- 回调地狱的根本问题在于：\n  - 缺乏顺序性： 回调地狱导致的调试困难，和大脑的思维方式不符\n  - 嵌套函数存在耦合性，一旦有所改动，就会牵一发而动全身，即（控制反转）\n  - 嵌套函数过多的多话，很难处理错误\n```\najax('XXX1', () => {\n    // callback 函数体\n    ajax('XXX2', () => {\n        // callback 函数体\n        ajax('XXX3', () => {\n            // callback 函数体\n        })\n    })\n})\n```\n- 优点：解决了同步的问题（只要有一个任务耗时很长，后面的任务都必须排队等着，会拖延整个程序的执行。）\n\n## 2. Promise\nPromise就是为了解决callback的问题而产生的。\n\nPromise 实现了链式调用，也就是说每次 then 后返回的都是一个全新 Promise，如果我们在 then 中 return ，return 的结果会被 Promise.resolve() 包装\n\n- 优点：解决了回调地狱的问题\n```\najax('XXX1')\n  .then(res => {\n      // 操作逻辑\n      return ajax('XXX2')\n  }).then(res => {\n      // 操作逻辑\n      return ajax('XXX3')\n  }).then(res => {\n      // 操作逻辑\n  })\n```\n- 缺点：无法取消 Promise ，错误需要通过回调函数来捕获\n\n## 3. Generator\n>特点：可以控制函数的执行，可以配合 co 函数库使用\n```\nfunction *fetch() {\n    yield ajax('XXX1', () => {})\n    yield ajax('XXX2', () => {})\n    yield ajax('XXX3', () => {})\n}\nlet it = fetch()\nlet result1 = it.next()\nlet result2 = it.next()\nlet result3 = it.next()\n```\n\n## 4. Async/await\nasync、await 是异步的终极解决方案\n\n- 优点是：代码清晰，不用像 Promise 写一大堆 then 链，处理了回调地狱的问题\n\n- 缺点：await 将异步代码改造成同步代码，如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。\n```\nasync function test() {\n  // 以下代码没有依赖性的话，完全可以使用 Promise.all 的方式\n  // 如果有依赖性的话，其实就是解决回调地狱的例子了\n  await fetch('XXX1')\n  await fetch('XXX2')\n  await fetch('XXX3')\n}\n```\n\n下面来看一个使用 await 的例子：\n\n```\nlet a = 0\nlet b = async () => {\n  a = a + await 10\n  console.log('2', a) // -> '2' 10\n}\nb()\na++\nconsole.log('1', a) // -> '1' 1\n```\n对于以上代码你可能会有疑惑，让我来解释下原因\n\n- 首先函数 b 先执行，在执行到 await 10 之前变量 a 还是 0，因为 await 内部实现了 generator ，generator 会保留堆栈中东西，所以这时候 a = 0 被保存了下来\n- 因为 await 是异步操作，后来的表达式不返回 Promise 的话，就会包装成 Promise.reslove(返回值)，然后会去执行函数外的同步代码\n- 同步代码执行完毕后开始执行异步代码，将保存下来的值拿出来使用，这时候 a = 0 + 10\n\n上述解释中提到了 await 内部实现了 generator，其实 await 就是 generator 加上 Promise的语法糖，且内部实现了自动执行 generator。  \n如果你熟悉 co 的话，其实自己就可以实现这样的语法糖。\n\n\n## 参考\n- [ES6 --- JS异步编程的几种解决方法及其优缺点](https://www.cnblogs.com/rxqlx/p/10338604.html)\n- [谈谈JavaScript异步函数发展历程](https://www.jb51.net/article/72947.htm)\n- [JS 异步解决方案的发展历程以及优缺点](https://github.com/sisterAn/blog/issues/29)\n"
  },
  {
    "path": "4.5.7 暂时性死区.md",
    "content": "# 暂时性死区\n\n\n总之，在代码块内，使用let命令声明变量之前，该变量都是不可用的。这在语法上，称为“暂时性死区”（temporal dead zone，简称TDZ）\n\n\n```\nvar tmp = 123;\n\nif (true) {\n   tmp = 'abc'; // ReferenceError\n   let tmp;\n}\n\n\n代码中，存在全局变量tmp，但是块级作用域内let又声明了一个局部变量tmp，导致后者绑定这个块级作用域，所以在let声明变量前，对tmp赋值会报错\n```\n"
  },
  {
    "path": "4.5.8 理解JS的节流、防抖及使用场景.md",
    "content": "# 理解JS的节流、防抖及使用场景\n\n以下场景往往由于事件频繁被触发，因而频繁执行DOM操作、资源加载等重行为，导致UI停顿甚至浏览器崩溃。\n\n  - 1.window对象的resize、scroll事件\n\n  - 2.拖拽时的mousemove事件\n\n  - 3.射击游戏中的mousedown、keydown事件\n\n  - 4.文字输入、自动完成的keyup事件\n\n\n## 函数防抖(debounce)\n\n>在事件被触发n秒后再执行回调，如果在这n秒内又被触发，则重新计时。\n\n### demo\n```\n//模拟一段ajax请求\nfunction ajax(content) {\n  console.log('ajax request ' + content)\n}\n\nlet inputa = document.getElementById('unDebounce')\n\ninputa.addEventListener('keyup', function (e) {\n    ajax(e.target.value)\n})\n\n```\n### 优化\n\n```\n//模拟一段ajax请求\nfunction ajax(content) {\n  console.log('ajax request ' + content)\n}\n\nfunction debounce(fun, delay) {\n    return function (args) {\n        let that = this\n        let _args = args\n        clearTimeout(fun.id)\n        fun.id = setTimeout(function () {\n            fun.call(that, _args)\n        }, delay)\n    }\n}\n    \nlet inputb = document.getElementById('debounce')\n\nlet debounceAjax = debounce(ajax, 500)\n\ninputb.addEventListener('keyup', function (e) {\n        debounceAjax(e.target.value)\n    })\n\n\n可以看到，我们加入了防抖以后，当你在频繁的输入时，并不会发送请求，只有当你在指定间隔内没有输入时，才会执行函数。\n如果停止输入但是在指定间隔内又输入，会重新触发计时。\n```\n\n- demo-2\n```\nwindow.onscroll = function(){\n  //lazyload();\n  debounce(lazyload,window);\n};\nfunction debounce(method,context){\n  clearTimeout(method.timeout);\n  method.timeout = setTimeout(function(){\n    method.call(context);\n  },500);\n}\nfunction lazyload(){\n  console.log(\"scroll执行了\"+scrollnum);\n}\n\n\n链接：https://www.jianshu.com/p/4f3e2c8f5e95\n\n```\n\n## 函数节流(throttle)\n\n>规定在一个单位时间内，只能触发一次函数。如果这个单位时间内触发多次函数，只有一次生效。\n\n### demo\n```\nfunction throttle(fun, delay) {\n        let last, deferTimer\n        return function (args) {\n            let that = this\n            let _args = arguments\n            let now = +new Date()\n            if (last && now < last + delay) {\n                clearTimeout(deferTimer)\n                deferTimer = setTimeout(function () {\n                    last = now\n                    fun.apply(that, _args)\n                }, delay)\n            }else {\n                last = now\n                fun.apply(that,_args)\n            }\n        }\n    }\n\n    let throttleAjax = throttle(ajax, 1000)\n\n    let inputc = document.getElementById('throttle')\n    inputc.addEventListener('keyup', function(e) {\n        throttleAjax(e.target.value)\n    })\n    \n可以看到，我们在不断输入时，ajax会按照我们设定的时间，每1s执行一次。\n```\n\n不管我们设定的执行时间间隔多小，总是1s内只执行一次。\n\n- demo-2\n```\nfunction throttle2(method, delay, time) {\n  var timeout,startTime = new Date();\n  return function() {\n    var context = this,\n    args = arguments,\n    curTime = new Date();\n    clearTimeout(timeout);\n    // 如果达到了规定的触发时间间隔，触发 handler\n    if (curTime - startTime >= time) {\n      method.apply(context, args);\n      startTime = curTime;\n      // 没达到触发间隔，重新设定定时器\n    } else {\n      timeout = setTimeout(method, delay);\n    }\n};\n\n链接：https://www.jianshu.com/p/4f3e2c8f5e95\n\n```\n\n## sum\n- 函数防抖和函数节流都是防止某一时间频繁触发，但是这两兄弟之间的原理却不一样。\n- 函数防抖是某一段时间内只执行一次，而函数节流是间隔时间执行。\n\n## 结合应用场景\n\n- debounce: **事件被触发 n  秒后再执行回调**；\n\n  - search搜索联想，用户在不断输入值时，用防抖来节约请求资源。\n  - window触发resize的时候，不断的调整浏览器窗口大小会不断的触发这个事件，用防抖来让其只触发一次\n\n\n- throttle：**一个单位时间内只触发一次回调函数**\n\n  - 鼠标不断点击触发，mousedown(单位时间内只触发一次)\n  - 监听滚动事件，比如是否滑到底部自动加载更多，用throttle来判断\n\n\n\n## ES6 中使用\n\n```\n/ 防抖\nexport function _debounce(fn, delay) {\n\n    var delay = delay || 200;\n    var timer;\n    // console.log(fn)\n    return function () {\n        var that = this;\n        var args = arguments;\n        if (timer) {\n            clearTimeout(timer);\n        }\n        timer = setTimeout(function () {\n            timer = null;\n            fn.apply(that, args);\n        }, delay);\n    };\n}\n// 节流\nexport function _throttle(fn, interval) {\n    var last;\n    var timer;\n    var interval = interval || 200;\n    return function () {\n        var that = this;\n        var args = arguments;\n        var now = +new Date();\n        if (last && now - last < interval) {\n            clearTimeout(timer);\n            timer = setTimeout(function () {\n                last = now;\n                fn.apply(that, args);\n            }, interval);\n        } else {\n            last = now;\n            fn.apply(that, args);\n        }\n    }\n}\n\n/**\n * 手机号格式化\n * @param {String} phone\n */\nconst formatPhone = (phone) => {\n  phone = phone.toString();\n  console.log(phone)\n  return phone.substr(0, 3) + '****' + phone.substr(7, 11);\n};\n\n```\n\n\n## 参考\n- https://juejin.im/post/5b8de829f265da43623c4261\n- http://www.cnblogs.com/zichi/p/5331426.html\n- https://davidwalsh.name/javascript-debounce-function\n- https://css-tricks.com/the-difference-between-throttling-and-debouncing/\n- https://ict.ken.be/javascript-debounce-vs-throttle-function\n- https://stackoverflow.com/questions/25991367/difference-between-throttling-and-debouncing-a-function\n- https://remysharp.com/2010/07/21/throttling-function-calls\n- https://segmentfault.com/a/1190000008768202\n- http://www.alloyteam.com/2012/11/javascript-throttle/\n"
  },
  {
    "path": "4.5.9 统计字符串中特定字符包含的个数.md",
    "content": "# 统计字符串中特定字符包含的个数\n\n\n```\nfunction getPlaceholderCount(strSource) {\n  //统计字符串中特定字符包含的个数\n  var thisCount = 0;\n  strSource.replace(/4/g, function (m, i) {\n    //m为找到的{xx}元素、i为索引\n    console.log(m,i)\n    thisCount++;\n  });\n  return thisCount;\n}\n```\n"
  },
  {
    "path": "4.6.0 网站优化懒加载技术--lazyload.md",
    "content": "# 网站优化\n\n- 1.lazyload\n\n- 2.延迟加载（用setTimeout去做，有点不着调啊）\n\n- 3预加载（此方法适用于增加用户体验，但是更加对服务器有压力了）\n\n- 4.增加带宽，换好的服务器（你懂的，一般老板不到最后时刻都不会这样的）\n\n## 懒加载技术--lazyload\n\n图片懒加载在一些图片密集型的网站中运用比较多，通过图片懒加载可以让一些不可视的图片不去加载，\n\n避免一次性加载过多的图片导致请求阻塞（浏览器一般对同一域名下的并发请求的连接数有限制），\n\n这样就可以提高网站的加载速度，提高用户体验\n\n>看到最新的chrome已经原生实现了懒加载了吧，img里面加个lazyload就行\n\n\n## 参考\n- demo: https://github.com/VikiLee/LazyImage\n- https://juejin.im/post/5bbc60e8f265da0af609cd04\n"
  },
  {
    "path": "4.6.1 手写bind方法.md",
    "content": "# 手写bind方法\n\n## JavaScript 中 this 的四条绑定规则\n\n- 1.默认绑定\n\n独立函数调用时，this指向全局对象，如果使用严格模式，那么全局对象无法使用默认绑定，this绑定至undefined并抛错（TypeError: this is undefined）\n\n- 2.隐式绑定\n\n当函数作为引用属性被添加到对象中，隐式绑定规则会把函数调用中的this绑定到这个上下文对象\n\n- 3.显示绑定\n\n运用apply call 方法，在调用函数时候绑定this，也就是指定调用的函数的this值\n\n- 4.new绑定\n\n就是使用new操作符的时候的this绑定\n\n上述四条规则优先级由上到下依次递增。\n\n\n## bind有如下三个功能点：\n\n- 改变原函数的 this 指向，即绑定上下文，返回原函数的拷贝\n- 当绑定函数被调用时，bind的额外参数将置于实参之前传递给被绑定的方法。\n>注意，一个绑定函数也能使用new操作符创建对象,这种行为就像把原函数当成构造器，thisArg 参数无效。也就是 new 操作符修改 this 指向的优先级更高。\n\n## 实现一个bind\n- 输入：接受一个或者多个参数，第一个是要绑定的上下文，额外参数当作绑定函数的前置参数。\n- 输出：返回原函数的拷贝，即返回一个函数，这个函数呢具备原函数的功能\n\n\n\n```\n// 定义这个方法为myBind\nFunction.prototype.myBind = function(thisArg) {\n  if (typeof this !== 'function') {\n    return;\n  }\n  var _self = this;\n  var args = Array.prototype.slice.call(arguments, 1) //从第二个参数截取\n  return function() {\n    return _self.apply(thisArg, args.concat(Array.prototype.slice.call(arguments))); // 注意参数的处理\n  }\n}\n```\n\n- 测试一下：\n```\nfunction foo(name) {\nthis.name = name;\n}\n\nvar obj = {}\n\n//上下文 功能  done\nvar bar = foo.myBind(obj)\nbar('jack')\nconsole.log(obj.name) //'jack'\n\n// 参数 功能   done\nvar tar = foo.myBind(obj, 'rose');\ntar()\nconsole.log(obj.name) //'rose'\n// new 功能   error\nvar alice = new bar('alice')\nconsole.log(obj.name) //alice   obj name should be 'jack'\nconsole.log(alice.name) //undefined, alice name should be 'alice'\n```\n\n可以看到使用new实例化被绑定的方法，上下文还指向了传入的obj，这个方法有点问题，我们需要考虑到的是在myBind的实现里面，需要检测new的操作\n\n我们先考虑一下new操作符在调用构造函数时做了哪些操作？\n\n比如说 var a = new b()\n\n- 创建一个新的对象 newObj{}\n- 继承被实例化函数的原型 ：newObj.__proto__ = b.prototype\n- 将这个对象newObj绑定到构造函数b中的 this\n- 如果没有返回其他对象，new 操作符调用的函数则会返回这个对象newObj\n\n\n- 如下修改：\n```\nFunction.prototype.myBind = function(thisArg) {\n  if (typeof this !== 'function') {\n    return;\n  }\n  var _self = this;\n  var args = Array.prototype.slice.call(arguments, 1)\n  var fnBound = function () {\n    // 检测 New\n    // 如果当前函数的this指向的是构造函数中的this 则判定为new 操作\n    var _this = this instanceof _self ? this : thisArg;\n    return _self.apply(_this, args.concat(Array.prototype.slice.call(arguments)));\n  }\n  // 为了完成 new操作\n  // 还需要做一件事情 执行原型 链接 （思考题，为什么？\n  fnBound.prototype = this.prototype;\n  return fnBound;\n}\n```\n\n\n## 参考\n- [前端面试之手写一个bind方法](https://zhuanlan.zhihu.com/p/45992705)\n- [JavaScript 中 this 的四条绑定规则](https://juejin.im/entry/57dfa0d45bbb50005e6ddeb4)\n"
  },
  {
    "path": "4.6.2 localeCompare.md",
    "content": "# localeCompare\n\n>目前还有兼容性问题\n\n定义：用本地特定的顺序来比较两个字符串。\n\n语法：stringObject.localeCompare(target)\n\n参数：target——要以本地特定的顺序与 stringObject 进行比较的字符串。\n\n返回值：说明比较结果的数字。\n\n（1）如果 stringObject 小于 target，则 localeCompare() 返回小于 0 的数。\n\n（2）如果 stringObject 大于 target，则该方法返回大于 0 的数。\n\n（3）如果两个字符串相等，或根据本地排序规则没有区别，该方法返回 0。\n\n>说明：把 < 和 > 运算符应用到字符串时，它们只用字符的 Unicode 编码比较字符串，而不考虑当地的排序规则。以这种方法生成的顺序不一定是正确的。\nlocaleCompare() 方法提供的比较字符串的方法，考虑了默认的本地排序规则。\nECMAscript 标准并没有规定如何进行本地特定的比较操作，它只规定该函数采用底层操作系统提供的排序规则。\n\n## 参考\n- [JS排序：localeCompare() 方法实现中文排序、sort方法实现数字英文混合排序](https://www.cnblogs.com/goloving/p/7662676.html)\n"
  },
  {
    "path": "4.6.3 表单可以跨域吗.md",
    "content": "# 表单可以跨域吗\n\n>因为原页面用 form 提交到另一个域名之后，原页面的脚本无法获取新页面中的内容。\n所以浏览器认为这是安全的。\n而 AJAX 是可以读取响应内容的，因此浏览器不能允许你这样做。\n如果你细心的话你会发现，其实请求已经发送出去了，你只是拿不到响应而已。\n所以浏览器这个策略的本质是，一个域名的 JS ，在未经允许的情况下，不得读取另一个域名的内容。\n但浏览器并不阻止你向另一个域名发送请求。\n\n- 贺老:\nform 提交之后会更新页面，而假如更新到指定 iframe/window 中，如果是跨域的，父页面访问其内容一样受到 same origin 策略限制。\n\n\n- 其他\n利用form表单是可以进行跨域攻击的(本人亲测过)，\n但是适用范围很小，毕竟目前采用form进行交互的网站很少了，而且有些还对referer属性进行了判断。\n至于form表单可以跨域我认为这是个历史遗留问题，\n毕竟crsf攻击开始被重视的时候form表单提交这种方式早就有了，而正好xhr开始流行就是在那段时间\n\n\n## 参考\n- [为什么form表单提交没有跨域问题，但ajax提交有跨域问题？](https://www.zhihu.com/question/31592553)\n"
  },
  {
    "path": "4.6.3.1 前端跨域.md",
    "content": "# 4.6.3.1 前端跨域\n\n## 什么是同源策略？\n\n>同源策略/SOP（Same origin policy）是一种约定，由Netscape公司1995年引入浏览器，它是浏览器最核心也最基本的安全功能，如果缺少了同源策略，浏览器很容易受到XSS、CSFR等攻击。\n\n所谓同源是指 **\"协议+域名+端口\"** 三者相同，即便两个不同的域名指向同一个ip地址，也非同源。\n\n- 同源策略限制以下几种行为：\n\n  - 1.) Cookie、LocalStorage 和 IndexDB 无法读取\n  - 2.) DOM 和 Js对象无法获得\n  - 3.) AJAX 请求不能发送\n\n- 跨域解决方案\n  - 1、 通过jsonp跨域\n  - 2、 document.domain + iframe跨域\n  - 3、 location.hash + iframe\n  - 4、 window.name + iframe跨域\n  - 5、 postMessage跨域\n  - 6、 跨域资源共享（CORS）\n  - 7、 nginx代理跨域\n  - 8、 nodejs中间件代理跨域\n  - 9、 WebSocket协议跨域\n\n\n## 1.JSONP\n- 原理: 动态创建 script 标签\n\n- 缺点： 只能实现get一种请求\n\n- demo:\n```\n<script>\n    var script = document.createElement('script');\n    script.type = 'text/javascript';\n\n    // 传参一个回调函数名给后端，方便后端返回时执行这个在前端定义的回调函数\n    script.src = 'http://www.**.com/login?user=admin&callback=handleCallback';\n    document.head.appendChild(script);\n\n    // 回调执行函数\n    function handleCallback(res) {\n        alert(JSON.stringify(res));\n    }\n </script>\n \n# 服务端返回如下（返回时即执行全局函数）：\n\nhandleCallback({\"status\": true, \"user\": \"admin\"})\n\n\n# 2.）jquery ajax：\n\n$.ajax({\n    url: 'http://www.domain2.com:8080/login',\n    type: 'get',\n    dataType: 'jsonp',  // 请求方式为jsonp\n    jsonpCallback: \"handleCallback\",    // 自定义回调函数名\n    data: {}\n});\n\n\n# 3.）vue.js：\n\nthis.$http.jsonp('http://www.domain2.com:8080/login', {\n    params: {},\n    jsonp: 'handleCallback'\n}).then((res) => {\n    console.log(res); \n})\n\n\n# 后端node.js代码示例：\n\nvar querystring = require('querystring');\nvar http = require('http');\nvar server = http.createServer();\n\nserver.on('request', function(req, res) {\n    var params = qs.parse(req.url.split('?')[1]);\n    var fn = params.callback;\n\n    // jsonp返回设置\n    res.writeHead(200, { 'Content-Type': 'text/javascript' });\n    res.write(fn + '(' + JSON.stringify(params) + ')');\n\n    res.end();\n});\n\nserver.listen('8080');\nconsole.log('Server is running at port 8080...');\n \n```\n\n## 2.CORS\n\n>只服务端设置Access-Control-Allow-Origin即可，前端无须设置，若要带cookie请求：前后端都需要设置。\n\n若请求满足所有下述条件，则该请求可视为“简单请求”：\n```\n使用下列方法之一：\nGET\nHEAD\nPOST\n\n除了被用户代理自动设置的首部字段（例如 Connection ，User-Agent）和在 Fetch 规范中定义为 禁用首部名称 的其他首部，允许人为设置的字段为 Fetch 规范定义的 对 CORS 安全的首部字段集合。\n该集合为：\nAccept\nAccept-Language\nContent-Language\nContent-Type （需要注意额外的限制）\nDPR\nDownlink\nSave-Data\nViewport-Width\nWidth\n\nContent-Type 的值仅限于下列三者之一：\ntext/plain\nmultipart/form-data\napplication/x-www-form-urlencoded\n\n请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器；XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。\n\n请求中没有使用 ReadableStream 对象。\n```\n\n- axios设置：\n\n```\naxios.defaults.withCredentials = true\n```\n- 服务端设置：\n\n```\n# 1.）Java后台：\n\n/*\n * 导入包：import javax.servlet.http.HttpServletResponse;\n * 接口参数中定义：HttpServletResponse response\n */\n\n// 允许跨域访问的域名：若有端口需写全（协议+域名+端口），若没有端口末尾不用加'/'\nresponse.setHeader(\"Access-Control-Allow-Origin\", \"http://www.domain1.com\"); \n\n// 允许前端带认证cookie：启用此项后，上面的域名不能为'*'，必须指定具体的域名，否则浏览器会提示\nresponse.setHeader(\"Access-Control-Allow-Credentials\", \"true\"); \n\n// 提示OPTIONS预检时，后端需要设置的两个常用自定义头\nresponse.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type,X-Requested-With\");\n\n\n# 2.）Nodejs后台示例：\n\nvar http = require('http');\nvar server = http.createServer();\nvar qs = require('querystring');\n\nserver.on('request', function(req, res) {\n    var postData = '';\n\n    // 数据块接收中\n    req.addListener('data', function(chunk) {\n        postData += chunk;\n    });\n\n    // 数据接收完毕\n    req.addListener('end', function() {\n        postData = qs.parse(postData);\n\n        // 跨域后台设置\n        res.writeHead(200, {\n            'Access-Control-Allow-Credentials': 'true',     // 后端允许发送Cookie\n            'Access-Control-Allow-Origin': 'http://www.domain1.com',    // 允许访问的域（协议+域名+端口）\n            /* \n             * 此处设置的cookie还是domain2的而非domain1，因为后端也不能跨域写cookie(nginx反向代理可以实现)，\n             * 但只要domain2中写入一次cookie认证，后面的跨域接口都能从domain2中获取cookie，从而实现所有的接口都能跨域访问\n             */\n            'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'  // HttpOnly的作用是让js无法读取cookie\n        });\n\n        res.write(JSON.stringify(postData));\n        res.end();\n    });\n});\n\nserver.listen('8080');\nconsole.log('Server is running at port 8080...');\n```\n\n## 3.代理\n\n\n>Nginx配置\n\n\n- 1、 nginx配置解决iconfont跨域\n>浏览器跨域访问js、css、img等常规静态资源被同源策略许可，但iconfont字体文件(eot|otf|ttf|woff|svg)例外，此时可在nginx的静态资源服务器中加入以下配置。\n\n```\nlocation / {\n  add_header Access-Control-Allow-Origin *;\n}\n```\n- 2、 nginx反向代理接口跨域\n>跨域原理： 同源策略是浏览器的安全策略，不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议，不会执行JS脚本，不需要同源策略，也就不存在跨越问题。\n\n实现思路：通过nginx配置一个代理服务器（域名与domain1相同，端口不同）做跳板机，反向代理访问domain2接口，并且可以顺便修改cookie中domain信息，方便当前域cookie写入，实现跨域登录。\n\nnginx具体配置：\n\n```\n#proxy服务器\nserver {\n    listen       81;\n    server_name  www.domain1.com;\n\n    location / {\n        proxy_pass   http://www.domain2.com:8080;  #反向代理\n        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名\n        index  index.html index.htm;\n\n        # 当用webpack-dev-server等中间件代理接口访问nignx时，此时无浏览器参与，故没有同源限制，下面的跨域配置可不启用\n        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时，可为*\n        add_header Access-Control-Allow-Credentials true;\n    }\n}\n\n# 1.) 前端代码示例：\n\nvar xhr = new XMLHttpRequest();\n\n// 前端开关：浏览器是否读写cookie\nxhr.withCredentials = true;\n\n// 访问nginx中的代理服务器\nxhr.open('get', 'http://www.domain1.com:81/?user=admin', true);\nxhr.send();\n\n\n# 2.) Nodejs后台示例：\n\nvar http = require('http');\nvar server = http.createServer();\nvar qs = require('querystring');\n\nserver.on('request', function(req, res) {\n    var params = qs.parse(req.url.substring(2));\n\n    // 向前台写cookie\n    res.writeHead(200, {\n        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:脚本无法读取\n    });\n\n    res.write(JSON.stringify(params));\n    res.end();\n});\n\nserver.listen('8080');\nconsole.log('Server is running at port 8080...');\n```\n\n## 4.WebSocket协议跨域\n\n>WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信，同时允许跨域通讯，是server push技术的一种很好的实现。  \n原生WebSocket API使用起来不太方便，我们使用Socket.io，它很好地封装了webSocket接口，提供了更简单、灵活的接口，也对不支持webSocket的浏览器提供了向下兼容。\n\n- 1.）前端代码：\n\n```\n<div>user input：<input type=\"text\"></div>\n<script src=\"https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js\"></script>\n<script>\nvar socket = io('http://www.domain2.com:8080');\n\n// 连接成功处理\nsocket.on('connect', function() {\n    // 监听服务端消息\n    socket.on('message', function(msg) {\n        console.log('data from server: ---> ' + msg); \n    });\n\n    // 监听服务端关闭\n    socket.on('disconnect', function() { \n        console.log('Server socket has closed.'); \n    });\n});\n\ndocument.getElementsByTagName('input')[0].onblur = function() {\n    socket.send(this.value);\n};\n</script>\n```\n\n- 2.）Nodejs socket后台：\n\n```\nvar http = require('http');\nvar socket = require('socket.io');\n\n// 启http服务\nvar server = http.createServer(function(req, res) {\n    res.writeHead(200, {\n        'Content-type': 'text/html'\n    });\n    res.end();\n});\n\nserver.listen('8080');\nconsole.log('Server is running at port 8080...');\n\n// 监听socket连接\nsocket.listen(server).on('connection', function(client) {\n    // 接收信息\n    client.on('message', function(msg) {\n        client.send('hello：' + msg);\n        console.log('data from client: ---> ' + msg);\n    });\n\n    // 断开处理\n    client.on('disconnect', function() {\n        console.log('Client socket has closed.'); \n    });\n});\n```\n\n## 5.document.domain + iframe跨域\n\n>仅限主域相同，子域不同的跨域应用场景。\n\n>实现原理：两个页面都通过js强制设置document.domain为基础主域，就实现了同域。\n\n```\n# 1.）父窗口：(http://www.domain.com/a.html)\n\n<iframe id=\"iframe\" src=\"http://child.domain.com/b.html\"></iframe>\n<script>\n    document.domain = 'domain.com';\n    var user = 'admin';\n</script>\n\n\n# 2.）子窗口：(http://child.domain.com/b.html)\n\n<script>\n    document.domain = 'domain.com';\n    // 获取父窗口中变量\n    alert('get js data from parent ---> ' + window.parent.user);\n</script>\n```\n\n## 6. location.hash + iframe跨域\n\n>实现原理： a欲与b跨域相互通信，通过中间页c来实现。 三个页面，不同域之间利用iframe的location.hash传值，相同域之间直接js访问来通信。\n\n>具体实现：A域：a.html -> B域：b.html -> A域：c.html，a与b不同域只能通过hash值单向通信，b与c也不同域也只能单向通信，但c与a同域，所以c可通过parent.parent访问a页面所有对象。\n\n```\n# 1.）a.html：(http://www.domain1.com/a.html)\n\n<iframe id=\"iframe\" src=\"http://www.domain2.com/b.html\" style=\"display:none;\"></iframe>\n<script>\n    var iframe = document.getElementById('iframe');\n\n    // 向b.html传hash值\n    setTimeout(function() {\n        iframe.src = iframe.src + '#user=admin';\n    }, 1000);\n    \n    // 开放给同域c.html的回调方法\n    function onCallback(res) {\n        alert('data from c.html ---> ' + res);\n    }\n</script>\n\n# 2.）b.html：(http://www.domain2.com/b.html)\n\n<iframe id=\"iframe\" src=\"http://www.domain1.com/c.html\" style=\"display:none;\"></iframe>\n<script>\n    var iframe = document.getElementById('iframe');\n\n    // 监听a.html传来的hash值，再传给c.html\n    window.onhashchange = function () {\n        iframe.src = iframe.src + location.hash;\n    };\n</script>\n\n# 3.）c.html：(http://www.domain1.com/c.html)\n\n<script>\n    // 监听b.html传来的hash值\n    window.onhashchange = function () {\n        // 再通过操作同域a.html的js回调，将结果传回\n        window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));\n    };\n</script>\n```\n\n## 7. window.name + iframe跨域\n\n>window.name属性的独特之处：name值在不同的页面（甚至不同域名）加载后依旧存在，并且可以支持非常长的 name 值（2MB）。\n\n```\n1.）a.html：(http://www.domain1.com/a.html)\n\nvar proxy = function(url, callback) {\n    var state = 0;\n    var iframe = document.createElement('iframe');\n\n    // 加载跨域页面\n    iframe.src = url;\n\n    // onload事件会触发2次，第1次加载跨域页，并留存数据于window.name\n    iframe.onload = function() {\n        if (state === 1) {\n            // 第2次onload(同域proxy页)成功后，读取同域window.name中数据\n            callback(iframe.contentWindow.name);\n            destoryFrame();\n\n        } else if (state === 0) {\n            // 第1次onload(跨域页)成功后，切换到同域代理页面\n            iframe.contentWindow.location = 'http://www.domain1.com/proxy.html';\n            state = 1;\n        }\n    };\n\n    document.body.appendChild(iframe);\n\n    // 获取数据以后销毁这个iframe，释放内存；这也保证了安全（不被其他域frame js访问）\n    function destoryFrame() {\n        iframe.contentWindow.document.write('');\n        iframe.contentWindow.close();\n        document.body.removeChild(iframe);\n    }\n};\n\n// 请求跨域b页面数据\nproxy('http://www.domain2.com/b.html', function(data){\n    alert(data);\n});\n2.）proxy.html：(http://www.domain1.com/proxy....\n中间代理页，与a.html同域，内容为空即可。\n\n3.）b.html：(http://www.domain2.com/b.html)\n\n<script>\n    window.name = 'This is domain2 data!';\n</script>\n```\n总结：通过iframe的src属性由外域转向本地域，跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制，但同时它又是安全操作。\n\n## 8.postMessage跨域\n\n>postMessage是HTML5 XMLHttpRequest Level 2中的API，且是为数不多可以跨域操作的window属性之一，它可用于解决以下方面的问题：  \na.） 页面和其打开的新窗口的数据传递  \nb.） 多窗口之间消息传递  \nc.） 页面与嵌套的iframe消息传递  \nd.） 上面三个场景的跨域数据传递  \n\n- 用法：postMessage(data,origin)方法接受两个参数\n- data： html5规范支持任意基本类型或可复制的对象，但部分浏览器只支持字符串，所以传参时最好用JSON.stringify()序列化。\n- origin： 协议+主机+端口号，也可以设置为\"*\"，表示可以传递给任意窗口，如果要指定和当前窗口同源的话设置为\"/\"。\n\n```\n1.）a.html：(http://www.domain1.com/a.html)\n\n<iframe id=\"iframe\" src=\"http://www.domain2.com/b.html\" style=\"display:none;\"></iframe>\n<script>       \n    var iframe = document.getElementById('iframe');\n    iframe.onload = function() {\n        var data = {\n            name: 'aym'\n        };\n        // 向domain2传送跨域数据\n        iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');\n    };\n\n    // 接受domain2返回数据\n    window.addEventListener('message', function(e) {\n        alert('data from domain2 ---> ' + e.data);\n    }, false);\n</script>\n2.）b.html：(http://www.domain2.com/b.html)\n\n<script>\n    // 接收domain1的数据\n    window.addEventListener('message', function(e) {\n        alert('data from domain1 ---> ' + e.data);\n\n        var data = JSON.parse(e.data);\n        if (data) {\n            data.number = 16;\n\n            // 处理后再发回domain1\n            window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');\n        }\n    }, false);\n</script>\n```\n\n\n## 图片跨域\n \n- 加 `crossOrigin`, 图片服务器需要配置 `Access-Control-Allow-Origin`\n\n```\nvar canvas = document.createElement('canvas');\nvar context = canvas.getContext('2d');\n\nvar img = new Image();\nimg.crossOrigin = '';\nimg.onload = function () {\n    context.drawImage(this, 0, 0);\n    context.getImageData(0, 0, this.width, this.height);\n};\nimg.src = 'https://avatars3.githubusercontent.com/u/496048?s=120&v=4';';\n```\n\n增加一个img.crossOrigin = ''即可，虽然JS代码这里设置的是空字符串，实际上起作用的属性值是anonymous。\n\ncrossOrigin可以有下面两个值：\n\n>关键字\t释义  \nanonymous\t元素的跨域资源请求不需要凭证标志设置。  \nuse-credentials\t元素的跨域资源请求需要凭证标志设置，意味着该请求需要提供凭证。  \n\n其中，只要crossOrigin的属性值不是use-credentials，全部都会解析为anonymous，包括空字符串，包括类似'abc'这样的字符。\n\n## 参考\n- [前端常见跨域解决方案](https://segmentfault.com/a/1190000011145364)\n\n- [解决canvas图片getImageData,toDataURL跨域问题](https://www.zhangxinxu.com/wordpress/2018/02/crossorigin-canvas-getimagedata-cors/)\n\n- [MDN-浏览器的同源策略](https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy)\n- [CORS](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS)\n"
  },
  {
    "path": "4.6.4 jsonp为什么不支持post方法.md",
    "content": "# jsonp为什么不支持post方法\n\n预先定义callback函数\n\n```\nfunction myfunc(data) {\n   console.log(data)\n}\n```\n\n- dom中插入script标签\n<!-- callback参数对象对应上面callback函数名 -->\n\n<script src=\"//example.com/jsonp.js?callback=myfunc\"></script>\n\n\n浏览器请求//example.com/jsonp.js?callback=myfunc, 得到内容\n\nmyfunc({\"foo\": \"bar\"}) //数据传入到了callback函数\n\n本质上是通过script标签获取数据, script标签是只支持GET的\n\n## demo 简单实现\n```\n// 提供jsonp服务的url地址（不管是什么类型的地址，最终生成的返回值都是一段javascript代码）\nvar url = \"http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler\";\n\n// 创建script标签，设置其属性\nvar script = document.createElement('script');\nscript.setAttribute('src', url);\n\n// 把script标签加入head，此时调用开始\ndocument.getElementsByTagName('head')[0].appendChild(script); \n\n```\n## JSONP 封装了一个对象\n```\nvar JSONP = {\n    // 获取当前时间戳\n    now: function() {\n        return (new Date()).getTime();\n    },\n    \n    // 获取16位随机数\n    rand: function() {\n        return Math.random().toString().substr(2);\n    },\n    \n    // 删除节点元素\n    removeElem: function(elem) {\n        var parent = elem.parentNode;\n        if(parent && parent.nodeType !== 11) {\n            parent.removeChild(elem);\n        }\n    },\n    \n    // url组装\n    parseData: function(data) {\n        var ret = \"\";\n        if(typeof data === \"string\") {\n            ret = data;\n        }\n        else if(typeof data === \"object\") {\n            for(var key in data) {\n                ret += \"&\" + key + \"=\" + encodeURIComponent(data[key]);\n            }\n        }\n        // 加个时间戳，防止缓存\n        ret += \"&_time=\" + this.now();\n        ret = ret.substr(1);\n        return ret;\n    },\n    \n    getJSON: function(url, data, func) {\n        // 函数名称\n        var name;\n        \n        // 拼装url\n        url = url + (url.indexOf(\"?\") === -1 ? \"?\" : \"&\") + this.parseData(data);\n        \n        // 检测callback的函数名是否已经定义\n        var match = /callback=(\\w+)/.exec(url);\n        if(match && match[1]) {\n            name = match[1];\n        } else {\n            // 如果未定义函数名的话随机成一个函数名\n            // 随机生成的函数名通过时间戳拼16位随机数的方式，重名的概率基本为0\n            // 如:jsonp_1355750852040_8260732076596469\n            name = \"jsonp_\" + this.now() + '_' + this.rand();\n            // 把callback中的?替换成函数名\n            url = url.replace(\"callback=?\", \"callback=\"+name);\n            // 处理?被encode的情况\n            url = url.replace(\"callback=%3F\", \"callback=\"+name);\n        }\n        \n        // 创建一个script元素\n        var script = document.createElement(\"script\");\n        script.type = \"text/javascript\";\n        // 设置要远程的url\n        script.src = url;\n        // 设置id，为了后面可以删除这个元素\n        script.id = \"id_\" + name;\n        \n        // 把传进来的函数重新组装，并把它设置为全局函数，远程就是调用这个函数\n        window[name] = function(json) {\n            // 执行这个函数后，要销毁这个函数\n            window[name] = undefined;\n            // 获取这个script的元素\n            var elem = document.getElementById(\"id_\" + name);\n            // 删除head里面插入的script，这三步都是为了不影响污染整个DOM啊\n            JSONP.removeElem(elem);\n            // 执行传入的的函数\n            func(json);\n        };\n        \n        // 在head里面插入script元素\n        var head = document.getElementsByTagName(\"head\");\n        if(head && head[0]) {\n            head[0].appendChild(script);\n        }\n    }\n};\n```\n\n- [JSONP原理及简单实现 可做简单插件使用](https://www.cnblogs.com/naokr/p/6603936.html)\n\n\n## 参考\n- [segmentfault-JSONP为什么不支持POST请求？](https://segmentfault.com/q/1010000009389175)\n- [知乎-jsonp为什么不支持post请求？](https://www.zhihu.com/question/28890257)\n"
  },
  {
    "path": "4.6.5 实现一个 promise.md",
    "content": "# 实现一个 promise\n\n## 1.初步构建\n>Promise 的参数是函数 fn，  \n把内部定义 resolve 方法作为参数传到 fn 中，调用 fn。  \n当异步操作成功后会调用 resolve 方法，  \n然后就会执行 then 中注册的回调函数。  \n\n```\nfunction Promise(fn){\n  //需要一个成功时的回调\n  var doneCallback;\n  //一个实例的方法，用来注册异步事件\n  this.then = function(done){\n    doneCallback = done;\n  }\n  function resolve(){\n    doneCallback();\n  }\n  fn(resolve);\n}\n\n```\n\n## 2.加入链式支持\n\n>下面加入链式，成功回调的方法就得变成数组才能存储。同时我们给 resolve 方法添加参数，这样就不会输出 undefined。\n\n```\n下面加入链式，成功回调的方法就得变成数组才能存储\n\nfunction Promise(fn){\n  //需要成功以及成功时的回调\n  var doneList = [];\n  //一个实例的方法，用来注册异步事件\n  this.then = function(done ,fail){\n    doneList.push(done);\n    return this;\n  }\n  function resolve(){\n    doneList.forEach(function(fulfill){\n      fulfill();\n    });\n  }\n  fn(resolve);\n}\n\n\n这里promise里面如果是同步的函数的话，doneList里面还是空的，所以可以加个setTimeout来将这个放到js的最后执行。\n\n这里主要是参照了promiseA+的规范，就像这样\n\nfunction resolve(){\n  setTimeout(function(){\n    doneList.forEach(function(fulfill){\n      fulfill();\n    });\n  },0);\n}\n\n\n```\n>\n**就是通过setTimeout机制，将resolve中执行回调的逻辑放置到JS任务队列末尾，以保证在resolve执行时，then方法的回调函数已经注册完成**\n\n## 3.引入状态\n>这时如果promise已经执行完了，我们再给promise注册then方法就怎么都不会执行了，这个不符合预期，所以才会加入状态这种东西。更新过的代码如下\n\n```\nfunction Promise(fn){\n  //需要成功以及成功时的回调\n  var state = 'pending';\n  var doneList = [];\n  //一个实例的方法，用来注册异步事件\n  this.then = function(done){\n    switch(state){\n      case \"pending\":\n        doneList.push(done);\n        return this;\n        break;\n      case 'fulfilled':\n        done();\n        return this;\n        break;\n    }\n  }\n  function resolve(){\n    state = \"fulfilled\";\n    setTimeout(function(){\n      doneList.forEach(function(fulfill){\n        fulfill();\n      });\n    },0);\n  }\n  fn(resolve);\n}\n\n\n加上结果的传递：\n\nfunction resolve(newValue){\n  state = \"fulfilled\";\n  var value = newValue;\n  setTimeout(function(){\n    doneList.forEach(function(fulfill){\n      value = fulfill(value);\n    });\n  },0);\n}\n\n\n```\n\n\n## 4.支持串行\n\n```\nfunction Promise(fn){\n  //需要成功以及成功时的回调\n  var state = 'pending';\n  var doneList = [];\n  this.then = function(done){\n    switch(state){\n      case \"pending\":\n        doneList.push(done);\n        return this;\n        break;\n      case 'fulfilled':\n        done();\n        return this;\n        break;\n    }\n  }\n  function resolve(newValue){\n    state = \"fulfilled\";\n    setTimeout(function(){\n      var value = newValue;\n      //执行resolve时，我们会尝试将doneList数组中的值都执行一遍\n      //当遇到正常的回调函数的时候，就执行回调函数\n      //当遇到一个新的promise的时候，就将原doneList数组里的回调函数推入新的promise的doneList，以达到循环的目的\n      for (var i = 0;i<doneList.length;i++){\n        var temp = doneList[i](value)\n        if(temp instanceof Promise){\n            var newP =  temp;\n            for(i++;i<doneList.length;i++){\n                newP.then(doneList[i]);\n            }\n        }else{\n            value = temp;\n        }\n      }\n    },0);\n  }\n  fn(resolve);\n}\n\n\n```\n\n## 5.加入reject\n\n```\nfunction Promise(fn){\n  var state = 'pending';\n  var doneList = [];\n  var failList= [];\n  this.then = function(done ,fail){\n    switch(state){\n      case \"pending\":\n        doneList.push(done);\n        //每次如果没有推入fail方法，我也会推入一个null来占位\n        failList.push(fail || null);\n        return this;\n        break;\n      case 'fulfilled':\n        done();\n        return this;\n        break;\n      case 'rejected':\n        fail();\n        return this;\n        break;\n    }\n  }\n  function resolve(newValue){\n    state = \"fulfilled\";\n    setTimeout(function(){\n      var value = newValue;\n      for (var i = 0;i<doneList.length;i++){\n        var temp = doneList[i](value);\n        if(temp instanceof Promise){\n            var newP =  temp;\n            for(i++;i<doneList.length;i++){\n                newP.then(doneList[i],failList[i]);\n            }\n        }else{\n            value = temp;\n        }\n      }\n    },0);\n  }\n  function reject(newValue){\n    state = \"rejected\";\n    setTimeout(function(){\n      var value = newValue;\n      var tempRe = failList[0](value);\n      //如果reject里面传入了一个promise，那么执行完此次的fail之后，将剩余的done和fail传入新的promise中\n      if(tempRe instanceof Promise){\n        var newP = tempRe;\n        for(i=1;i<doneList.length;i++){\n            newP.then(doneList[i],failList[i]);\n        }\n      }else{\n        //如果不是promise，执行完当前的fail之后，继续执行doneList\n        value =  tempRe;\n        doneList.shift();\n        failList.shift();\n        resolve(value);\n      }\n    },0);\n  }\n  fn(resolve,reject);\n}\n\n```\n\n\n## 看一下实现Promise.all()方法的过程。还是那句话我们要明确这个方法接收的参数是数组并且返回的是promise对象。\n\n```\nfunction promiseAll(promises){\n    return new Promise(function(resolve,reject) {\n    //promises必须是一个数组\n        if(!(promises instanceof Array)) {\n            throw new TypeError(\"promises must be an Array\");\n        }\n        var len = promises.length,\n            resolvedCount = 0,\n            resolvedArray = new Array(len);\n        for(var i = 0;i < len ;i++) {\n            (function(i) {\n                Promise.resolve(promises[i])\n                    .then(value => {\n                        resolvedCount++;\n                        resolvedArray[i] = value;\n                        if(resolvedCount == len) {\n                            return resolve(resolvedArray);\n                        }\n                    },re => {\n                        return reject(re);\n                    })\n                    .catch(re => {\n                        console.log(re);\n                    })              \n            })(i)\n        }\n    })\n}\n\n原文：https://blog.csdn.net/weixin_38858002/article/details/82380700 \n```\n\n\n\n\n## 参考\n- [Promise简单实现（正常思路版）](https://www.jianshu.com/p/473cd754311f)\n- [手把手教你实现一个完整的 Promise](http://www.cnblogs.com/huansky/p/6064402.html)\n  - [github](https://github.com/huanshen/Promise/tree/master)\n\n- [主流规范---Promises/A+](https://promisesaplus.com/)\n\n"
  },
  {
    "path": "4.6.6 将简单对象数组转换成父子结构（具有children属性）的对象.md",
    "content": "# 将简单对象数组转换成父子结构（具有children属性）的对象\n\n\n```\nfunction treeData(data){\n  let tree = data.filter((father)=>{              //循环所有项\n    let branchArr = data.filter((child)=>{\n      return father.id == child.parentId      //返回每一项的子级数组\n    });\n    if(branchArr.length>0){\n      father.children = branchArr;    //如果存在子级，则给父级添加一个children属性，并赋值\n    }\n    return father.parentId==0;      //返回第一层\n  });\n  return tree   \n}\n\nvar data = [\n            {id:1,parentId:0,name:\"一级菜单A\",rank:1},\n            {id:2,parentId:0,name:\"一级菜单B\",rank:1},\n            {id:3,parentId:0,name:\"一级菜单C\",rank:1},\n            {id:4,parentId:1,name:\"二级菜单A-A\",rank:2},\n            {id:5,parentId:1,name:\"二级菜单A-B\",rank:2},\n            {id:6,parentId:2,name:\"二级菜单B-A\",rank:2},\n            {id:7,parentId:4,name:\"三级菜单A-A-A\",rank:3},\n            {id:8,parentId:7,name:\"四级菜单A-A-A-A\",rank:4},\n            {id:9,parentId:8,name:\"五级菜单A-A-A-A-A\",rank:5},\n            {id:10,parentId:9,name:\"六级菜单A-A-A-A-A-A\",rank:6},\n            {id:11,parentId:10,name:\"七级菜单A-A-A-A-A-A-A\",rank:7},\n            {id:12,parentId:11,name:\"八级菜单A-A-A-A-A-A-A-A\",rank:8},\n            {id:13,parentId:12,name:\"九级菜单A-A-A-A-A-A-A-A-A\",rank:9},\n            {id:14,parentId:13,name:\"十级菜单A-A-A-A-A-A-A-A-A-A\",rank:10},\n          ];\n\ntreeData(data)\n```\n\n\n## 参考\n- [把一个线性数组转成树形数组](https://blog.csdn.net/Mr_JavaScript/article/details/82817177)\n"
  },
  {
    "path": "4.6.7 100 100的 canvas 占多少内存.md",
    "content": "# `100*100`的 canvas 占多少内存\n\n一个图片占多少内存在于图片的尺寸大小。\n\n以一张尺寸为900 × 600的图片为例，图片共有像素数：\n\n900 × 600 = 540,000像素(Pixel)。\n\n如果图片是RGB 色彩模式，占用的内存是：\n\n900 × 600 × 3 = 1,620,000 字节(bytes).\n\n后面\"× 3\"表示每个像素内RGB 颜色的信息需要3字节，也是24比特(bit)。 \n\n也可以说每个像素中 3 RGB值，每一个RGB值需要一个 8 比特，也是一个字节，总计24个比特。\n\n存储不同的色彩模式需要不同的内存，具体如下：\n```\n图片类型                            |      每像素多少字节 \n\n1 比特 数据图(Line art)        |      每像素1/8字节，也是一个比特。\n\n8 比特灰度(Grayscale)         |      每像素1字节。\n\n16 比特灰度(Grayscale)        |     每像素2字节。\n\n24 比特 RGB                        |     每像素3字节，这是图片中最常用的，如JPG格式。\n\n32 比特 印刷色彩模式(CMYK) |   每像素4字节\n\n48 比特 RGB                        |    每像素6字节\n```\n\n\n- webkit中\n```\nsize_t requestedPixelMemory = 4 * width() * height();\n```\n\n## 参考\n- [WebKit---HTMLCanvasElement.cpp--365行代码](https://github.com/WebKit/webkit/blob/master/Source/WebCore/html/HTMLCanvasElement.cpp#L365)\n- [MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas)\n- [一张图片占多大内存的计算-android](http://www.voidcn.com/article/p-pkyrbayd-beo.html)\n- [Android中一张图片占内存多大](https://github.com/AndroidPreView/AndroidNote/wiki/Android%E4%B8%AD%E4%B8%80%E5%BC%A0%E5%9B%BE%E7%89%87%E5%8D%A0%E5%86%85%E5%AD%98%E5%A4%9A%E5%A4%A7)\n- [经典面试题1：图片占多少内存](https://www.jianshu.com/p/1af904e9a6e4)\n- [掘金-100*100的 canvas 占多少内存](https://juejin.im/post/5bdeb357e51d4536140fc7df)\n"
  },
  {
    "path": "4.6.8 console.time方法与console.timeEnd方法.md",
    "content": "# console.time方法与console.timeEnd方法\n\n\n在Node.js中，当需要统计一段代码的执行时间时，\n\n可以使用console.time方法与console.timeEnd方法，\n\n其中console.time方法用于标记开始时间，\nconsole.timeEnd方法用于标记结束时间，并且将结束时间与开始时间之间经过的毫秒数在控制台中输出。\n\n这两个方法的使用方法如下所示。\n```\nconsole.time(label)  \nconsole.timeEnd(label) \n\n```\n这两个方法均使用一个参数，参数值可以为任何字符串，但是这两个方法所使用的参数字符串必须相同，\n\n才能正确地统计出开始时间与结束时间之间所经过的毫秒数。\n\n```\nconst fetch = require('node-fetch');\n\nconst sleep = (timeout = 2000) => new Promise(resolve => {\n  setTimeout(resolve, timeout);\n});\n\nasync function getZhihuColumn(id) {\n  await sleep(2000);\n  const url = `https://zhuanlan.zhihu.com/api/columns/${id}`;\n  const response = await fetch(url);\n  return await response.json();\n}\n\nconst showColumnInfo = async () => {\n  console.time('showColumnInfo');\n  const feweekly = await getZhihuColumn('feweekly');\n  const toolingtips = await getZhihuColumn('toolingtips');\n\n  console.log(`NAME: ${feweekly.name}`);\n  console.log(`INTRO: ${feweekly.intro}`);\n\n  console.log(`NAME: ${toolingtips.name}`);\n  console.log(`INTRO: ${toolingtips.intro}`);\n  console.timeEnd('showColumnInfo');\n};\n\nshowColumnInfo();\n```\n\n\n## 参考\n- [console.time方法与console.timeEnd方法]()\n"
  },
  {
    "path": "4.6.9 js 数组全排列.md",
    "content": "# js数组全排列\n\n\n## 递归实现js数组全排列\n\n>当n = 1时， 数组arr = [A]，全排列结果为 [A];  \n当n = 2时， 数组arr = [A, B]，全排列结果为  [A, B]  [B, A];   \n当n = 3时， 数组arr = [A, B, C]，全排列结果为\n```\n[A, B, C]\n[A, C, B]\n[B, A, C]\n[B, C, A]\n[C, A, B]\n[C, B, A]\n```\n到n = 3时可以看出全排列有以下规律:  \n- 1.固定第一个元素，将剩下的元素进行全排列处理；  \n- 2.将第一个元素与依次与第i（1<i<=arr.length）个元素互换，将剩下的元素进行全排列处理；\n\n很适合使用递归解决。只要写一个全排列函数permutation，能固定一个下标为i的元素，剩下元素再进行全排列即可。\n\n\n```\nES5中使用闭包将全排列函数封装。\n\n// 数组全排列\nfunction Permutation(arr) {\n    this.len = 0;    // 存储全排列次数\n    this.arr = arr.concat();   // 传入的数组\n    this.result = [];    // 存储全排列结果\n\n    // 首次创建对象时初始化方法\n    if (typeof Permutation.run == 'undefined') {\n        Permutation.prototype.start = function() {\n            this.run(0);\n        }\n\n        // 递归函数(核心方法)，index为数组下标\n        Permutation.prototype.run = function(index) {\n            // 单遍历到数组末端时，将结果储存在result数组中，全排列次数加1\n            if (index == this.arr.length - 1) {\n                this.result.push(this.arr.slice());\n                this.len++;\n                return;\n            }\n\n            for(let i = index; i < this.arr.length; i++) {\n                this.swap(this.arr, index, i);      // 与下标为i的元素交换位置\n                this.run(index + 1);                // 剩下元素全排列\n                this.swap(this.arr, index, i);      // 复原数组\n            }\n        }\n\n        // 交换位置\n        Permutation.prototype.swap = function(array, i, j) {\n            var t;\n            t = array[i];\n            array[i] = array[j]; \n            array[j] = t;\n        }\n    }\n}\n\nvar per = new Permutation(['A', 'B', 'C']);\nper.start();\nconsole.log(per.result);\nconsole.log(per.len);\n// [ [ 'A', 'B', 'C' ],\n//   [ 'A', 'C', 'B' ],\n//   [ 'B', 'A', 'C' ],\n//   [ 'B', 'C', 'A' ],\n//   [ 'C', 'B', 'A' ],\n//   [ 'C', 'A', 'B' ] ]\n// 6\n\n\nES5代码使用动态原型模式创建对象，主要是想让函数封装的尽量像一个类。在ES6中有class，语法可以更加简洁高效。\n\n  // ES6\n  class Permutation {\n    constructor(arr) {\n        this.arr = Array.from(arr);\n        this.result = [];\n        this.len = 0;\n        this.run(0);\n    }\n\n    run(index) {\n        if (index == this.arr.length - 1) {\n            this.result.push(Array.from(this.arr));\n            this.len++;\n            return;\n        }\n        for(let i = index; i < this.arr.length; i++) {\n            [this.arr[index], this.arr[i]] = [this.arr[i], this.arr[index]];\n            this.run(index + 1);\n            [this.arr[index], this.arr[i]] = [this.arr[i], this.arr[index]];\n        }\n    }\n  }\n\n  let p = new Permutation([\"A\", \"B\", \"C\"]);\n  console.log(p.result);\n  console.log(p.len);\n  // [ [ 'A', 'B', 'C' ],\n  //   [ 'A', 'C', 'B' ],\n  //   [ 'B', 'A', 'C' ],\n  //   [ 'B', 'C', 'A' ],\n  //   [ 'C', 'B', 'A' ],\n  //   [ 'C', 'A', 'B' ] ]\n  // 6\n以上就是全排列的js实现。\n\n```\n\n## 全排列另一个写法(可能好理解一些)\n```\nfunction permutation(arr){\n\tif (arr.length == 1)\n\t\treturn arr;\n\telse if (arr.length == 2)\n\t\treturn [[arr[0],arr[1]],[arr[1],arr[0]]];\n\telse {\n\t\tvar temp = [];\n\t\tfor (var i = 0; i < arr.length; i++) {\n\t\t\tvar save = arr[i];\n\t\t\tarr.splice(i, 1);//取出arr[i]\n\t\t\tvar res = permutation(arr);//递归排列arr[0],arr[1],...,arr[i-1],arr[i+1],...,arr[n]\n\t\t\tarr.splice(i, 0, save);//将arr[j]放入数组，保持原来的位置\n\t\t\tfor (var j = 0; j < res.length; j++) {\n\t\t\t\tres[j].push(arr[i]);\n\t\t\t\ttemp.push(res[j]);//将arr[j]组合起来\n\t\t\t}\n\t\t}\n\t\treturn temp;\n\t}\n}\n\n--------------------- \n作者：筱葭\n原文：https://blog.csdn.net/zhouziyu2011/article/details/62237216 \n```\n\n\n## 参考\n- [递归实现js数组全排列](https://www.jianshu.com/p/6e7f88ead393)\n"
  },
  {
    "path": "4.6.9 判断一个数组是否包含另一个数组.md",
    "content": "# 判断一个数组是否包含另一个数组\n>判断某个数组中是否包含另一个数组\n\n```\n//是否被包含,是返回true,不是返回false\nisContained =(a, b)=>{\n    if(!(a instanceof Array) || !(b instanceof Array)) return false;\n    if(a.length < b.length) return false;\n    var aStr = a.toString();\n    for(var i = 0, len = b.length; i < len; i++){\n      if(aStr.indexOf(b[i]) == -1) return false;\n    }\n    return true;\n  }\n\n\nvar a = [1,2,3,4,5];\nvar b = [1,4,3,2];\nvar c = [1,6];\nalert(isContained(a,b));//true\nalert(isContained(a,c));//false\n\n\n======================================\n\nvar a = [1,2,5,7,3];\nvar b = [1,7,2];\nfunction isContained(aa,bb){\n\tif(!(aa instanceof Array)||!(bb instanceof Array)||((aa.length < bb.length))){\n\t\treturn false;\n\t}\n\tvar aaStr = aa.toString();\n\tfor (var i = 0 ;i < bb.length;i++) {\n\t\tif(aaStr.indexOf(bb[i]) < 0) return false;\n\t}\n\treturn true;\n}\nvar c = isContained(a,b);\nconsole.log(c);\n\n```\n\n\n## 参考\n- [javascript 判断某个数组中是否包含另一个数组](https://blog.csdn.net/qq_25905803/article/details/78677333)\n- [js判断一个数组是否包含另一个数组](https://blog.csdn.net/ming614/article/details/80511171)\n"
  },
  {
    "path": "4.7.0 判断对象数组是否存在某个对象.md",
    "content": "# 4.7.0 判断对象数组是否存在某个对象\n\n\n- 转成json字符串判断\n\n```\nJSON.stringify([{a:1},{a:2},{a:3},{a:2},{a:3}]).includes(JSON.stringify({a:1}))\n```\n"
  },
  {
    "path": "4.7.1 Math.max() 求数组最大数.md",
    "content": "\n## 求数组最大数\n\n```\n方法一：\n\n\nfunction findMax(arr) {\n\tvar max = arr[0];\n\tfor (var i = 1; i < arr.length; i++) {\n\t\tif (arr[i] > max)\n\t\t\tmax = arr[i];\n\t}\n\treturn max;\n}\n\n\n方法二：\n\nMath.max(...arr)\nfunction findMax(arr) {\n\treturn Math.max.apply(Math, arr);\n}\n\n\n3.for循环\n\n\nlet max = arr[0];\nfor (let i = 0; i < arr.length - 1; i++) {\n    max = max < arr[i+1] ? arr[i+1] : max\n}\n\n4.数组sort()\n\n\narr.sort((num1, num2) => {\n    return num1 - num2 < 0\n})\narr[0]\n\n\n5.数组reduce\n\n\narr.reduce((num1, num2) => {\n    return num1 > num2 ? num1 : num2}\n)\n```\n\n\n## 参考\n- [浅谈数组全排列、去重、求最大值的JavaScript实现](https://blog.csdn.net/zhouziyu2011/article/details/62237216)\n"
  },
  {
    "path": "4.7.2  canvas getImageData() 方法.md",
    "content": "#  canvas getImageData() 方法\n\n通过  canvas getImageData() 方法 可以拿到 ImageData 对象，该对象拷贝了画布指定矩形的像素数据\n\n>注意：ImageData 对象不是图像，它规定了画布上一个部分（矩形），并保存了该矩形内每个像素的信息。\n\n- 对于 ImageData 对象中的每个像素，都存在着四方面的信息，即 RGBA 值：\n```\nR - 红色（0-255）\nG - 绿色（0-255）\nB - 蓝色（0-255）\nA - alpha 通道（0-255; 0 是透明的，255 是完全可见的）\n```\n\ncolor/alpha 信息以数组形式存在，并存储于 ImageData 对象的 data 属性中。\n\n## Uint8ClampedArray\n\n ImageData 对象的 data 属性中返回的是一个 Uint8ClampedArray；\n\nUint8ClampedArray（8位无符号整型固定数组） 类型化数组表示一个由值固定在0-255区间的8位无符号整型组成的数组\n\n\n## demo\n\n```\n var c=document.getElementById(\"myCanvas\");     \n var ctx=c.getContext(\"2d\");\n    var img=document.getElementById(\"scream\");\n    ctx.drawImage(img,0,0);\n    var imgData=ctx.getImageData(0,0,c.width,c.height);\n    console.log(imgData)\n    // 反转颜色\n    for (var i=0;i<imgData.data.length;i+=4)\n\t{\n\t\timgData.data[i]=255-imgData.data[i];\n\t\timgData.data[i+1]=255-imgData.data[i+1];\n\t\timgData.data[i+2]=255-imgData.data[i+2];\n\t\timgData.data[i+3]=255;\n    }\n\tctx.putImageData(imgData,0,0);\n```\n\n\n## 参考\n- [Uint8ClampedArray](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray)\n- [HTML canvas getImageData() 方法](http://www.runoob.com/tags/canvas-getimagedata.html)\n"
  },
  {
    "path": "4.7.3 二叉树的几种遍历方式.md",
    "content": "# 二叉树的几种遍历方式\n\n## 二叉树的遍历主要分三种：\n\n- 先（根）序遍历：根左右\n- 中（根）序遍历：左根右\n- 后（根）序遍历：左右根\n\n\n### 1.先序遍历二叉树\n\n1)算法的递归定义是：\n\n　　若二叉树为空，则遍历结束；否则\n\n　　⑴ 访问根结点；\n\n　　⑵ 先序遍历左子树(递归调用本算法)；\n\n　　⑶ 先序遍历右子树(递归调用本算法)。\n\n### 2.中序遍历二叉树：\n\n1）算法的递归定义是：\n\n　　若二叉树为空，则遍历结束；否则\n\n　　⑴ 中序遍历左子树(递归调用本算法)；\n\n　　⑵ 访问根结点；\n\n　　⑶ 中序遍历右子树(递归调用本算法)。\n\n\n### 3.后序遍历二叉树:\n\n1)递归算法\n\n　　若二叉树为空，则遍历结束；否则\n\n　　⑴ 后序遍历左子树(递归调用本算法)；\n\n　　⑵ 后序遍历右子树(递归调用本算法) ；\n\n　　⑶ 访问根结点 。\n\n\n\n\n## 参考\n- [ 树和二叉树,二叉树的遍历和基本操作](http://www.cnblogs.com/webFrontDev/p/3865719.html)\n- [JS 二叉树的三种遍历(递归)方式](https://blog.csdn.net/zsy_snake/article/details/80353336)\n"
  },
  {
    "path": "4.7.4 将 arguments 对象转为数组.md",
    "content": "# 将 arguments 对象转为数组 `[].slice.call(arguments);`\n\n## arguments 是什么\n\narguments 可以看做一个数组。每一个js函数内部都有 arguments，它代表传入的参数数组。\n\n```\nfunction a() {\n    console.log(arguments);\n}\n\na(1, 2, 3); // log出[1, 2, 3]\n\n输出：\n# arguments 应该说是一个类数组\n\nArguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]\n0: 1\n1: 2\n2: 3\ncallee: ƒ a()\nlength: 3\nSymbol(Symbol.iterator): ƒ values()\n__proto__: Object\n\n# \n[].slice.call(arguments, 1)返回的是arguments数组从1号位开始的片段\n[].slice.call(arguments);\n\n========================\nfunction a() {\n  console.log(arguments);\n  var a = [].slice.call(arguments, 1)//返回的是arguments数组从1号位开始的片段\n  var b = [].slice.call(arguments); // [].slice.call(arguments,0);\nconsole.log(a,b)\n}\n\na(1, 2, 3); // log出[1, 2, 3]\nVM203:2 Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]\nVM203:5 (2) [2, 3] (3) [1, 2, 3]\n```\n\n\n## slice大致内部实现\n```\nArray.prototype.slice = function(start,end){\n     var result = new Array();\n     start = start || 0;\n     end = end || this.length; //this指向调用的对象，当用了call后，能够改变this的指向，也就是指向传进来的对象，这是关键\n     for(var i = start; i < end; i++){\n          result.push(this[i]);\n     }\n     return result;\n}\n```\n\n\n## 参考\n- [请问这句语句var args=[].slice.call(arguments,1)是什么意思](https://segmentfault.com/q/1010000005643934)\n- [解析 Array.prototype.slice.call(arguments,0)](http://www.cnblogs.com/papi/p/9234964.html)\n"
  },
  {
    "path": "4.7.5 script标签中defer和async属性的区别.md",
    "content": "# script标签中defer和async属性的区别\n\n## script标签存在两个属性，defer和async，因此script标签的使用分为三种情况：\n\n  - 1.<script src=\"example.js\"></script>\n\n    - 没有defer或async属性，浏览器会立即加载并执行相应的脚本。也就是说在渲染script标签之后的文档之前，不等待后续加载的文档元素，读到就开始加载和执行，此举会阻塞后续文档的加载；\n\n  - 2.<script async src=\"example.js\"></script>\n\n    - 有了async属性，表示后续文档的加载和渲染与js脚本的加载和执行是并行进行的，即异步执行；\n\n  - 3.<script defer src=\"example.js\"></script>\n\n    - 有了defer属性，加载后续文档的过程和js脚本的加载(此时仅加载不执行)是并行进行的(异步)，js脚本的执行需要等到文档所有元素解析完成之后，DOMContentLoaded事件触发执行之前。\n\n\n## 区别\n1.defer和async在网络加载过程是一致的，都是异步执行的；\n\n2.两者的区别在于脚本加载完成之后何时执行，可以看出defer更符合大多数场景对应用脚本加载和执行的要求；\n\n3.如果存在多个有defer属性的脚本，那么它们是按照加载顺序执行脚本的；而对于async，它的加载和执行是紧紧挨着的，无论声明顺序如何，只要加载完成就立刻执行，它对于应用脚本用处不大，因为它完全不考虑依赖。\n\n\n## 参考\n- [script标签中defer和async属性的区别](http://www.cnblogs.com/neusc/archive/2016/08/12/5764162.html)\n"
  },
  {
    "path": "4.7.6  现在有一个函数A和函数B，请你实现B继承A.md",
    "content": "#  现在有一个函数A和函数B，请你实现B继承A\n\n## 1.使用原型链\n\n>思想：**利用原型让一个引用类型继承另一个引用类型的属性和方法**  \nb继承a实际上就是创建a的实例，并将该实例赋予给b.prototype.实现的本质是重写原型对象，代之以一个新类型的实例。  \n换句话说原来存在于a中的属性和方法现在也存在于b.prototype中  \n\n```\nfunction A() {}\nfunction B() {}\n\nB.prototype = new A();\n\n```\n\n-  缺点：\n  - 1.包含引用类型值的原型属性会被所有实例共享，  \n    (基本类型值也会被所有实例共享，但是子类型实例可以通过设置一个同名属性屏蔽超类型原型中的属性从而拥有自己的属性，  \n    而引用类型的值是会修改原型中的属性的，从而影响所有实例)  \n    这也是为什么要在构造函数中，而不是在原型对象中定义属性的原因。\n\n  - 2.在创建子类型的实例时，不能向超类型的构造函数中传递参数。  \n  实际上，应该说是没有办法在不影响所有对象实例的情况下，给超类型的构造函数传递参数。\n\n>基于以上两点，实践中很少会单独使用原型链。\n--------------------- \n\n\n## 2.借用构造函数\n\n>思想：**在子类型构造函数的内部调用超类型构造函数。**  \n 函数只不过是在特定环境中执行代码的对象。调用 call() 方法，\n\n```\nfunction A(){}\nfunction B(){\n  A.call(this);\n}\n```\n\n- 优点：\n  - 1.可以在子类型中向超类型传递参数（相对于原型链而言）。\n\n  - 2.可以解决原型链继承所带来的引用类型值所带来的问题。\n\n- 缺点：\n  - 1.为了确保SuperType构造函数不会重写子类型的属性，必须在调用超类型构造函数后，再添加应该在子类型中定义的属性。  \n  （这也算不上什么缺点，原型链也是这样的，必须确保超类型的实例替换掉子类型的原型之后再定义自己的原型方法）\n\n  - 2.方法都在构造函数中定义了，因此函数复用就无从谈起了。\n\n  - 3.在超类型的原型中定义的方法，对子类型而言也是不可见的，  \n    （？因为这是原型链的原理决定的，只有子类型实例会继承超类型的属性和原型方法，而构造函数模式只是在子类型中引用了超类型构造函数）  \n    结果所有类型都只能使用构造函数模式。\n\n>故借用构造函数模式也是很少单独使用的。\n\n## 3.组合继承\n\n>思想：用原型链实现对原型属性和方法的继承，而通过借用构造函数来实现对实例属性的继承。  \n这样，既通过在原型上定义方法实现了函数复用，又能够保证每个实例都有它自己的属性。\n\n\n```\nfunction B(){}\nfunction A(){}\nB.prototype = new A();\n\nfunction B(){\n  A.call(this);\n}\n```\n\n\n\n- 优点：\n  - 1.可以让每个不同的实例拥有自己的属性，同时又共享了相同的方法。\n\n  - 2.避免了原型链继承和借用构造函数继承的缺点，融合了它们的优点。\n\n- 缺点：\n  - 1.无论什么情况下，都会调用两次超类型构造函数。\n\n故组合继承模式成为了JS中最常用的继承模式。\n\n而且，instanceof和isPrototypeOf ()也能用于识别基于组合继承创建的对象。\n\n\n### 4.原型式继承\n\n>思想：借助原型可以基于已有的对象创建新对象。从本质上讲，object（）对传入其中的对象执行了一次浅复制。\n\n```\nfunction object(o){ \n    function F(){}\n    F.prototype=o;\n    return new F();\n\n}\n\n```\n\n- 优点：1.在没有必要兴师动众地创建构造函数，而只想让一个对象与另一个对象保持类似的情况下，原型式继承是完全可以胜任的。\n\n- 缺点：原型式继承与原型链继承有相同的缺点，包含引用类型的值始终都会共享相应的值。\n\n\n## 5.寄生式继承\n\n>思想： 与原型式继承紧密相关，创建一个仅用于封装继承过程的函数，该函数在内部以某种方式来增强对象，最后再像真的是它做了所有工作一样返回对象\n\n```\nfunction createAnother(original){\n  var clone=object(original);\n  clone.sayHi=function(){\n    alert(\"hi\");\n  };\n  return clone;\n}\n```\n\n- 优点：\n  - 1.在主要考虑对象而不是自定义类型和构造函数的情况下，寄生式继承也是一种有用的模式。\n\n  - 2.object（）函数不是必需的，任何能返回新对象的函数都适用于此模式。\n\n- 缺点:1.适用寄生式继承来为对象添加函数，会由于不能做到函数复用而降低效率，这一点与构造函数模式类似。\n\n## 6.寄生组合式继承\n\n> 思想：通过借用构造函数来继承属性，通过原型链的混成形式来继承方法。  \n不必为了指定子类型的原型而调用超类型的构造函数，我们所需要的无非是超类型原型的一个副本而已。    \n本质上，就是使用寄生式继承来继承超类型的原型，然后将结果指定给子类型的原型\n\n```\nfunction SuperType(name){\n  this.name=name;\n  this.colors=[\"red\",\"blue\",\"green\"];\n}\n\nSuperType.prototype.sayName=function(){\n  alert(this.name);\n};\n\nfunction SubType(name,age){\n  SuperType.call(this,name);\n  this.age=age;\n}\n\ninheritPrototype(subType,superType);//只继承超类型原型中的方法！\n\nSubType.prototype.sayAge=function(){\n  alert (this.age);\n}\n\n```\n\n-  优点：\n  - 1.它只调用了一次超类型的构造函数，效率较高。并且避免了在子类型的原型上创建不必要的，多余的属性。节省内存。\n  - 2.原型链还能保持不变。因此还能正常使用instanceof和isPrototypeOf()。\n\n## 参考\n- [JavaScript高级程序设计 (第3版)](https://fairyly.github.io/mybooks/JavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%EF%BC%88%E7%AC%AC3%E7%89%88%EF%BC%89%E4%B8%AD%E6%96%87%20%E9%AB%98%E6%B8%85%20%E5%AE%8C%E6%95%B4.pdf)\n- [在JS中b如何继承a](https://blog.csdn.net/lee1996jun/article/details/79531960)\n- [JS原型链与继承](https://juejin.im/post/58f94c9bb123db411953691b)\n"
  },
  {
    "path": "4.7.7 ES6 --- rest 参数.md",
    "content": "# ES6 引入 rest 参数\n\n>ES6 引入 rest 参数`（形式为...变量名）`，用于获取函数的多余参数，这样就不需要使用 `arguments` 对象了。  \n`rest` 参数搭配的变量是一个数组，该变量将多余的参数放入数组中。\n\n## rest 参数\n\n- 下面是一个 rest 参数代替arguments变量的例子。\n```\n// arguments变量的写法\nfunction sortNumbers() {\n  return Array.prototype.slice.call(arguments).sort();\n}\n\n// rest参数的写法\nconst sortNumbers = (...numbers) => numbers.sort();\n\n```\n## \n`arguments` 对象不是数组，而是一个类似数组的对象。  \n所以为了使用数组的方法，必须使用``Array.prototype.slice.call``先将其转为数组。  \n`rest` 参数就不存在这个问题，它就是一个真正的数组，数组特有的方法都可以使用。  \n\n下面是一个利用 `rest` 参数改写数组 `push `方法的例子。\n```\nfunction push(array, ...items) {\n  items.forEach(function(item) {\n    array.push(item);\n    console.log(item);\n  });\n}\n\nvar a = [];\npush(a, 1, 2, 3)\n```\n\n>注意，rest 参数之后不能再有其他参数（即只能是最后一个参数），否则会报错。\n\n```\n// 报错\nfunction f(a, ...b, c) {\n  // ...\n}\n```\n\n\n## 参考\n- [es6---rest 参数](http://es6.ruanyifeng.com/#docs/function#rest-%E5%8F%82%E6%95%B0)\n"
  },
  {
    "path": "4.7.8 尾调用.md",
    "content": "# 尾调用\n\n>就是指某个函数的最后一步是调用另一个函数。\n\n```\nfunction f(x){\n  return g(x);\n}\n```\n\n>是调用函数之后，还有别的操作，都不属于尾调用\n\n>尾调用不一定出现在函数尾部，只要是最后一步操作即可。\n\n\n## 尾调用优化\n\n>函数调用会在内存形成一个\"调用记录\"，又称\"调用帧\"（call frame），保存调用位置和内部变量等信息。  \n如果在函数A的内部调用函数B，那么在A的调用记录上方，还会形成一个B的调用记录。  \n等到B运行结束，将结果返回到A，B的调用记录才会消失。  \n如果函数B内部还调用函数C，那就还有一个C的调用记录栈，以此类推。  \n所有的调用记录，就形成一个\"调用栈\"（call stack）。\n\n![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015041002.png)\n\n>\"尾调用优化\"（Tail call optimization），即只保留内层函数的调用记录。  \n如果所有函数都是尾调用，那么完全可以做到每次执行时，调用记录只有一项，这将大大节省内存。  \n这就是\"尾调用优化\"的意义。\n\n\n## 尾递归\n\n>函数调用自身，称为递归。如果尾调用自身，就称为尾递归。\n\n>递归非常耗费内存，因为需要同时保存成千上百个调用记录，很容易发生\"栈溢出\"错误（stack overflow）。  \n但对于尾递归来说，由于只存在一个调用记录，所以永远不会发生\"栈溢出\"错误。\n\n\n## 柯里化（currying），意思是将多参数的函数转换成单参数的形式。这里也可以使用柯里化。\n\n>对于其他支持\"尾调用优化\"的语言（比如Lua，ES6），只需要知道循环可以用递归代替，而一旦使用递归，就最好使用尾递归。\n\n## 参考\n- [尾调用优化](http://www.ruanyifeng.com/blog/2015/04/tail-call.html)\n"
  },
  {
    "path": "4.7.9 BigInt JavaScript 中的任意精度整数.md",
    "content": "# BigInt：JavaScript 中的任意精度整数\n>BigInt 是 JavaScript 中的一个新的数字类型，可以用任意精度表示整数。使用 BigInt，即使超出 Number 的安全整数范围限制，也可以安全地存储和操作大整数。\n\n>BigInt 可以正确执行整数运算而不会溢出。这本身就有无数新的可能性。例如，大数据的数学运算通常用于金融领域。\n\n>BigInt 可以成为实现 BigDecimal 的基础。这将有助于用小数精确地表示货币金额，并准确地对它们进行操作 (a.k.a. the 0.10+0.20!==0.30 problem).\n\n\n## BigInt 是 JavaScript 语言中的一个新的原始类型。因此，可以使用 typeof 运算符来检测类型：\n\n```\ntypeof 123n\n// \"bigint\" \n```\n- 因为 BigInt 是一个单独的类型，所以 BigInt 永远不会等于 Number，例如 42n!==42。  \n  要比较 BigInt 和 Number，在比较之前将其中一个转换为另一个的类型或使用两个等号等号（ ==）：  \n  ```\n  BigInt(42) == 42 // true\n  ```\n- BigInt 和 Number 不是严格相等的，但是宽松相等的。\n\n- 一个问题是，不允许在 BigInt 和 Number 之间混合运算,两者必须转换成同一种类型。这是一件好事，因为任何隐含的强制类型转换都会失去信息\n  - 在两种类型来回转换时要小心，因为 BigInt 变量在转换成 Number 变量时可能会丢失精度。\n-  BigInt() 不是构造函数，因此不能使用 new 操作符。\n- 定义一个 BigInt 变量：在一个整数字面量后面加 n，如：10n，或者调用函数BigInt()。\n- 当使用 BigInt 时，带小数的运算会被取整。\n\n## 参考\n- [BigInt：JavaScript 中的任意精度整数](https://blog.csdn.net/vca54lu0kv27w8zzbd/article/details/80178041)\n- [google---bigint](https://developers.google.com/web/updates/2018/05/bigint)\n- [MDN---BigInt](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt)\n"
  },
  {
    "path": "4.8.1 webpack 打包原理及优化.md",
    "content": "# webpack 打包原理\n\n\n\n>webpack 中每个模块有一个唯一的 id，是从 0 开始递增的。  \n整个打包后的 bundle.js 是一个匿名函数自执行。  \n参数则为一个数组。数组的每一项都为个 function。  \nfunction 的内容则为每个模块的内容，并按照 require 的顺序排列。\n\n>识别入口文件,识别模块依赖，来打包代码。webpack做的就是分析代码,转换代码，编译代码，输出代码\n\n## Webpack的两个最核心的原理分别是：\n\n- 一切皆模块\n>正如js文件可以是一个“模块（module）”一样，其他的（如css、image或html）文件也可视作模 块。   \n因此，你可以require('myJSfile.js')亦可以require('myCSSfile.css')。   \n这意味着我们可以将事物（业务）分割成更小的易于管理的片段，从而达到重复利用等的目的。\n\n- 按需加载\n>传统的模块打包工具（module bundlers）最终将所有的模块编译生成一个庞大的bundle.js文件。  \n但是在真实的app里边，“bundle.js”文件可能有10M到15M之大可能会导致应用一直处于加载中状态。  \n因此Webpack使用许多特性来分割代码然后生成多个“bundle”文件，而且异步加载部分代码以实现按需加载。\n\n\n## 如何实现一个简单的webpack\n- 读取文件分析模块依赖\n- 对模块进行解析执行(深度遍历)\n- 针对不同的模块使用相应的loader\n- 编译模块，生成抽象语法树AST。\n- 循环遍历AST树，拼接输出js。\n\n\n## loader原理\n>在解析对于文件，会自动去调用响应的loader,  \nloader 本质上是一个函数，输入参数是一个字符串，输出参数也是一个字符串。  \n当然，输出的参数会被当成是 JS 代码，从而被 esprima 解析成 AST，触发进一步的依赖解析。  \nwebpack会按照从右到左的顺序执行loader。\n\n\n>一种正确的思路是：  \n使用JS代码解析工具（如[esprima](https://github.com/jquery/esprima)或者[acorn](https://github.com/ternjs/acorn)），  \n将JS代码转换成抽象语法树（AST），  \n再对AST进行遍历。  \n这部分的核心代码是 [parse.js](https://github.com/youngwind/fake-webpack/blob/1bfcd0edf1/lib/parse.js)。\n\n## webpack优化\n- 1.externals配置优化  :使用 externals 将第三方库以 cdn 的方式去引入\n设置externals配置项分离不需要打包的库文件，然后在模版文件中使用script引入即可，配置代码片段如下：\n```\nexternals: {\n  'jquery': 'jquery'\n},\n\nentry: {\n    entry: './src/main.js',\n    vendor: ['vue', 'vue-router', 'vuex', 'element-ui']\n},\n// 这里的output为base中的output，不是生产的output\noutput: {\n    path: config.build.assetsRoot,\n    filename: '[name].js',\n    libraryTarget: \"umd\",\n    publicPath: process.env.NODE_ENV === 'production' ?\n        config.build.assetsPublicPath : config.dev.assetsPublicPath\n},\nexternals: {\n    echarts: 'echarts',\n    _: 'lodash'\n},\n```\n- 2. CDN: 背景图片会有问题\n  - url-loader中单独配置cdn，做到js访问线上路径，静态资源使用cdn，两者互不影响\n- url-loader不能检测到js中的background，所以我们凡是在js中引用的地址，必须在外面先import这张图片，url-loader才会解析并打包\n\n  - [webpack之前端性能优化](https://www.cnblogs.com/ssh-007/p/7944491.html)\n\n- 3.打包后的js过大，将js打包多个文件\n  - 由于webpack打包后的js过大，以至于在加载资源时间过长。所以将文件打包成多个js文件，在需要的时候按需加载。\n优化方案：\n```\nentry:{ \n main:'xxx.js'\n} \nplugins:{\n new commonsChunkPlugin({\n name:'commons',\n minChunks:function(module){\n  // 下边return参考的vue-cli配置\n  // any required modules inside node_modules are extracted to vendor\n  return (\n   module.resource &&\n   /\\.js$/.test(module.resource) &&\n   module.resource.indexOf(\n   path.join(__dirname, '../node_modules')\n   ) === 0\n  )\n }\n}) ,\n// 以下才是关键\nnew commonsChunkPlugin({\n name:'charts',\n chunks:['commons'] \n minChunks:function(module){\n  return (\n   module.resource &&\n   /\\.js$/.test(module.resource) &&\n   module.resource.indexOf(\n   path.join(__dirname, '../node_modules')\n   ) === 0 && ['jquery.js', 'highcharts.js','echarts'].indexOf( module.resource.substr(module.resource.lastIndexOf('/')+1).toLowerCase() ) != -1\n  )\n }\n}) \n}\n```\n\n- 4.使用webpack.optimize.UglifyJsPlugin插件压缩混淆js代码，使用方法如下：\n```\nplugins: [//webpack.config.jsnew webpack.optimize.UglifyJsPlugin({    warnings: false,\n    compress: {\n        join_vars: true,\n        warnings: false,\n    },\n    toplevel: false,\n    ie8: false,\n]\n```\n\n- 5.webpack优化之preload和prefetch\n\n```\nprefetch\n<link rel=\"prefetch\" ></link>\n\n这段代码告诉浏览器，这段资源将会在未来某个导航或者功能要用到，但是本资源的下载顺序权重比较低。也就是说prefetch通常用于加速下一次导航，而不是本次的。\n被标记为prefetch的资源，将会被浏览器在空闲时间加载。\n\n\npreload\n<link rel=\"preload\" ></link>\n\npreload通常用于本页面要用到的关键资源，包括关键js、字体、css文件。\npreload将会把资源得下载顺序权重提高，使得关键数据提前下载好，优化页面打开速度。\n\n```\n\n- 参考： [webpack优化之preload和prefetch](https://www.jianshu.com/p/d2152789759d)\n\n\n## 优化\n\n```\n1. 缩小文件搜索范围,配置比如resolve.modules,resolve.modules,resolve.mainFields,resolve.alias ,resolve.extensions ,module.noParse 配置\n2. 使用DllPlugin 要给 Web 项目构建接入动态链接库\n3.HappyPack 就能让 Webpack 做到这点，它把任务分解给多个子进程去并发的执行，子进程处理完后再把结果发送给主进程\n4.当 Webpack 有多个 JavaScript 文件需要输出和压缩时，原本会使用 UglifyJS 去一个个挨着压缩再输出， 但是 ParallelUglifyPlugin 则会开启多个子进程，把对多个文件的压缩工作分配给多个子进程去完成\n5.可以监听文件的变化，当文件发生变化后可以自动刷新浏览器，从而提高开发效率。\n6.(Hot Module Replacement)的技术可在不刷新整个网页的情况下做到超灵敏的实时预览。 原理是当一个源码发生变化时，只重新编译发生变化的模块，再用新输出的模块替换掉浏览器中对应的老模块。\n7.Tree Shaking 可以用来剔除 JavaScript 中用不上的死代码。它依赖静态的 ES6 模块化语法，例如通过 import 和 export 导入导出\n8.可以使用CommonsChunkPlugin 把多个页面公共的代码抽离成单独的文件进行加载\n9.Webpack 内置了强大的分割代码的功能去实现按需加载，可以用import实现路由按需加载。\n10.Scope Hoisting 可以让 Webpack 打包出来的代码文件更小、运行的更快， 它又译作 \"作用域提升\"\n11.可以使用可视化分析工具 Webpack Analyse等去分析输出结果，从页进行优化.\n12. 对于 Webpack4，打包项目使用 production 模式，这样会自动开启代码压缩\n13.优化图片，对于小图可以使用 base64 的方式写入文件中\n14. 给打包出来的文件名添加哈希，实现浏览器缓存文件\n\n\n参考链接：https://www.jianshu.com/p/9bc2f63da883\n```\n\n- 参考: [webpack优化入门详解](https://juejin.im/post/5a869044f265da4e9c632f94)\n  - [vue + webpack 前端性能优化](https://juejin.im/post/5bc5c106e51d450e7a253e1b)\n\n##  参考\n- [webpack 打包原理](https://www.jianshu.com/p/e24ed38d89fd)\n- [如何实现一个简单的webpack](https://github.com/youngwind/blog/issues/99)\n- [webpack 源码解析](https://lihuanghe.github.io/2016/05/30/webpack-source-analyse.html)\n- [Webpack——令人困惑的地方](https://github.com/chemdemo/chemdemo.github.io/issues/13)\n\n"
  },
  {
    "path": "4.8.1.1 webpack-dev-server是怎么跑起来?.md",
    "content": "# dev-server是怎么跑起来\n\n## 1.1 webpack-dev-middleware\n\nwebpack-dev-middleware 的出现很好地解决了上述问题 —— 作为一个 webpack 中间件，  \n\n它会开启 watch mode 监听文件变更，并自动地在内存中快速地重新打包、提供新的 bundle。\n\n说白了就是 —— 自动编译（watch mode）+速度快（全部走内存）！\n\n\n\n## 1.2 webpack-hot-middleware \n\n>虽然 webpack-dev-middleware 会在文件变更后快速地重新打包，  \n但是每次都得手动刷新客户端页面来访问新的内容，还是略为麻烦。  \n这是因为 webpack-dev-middleware 在应用执行的时候，没办法感知到模块的变化。\n\n那么是否有办法可以让页面也能自动更新呢？webpack-hot-middleware 便是帮忙填这个坑的人\n\nwebpack-hot-middleware 提供的这种能力称为 HMR，所以在介绍 webpack-hot-middleware 之前，我们先来科普一下 HMR。\n\nHMR 即模块热替换（hot module replacement）的简称，它可以在应用运行的时候，不需要刷新页面，就可以直接替换、增删模块。\n\nwebpack 可以通过配置 webpack.HotModuleReplacementPlugin 插件来开启全局的 HMR 能力\n\n\n## 1.3 webpack-dev-server\n\n>虽然 webpack-dev-middleware + webpack-hot-middleware 的组合为开发过程提供了便利，但它们仅适用于服务侧开发的场景。\n\n很多时候我们仅仅对客户端页面做开发，没有直接的 server 来提供支持，这时候就需要 webpack-dev-server 来解囊相助了。\n\n顾名思义，webpack-dev-server 相对前两个工具多了个“server”，\n\n\n## 参考\n- [webpack 插件拾趣 (1) —— webpack-dev-server](https://www.cnblogs.com/vajoy/p/7000522.html)\n"
  },
  {
    "path": "4.8.2 0.1 + 0.2 === 0.3及超大整数相加 .md",
    "content": "# 0.1 + 0.2 === 0.3 \n\n## IEEE 754 规范\n\n>在JavaScript中，所有的Number都是以64-bit的双精度浮点数存储的\n\n二进制在存储的时候是以二进制的“科学计数法”来存储的，我们回顾下十进制的科学计数法，  \n比如54846.3，这个数我们在用标准的科学计数法应该是这样的：5.48463e4，  \n这里有三部分，第一是符号，这是一个正数，只是一般省略正号不写，第二是有效数字部分，这里就是5.48463，最后是指数部分，这里是4。\n\n- 双精度的浮点数在这64位上划分为3段，而这3段也就确定了一个浮点数的值，64bit的划分是“1-11-52”的模式，具体来说：\n\n  - 1.就是1位最高位（最左边那一位）表示符号位，0表示正，1表示负\n\n  - 接下去11位表示指数部分\n\n  - 最后52位表示尾数部分，也就是有效域部分\n\n>JavaScript中0.1+0.2不等于0.3  \n这个是二进制浮点数最大的问题（不仅 JavaScript，所有遵循 IEEE 754 规范的语言都是如此）\n```\n\n(0.1*10 + 0.2*10) /10 \n\n```\n\n## 规避浮点数计算精度问题，可通过以下几种方法：\n\n- ● 调用round() 方法四舍五入或者toFixed() 方法保留指定的位数（对精度要求不高，可用这种方法）\n- ● 将小数转为整数再做计算，即前文提到的那个简单的解决方案\n- ● 使用特殊的进制数据类型，如前文提到的 [bignumber](https://github.com/MikeMcl/bignumber.js)（对精度要求很高，可借助这些相关的类库）\n\n\n## JavaScript实现超大整数相加\n\n>问题：大数相加精度丢失问题（IEEE 754 规范）  \n所以就不能简单的把两个数字，转为Number类型，进行相加。  \n需要取两个数字的每一位，进行相加，大于10，就进1，把结果保存在一个字符串中。\n\n>思路:  \nJavaScript 能表示的最大安全整数是 9007199254740991，可以用API Number.MAX_SAFE_INTEGER 看一下 \n\n```\nfunction add(a,b){\n    // 保存最终结果\n    var res='';\n\n    // 保存两位相加的结果 和 进位值\n    var c=0;\n\n    // 字符串转数组\n    a = a.split('');\n    b = b.split('');\n    while (a.length || b.length || c){\n        // ~~ 用来把String类型 转为 Number类型\n        // 把两位相加的结果 和 进位值相加\n        c += ~~a.pop() + ~~b.pop();\n\n        // 取余，把余数拼接到最终结果中\n        res = c % 10 + res;\n\n        // 保存进位，true 或者 false\n        c = c>9;\n    }\n    return res;\n}\nadd('11111111111111111','22222222222222222');\n\n//  \"33333333333333333\"\nadd('100000000000000000000000','200000000000000000001')\n//  \"100200000000000000000001\"\n```\n**1、~ 是JavaScript中的操作符，按位非，~~ 经常用来进行取整和类型转换，他和显示的用Number进行类型转换还是有区别的，比如处理 undefined 的时候。**\n```\n~~undefined\n// 0\n\nNumber(undefined)\n// NaN\n\n\n~~false === 0  \n~~true === 1\n~~undefined === 0\n~~!undefined === 1\n~~null === 0\n~~!null === 1　　\n~~\"\" === 0\n~~!\"\" === 1\n```\n\n\n\n**而在两个大整数，长度不一样的时候，其中一个数 已经 pop 了所有数组中的元素之后，还要pop的话，就会返回 undefined ，所以如果用 Number 显示的转化，起码要写成这样。**\n```\nvar ai = a.pop();\nai = ai===undefined? 0:Number(ai);\n\nvar bi = b.pop();\nbi = bi===undefined? 0:Number(bi);\n\nc += ai + bi;\n\n明显是用 ~~ 方便。\n\n2、在保存进位值的时候，用的并不是 1 和 0 ，而是true 和 false，这是因为隐式类型转换的时候，true会转为1，false会转为0。\n1+true // 2\n1+false // 1\n```\n\n\n- 参考\n  - [IEEE-754浮点数](https://segmentfault.com/a/1190000009084877)\n  - [IEEE754 浮点数格式 与 Javascript number 的特性](https://segmentfault.com/a/1190000008268668) \n  - [实现超出整数存储范围的两个大正整数相加 function add(a, b)。](https://www.jianshu.com/p/5967985c1e83)\n\n\n- BigInt：JavaScript 中的任意精度整数\n >因为 BigInt 是一个单独的类型，所以 BigInt 永远不会等于 Number，例如 42n!==42\n\n- 另一种做法\n\n```\n//实现超出整数存储范围的两个大整数相加，参数a和b以及函数的返回值都是字符串\n        function func(){\n            var a = '212323443645769879834242345'\n            var b = '2056764062873453875236'\n            var n1 = a.length;\n            var n2 = b.length;\n            for(let i = 0; i < Math.max(n1, n2)-Math.min(n1, n2); i++){\n                if(n1 > n2) b = '0' + b;\n                if(n1 < n2) a = '0' + a;\n            }\n            a = a.split('').reverse();\n            b = b.split('').reverse();\n            //split()基于指定的分隔符将一个字符串分隔成多个子字符串, 并将结果放在一个数组中\n            //reverse() 反转数组项的顺序\n            var n = Math.max(n1, n2);\n            var result = Array.apply(this, Array(n)).map((item, i) => {\n                return 0;\n            })\n            //生成一个长度为n的每个元素都为0的数组\n            for(let k = 0; k < n; k++){\n                var temp = parseInt(a[k])+parseInt(b[k])\n                if(temp > 9){\n                    result[k] += temp -10 ;\n                    result[k+1] = 1;\n                }else{\n                    result[k] += temp;\n                }\n            }\n            //parseInt() 解析一个字符串，并返回整数\n            console.log(result.reverse().join('').toString())\n            //将数组项基于指定的分隔符以字符串输出\n        }\n\n```\n\n## demo\n\n```\n# 小数转成百分比\n\n0.87 -----> 87%\n\n0.87*1000/10 + '%'   87%  这种情况下是可以的\n\nbut:\n\n'2.2'*100000/10     会出现 22000.000000000004\n'2.3'*100000/10     会出现 22999.999999999996\n```\n\n\n## 另外处理\n\n```\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.enableBoundaryChecking = exports.digitToCNchar = exports.float2Fixed = exports.digitLength = exports.round = exports.divide = exports.times = exports.minus = exports.plus = exports.strip = void 0;\n/**\n * @desc 解决浮动运算问题，避免小数点后产生多位数和计算精度损失。\n * 问题示例：2.3 + 2.4 = 4.699999999999999，1.0 - 0.9 = 0.09999999999999998\n */\n/**\n * 把错误的数据转正\n * strip(0.09999999999999998)=0.1\n */\nfunction strip(num, precision) {\n    if (precision === void 0) { precision = 15; }\n    return +parseFloat(Number(num).toPrecision(precision));\n}\nexports.strip = strip;\n/**\n * Return digits length of a number\n * @param {*number} num Input number\n */\nfunction digitLength(num) {\n    var eSplit = num.toString().split(/[eE]/);\n    var len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0);\n    return len > 0 ? len : 0;\n}\nexports.digitLength = digitLength;\n/**\n * 把小数转成整数，支持科学计数法。如果是小数则放大成整数\n * @param {*number} num 输入数\n */\nfunction float2Fixed(num) {\n    if (num.toString().indexOf('e') === -1) {\n        return Number(num.toString().replace('.', ''));\n    }\n    var dLen = digitLength(num);\n    return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num);\n}\nexports.float2Fixed = float2Fixed;\n/**\n * 检测数字是否越界，如果越界给出提示\n * @param {*number} num 输入数\n */\nfunction checkBoundary(num) {\n    if (_boundaryCheckingState) {\n        // @ts-ignore\n        if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {\n            console.warn(num + \" is beyond boundary when transfer to integer, the results may not be accurate\");\n        }\n    }\n}\n/**\n * 迭代操作\n */\nfunction iteratorOperation(arr, operation) {\n    var num1 = arr[0], num2 = arr[1], others = arr.slice(2);\n    var res = operation(num1, num2);\n    others.forEach(function (num) {\n        res = operation(res, num);\n    });\n    return res;\n}\n/**\n * 精确乘法\n */\nfunction times() {\n    var nums = [];\n    for (var _i = 0; _i < arguments.length; _i++) {\n        nums[_i] = arguments[_i];\n    }\n    if (nums.length > 2) {\n        return iteratorOperation(nums, times);\n    }\n    var num1 = nums[0], num2 = nums[1];\n    var num1Changed = float2Fixed(num1);\n    var num2Changed = float2Fixed(num2);\n    var baseNum = digitLength(num1) + digitLength(num2);\n    var leftValue = num1Changed * num2Changed;\n    checkBoundary(leftValue);\n    return leftValue / Math.pow(10, baseNum);\n}\nexports.times = times;\n/**\n * 精确加法\n */\nfunction plus() {\n    var nums = [];\n    for (var _i = 0; _i < arguments.length; _i++) {\n        nums[_i] = arguments[_i];\n    }\n    if (nums.length > 2) {\n        return iteratorOperation(nums, plus);\n    }\n    var num1 = nums[0], num2 = nums[1];\n    // 取最大的小数位\n    var baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));\n    // 把小数都转为整数然后再计算\n    return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;\n}\nexports.plus = plus;\n/**\n * 精确减法\n */\nfunction minus() {\n    var nums = [];\n    for (var _i = 0; _i < arguments.length; _i++) {\n        nums[_i] = arguments[_i];\n    }\n    if (nums.length > 2) {\n        return iteratorOperation(nums, minus);\n    }\n    var num1 = nums[0], num2 = nums[1];\n    var baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));\n    return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;\n}\nexports.minus = minus;\n/**\n * 精确除法\n */\nfunction divide() {\n    var nums = [];\n    for (var _i = 0; _i < arguments.length; _i++) {\n        nums[_i] = arguments[_i];\n    }\n    if (nums.length > 2) {\n        return iteratorOperation(nums, divide);\n    }\n    var num1 = nums[0], num2 = nums[1];\n    var num1Changed = float2Fixed(num1);\n    var num2Changed = float2Fixed(num2);\n    checkBoundary(num1Changed);\n    checkBoundary(num2Changed);\n    // fix: 类似 10 ** -4 为 0.00009999999999999999，strip 修正\n    return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1))));\n}\nexports.divide = divide;\n/**\n * 四舍五入\n */\nfunction round(num, ratio) {\n    var base = Math.pow(10, ratio);\n    var result = divide(Math.round(Math.abs(times(num, base))), base);\n    if (num < 0 && result !== 0) {\n        result = times(result, -1);\n    }\n    return result;\n}\nexports.round = round;\nfunction digitToCNchar(money) {\n    var digit = [\"零\", \"壹\", \"贰\", \"叁\", \"肆\", \"伍\", \"陆\", \"柒\", \"捌\", \"玖\"];\n    var declmalUtil = [\"角\", \"分\"];\n    var IntUtilExc = [\"元\", \"万\", \"亿\", \"兆\"];\n    var IntUtilBac = [\"\", \"拾\", \"佰\", \"仟\"];\n    var head = money < 0 ? \"负\" : \"\";\n    money = Math.abs(money);\n    var res = \"\";\n    //处理小数\n    for (var i = 0; i < declmalUtil.length; i++) {\n        var a = Math.pow(10, i + 1);\n        a = Math.floor(times(a, money)) % 10;\n        res += (digit[a] + declmalUtil[i]).replace(/(零.)+/, '');\n    }\n    if (res.length < 1) {\n        res = \"整\";\n    }\n    //处理整数部分\n    var IntPart = Math.floor(money);\n    for (var i = 0; i < IntUtilExc.length && IntPart > 0; i++) {\n        var part = \"\";\n        for (var j = 0; j < IntUtilBac.length; j++) {\n            var a = IntPart % 10;\n            IntPart = Math.floor(IntPart / 10);\n            part = digit[a] + IntUtilBac[j] + part;\n        }\n        res = part + IntUtilExc[i] + res;\n    }\n    res = res.replace(/(零[拾佰仟])*零元/, \"元\");\n    res = res.replace(/^(零.)+/, \"\");\n    res = res.replace(/(零[拾佰仟])+/g, \"零\");\n    res = res.replace(/零([万亿兆])+/g, \"$1\");\n    res = res.replace(/零([万亿兆])+/g, \"\");\n    res = res.replace(/^整$/, \"零元整\");\n    return head + res;\n}\nexports.digitToCNchar = digitToCNchar;\nvar _boundaryCheckingState = true;\n/**\n * 是否进行边界检查，默认开启\n * @param flag 标记开关，true 为开启，false 为关闭，默认为 true\n */\nfunction enableBoundaryChecking(flag) {\n    if (flag === void 0) { flag = true; }\n    _boundaryCheckingState = flag;\n}\nexports.enableBoundaryChecking = enableBoundaryChecking;\nexports.default = {\n    strip: strip,\n    plus: plus,\n    minus: minus,\n    times: times,\n    divide: divide,\n    round: round,\n    digitLength: digitLength,\n    float2Fixed: float2Fixed,\n    digitToCNchar: digitToCNchar,\n    enableBoundaryChecking: enableBoundaryChecking,\n};\n```\n\n\n## 参考\n- [如何避开JavaScript浮点数计算精度问题（如0.1+0.2!==0.3）](https://blog.csdn.net/u013347241/article/details/79210840)\n- [bignumber.js](https://github.com/MikeMcl/bignumber.js)\n- [mathjs](https://github.com/josdejong/mathjs)\n- [MikeMcl/decimal.js](https://github.com/MikeMcl/decimal.js)\n- [num_operation](https://github.com/hzxshark/num_operation/blob/master/index.js)\n"
  },
  {
    "path": "4.8.2 骨架屏.md",
    "content": "# 骨架屏\n\n>\n\n## 生成骨架屏的方式主要有：\n\n- 1.手写HTML、CSS的方式为目标页定制骨架屏\n  >做法可以参考[<Vue页面骨架屏注入实践>](https://segmentfault.com/a/1190000014832185)，主要思路就是使用 [vue-server-renderer}(https://ssr.vuejs.org/zh/api/) 这个本来用于服务端渲染的插件，  \n  用来把我们写的.vue文件处理为HTML，插入到页面模板的挂载点中，完成骨架屏的注入。    \n  这种方式不甚文明，如果页面样式改变了，还得改一遍骨架屏，增加了维护成本。  \n  骨架屏的样式实现参考 [CodePen](https://codepen.io/janily/pen/rGqQgJ)  \n- 使用图片作为骨架屏；\n  >简单暴力，让UI同学花点功夫吧哈哈；小米商城的移动端页面采用的就是这个方法，它是使用了一个Base64的图片来作为骨架屏。  \n- 自动生成并自动插入静态骨架屏\n  >这种方法跟第一种方法类似，不过是自动生成骨架屏，可以关注下饿了么开源的插件 [page-skeleton-webpack-plugin](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Fpage-skeleton-webpack-plugin) ，它根据项目中不同的路由页面生成相应的骨架屏页面，并将骨架屏页面通过 webpack 打包到对应的静态路由页面中，不过要注意的是这个插件目前只支持history方式的路由，不支持hash方式，  \n  且目前只支持首页的骨架屏，并没有组件级的局部骨架屏实现，作者说以后会有计划实现\n\n\n\n- 2.dps\n\n>基于 DOM 的骨架屏自动生成方案，核心 `evalDOM` 函数\n\n  - [famanoder/dps](https://github.com/famanoder/dps)\n  \n  \n  \n- 3.react-content-loader\n\n>生成骨架布局，支持 React、React Native、Vue 和 普通 HTML。\n\n\n## 参考\n- [Vue项目骨架屏注入实践](https://juejin.im/post/5b79a2786fb9a01a18267362)\n- [一种自动化生成骨架屏的方案](https://github.com/Jocs/jocs.github.io/issues/22)\n- [danilowoz/react-content-loader](https://github.com/danilowoz/react-content-loader)\n"
  },
  {
    "path": "4.8.2.1 toFixed精度丢失解决.md",
    "content": "# 4.8.2.1 toFixed精度丢失解决\n\n\n```\nfunction toFixed(number, precision) {\n    var str = number + ''\n    var len = str.length\n    var last = str.substring(len - 1, len)\n    if (last == '5') {\n        last = '6'\n        str = str.substring(0, len - 1) + last\n        return (str - 0).toFixed(precision)\n    } else {\n        return number.toFixed(precision)\n    }\n}\nconsole.log(toFixed(1.333335, 5))\n```\n\n- 修复方式\n\n```\n//先扩大再缩小法 \nfunction toFixed(num, s) {\n    var times = Math.pow(10, s)\n    // 0.5 为了舍入\n    var des = num * times + 0.5\n    // 去除小数\n    des = parseInt(des, 10) / times\n    return des + ''\n}\nconsole.log(toFixed(1.333332, 5))\n```\n\n- Math.js、BigDecimal.js\n\n## 参考\n- [number-precision](https://github.com/nefe/number-precision)\n- [mathjs]()\n"
  },
  {
    "path": "4.8.2.1 去掉小数尾部零.md",
    "content": "# 4.8.2.1 去掉小数尾部零\n\n\n```\n# 一个数字，如果是整数，返回整数；如果是小数，保留两位，尾部零不要\n\nNumber(val).tofixed(2) - '0'\n\nformatNum(val) {\n    let str = Number(val).toFixed(1);\n    if (!Boolean(str)) return '0';\n    if (!(/^[0-9.]+$/g.test(str))) return '0';\n    while (str.includes(\".\") && (str.endsWith('.') || str.endsWith('0'))) {\n        str = str.slice(0, -1)\n    }\n    return str;\n  },\n\n```\n"
  },
  {
    "path": "4.8.3 document.execCommand.md",
    "content": "# document.execCommand\n\ndocument.execCommand()方法处理Html数据时常用语法格式如下:\n\ndocument.execCommand(sCommand[,交互方式, 动态参数])\n\n其中：sCommand为指令参数（如下例中的”2D-Position”），交互方式参数如果是true的话将显示对话框，如果为false的话，\n\n则不显示对话框（下例中的”false”即表示不显示对话框），动态参数一般为一可用值或属性值（如下例中的”true”）。\n\ndocument.execCommand(”2D-Position”,”false”,”true”);\n\n调用execCommand()可以实现浏览器菜单的很多功能. 如保存文件,打开新文件,撤消、重做操作…等等. 有了这个方法,就可以很容易的实现网页中的文本编辑器.\n\n如果灵活运用,可以很好的辅助我们完成各种项目.\n\n使用的例子如下:\n\n```\n1、〖全选〗命令的实现\n\n[说明]将选种网页中的全部内容！\n[举例]在之间加入：\n全选\n\n2、〖打开〗命令的实现\n\n[说明]这跟VB等编程设计中的webbrowser控件中的命令有些相似，大家也可依此琢磨琢磨。\n[举例]在之间加入：\n打开\n\n3、〖另存为〗命令的实现\n\n[说明]将该网页保存到本地盘的其它目录！\n[举例]在之间加入：\n另存为\n\n4、〖打印〗命令的实现\n\n[说明]当然，你必须装了打印机！\n[举例]在之间加入：\n打印\n\nJs代码 下面列出的是指令参数及意义\n\n//相当于单击文件中的打开按钮\ndocument.execCommand(”Open”);\n\n//将当前页面另存为\ndocument.execCommand(”SaveAs”);\n\n//剪贴选中的文字到剪贴板;\ndocument.execCommand(”Cut”,”false”,null);\n\n//删除选中的文字;\ndocument.execCommand(”Delete”,”false”,null);\n\n//改变选中区域的字体;\ndocument.execCommand(”FontName”,”false”,sFontName);\n\n//改变选中区域的字体大小;\ndocument.execCommand(”FontSize”,”false”,sSize|iSize);\n\n//设置前景颜色;\ndocument.execCommand(”ForeColor”,”false”,sColor);\n\n//使绝对定位的对象可直接拖动;\ndocument.execCommand(”2D-Position”,”false”,”true”);\n\n//使对象定位变成绝对定位;\ndocument.execCommand(”AbsolutePosition”,”false”,”true”);\n\n//设置背景颜色;\ndocument.execCommand(”BackColor”,”false”,sColor);\n\n//使选中区域的文字加粗;\ndocument.execCommand(”Bold”,”false”,null);\n\n//复制选中的文字到剪贴板;\ndocument.execCommand(”Copy”,”false”,null);\n\n//设置指定锚点为书签;\ndocument.execCommand(”CreateBookmark”,”false”,sAnchorName);\n\n//将选中文本变成超连接,若第二个参数为true,会出现参数设置对话框;\ndocument.execCommand(”CreateLink”,”false”,sLinkURL);\n\n//设置当前块的标签名;\ndocument.execCommand(”FormatBlock”,”false”,sTagName);\n\n//相当于单击文件中的打开按钮\ndocument.execCommand(”Open”);\n\n//将当前页面另存为\ndocument.execCommand(”SaveAs”);\n\n//剪贴选中的文字到剪贴板;\ndocument.execCommand(”Cut”,”false”,null);\n\n//删除选中的文字;\ndocument.execCommand(”Delete”,”false”,null);\n\n//改变选中区域的字体;\ndocument.execCommand(”FontName”,”false”,sFontName);\n\n//改变选中区域的字体大小;\ndocument.execCommand(”FontSize”,”false”,sSize|iSize);\n\n//设置前景颜色;\ndocument.execCommand(”ForeColor”,”false”,sColor);\n\n//使绝对定位的对象可直接拖动;\ndocument.execCommand(”2D-Position”,”false”,”true”);\n\n//使对象定位变成绝对定位;\ndocument.execCommand(”AbsolutePosition”,”false”,”true”);\n\n//设置背景颜色;\ndocument.execCommand(”BackColor”,”false”,sColor);\n\n//使选中区域的文字加粗;\ndocument.execCommand(”Bold”,”false”,null);\n\n//复制选中的文字到剪贴板;\ndocument.execCommand(”Copy”,”false”,null);\n\n//设置指定锚点为书签;\ndocument.execCommand(”CreateBookmark”,”false”,sAnchorName);\n\n//将选中文本变成超连接,若第二个参数为true,会出现参数设置对话框;\ndocument.execCommand(”CreateLink”,”false”,sLinkURL);\n\n//设置当前块的标签名;\ndocument.execCommand(”FormatBlock”,”false”,sTagName);\n\ndocument对象execCommand通常在IE中在线处理Html数据时非常有用，它可以让你轻而易举实现文字的加粗、加颜色、加字体等一系列的命令。\n\nD-Position 允许通过拖曳移动绝对定位的对象。\nAbsolutePosition 设定元素的 position 属性为“absolute”(绝对)。\nBackColor 设置或获取当前选中区的背景颜色。\nBlockDirLTR 目前尚未支持。\nBlockDirRTL 目前尚未支持。\nBold 切换当前选中区的粗体显示与否。\nBrowseMode 目前尚未支持。\nCopy 将当前选中区复制到剪贴板。\nCreateBookmark 创建一个书签锚或获取当前选中区或插入点的书签锚的名称。\nCreateLink 在当前选中区上插入超级链接，或显示一个对话框允许用户指定要为当前选中区插入的超级链接的 URL。\nCut 将当前选中区复制到剪贴板并删除之。\nDelete 删除当前选中区。\nDirLTR 目前尚未支持。\nDirRTL 目前尚未支持。\nEditMode 目前尚未支持。\nFontName 设置或获取当前选中区的字体。\nFontSize 设置或获取当前选中区的字体大小。\nForeColor 设置或获取当前选中区的前景(文本)颜色。\nFormatBlock 设置当前块格式化标签。\nIndent 增加选中文本的缩进。\nInlineDirLTR 目前尚未支持。\nInlineDirRTL 目前尚未支持。\nInsertButton 用按钮控件覆盖当前选中区。\nInsertFieldset 用方框覆盖当前选中区。\nInsertHorizontalRule 用水平线覆盖当前选中区。\nInsertIFrame 用内嵌框架覆盖当前选中区。\nInsertImage 用图像覆盖当前选中区。\nInsertInputButton 用按钮控件覆盖当前选中区。\nInsertInputCheckbox 用复选框控件覆盖当前选中区。\nInsertInputFileUpload 用文件上载控件覆盖当前选中区。\nInsertInputHidden 插入隐藏控件覆盖当前选中区。\nInsertInputImage 用图像控件覆盖当前选中区。\nInsertInputPassword 用密码控件覆盖当前选中区。\nInsertInputRadio 用单选钮控件覆盖当前选中区。\nInsertInputReset 用重置控件覆盖当前选中区。\nInsertInputSubmit 用提交控件覆盖当前选中区。\nInsertInputText 用文本控件覆盖当前选中区。\nInsertMarquee 用空字幕覆盖当前选中区。\nInsertOrderedList 切换当前选中区是编号列表还是常规格式化块。\nInsertParagraph 用换行覆盖当前选中区。\nInsertSelectDropdown 用下拉框控件覆盖当前选中区。\nInsertSelectListbox 用列表框控件覆盖当前选中区。\nInsertTextArea 用多行文本输入控件覆盖当前选中区。\nInsertUnorderedList 切换当前选中区是项目符号列表还是常规格式化块。\nItalic 切换当前选中区斜体显示与否。\nJustifyCenter 将当前选中区在所在格式化块置中。\nJustifyFull 目前尚未支持。\nJustifyLeft 将当前选中区所在格式化块左对齐。\nJustifyNone 目前尚未支持。\nJustifyRight 将当前选中区所在格式化块右对齐。\nLiveResize 迫使 MSHTML 编辑器在缩放或移动过程中持续更新元素外观，而不是只在移动或缩放完成后更新。\nMultipleSelection 允许当用户按住 Shift 或 Ctrl 键时一次选中多于一个站点可选元素。\nOpen 目前尚未支持。\nOutdent 减少选中区所在格式化块的缩进。\nOverWrite 切换文本状态的插入和覆盖。\nPaste 用剪贴板内容覆盖当前选中区。\nPlayImage 目前尚未支持。\nPrint 打开打印对话框以便用户可以打印当前页。\nRedo 目前尚未支持。\nRefresh 刷新当前文档。\nRemoveFormat 从当前选中区中删除格式化标签。\nRemoveParaFormat 目前尚未支持。\nSaveAs 将当前 Web 页面保存为文件。\nSelectAll 选中整个文档。\nSizeToControl 目前尚未支持。\nSizeToControlHeight 目前尚未支持。\nSizeToControlWidth 目前尚未支持。\nStop 目前尚未支持。\nStopImage 目前尚未支持。\nStrikeThrough 目前尚未支持。\nSubscript 目前尚未支持。\nSuperscript 目前尚未支持。\nUnBookmark 从当前选中区中删除全部书签。\nUnderline 切换当前选中区的下划线显示与否。\nUndo 目前尚未支持。\nUnlink 从当前选中区中删除全部超级链接。\nUnselect 清除当前选中区的选中状态。\n```\n\n\n\n## 参考\n- [document.execCommand()方法处理Html数据](https://segmentfault.com/a/1190000015560036)\n"
  },
  {
    "path": "4.8.4 js正则替换日期格式.md",
    "content": "# js正则替换日期格式\n\n## 2018-1-1  ===> 2018-01-01\n```\n'2017-6-5'.replace(/-(\\d{1})/g,'-0$1') // 匹配 - 后是一位的数字，替换成 -0 形式\n// \"2017-06-05\"\n\n这个替换规则应该是把-(\\d{1})整个替换了，分组是为后面$1用的\n\n上面的做法目前有些问题的，如果 2017-06-12 会被替换成 2017-006-012\n\n\n就是说需要把不符合规则的替换，而不是把符合规则的也给替换掉\n\n所以就先判断匹配不，不匹配了就再去正则替换\n\n'2017-6-2'.match(/^(\\d{4})(-)(\\d{2})(-)(\\d{2})$/)\n\nfunction format(date) {\n  let flag = date.match(/^(\\d{4})(-)(\\d{2})(-)(\\d{2})$/)\n  if(!flag) {\n    return date.replace(/-(\\d{1})/g,'-0$1')\n  }\n  return date\n}\n\nformat('2017-4-4')  // \"2017-04-04\"\n\nformat('2017-04-04')  // \"2017-04-04\"\n```\n\n\n## 参考\n- [js正则替换日期格式问题](https://segmentfault.com/q/1010000009592228)\n- [js正则表达式中replace的0,1是什么？](https://segmentfault.com/q/1010000005727586)\n"
  },
  {
    "path": "4.8.5 react-router的实现原理.md",
    "content": "# react-router的实现原理\n\n文章主要包含两大部分: 一是对react-router赖以依存的history进行研究；二是分析react-router是如何实现URL与UI同步的。\n\n## 1.react-router依赖基础 - history\n\n- 1.1History整体介绍\n\n[history](https://github.com/reactjs/history/blob/master/docs/Glossary.md?name=232#sssss)是一个独立的第三方js库，可以用来兼容在不同浏览器、不同环境下对历史记录的管理，拥有统一的API。具体来说里面的history分为三类:\n\n  - 老浏览器的history: 主要通过hash来实现，对应createHashHistory\n\n  - 高版本浏览器: 通过html5里面的history，对应createBrowserHistory\n\n  - node环境下: 主要存储在memeory里面，对应createMemoryHistory\n\n- 1.2内部解析\n\n三个API的大致的技术实现如下:\n\n```\ncreateBrowserHistory: 利用HTML5里面的history\n\ncreateHashHistory: 通过hash来存储在不同状态下的history信息\n\ncreateMemoryHistory: 在内存中进行历史记录的存储\n```\n\n- 1.2.1 执行URL前进\n```\ncreateBrowserHistory: pushState、replaceState\n\ncreateHashHistory: location.hash=*** location.replace()\n\ncreateMemoryHistory: 在内存中进行历史记录的存储\n```\n\n\n## 参考\n- [react-router的实现原理](https://blog.csdn.net/tangzhl/article/details/79696055)\n"
  },
  {
    "path": "4.8.6 写一个 Webpack Loader.md",
    "content": "# 写一个 Webpack Loader\n\n## loader: 模块和资源的转换器\n>loader 的功能：把源模块转换成通用模块。\n\n- webpack 规定 use 数组中 loader 的执行顺序是从最后一个到第一个\n\n\n## 用法准则\n\n```\n简单易用。\n使用链式传递。\n模块化的输出。\n确保无状态。\n使用 loader utilities。\n记录 loader 的依赖。\n解析模块依赖关系。\n提取通用代码。\n避免绝对路径。\n使用 peer dependencies。\n```\n\n\n## loader 工具库(Loader Utilities) \n\n充分利用 loader-utils 包。它提供了许多有用的工具，  \n但最常用的一种工具是获取传递给 loader 的选项。  \nschema-utils 包配合 loader-utils，用于保证 loader 选项，进行与 JSON Schema 结构一致的校验。\n\n这里有一个简单使用两者的例子：\n\n```\nloader.js\n\nimport { getOptions } from 'loader-utils';\nimport validateOptions from 'schema-utils';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    test: {\n      type: 'string'\n    }\n  }\n};\n\nexport default function(source) {\n  const options = getOptions(this);\n\n  validateOptions(schema, options, 'Example Loader');\n\n  // 对资源应用一些转换……\n\n  return `export default ${ JSON.stringify(source) }`;\n}\n```\n\n\n\n## 参考\n- [writing-a-loader](https://webpack.docschina.org/contribute/writing-a-loader)\n- [手把手教你撸一个 Webpack Loader](https://blog.csdn.net/lszy16/article/details/79162960)\n"
  },
  {
    "path": "4.8.6.1 webpack 插件机制.md",
    "content": "# webpack 插件机制\n\n>webpack 插件是一个具有 apply 方法的 JavaScript 对象。apply 属性会被 webpack compiler 调用，并且 compiler 对象可在整个编译生命周期访问。\n\n\n## 特点\n>webpack 插件\n先来瞅瞅 webpack 插件在项目中的运用\n```\nconst MyPlugin = require('myplugin')\nconst webpack = require('webpack')\n\nwebpack({\n  ...,\n  plugins: [new MyPlugin()]\n  ...,\n})\n```\n\n-那么符合什么样的条件能作为 webpack 插件呢？一般来说，webpack 插件有以下特点：\n\n- 1.独立的 JS 模块，暴露相应的函数\n\n- 2.函数原型上的 apply 方法会注入 compiler 对象\n\n- 3.compiler 对象上挂载了相应的 webpack 事件钩子\n\n- 4.事件钩子的回调函数里能拿到编译后的 compilation 对象，如果是异步钩子还能拿到相应的 callback\n\n下面结合代码来看看：\n```\nfunction MyPlugin(options) {}\n// 2.函数原型上的 apply 方法会注入 compiler 对象\nMyPlugin.prototype.apply = function(compiler) {\n  // 3.compiler 对象上挂载了相应的 webpack 事件钩子 4.事件钩子的回调函数里能拿到编译后的 compilation 对象\n  compiler.plugin('emit', (compilation, callback) => {\n    ...\n  })\n}\n// 1.独立的 JS 模块，暴露相应的函数\nmodule.exports = MyPlugin\n```\n\n\n## compiler 对象\n>compiler 即 webpack 的编辑器对象，在调用 webpack 时，会自动初始化 compiler 对象\n\ncompiler 对象中包含了所有 webpack 可配置的内容，开发插件时，我们可以从 compiler 对象中拿到所有和 webpack 主环境相关的内容。\n\n\n\n## compilation 对象\n>compilation 对象代表了一次单一的版本构建和生成资源。  \n当运行 webpack 时，每当检测到一个文件变化，一次新的编译将被创建，从而生成一组新的编译资源。  \n一个编译对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。\n\n\n\n\n\n\n\n\n\n\n\n## 参考\n- [webpack-plugin](https://webpack.docschina.org/concepts/plugins/#%E5%89%96%E6%9E%90)\n- [全面的Webpack教程《深入浅出Webpack》电子书](https://github.com/gwuhaolin/dive-into-webpack/)\n- [探寻 webpack 插件机制](https://www.cnblogs.com/MuYunyun/p/8875908.html)\n- [看清楚真正的 Webpack 插件](https://zoumiaojiang.com/article/what-is-real-webpack-plugin/#compiler)\n- [如何写一个webpack插件（一）](https://github.com/lcxfs1991/blog/issues/1)\n- [《深入浅出Webpack》:5-4 编写 Plugin](http://webpack.wuhaolin.cn/5%E5%8E%9F%E7%90%86/5-4%E7%BC%96%E5%86%99Plugin.html)\n"
  },
  {
    "path": "4.8.7 深入理解Babel原理及其使用.md",
    "content": "# 深入理解Babel原理及其使用\n\n\n## bable\n>简称：转码器(转译器:因为它只是把同种语言的高版本规则翻译成低版本规则，而不像编译器那样，输出的是另一种更低级的语言代码)\n\n## babel的工作原理\n>和编译器类似，babel的转译过程也分为三个阶段：\nparsing、transforming、generating，\n\n- 以ES6代码转译为ES5代码为例，babel转译的具体过程如下：\n```\nES6代码输入 ==》 babylon进行解析 ==》 得到AST\n==》 plugin用babel-traverse对AST树进行遍历转译 ==》 得到新的AST树\n==》 用babel-generator通过AST树生成ES5代码\n```\n\n\n\n## 1.polyfill (垫片库)\n>polyfill是一个针对ES2015+环境的shim，    \n实现上来说babel-polyfill包只是简单的把core-js和regenerator runtime包装了下，  \n这两个包才是真正的实现代码所在（后文会详细介绍core-js）。\n\n\n\n## 2.runtime\n- polyfill和runtime的区别\n\n直接使用babel-polyfill对于应用或页面等环境在你控制之中的情况来说，并没有什么问题。  \n但是对于在library中使用polyfill，就变得不可行了。  \n因为library是供外部使用的，但外部的环境并不在library的可控范围，  \n而polyfill是会污染原来的全局环境的（因为新的原生对象、API这些都直接由polyfill引入到全局环境）。  \n这样就很容易会发生冲突，所以这个时候，babel-runtime就可以派上用场了\n\n\n\n## 3.通过core-js实现按需引入polyfill或runtime\n>core-js包才上述的polyfill、runtime的核心，因为polyfill和runtime其实都只是对core-js和regenerator的再封装，方便使用而已。\n但是polyfill和runtime都是整体引入的，不能做细粒度的调整，  \n如果我们的代码只是用到了小部分ES6而导致需要使用polyfill和runtime的话，会造成代码体积不必要的增大（runtime的影响较小）。   \n所以，按需引入的需求就自然而然产生了，这个时候就得依靠core-js来实现了。\n\n\n\n## 参考\n- [深入理解Babel原理及其使用](http://www.fly63.com/article/detial/197)\n"
  },
  {
    "path": "4.8.8 垃圾回收机制.md",
    "content": "# 垃圾回收机制\n>垃圾收集机制原理：  \n垃圾收集器会按照固定的时间间隔（或代码执行中预定的收集时间）， 周期性地执行这一操作：  \n找出那些不再继续使用的变量，然后释放其占用的内存。\n\n\n## 1.标记清除\n\nJavaScript中最重用的垃圾收集方式是标记清除（mark-and-sweep）。Take is cheap, let me show you the code.\n\n \n\n>当运行addTen()这个函数的时候，就是当变量进入环境时，就将这个变量标记为“进入环境”。    \n从逻辑上讲，永远不能释放进入环境的变量所占用的内存，因为只要执行流进入相应的环境，就可能会用到它们。  \n而当变量离开环境时，则将其标记为“离开环境”。\n\n\n\n## 2.引用计数\n\n另一种不太常见的垃圾收集策略叫做引用计数（reference counting）。引用计数的含义是跟踪记录每个值被引用的次数。\n\n>当声明了一个变量并将一个引用类型值赋值该变量时，则这个值的引用次数就是1.  \n如果同一个值又被赋给另外一个变量，则该值得引用次数加1。  \n相反，如果包含对这个值引用的变量又取 得了另外一个值，则这个值的引用次数减 1。  \n当这个值的引用次数变成 0  时，则说明没有办法再访问这个值了，因而就可以将其占用的内存空间回收回来。  \n这样，当垃圾收集器下次再运行时，它就会释放那 些引用次数为零的值所占用的内存。 \n\n问题：循环引用。\n\n假如这个函数被重复多次调用，就会导致大量内存得不到回收。  \n\n为此放弃了引用计数方式，转而采用标记清除来实现其垃圾收集机制。  \n\n可是，引用计数导致的麻烦并未就此终结。\n\nIE 中有一部分对象并不是原生 JavaScript 对象。  \n例如，其 BOM 和 DOM 中的对象就是使用 C++以 COM（Component Object Model，组件对象模型）对象的形式实现的，  \n而 COM对象的垃圾 收集机制采用的就是引用计数策略。\n\n因此，即使 IE的 JavaScript引擎是使用标记清除策略来实现的，  \n但 JavaScript访问的 COM对象依然是基于引用计数策略的。  \n换句话说，只要在IE中涉及 COM对象，就会存在循环引用的问题。\n\n## 参考\n- [JavaScript垃圾收集-标记清除和引用计数](https://www.cnblogs.com/scottjeremy/p/6870729.html)\n- [js的内存泄漏场景、监控以及分析](https://www.cnblogs.com/dasusu/p/12200176.html)\n"
  },
  {
    "path": "4.8.9 React Native 的运行机制 和 Cordova PhoneGap 的运行机制.md",
    "content": "# React Native 的运行机制\n\n>1.初始化 React Native   \n2.读取 JavaScript 源码  \n3.JavaScript 加载进内存, JSX 代码已经被转化成原生的 JavaScript 代码  \n4.初始化模块信息  \n  这一步在方法 initModulesWithDispatchGroup中实现,主要任务是找到所有需要暴露给 JavaScript的类  \n  初始化 JavaScript 代码的执行器,即 RCTJSCExecutor 对象  \n5.生成模块列表并写入 JavaScript 端  \n6.执行 JavaScript 源码\n\n>在 React Native 中,Objective-C 和 JavaScript 的交互都是通过传递 ModuleId 、 MethodId、CallbackID和 Arguments 进行的,大概流程是这样:\n\nJS调用OC方法——> 通过配置表转换参数(ModuleId MethodId CallbackID Arguments)——>     \n传递参数到消息队列——> OC拿到参数并通过配置列表执行对应函数——>     \n执行后返回回调ID和参数 ——> JS执行通过ID执行回调函数 \n\n- 参考：[React-Native运行机制简介Script](https://www.aliyun.com/jiaocheng/1005640.html)\n\n\n## 1.区别\n>Cordova 和 React-Native 是使用 Web 开发移动端的两大框架。  \nCordova 是 Apache 旗下的。 React-Native 是 Facebook 旗下的在2013年发布的一个前端框架。两者皆开源。  \n下面的内容主要记录了这两大框架的优劣。以及移动端开发中有关 WebView 比较可行的几种选择。  \nCordova文档 ， React-Native文档 。\n\n### 1.1对比 \n- 跨平台特性\n\nCordova: write once, run anywhere ( 一次开发，随处运行)  \nReact-Native: Learn once, write anywhere ( 一次学习，随处开发)\n\n- 功能支持\n\nCordova: 基本功能完全具备，对于底层，如摄像头之类的，需要插件。  \nReact-Native: 完全支持。 Android 端不是很完善   \n\n- 风险程度\n\nNative 比 cordova 高。  \n\n- 开发成本\n\nCordova: 完全基于 html，css，js 。写一次代码，两个平台都适用。  \nReact-Native: 具有相同语法体系，但需要根据不同平台编写不同代码。\n\n- 运行速度\n\nCordova: 相对较慢  \nReact-Native: 跟 Native 基本相当  \n\n- WebView问题 \n\n内存泄漏\n\n### 1.2 优劣对比\n\n- cordova  ionic ：\n\n优势：\n    - ios 和 android 基本上可以共用代码，纯web思维，开发速度快，简单方便，一次编码，到处运行，如果熟悉web开发，则开发难度较低。\n    - 文档很全，系统级支持封装较好，所有UI组件都是有html模拟，可以统一使用。\n\n    - 可实现在线更新 允许加载动态加载web js\n\n    - 文档多，开发者多，视频教程多 容易学习    遇到问题容易解决  技术成熟\n\n\n\n劣势：\n\n   占用内存高一些（不过手机内存都大了不影响），不适合做游戏类型app，     \n   web技术无法解决一切问题，对于比较耗性能的地方无法利用native的思维实现优势互补，如高体验的交互，动画等。\n\n\n- react-native ：\n\n优势：\n\n1、虽然不能做到一处编码到处运行，但是基本上即使是两套代码，也是相同的jsx语法，使用js进行开发。用户体验，高于html，开发效率较高   \n2、flexbox 布局 据说比native的自适应布局更加简单高效\n    可实现在线更新 2015.7.28 AppStore审核政策调整：允许运行于JavascriptCore的动态加载代码\n    更贴近原生开发\n\n劣势：\n\n1、（引）对开发人员要求较高，不是懂点web技术就行的，当官方封装的控件、api无法满足需求时 就必然需要懂一些native的东西去扩展，  \n扩展性仍然远远不如web，也远远不如直接写Native code。\n\n2、（引）官方说得很隐晦：learn once, write anywhere。人家可没说run anywhere。事实上，  \n从官方的api来看SliderIOS，SwitchIOS..等等这些控件，之后势必会出现SliderAndroid，SwitchAndroid...，  \n也就是很可能针对不同的平台会需要写多套代码。  \n\n3、发展还不成熟，目前很多ui组件只有ios的实现，android的需要自己实现。\n >（引）从Native到Web，要做很多概念转换，势必造成双方都要妥协。  \n 比如web要用一套CSS的阉割版，Native通过css-layout拿到最终样式再转换成native原生的表达方式（比如iOS的Constraint\\origin\\Center等属性），  \n 再比如动画。另外，若Android和iOS都要做相同的封装，概念转换就更复杂   \n \n5、文档还不够完整 学习曲线偏高\n4.文档少  学习起来困难\n```\n\n\n\n## Cordova PhoneGap 的运行机制\n\n>它采用HTML5+JavaScript的模式来开发应用。    \nPhoneGap用JavaScript统一封装了几大平台的本地api（Andriod，IOS，WP8/7，WINRT）等等。   \n这样的话从一个平台移植到另外一个平台只需要把HTML代码跟JS原封不动的拿过去，打包一下就可以了。   \nPhoneGap后来被Adobe收购，然后又贡献给了开源社区，现在由Apache管理，改名cordova\n\n\n## 参考\n- [React Native通信机制详解](http://blog.cnbang.net/tech/2698/)\n- [Cordova和React-Native两种框架的对比](https://blog.csdn.net/timtian008/article/details/54890514)\n- [React-Native简介与运行原理解析](https://www.jianshu.com/p/82a28c8b673b)\n"
  },
  {
    "path": "4.9.0 交换两个变量（不止数字）.md",
    "content": "# 交换两个变量（不止数字）\n\n## 数字交换（不使用其他变量）\n\n```\nlet a = 8;\nlet b = 10;\n\nb = b-a;\na = a+b;\nb = a-b;\n\n```\n\n\n## 交换变量\n\n```\nlet a = 'test1';\nlet b = 'test2';\n\n[a,b]  = [b,a]\n\na // \"test2\"\nb // \"test1\"\n\n```\n\n\n\n\n\n\n##  参考\n- [es6 - 变量的解构赋值](http://es6.ruanyifeng.com/?search=%E9%80%97%E5%8F%B7&x=0&y=0#docs/destructuring#%E7%94%A8%E9%80%94)\n"
  },
  {
    "path": "4.9.1  Object.assign 实现.md",
    "content": "#  Object.assign\n\n>Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。\n\n\n```\n此polyfill不支持 symbol 属性，因为ES5 中根本没有 symbol ：\n\nif (typeof Object.assign != 'function') {\n  // Must be writable: true, enumerable: false, configurable: true\n  Object.defineProperty(Object, \"assign\", {\n    value: function assign(target, varArgs) { // .length of function is 2\n      'use strict';\n      if (target == null) { // TypeError if undefined or null\n        throw new TypeError('Cannot convert undefined or null to object');\n      }\n\n      var to = Object(target);\n\n      for (var index = 1; index < arguments.length; index++) {\n        var nextSource = arguments[index];\n\n        if (nextSource != null) { // Skip over if undefined or null\n          for (var nextKey in nextSource) {\n            // Avoid bugs when hasOwnProperty is shadowed\n            if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {\n              to[nextKey] = nextSource[nextKey];\n            }\n          }\n        }\n      }\n      return to;\n    },\n    writable: true,\n    configurable: true\n  });\n}\n```\n\n\n\n\n## 参考\n- [MDN-Object.assign()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)\n"
  },
  {
    "path": "4.9.2 JavaScript设计模式.md",
    "content": "## 《JavaScript设计模式与开发实践》最全知识点汇总大全\n系列文章：\n\n[《JavaScript设计模式与开发实践》基础篇（1）—— this、call 和 apply](https://juejin.im/post/5c0d15a3f265da612e2876a2)\n\n[《JavaScript设计模式与开发实践》基础篇（2）—— 闭包和高阶函数](https://juejin.im/post/5c0e670ce51d452e2c697955\n\n[《JavaScript设计模式与开发实践》模式篇（1）—— 单例模式](https://juejin.im/post/5c0f5ca4f265da616d540801)\n\n[《JavaScript设计模式与开发实践》模式篇（2）—— 策略模式](https://juejin.im/post/5c0fc32ae51d455c9f4a3afc)\n\n[《JavaScript设计模式与开发实践》模式篇（3）—— 代理模式](https://juejin.im/post/5c10ff28518825778a56cb3e)\n\n[《JavaScript设计模式与开发实践》模式篇（4）—— 迭代器模式](https://juejin.im/post/5c11083de51d45702018aa47)\n\n[《JavaScript设计模式与开发实践》模式篇（5）—— 观察者模式](https://juejin.im/post/5c1261526fb9a049b221c1d1)\n\n[《JavaScript设计模式与开发实践》模式篇（6）—— 命令模式](https://juejin.im/post/5c1379cd6fb9a049c2325166)\n\n[《JavaScript设计模式与开发实践》模式篇（7）—— 组合模式](https://juejin.im/post/5c163d045188252704369430)\n\n[《JavaScript设计模式与开发实践》模式篇（8）—— 模板方法模式](https://juejin.im/post/5c179e446fb9a04a102f3317)\n\n[《JavaScript设计模式与开发实践》模式篇（9）—— 享元模式](https://juejin.im/post/5c1902e751882546150aef0c)\n\n[《JavaScript设计模式与开发实践》模式篇（10）—— 职责链模式](https://juejin.im/post/5c1a37ca6fb9a049f8193cc8)\n\n[《JavaScript设计模式与开发实践》模式篇（11）—— 中介者模式](https://juejin.im/post/5c1ee1d66fb9a049ab0d9af6)\n\n[《JavaScript设计模式与开发实践》模式篇（12）—— 装饰者模式](https://juejin.im/post/5c20f4566fb9a049b82a7a93)\n\n[《JavaScript设计模式与开发实践》模式篇（13）—— 状态模式](https://juejin.im/post/5c23725bf265da61764aedc7)\n\n[《JavaScript设计模式与开发实践》模式篇（14）—— 适配器模式](https://juejin.im/post/5c237cb3518825444612dd2a)\n\n[《JavaScript设计模式与开发实践》原则篇（1）—— 单一职责原则](https://juejin.im/post/5c261e9fe51d4570f1455253)\n\n[《JavaScript设计模式与开发实践》原则篇（2）—— 最少知识原则](https://juejin.im/post/5c282b8a51882538472091ba)\n\n[《JavaScript设计模式与开发实践》原则篇（3）—— 开放-封闭原则](https://juejin.im/post/5c2e0c3f6fb9a049c30b5f2d)\n\n\n## 参考\n- [《JavaScript设计模式与开发实践》最全知识点汇总大全](https://juejin.im/post/5c2e10a76fb9a049c0432697)\n"
  },
  {
    "path": "4.9.3 a标签target=”_blank”的安全问题 .md",
    "content": "# a标签target=”_blank”的安全问题\n\n\n举个例子，在页面a.html中有这样一段代码：\n\n```\n<a href=\"b.html\" target=\"_blank\">跳转</a>;\n\n```\n\n当我们点击页面a.html中的跳转链接时，浏览器会在新的窗口或标签页中打开b.html，假如这个时候b.html中有这样一段js代码：\n\n```\nif (window.opener) {\n    window.opener.location.href = 'eval.html';\n}\n\n```\n\n当页面b.html被打开的同时原来打开a.html的标签页会被重定向到eval.html, eval.html可以是和原来域完全不相关的其它域的资源。\n\n\n因此 对于使用了 target=\"_blank\" 并且跳转到外部链接的超链接，加上 rel=\"noopener noreferrer\" 属性即可，此时外部链接获取到的 opener 为 null。\n\nrel=\"noopener\" 可以确保 window.opener 为 null ,\n\n在 Chrome 49+ 和 Opera 36，而对于旧版本浏览器和火狐浏览器，可以加上 rel=\"noreferrer\" 更进一步禁用 HTTP 的 Referer 头，  \n\n或者使用 js 打开新页面。如下js代码：\n\n```\n<div id=\"btn\">test</div>\n<script type=\"text/javascript\">\n  const safeOpen = url => {\n    var otherWindow = window.open();\n    otherWindow.opener = null;\n    otherWindow.location = url;\n  }\n  document.getElementById('btn').onclick = function() {\n    safeOpen('http://wwww.baidu.com');\n  }\n</script>\n```\n\n理解 rel=\"external nofollow noreferrer\"\n\nrel = 'nofollow' 的作用是：它是来告诉搜索引擎，不要将该链接计入权重，  \n\n因为在很多情况下，我们可以将一些不想传递权重的链接进行nofollow处理，比如一些非本站的链接，不想传递权重，但是又需要加在页面中，  \n\n比如一些 统计代码，备案号链接，供用户查询链接等等这些。\n\nrel = 'external' 的作用是：它告诉搜索引擎，这个链接不是本站链接.\n\nrel = 'external nofollow'的作用就是上面两种属性一起的含义了，可以理解为：这个链接非本站链接，不要爬取也不要传递权重。\n\n因此对于SEO角度来讲，可以有效的减少蜘蛛爬行的流失。\n\nrel=\"nofollow noopener noreferrer\" 超链接 target=\"_blank\" 要增加 rel=\"nofollow noopener noreferrer\" 来堵住钓鱼安全漏洞。\n\n如果你在链接上使用 target=\"_blank\"属性，并且不加上rel=\"noopener\"属性，那么你就让用户暴露在一个非常简单的钓鱼攻击之下。\n\n\n## 参考\n- [关于html的a标签的target=\"__blank \"的安全漏洞问题](https://www.cnblogs.com/tugenhua0707/p/9709424.html)\n- [a标签target=”_blank”的安全问题及解决办法](https://www.deanhan.cn/target-blank-problem.html)\n"
  },
  {
    "path": "4.9.4 计算两个数组的交集.md",
    "content": "# 计算两个数组的交集\n\n>  一开始我想直接一个循环 + includes 就搞定，太简单了，而看到[第 59 题：给定两个数组，写一个方法来计算它们的交集](https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/102)  \n这里的时候，发现确实想的太简单  \n\n\n```\n例如\n\n const b = [1]\n const a = [1, 1]\n\nfunction fn(arr1, arr2) {\n   return arr1.filter(item => {\n      return arr2.includes(item)\n   })\n}\n\n期望\n\nconsole.log(fn(a, b))//[1]\n\n然后确是 [1, 1]\n```\n\n- 使用 空间换时间：\n\n```\nfunction getIntersection (num1, num2) {\n    const maps = {};\n    const result = [];\n    num1.forEach(num => {\n        if (maps[num]) {\n            maps[num] += 1;\n        } else {\n            maps[num] = 1;\n        }\n    });\n\n    num2.forEach(num => {\n        if (maps[num]) {\n            result.push(num);\n            maps[num] -= 1;\n        }\n    });\n    return result;\n}\n```\n\n- Set\n\n```\nconst intersect = (a, b) => [...new Set(a.filter((item) => b.includes(item)))];\n\nconst b = [ 6, 6];\nconst a = [ 6];\nconsole.log(intersect(a, b))  // [6]\n```\n\n\n## 参考\n- [第 59 题：给定两个数组，写一个方法来计算它们的交集](https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/102)\n"
  },
  {
    "path": "4.9.5 request is not finished.md",
    "content": "# 请求接口后出现\n>`Provisional headers are shown   /    request is not finished yet`\n\n\n最后发现接口响应后代码里有个 `for` 循环\n\n在 `for` 循环中  `splice` 操作\n\n最后就出现  `request is not finished yet`\n\n\n"
  },
  {
    "path": "4.9.6 a.b.c.d和a['b']['c']['d']，哪个性能更高.md",
    "content": "# 4.9.6 a.b.c.d和a['b']['c']['d']，哪个性能更高\n\n>原作者说，往深处走，会涉及ast抽象语法树、编译原理、v8内核对原生js实现问题\n\n\n- [xtx1130](https://github.com/xtx1130)写了下a.b.c.d v8的实现\n\n- 有人从 ast 分析\n\n- 还有人按照 V8 ByteCode 考虑\n\n## 参考\n- [Weekly-FE-Interview/issues/19](https://github.com/airuikun/Weekly-FE-Interview/issues/19)\n"
  },
  {
    "path": "4.9.7 类型判定探索.md",
    "content": "# 4.9.7 类型判定探索\n\n\n```\ntypeof null; // \"object\"\ntypeof undefined;  //\"undefined\"\ntypeof {};  //\"object\"\ntypeof [];  //\"object\"\n\nisNaN(1); // false\nisNaN('a'); // true\n\n[] instanceof Array;  // true\n\n[].constructor == Array; // true\n```\n\n## `isArray`\n\n```\n// 1.\nfunction isArray(arr) {\n  return arr instanceof Array;\n}\n\n// 2.\nfunction isArray(arr) {\n  return !!arr && arr.constructor == Array;\n}\n\n// 3.\nfunction isArray(arr) {\n  return typeof arr.sort == 'function';\n}\n\n// 4.\nfunction isArray(arr) {\n  try {\n    Array.prototype.toString.call(arr);\n    return true;\n  }catch() {\n    \n  }\n  return false;\n}\n```\n\n\n## `isNaN`, `isNull`, `isUndefined`\n\n```\n// isNaN\nfunction isNaN(obj) {\n  return obj !== obj;\n}\n\n// isNull\nfunction isNull(obj) {\n  return obj === null;\n}\n\n// isUndefined\nfunction isUndefined(obj) {\n  return obj === void 0;\n}\n```\n\n## ``\n\n\n## `isWindow`\n\n```\nwindow == document // ie678 true\ndocument == window // ie678 false\n\n// 1.\nvar isWindow = function(obj) {\n  return != null && obj === obj.window;\n}\n// 判定粗糙，通过不了伪造的对象\nvar a = {};\na.window = a;\nisWindow(a) // true;\n\n// 2.\nvar isWindow = function(obj) {\n  if(!obj)\n  return false;\n  return obi == obj.docuemnt && obj.docuemn !== obj;\n}\n```\n\n\n## `isArrayLike`\n\n```\n// 1.\nfunction isArrayLike(obj) {\n  var length = !obj && 'length'in obj && obj.length,type = jQuery.type(obj);\n  if(type === 'function' || jQuery.isWindow(obj)) {\n    return false;\n  }\n  return type === 'array' || length === 0 || typeof length === 'number' && length >0 && (length - 1 ) in obj;\n}\n```\n"
  },
  {
    "path": "4.9.8 字符串和数组的扩展方法.md",
    "content": "# 4.9.8 字符串和数组的扩展方法\n\n\n## 字符串\n- contains\n```\nfunction contains(target, it) {\n  return target.indexOf(it) != -1;\n}\n```\n\n- startsWith\n\n```\nfunction startsWith(target, str, ignorecase) {\n  var start_str = target.substr(0, str.length)\n  return ignorecase ? start_str.toLocaleLowerCase === str.toLocaleLowerCase : start_str === str;\n}\n```\n\n- endsWith\n\n```\nfunction startsWith(target, str, ignorecase) {\n  var end_str = target.substring(target.length - str.length)\n  return ignorecase ? end_str.toLocaleLowerCase === str.toLocaleLowerCase : end_str === str;\n}\n```\n- repeat\n\n```\n// 1.\nfunction repeat(target, n) {\n  return (new Array(n+1).join(target));\n}\n\n// 2.\nfunction repeat(target, n) {\n  return Array.prototype.join.call({length : n+1},target);\n}\n```\n\n- byteLen: 取得一个字符串所有字节的长度；\n\n```\nfunction byteLen(target) {\n  var length = target.length, i =0;\n  for(; i < target.length; i++) {\n    if(target.charCodeAt(i) > 255) {\n      length++;\n    }\n  }\n  return length;\n}\n```\n\n- escapeHTML: 防止XSS攻击，转义字符串\n\n\n```\nfunction escapeHTML(target){\n  return target.replace(/&/g, '&amp')\n  .replace(/</g, '&lt;')\n  .replace(/>/g , '&gt;')\n  .replace(/\"/g, '&quot;')\n  .repalce(/'/g, '&#39;');\n}\n```\n\n- unescapeHTML: 将字符串中的html实体字符还原成对应字符\n\n```\nfunction unescapeHTML(target){\n  return String(target)\n  .replace(/&#39;/g, '\\'')\n  .replace(/&quot;/g, '\"')\n  .replace(/&lt;/g , '<')\n  .replace(/&gt;/g, '>')\n  .repalce(/&amp;/g, '&');\n}\n```\n\n- trim: 去空白\n\n```\n// 1.\nfunction trim(str) {\n  return str.replace(/^\\s\\s*/, '').replace(/\\s\\s*$/, '');\n}\n\n// 2.\nfunction trim(str) {\n  return str.replace(/\\s+/g,'');\n}\n```\n\n- truncate: 对字符串进行截断处理，但超过限定长度，默认3个点号；\n\n```\nfunction truncate(target, length, truncation) {\n  var length = length || 30;\n  var truncation = truncation === void(0) ? '...' : truncation;\n  return target.length >length ? target.slice(0, length - truncation.length) + truncation : String(target)\n}\n```\n\n- camelize: 转换驼峰风格\n\n```\nfunction camelize(target) {\n  if(target.indexOf('-') < 0 && target.indexOf('_') < 0) {\n    return target;\n  }\n  return target.replace( /[-_][^-_]/g,function (match){\n    return match.charAt(1).toUpperCase();\n  })\n}\n```\n- underscored : 转换下划线风格；\n```\nfunction underscored(target) {\n  return target.replace(/([a-z\\d])([A-Z])/g, '$1_$2').replace(/\\-/g, '_').toLowerCase();\n}\n```\n\n- dasherLize: 转换连字符\n\n```\nfunction dasherLize(target) {\n  return target.replace(/_/g, '-')\n}\n```\n\n- stripTags: 移除字符串中的 html 标签\n\n```\nfunction stripTags(target) {\n  var rtag = /<\\w+(\\s+(\"[^\"]*\"|'[^']*'|[^>])+)?<|<\\w+>/gi\n  return String(target || \"\").replace(rtag, '');\n}\n```\n\n- stripScripts : 移除 script 标签\n\n```\nfunction stripScripts(target) {\n  return String(target || \"\").replace(/<script [^>]*>([\\S\\s]*?)</script>/img, '');\n}\n```\n\n## 数组\n\n\n- contains: 判定数组包含指定目标\n\n```\nfunction contains(target,item) {\n  return target.indexOf(item) > -1;\n}\n```\n\n- removeAt:移除数组中指定元素\n\n```\nfunction removeAt(target,index) {\n  return !!target.splice(index, 1).length;\n}\n```\n\n\n- remove: 移除数组中第一个匹配传参的那个元素\n\n```\nfunction removeAt(target,item) {\n  var index = target.indexOf(item);\n  if(~index)\n    return !!target.splice(index, 1).length;\n  return false;\n}\n```\n\n- random: 从数组中随机抽选一个元素出来\n\n```\nfunction random(target) {\n  return target[Math.floor(Math.random() * target.length)];\n}\n```\n\n- faltten: 数组扁平化\n\n```\nfunction faltten(target) {\n  var result = [];\n  target.forEach(function (item){\n    if(Array.isArray(item)) {\n      result = result(faltten(item));\n    }else {\n      result.push(item);\n    }\n  })\n  return result;\n}\n```\n\n\n- unique:数组去重\n\n```\nfunction unique(target) {\n  var result = [];\n  loop: for(var i = 0, n = target.length; i<n; i++){\n    for(var j = i+1; j < n; j++) {\n      if(target[j] === target[i]) {\n        continue loop;\n      }\n    }\n    result.push(target[i]);\n  }\n  return result;\n}\n```\n\n- compact: 过滤数组中的null和 undefined，不影响原数组\n\n```\nfunction compact(target) {\n  return target.filter(function (ele) {\n    return ele != null;\n  })\n}\n```\n\n- pluck: 取得对象数组中每个元素的指定元素\n\n```\nfunction pluck(target, name) {\n  var result = [], prop;\n  target.forEach(function(item) {\n    prop = item[name];\n    if(prop != null) result.push(prop);\n  })\n  return result;\n}\n```\n\n- groupBy: 根据指定条件（如回调对象的某个属性），进行分组，构成对象返回\n\n```\nfunction groupBy(target, val) {\n  var result = [];\n  var iterator = $.isFunction(val)? val: function(obj) { return obj[val]};\n  target.forEach(function(item, index) {\n    var key = iterator(item, index );\n    (result[key] || (result[key] = [])).push(item);\n  }\n  return result;\n}\n```\n\n- sortBy: 根据指定条件排序，适用于对象数组\n\n```\nfunction sortBy(target, fn, scope) {\n  var array = target.map(function(item, index) {\n    return {\n      el: item,\n      re: fn.call(scope, iitem, index)\n    }\n  }).sort(function(left, right) {\n    var a = left.re, b= right.re;\n    return a<b? -1:a>b? 1:0\n  })\n  return pluck(array, 'el')\n}\n```\n\n\n- union: 对两个数组取并集\n\n```\nfunction union(target, array) {\n  return unique(target.concat(array));\n}\n```\n\n\n- intersect: 对两个数组取交集\n\n```\nfunction intersect(target, array) {\n  return target.filter(function(n) {\n    return ~array.indexOf(n);\n  })\n}\n```\n\n- min:返回数组中最小值，仅用于数字数组\n\n```\nfunction min(target) {\n  return Math.min.apply(0, target);\n}\n```\n\n- max:返回数组中最大值，仅用于数字数组\n\n```\nfunction max(target) {\n  return Math.max.apply(0, target);\n}\n```\n\n\n## 日期扩展\n\n- 两个日期相隔多少天\n\n```\n// start, finish 都是 new Date() 类型\nfunction getDatePeriod(start, finish) {\n  return Math.abs(start*1 - finish*1)/60/60/1000/24;\n}\n\n// getDatePeriod(new Date(), new Date('2019-01-01'))\n```\n\n- 传入一个Date类型的日期，求出它所在月的第一天\n\n```\nfunction getFirstDateInMonth(date) {\n  return new Date(date.getFullYear(), date.getMonth(), 1).getDate();\n}\n```\n\n- 传入一个Date类型的日期，求出它所在月的最后一天\n\n```\nfunction getLastDateInMonth(date) {\n  return new Date(date.getFullYear(), date.getMonth()+1, 0).getDate();\n}\n```\n\n- 传入一个Date类型的日期，求出它所在季度的第一天\n\n```\nfunction getLastDateInMonth(date) {\n  return new Date(date.getFullYear(), ~~(date.getMonth()/3)*3, 1).getDate();\n}\n```\n\n- 传入一个Date类型的日期，求出它所在季度的最后一天\n\n```\nfunction getLastDateInMonth(date) {\n  return new Date(date.getFullYear(), ~~(date.getMonth()/3)*3 +3, 0).getDate();\n}\n```\n\n- 判断闰年\n\n```\nfunction isLeapYear(date) {\n  return new Date(this.getFullYear(), 2, 0).getDate() == 29\n}\n```\n\n\n\n\n\n\n"
  },
  {
    "path": "4.9.9 ~和~~ 运算符.md",
    "content": "# 4.9.9 ~和~~ 运算符\n\n## 非位（~）\n\n~”运算符（位非）用于对一个二进制操作数逐位进行取反操作。\n- 第 1 步：把运算数转换为 32 位的二进制整数。\n- 第 2 步：逐位进行取反操作。\n- 第 3 步：把二进制反码转换为十进制浮点数。\n\n对 12 进行位非运算，则返回值为 -13。\n\n```\nconsole.log( ~ 12 );  //返回值-13\n```\n\n\n\n## `~~`: 它被用作一个更快的替代 Math.floor() ,向下取整\n\n```\nconsole.log(~~2.444);  // 2\n```\n\n\n## 参考\n- [JavaScript-bitwise-operators-in-practice](http://rocha.la/JavaScript-bitwise-operators-in-practice)\n"
  },
  {
    "path": "5.0 前端面试题.md",
    "content": "# 面试题\n\n* [我的前端进阶之路](http://www.cnblogs.com/libin-1/p/6864344.html)\n\n* [前端面试题整理](http://www.cnblogs.com/haoyijing/p/5789348.html)\n\n\n- 实现一个函数clone，可以对JavaScript中的5种主要的数据类型（包括Number、String、Object、Array、Boolean）进行值复制。\n\n  ```\n  function clone3(obj){  \n    function Clone(){}  \n    Clone.prototype = obj;  \n    var o = new Clone();  \n    for(var a in o){  \n        if(typeof o[a] == \"object\") {  \n            o[a] = clone3(o[a]);  \n        }  \n    }  \n    return o;  \n  }  \n  \n  function clone(obj){  \n    var o;  \n    switch(typeof obj){  \n    case 'undefined': break;  \n    case 'string'   : o = obj + '';break;  \n    case 'number'   : o = obj - 0;break;  \n    case 'boolean'  : o = obj;break;  \n    case 'object'   :  \n        if(obj === null){  \n            o = null;  \n        }else{  \n            if(obj instanceof Array){  \n                o = [];  \n                for(var i = 0, len = obj.length; i < len; i++){  \n                    o.push(clone(obj[i]));  \n                }  \n            }else{  \n                o = {};  \n                for(var k in obj){  \n                    o[k] = clone(obj[k]);  \n                }  \n            }  \n        }  \n        break;  \n    default:          \n        o = obj;break;  \n    }  \n    return o;     \n  }  \n  ```\n\n\n* 遇到改写函数 function sum(a,y,z){return x+y+z;} 可以被 sum(x)(y)(z) 调用\n\n```\nvar sum = function(a){\n  return function(b){\n    return function(c){\n      return a+b+c;\n    }\n  }\n}\n```\n"
  },
  {
    "path": "5.1.0 2万5千字大厂面经.md",
    "content": "# 2万5千字大厂面经\n\n\n## 参考\n- https://juejin.im/post/5ba34e54e51d450e5162789b\n"
  },
  {
    "path": "5.1.1 前端面试题详细答案.md",
    "content": "# 前端和计算机相关知识\n```\n你能描述一下渐进增强和优雅降级之间的不同吗\n浏览器兼容问题\n如何对网站的文件和资源进行优化?\n怎么学习前端?怎么接触前端新知识?\n关于前后端分离\n关于浏览器内核(渲染引擎)\n浏览器加载文件顺序以及repaint/reflow\n为什么使用多个域名来存储网络资源会更有效？\n进程和线程的区别\n前端开发的优化问题\nFlash，Ajax各自的优缺点，使用中如何取舍？\nCSS\nCSS3\ncss居中的方式\n请写一个简单的幻灯效果页面\n什么是无样式内容闪烁?如何避免?\ndisplay:none和visibility:hidden的区别\n解释浮动和工作原理\n清除浮动\n解释CSS Sprits,以及你要如何使用?\n你最喜欢的图片替换方法是什么?你将如何使用?\n讨论CSS hacks, 条件引用或其他\n如何为有功能限制的浏览器提供网页\n在书写高效CSS时会有哪些问题需要考虑?\n如何优化网页的打印样式?\n描述下你曾经使用过的CSS 预处理的优缺点\n如果设计中使用了非标准的字体, 你将如何实现?\n解释下浏览器是如何判断元素是否匹配某个 CSS 选择器？\n\n解释一下你对盒模型的理解，以及如何在 CSS 中告诉浏览器使用不同的盒模型来渲染你的布局。\n\n伪类的用法：\n\n描述下\"reset\"css文件的作用和使用它的好处\n\n请解释一下 * { box-sizing: border-box; } 的作用, 并且说明使用它有什么好处？\n\nblock, inline和inline-block的区别\ncss动画和js动画的优缺点\n\n你用过媒体查询，或针对移动端的布局/CSS 吗？\n\n有哪些隐藏内容的方法(同时还要保证屏幕阅读器可用)\nCSS选择器级别\nalt和title的区别\n知道bfc吗?\n行内元素，块级元素，空元素各有哪些？\nhtml\nh5的改进:\n\n什么是语义化的html?\n\n从前端角度出发谈谈做好seo应该注意什么?\n文档类型(DOCTYPE)\n\n使用XHTML的局限有哪些?\n\n如果网页内容需要多语言,要怎么做?\n\ndata-*属性的作用\n\n如果把 HTML5 看作做一个开放平台，那它的构建模块有哪些？\n\n请描述一下 cookies，sessionStorage 和 localStorage 的区别？\n\n浏览器本地存储与服务器端存储之间的区别\n\nsessionStorage和页面js数据对象的区别\n\ncanvas和svg的区别?\n\nhref和src的区别\njs\n\najax, 跨域, jsonp\n\napply和call的用法和区别:\n\nbind函数的兼容性\n\n解释下事件代理\n\n解释下js中this是怎么工作的?\n\n继承\n\nAMD vs. CommonJS？\n\n什么是哈希表?\n\n什么是闭包? 闭包有什么作用?\n\n伪数组:\n\nundefined和null的区别, 还有undeclared:\n\n事件冒泡机制:　　\n\n解释下为什么接下来这段代码不是 IIFE(立即调用的函数表达式)：function foo(){ }();?\n\n\"attribute\" 和 \"property\" 的区别是什么？\n\n请指出 document load 和 document ready 两个事件的区别。\n\n什么是use strict? 其好处坏处分别是什么?\n\n浏览器端的js包括哪几个部分?\n\nDOM包括哪些对象?\n\njs有哪些基本类型?\n\n基本类型与引用类型有什么区别?\n\n关于js的垃圾收集例程\n\nES5中, 除了函数,什么能够产生作用域?\n\njs有几种函数调用方式?\n\n描述事件模型?IE的事件模型是怎样的？事件代理是什么？事件代理中怎么定位实际事件产生的目标？\n\njs动画有哪些实现方法?\n\n还有什么实现动画的方法?\n\n面向对象有哪几个特点? \n\n如何判断属性来自自身对象还是原型链?\n\nES6新特性\n\n如何获取某个DOM节点，节点遍历方式\n\n用LESS如何给某些属性加浏览器前缀？\n\njs异步模式如何实现？\n\n事件机制，如何绑定事件处理函数\n\n图片预加载\n\n如果在同一个元素上绑定了两个click事件, 一个在捕获阶段执行, 一个在冒泡阶段执行. 那么当触发click条件时, 会执行几个事件? 执行顺序是什么?\n\njs中怎么实现块级作用域?\n\n构造函数里定义function和使用prototype.func的区别？\n\njs实现对象的深克隆\njquery问题\n一些编程题\n\n匿名函数变量\nthis指向\n定时\n点击一个ul的五个li元素，分别弹出他们的序号，怎么做？\n\njs算法题\n\n js实现数组去重怎么实现?\n\n一、前端编程\n\n1. 你能描述一下渐进增强和优雅降级之间的不同吗?\n\n答:\n\n定义:\n优雅降级（graceful degradation): 一开始就构建站点的完整功能，然后针对浏览器测试和修复\n渐进增强（progressive enhancement): 一开始只构建站点的最少特性，然后不断针对各浏览器追加功能。 \n都关注于同一网站在不同设备里不同浏览器下的表现程度\n区别:\n“优雅降级”观点认为应该针对那些最高级、最完善的浏览器来设计网站. 而将那些被认为“过时”或有功能缺失的浏览器下的测试工作安排在开发周期的最后阶段，并把测试对象限定为主流浏览器（如 IE、Mozilla 等）的前一个版本。\n“渐进增强”观点则认为应关注于内容本身。请注意其中的差别：我甚至连“浏览器”三个字都没提。\n理解:\n\"优雅降级\"就是首先完整地实现整个网站,包括其中的功能和效果. 然后再为那些无法支持所有功能的浏览器增加候选方案, 使之在旧式浏览器上以某种形式降级体验却不至于完全失效.\n\"渐进增强\"则是从浏览器支持的基本功能开始, 首先为所有设备准备好清晰且语义化的html及完整内容, 然后再以无侵入的方法向页面增加无害于基础浏览器的额外样式和功能. 当浏览器升级时, 它们会自动呈现并发挥作用.\n　　参考自: 渐进增强、优雅降级  渐进增强和优雅降级\n\n2. 浏览器兼容问题:\n\n问题1   不同浏览器的标签默认的外补丁和内补丁不同.\n即随便写几个标签, 在不加样式控制的情况下, 各自的margin和padding差异较大.\n解决方法: CCS里:   *{margin:0; padding:0}\n问题2   块属性标签float后，又有横行的margin情况下，在IE6显示margin比设置的大　　\n会使得ie6后面的一块被顶到下一行.\n解决方案: 在float的标签样式中加入 display: inline; 将其转化为行内属性\n问题3  设置较小高度标签（一般小于10px），在IE6，IE7，遨游中高度超出自己设置高度\nIE6、7和遨游里这个标签的高度不受控制，超出自己设置的高度\n解决方案: 给超出高度的标签设置overflow:hidden;或者设置行高line-height 小于你设置的高度\n原因: ie8之前的浏览器都会给标签一个最小默认的行高的高度. 即使标签是空的,这个标签的高度还是会达到默认的行高.\n问题4  行内属性标签，设置display:block后采用float布局，又有横行的margin的情况，IE6间距bug\n解决: 在display:block;后面加入display:inline;display:table;\n问题5  图片默认有间距\n几个img标签放在一起的时候，有些浏览器会有默认的间距，加了问题一中提到的通配符也不起作用。\n解决: 使用float属性为img布局\n问题6  标签最低高度设置min-height不兼容\n因为min-height本身就是一个不兼容的CSS属性，所以设置min-height时不能很好的被各个浏览器兼容\n如果我们要设置一个标签的最小高度200px，需要进行的设置为：{min-height:200px; height:auto !important; height:200px; overflow:visible;}\n问题7  透明度的兼容CSS设置\n使用hacker\nIE6认识的hacker是下划线_和*\nIE7,遨游认识的hacker是*\n问题8  IE ol的序号全为1, 不递增\n解决: li设置样式{display: list-item}\n问题9 \n ie6,7不支持display:inline-block\n\n解决方法: 设置inline并触法haslayout\ndisplay:inline-block; *display:inline; *zoom:1 \n　　参考自: 常见浏览器兼容性问题与解决方案　　\n\n3. 如何对网站的文件和资源进行优化?\n\n答: \n\n文件合并（同上题“假若你有5个不同的 CSS 文件, 加载进页面的最好方式是？”）\n减少调用其他页面、文件的数量。一般我们为了让页面生动活泼会大量使用background来加载背景图，而每个 background的图像都会产生1次HTTP请求，要改善这个状况，可以采用css的1个有用的background-position属 性来加载背景图，我们将需要频繁加载的多个图片合成为1个单独的图片，需要加载时可以采用：background:url(....) no-repeat x-offset y-offset;的形式加载即可将这部分图片加载的HTTP请求缩减为1个。\n每个http请求都会产生一次从你的浏览器到服务器端网络往返过程，并且导致推迟到达服务器端和返回浏览器端的时间，我们称之为延迟。\n文件最小化/文件压缩\n即将需要传输的内容压缩后传输到客户端再解压，这样在网络上传输的 数据量就会大幅减小。通常在服务器上的Apache、Nginx可以直接开启这个设置，也可以从代码角度直接设置传输文件头，增加gzip的设置，也可以 从 负载均衡设备直接设置。不过需要留意的是，这个设置会略微增加服务器的负担。建议服务器性能不是很好的网站，要慎重考虑。\njs和css文件在百度上搜一个压缩网站就能压缩，但是在实际开发的项目中，使用gulp、webpack等工具可以打包出合并压缩后的文件，小图片可以在打包时转换成base64方式引入，大图片可以被压缩，html文件也是可以被压缩的\n使用 CDN 托管\nCDN的全称是Content Delivery Network，即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节，使内容传输的更快、更稳定。其目的是使用户可就近取得所需内容，解决 Internet网络拥挤的状况，提高用户访问网站的响应速度。\n缓存的使用\nAjax调用都采用缓存调用方式，一般采用附加特征参数方式实现，注意其中的<script src=”xxx.js?{VERHASH}”，{VERHASH}就是特征参数，这个参数不变化就使用缓存文件，如果发生变化则重新下载新文件或更新信息。\ncss文件放置在head，js放置在文档尾\n在服务器端配置control-cache  last-modify-date\n在服务器配置Entity-Tag     if-none-match\n用更少的时间下载更多的文件，提高网站加载速度，提高用户体验，可以使用以下方法：\n1.css sprites----将小图片合并为一张大图片，使用background-position等css属性取得图片位置\n2.将资源放在多个域名下-----打开控制台，可以看到很多网站都是这么做的~\n3.图片延迟加载-----很多电商网站、新闻网站，尤其是用到瀑布流展示图片的时候，很多都这么做了，这个技术已经很普遍~\n书写代码的时候要注意优化： \n1.css\n将可以合并的样式合并起来，比如margin-top、margin-bottom等。\n给img图片设置宽高，因为浏览器渲染元素的时候没有找到这两个参数，需要一边下载图片一边计算大小，如果图片很多，浏览器需要不断地调整页面。这不但影响速度，也影响浏览体验。当浏览器知道了高度和宽度参数后，即使图片暂时无法显示，页面上也会腾出图片的空位，然后继续加载后面的内容。从而加载时间快了，浏览体验也更好了。\n2.js\n少改变DOM元素，少触发reflow，可以复用的代码提出来写成公共的等等……\n3.img\n优化图片，不失真的情况下尽量减少图片大小，使用iconfont等\n4. 怎么学习前端? 怎么接触web新知识? \n\n5. 关于前后端分离:\n\n前端：负责View和Controller层。\n后端：只负责Model层，业务处理/数据等。\n6. 关于浏览器内核(渲染引擎)\n\n先说一下浏览器的结构：\n①、用户界面（UI） - 包括菜单栏、工具栏、地址栏、后退/前进按钮、书签目录等，也就是能看到的除了显示页面的主窗口之外的部分；\n②、浏览器引擎（Rendering engine） - 也被称为浏览器内核、渲染引擎，主要负责取得页面内容、整理信息（应用CSS）、计算页面的显示方式，然后会输出到显示器或者打印机；\n③、JS解释器 - 也可以称为JS内核，主要负责处理javascript脚本程序，一般都会附带在浏览器之中，例如chrome的V8引擎；\n④、网络部分 - 主要用于网络调用，例如：HTTP请求，其接口与平台无关，并为所有的平台提供底层实现；\n⑤、UI后端 - 用于绘制基本的窗口部件，比如组合框和窗口等。\n⑥、数据存储 - 保存类似于cookie、storage等数据部分，HTML5新增了web database技术，一种完整的轻量级客户端存储技术。\n注：IE只为每个浏览器窗口启用单独的进程，而chrome浏览器却为每个tab页面单独启用进程，也就是每个tab都有独立的渲染引擎实例。\n现在的主要浏览器：\nIE、Firefox、Safari、Chrome、Opera。\n它们的浏览器内核（渲染引擎）：\nIE--Trident、\nFF(Mozilla)--Gecko、\nSafari--Webkit、\nChrome--Blink（WebKit的分支）、\nOpera--原为Presto，现为Blink。\n因此在开发中，兼容IE、FF、Opera（Presto引擎是逐步放弃的）、Chrome和Safari中的一种即可，Safari、Chrome的引擎是相似的。\n7. 浏览器加载文件(repaint/reflow)\n\n文件加载顺序\n浏览器加载页面上引用的CSS、JS文件、图片时，是按顺序从上到下加载的，每个浏览器可以同时下载文件的个数不同，因此经常有网站将静态文件放在不同的域名下，这样可以加快网站打开的速度。\nreflow\n在加载JS文件时，浏览器会阻止页面的渲染，因此将js放在页面底部比较好。\n因为如果JS文件中有修改DOM的地方，浏览器会倒回去把已经渲染过的元素重新渲染一遍，这个回退的过程叫reflow。\nCSS文件虽然不影响js文件的加载，但是却影响js文件的执行，即使js文件内只有一行代码，也会造成阻塞。因为可能会有var width = $('#id').width()这样的语句，这意味着，js代码执行前，浏览器必须保证css文件已下载和解析完成。\n办法：当js文件不需要依赖css文件时，可以将js文件放在头部css的前面。\n常见的能引起reflow的行为：\n1.改变窗囗大小\n2.改变文字大小\n3.添加/删除样式表\n4.脚本操作DOM\n5.设置style属性\n等等……\nreflow是不可避免的，只能尽量减小，常用的方法有：\n1.尽量不用行内样式style属性，操作元素样式的时候用添加去掉class类的方式\n2.给元素加动画的时候，可以把该元素的定位设置成absolute或者fixed，这样不会影响其他元素\nrepaint\n另外，有个和reflow相似的术语，叫做repaint（重绘），在元素改变样式的时候触发，这个比reflow造成的影响要小，所以能触发repaint解决的时候就不要触发reflow……\n8. 为什么利用多个域名来请求网络资源会更有效？\n\n动静分离需求，使用不同的服务器处理请求。处理动态内容的只处理动态内容，不处理别的，提高效率。CDN缓存更方便\n突破浏览器并发限制：相关问题： 浏览器同一时间可以从一个域名下载多少资源？（即： 浏览器并发请求数）\n也就是说：同一时间针对同一域名下的请求有一定数量限制。超过限制数目的请求会被阻止。不同浏览器这个限制的数目不一样。\nCookieless, 节省带宽，尤其是上行带宽 一般比下行要慢。\n用户的每次访问，都会带上自己的cookie ，挺大的。假如twitter 的图片放在主站域名下，那么用户每次访问图片时，request header 里就会带有自己的cookie ，header 里的cookie 还不能压缩，而图片是不需要知道用户的cookie 的，所以这部分带宽就白白浪费了。\n节约主域名的连接数，从而提高客户端网络带宽的利用率，优化页面响应。因为老的浏览器（IE6是典型），针对同一个域名只允许同时保持两个HTTP连接。将图片等资源请求分配到其他域名上，避免了大图片之类的并不一定重要的内容阻塞住主域名上其他后续资源的连接（比如ajax请求）。\n避免不必要的安全问题( 上传js窃取主站cookie之类的)\n9. 进程和线程的区别\n\n一个程序至少有一个进程,一个进程至少有一个线程.\n线程的划分尺度小于进程，使得多线程程序的并发性高。\n线程是独立调度的基本单位, 进程是拥有资源的基本单位\n另外，进程在执行过程中拥有独立的内存单元，而多个线程共享内存，从而极大地提高了程序的运行效率。\n线程在执行过程中与进程还是有区别的。\n每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。\n但是线程不能够独立执行，必须依存在应用程序中，由应用程序提供多个线程执行控制。\n从逻辑角度来看，\n多线程的意义在于一个应用程序中，有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用，来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。\n10. 前端开发的优化问题\n\n前端开发的优化问题：\n   　 (1) 减少http请求次数：css spirit,data uri\n       (2) JS，CSS源码压缩\n       (3) 前端模板 JS+数据，减少由于HTML标签导致的带宽浪费，前端用变量保存AJAX请求结果，每次操作本地变量，不用请求，减少请求次数\n       (4) 用innerHTML代替DOM操作，减少DOM操作次数，优化javascript性能\n       (5) 用setTimeout来避免页面失去响应\n       (6) 用hash-table来优化查找\n       (7) 当需要设置的样式很多时设置className而不是直接操作style \n       (8) 少用全局变量\n       (9) 缓存DOM节点查找的结果\n       (10) 避免使用CSS Expression\n       (11) 图片预载\n       (12) 避免在页面的主体布局中使用table，table要等其中的内容完全下载之后才会显示出来，显示比div+css布局慢\n如何控制网页在网络传输过程中的数据量\n       （1）启用GZIP压缩\n       （2）保持良好的编程习惯，避免重复的CSS，JavaScript代码，多余的HTML标签和属性\n \n11. Flash、Ajax各自的优缺点，在使用中如何取舍?\n\nAjax的优势\n　　(1) 可搜索型\n　　(2) 开放性\n　　(3) 费用\n　　(4) 易用性\n　　(5) 易于开发\nFlash的优势\n　　(1) 多媒体处理\n　　(2) 兼容性\n　　(3) 矢量图形 比SVG，Canvas优势大很多\n　　(4) 客户端资源调度，比如麦克风，摄像头\n　　参考：前端开发的优化问题\n\n \n\n二、CSS\n\n1. CSS3\n\n　　CSS3新特性：\n\n新增各种CSS选择器   (: not(.input): 所有class不是“input”的节点）\n圆角border-radiuis\n多列布局：multi-column layout\n阴影和反射： multi-column layout\n文字特效：text-shadow\n线性渐变： gradient\n旋转：transform\n缩放，定位，倾斜，动画，多背景：transform: \\scale(0.85,0.90) \\ translate(0px, -30px) \\ skew(-9deg, 0deg) \\ Animation\n　　参考：CSS3中的一些属性\n\n2. css居中的方式\n\n　　参考：实现div的水平居中和垂直居中\n\n3. 请写一个简单的幻灯效果页面\n\n4. 什么是无样式内容闪烁?如何避免?\n\n答: \n\nwhat?\n如果使用import方法对CSS进行导入,会导致某些页面在Windows 下的Internet Explorer出现一些奇怪的现象:以无样式显示页面内容的瞬间闪烁,这种现象称之为文档样式短暂失效(Flash of Unstyled Content),简称为FOUC。\nwhy?\n使用import导入样式表\n将样式表放在页面底部\n有几个样式表,放在页面不同位置\n原因即:当样式表晚于结构性html加载，当加载到此样式表时，页面将停止之前的渲染。此样式表被下载和解析后，将重新渲染页面，也就出现了短暂的花屏现象。\nhow?\n使用LINK标签将样式表放在文档HEAD中。\n5. display:none 和 visibility: hidden的区别\n\ndisplay:none  隐藏对应的元素，在文档布局中不再给它分配空间，它各边的元素会合拢，就当他从来不存在。\nvisibility:hidden  隐藏对应的元素，但是在文档布局中仍保留原来的空间。\n6. 解释浮动和工作原理\n\n答:　　\n\n浮动可以理解为让某个div元素脱离标准流，漂浮在标准流之上，和标准流不是一个层次。\n假如某个div元素A是浮动的，如果A元素上一个元素也是浮动的，那么A元素会跟随在上一个元素的后边(如果一行放不下这两个元素，那么A元素会被挤到下一行)；如果A元素上一个元素是标准流中的元素，那么A的相对垂直位置不会改变，也就是说A的顶部总是和上一个元素的底部对齐。\n清除浮动是为了清除使用浮动元素产生的影响。浮动的元素，高度会塌陷，而高度的塌陷使我们页面后面的布局不能正常显示。\n关于清除浮动:清除浮动可以理解为打破横向排列。 clear: none | left | right | both\n对于CSS的清除浮动(clear)，一定要牢记：这个规则只能影响使用清除的元素本身，不能影响其他元素。\n　　参考自: 经验分享：CSS浮动(float,clear)通俗讲解\n\n7. 清除浮动\n\n答:\n\n在浮动元素后面添加空标签 clear:both\n给父标签使用overflow: hidden/auto;zoom:1\n父级div定义, 使用伪类:after和zoom\n1\n2\n.clearfloat:after{display:block;clear:both;content:\"\";visibility:hidden;height:0}\n.clearfloat{zoom:1}\n　　zoom:1的作用： 触发IE下的hasLayout。zoom是IE浏览器专有属性，可以设置或检索对象的缩放比例。\n\n　　当设置了zoom的值之后，所设置的元素就会扩大或缩小，高度宽度就会重新计算了，这里一旦改变zoom值时其实也会发生重新渲染，运用这个原理，也就解决了ie下子元素浮动时候父元素不随着自动扩大的问题。\n\n8. 解释CSS Sprites, 以及你要如何使用?\n\n答: CSS Sprites其实就是把网页中一些背景图片整合到一张图片文件中，再利用CSS的“background-image”，“background- repeat”，“background-position”的组合进行背景定位，background-position可以用数字能精确的定位出背景图片的位置。\n\n9. 你最喜欢的图片替换方法是什么，你如何选择使用?\n\n答:\n\n　　1) <h1><img src=\"image.gif\" alt=\"Image Replacement\"></h1>　　\n\n　　2) 移开文字:\n\n1\n2\n3\n4\n<h1><span>Image Replacement</span></h1>\n \nh1{ background:url(hello_world.gif) no-repeat; width: 150px; height: 35px; }\nspan { display: none; }\n　　　　注意问题:①结构性需要增加一个标签包裹文本　　②需要把背景图设置在外标签上,并把文本外标签隐藏.\n\n　　　　缺点: 不利于阅览器浏览网页\n\n　　3) text-indent属性，并且给其设置一个较大的负值，x达到隐藏文本的效果\n\n1\n2\n3\n4\n5\n6\n7\n8\n<h1 class=\"technique-three\">w3cplus</h1>\n \n.technique-three {\n  width: 329px;\n  height: 79px;\n  background: url(images/w3cplus-logo.png);\n  text-indent: -9999px;\n}\n　　4) 我们此处使用一个透明的gif图片，通过在img标签中的“alt”属性来弥补display:none。这样阅读器之类的就能阅读到所替换的文本是什么\n\n复制代码\n<h1 class=\"technique-five\">\n  <img src=\"images/blank.gif\" alt=\"w3cplus\" /<\n  <span>w3cplus</span>\n</h1>\n\n.technique-five {\n  width: 329px;\n  height: 79px;\n  background: url(images/w3cplus-logo.png);\n}\n.technique-five span {\n  display: none;\n}\n复制代码\n　　5) 使用零高度来隐藏文本，但为了显示背景图片，需要设置一个与替换图片一样的大小的padding值\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n<h1 class=\"technique-six\">w3cplus</h1>\n \n.technique-six {\n  width: 329px;\n  padding: 79px 0 0 0;\n  height: 0px;\n  font-size: 0;\n  background: url(images/w3cplus-logo.png);\n  overflow: hidden;\n}\n　　6) 通过把span的大小都设置为“0”，来达到隐藏文本效果，这样阅读器就能完全阅读到，而且又达到了图片替换文本的效果\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n<h1 class=\"technique-seven\">\n    <span>w3cplus</span>\n</h1>\n \n.technique-seven {\n  width: 329px;\n  height: 79px;\n  background: url(images/w3cplus-logo.png);\n}\n.technique-seven span {\n  display: block;\n  width: 0;\n  height: 0;\n  font-size: 0;\n  overflow: hidden;\n}  \n　　7) 利用一个空白的span标签来放置背景图片，并对其进行绝对定位，使用覆盖文本，达到隐藏替换文本的效果。\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n<h1 class=\"technique-eight\">\n  <span></span>w3cplus\n</h1>\n \n.technique-eight {\n  width: 329px;\n  height: 79px;\n  position: relative;\n}\n.technique-eight span {\n  background: url(images/w3cplus-logo.png);\n  position: absolute;\n  width: 100%;\n  height: 100%;\n}\n　　8) 设置字体为微小值，但这里需要注意一点不能忘了设置字体色和替换图片色一样，不然会有一个小点显示出来\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n<h1 class=\"technique-nine\">w3cplus</h1>\n \n.technique-nine {\n  width: 329px;\n  height: 79px;\n  background: url(images/w3cplus-logo.png);\n  font-size: 1px;\n  color: white;\n}\n　　9) 使用css的clip属性来实现图片替换文本的效果\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n<h1 class=\"technique-ten\"><span>w3cplus</span></h1>\n \n.technique-ten {\n  width: 329px;\n  height: 79px;\n  background: url(images/w3cplus-logo.png);\n}\n             \n.technique-ten span {\n  border: 0 !important;\n  clip: rect(1px 1px 1px 1px);\n  clip: rect(1px,1px,1px,1px);\n  height: 1px !important;\n  margin: -1px;\n  overflow: hidden;\n  padding: 0 !important;\n  position: absolute !important;\n  width: 1px;\n}\n　　参考自: 十种图片替换文本CSS方法\n\n10. 讨论CSS hacks, 条件引用或者其他\n\nhacks　\n_width针对于ie6。*width,+width针对于ie6,7。\ncolor: red\\9;/*IE8以及以下版本浏览器*/（但是测试可以兼容到ie10。\n*+html与*html是IE特有的标签, firefox暂不支持.而*+html又为IE7特有标签（但是测试*html兼容ie6-10。*+兼容ie7-10）。\n!important 在IE中会被忽视，ie6,7,8不识别，ie9+（包括ie9）是识别的。\n条件引用\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n<!--[if !IE]><!--> 除IE外都可识别 <!--<![endif]-->\n<!--[if IE]> 所有的IE可识别 <![endif]-->\n<!--[if IE 6]> 仅IE6可识别 <![endif]-->\n<!--[if lt IE 6]> IE6以及IE6以下版本可识别 <![endif]-->\n<!--[if gte IE 6]> IE6以及IE6以上版本可识别 <![endif]-->\n<!--[if IE 7]> 仅IE7可识别 <![endif]-->\n<!--[if lt IE 7]> IE7以及IE7以下版本可识别 <![endif]-->\n<!--[if gte IE 7]> IE7以及IE7以上版本可识别 <![endif]-->\n<!--[if IE 8]> 仅IE8可识别 <![endif]-->\n<!--[if IE 9]> 仅IE9可识别 <![endif]-->\n11. 如何为有功能限制的浏览器提供网页?\n\n答: 功能限制的浏览器, 比如低版本IE, 手机浏览器, 等会在很多功能上不符合Web标准, 而应对方式主要有:\n\n只提供符合Web标准的页面\n提供另一个符合那些浏览器标准的页面\n兼容: 两种思路:\n渐进增强: 提供一个可用的原型，后来再为高级浏览器提供优化\n优雅降级: 据高级浏览器提供一个版本，然后有功能限制的浏览器只需要一个刚好能用的版本\n相关技术:\nMedia Query\nCSS hack\n条件判断<! --[if !IE]><!-->除IE外都可识别<!--<![endif]-->\n　　参考自: 如何为有功能限制的浏览器提供网页\n\n12. 在书写高效 CSS 时会有哪些问题需要考虑?\n\n答: \n\n　　　　1）reset。参照下题“描述下 “reset” CSS 文件的作用和使用它的好处”的答案。\n\n　　　　2）规范命名。尤其对于没有语义化的html标签，例如div，所赋予的class值要特别注意。\n\n　　　　2）抽取可重用的部件，注意层叠样式表的“优先级”。\n\n13. 如何优化网页的打印样式?\n\n　　针对打印机的样式: @media print{...}\n\n　　参考:如何优化网页的打印样式？  移动端H5知识普及 - CSS3媒体查询\n\n14. 描述下你曾经使用过的 CSS 预处理的优缺点\n\n答: 优点: \n\n结构清晰, 便于扩展\n可以方便屏幕浏览器私有语法差异\n可以轻松实现多重继承\n完全兼容css代码\n　　参考: 再谈 CSS 预处理器\n\n　　　　  less学习笔记\n\n15. 如果设计中使用了非标准的字体，你该如何去实现？ \n\n图片替代\nweb : fonts在线字库\n@font-face\n　　参考: 如果设计中使用了非标准的字体，你该如何去实现？ \n\n16. 解释下浏览器是如何判断元素是否匹配某个 CSS 选择器？\n\n作者：张靖超链接：https://www.zhihu.com/question/24959507/answer/29672263来源：知乎著作权归作者所有，转载请联系作者获得授权。\n　　从后往前判断。\n　　浏览器先产生一个元素集合，这个集合往往由最后一个部分的索引产生（如果没有索引就是所有元素的集合）。然后向上匹配，如果不符合上一个部分，就把元素从集合中删除，直到真个选择器都匹配完，还在集合中的元素就匹配这个选择器了。\n　　举个例子，有选择器：\n　　body.ready #wrapper > .lol233\n　　先把所有 class 中有 lol233 的元素拿出来组成一个集合，然后上一层，对每一个集合中的元素，如果元素的 parent id 不为 #wrapper 则把元素从集合中删去。 再向上，从这个元素的父元素开始向上找，没有找到一个 tagName 为 body 且 class 中有 ready 的元素，就把原来的元素从集合中删去。\n　　至此这个选择器匹配结束，所有还在集合中的元素满足。\n　　大体就是这样，不过浏览器还会有一些奇怪的优化。\n　　为什么从后往前匹配因为效率和文档流的解析方向。效率不必说，找元素的父亲和之前的兄弟比遍历所哟儿子快而且方便。关于文档流的解析方向，是因为现在的 CSS，一个元素只要确定了这个元素在文档流之前出现过的所有元素，就能确定他的匹配情况。应用在即使 html 没有载入完成，浏览器也能根据已经载入的这一部分信息完全确定出现过的元素的属性。\n　　为什么是用集合主要也还是效率。基于 CSS Rule 数量远远小于元素数量的假设和索引的运用，遍历每一条 CSS Rule 通过集合筛选，比遍历每一个元素再遍历每一条 Rule 匹配要快得多。\n17. 解释一下你对盒模型的理解，以及如何在 CSS 中告诉浏览器使用不同的盒模型来渲染你的布局。\n\n答:\n\n　　盒模型：文档中的每个元素被描绘为矩形盒子，渲染引擎的目的就是判定大小，属性——比如它的颜色、背景、边框方面——及这些盒子的位置。\n\n　　在CSS中，这些矩形盒子用标准盒模型来描述。这个模型描述了一个元素所占用的空间。每一个盒子有四条边界：外边距边界margin edge，边框边界border edge，内边距边界padding edge和内容边界content edge。\n\n　　内容区域是真正包含元素内容的区域，位于内容边界的内部，它的大小为内容宽度或content-box宽及内容高度或content-box高。如果box-sizing为默认值，width、min-width、max-width、height、min-height和max-height控制内容大小。\n\n　　内边距区域padding area用内容可能的边框之间的空白区域扩展内容区域。通常有背景——颜色或图片（不透明图片盖住背景颜色）。\n\n　　边框区域扩展了内边距区域。它位于边框边界内部，大小为border-box宽和border-box高。\n\n　　外边距区域margin area用空白区域扩展边框区域，以分开相邻的元素。它的大小为margin-box的高宽。\n\n　　在外边距合并的情况下，由于盒之间共享外边距，外边距不容易弄清楚。\n\n　　对于非替换的行内元素来说，尽管内容周围存在内边距与边框，但其占用空间（行高）由line-height属性决定。\n\n \n\n　　盒子模型分为两类：W3C标准盒子模型和IE盒子模型 （微软确实不喜欢服从他家的标准）\n\n　　这两者的关键差别就在于：\n\nW3C盒子模型——属性高（height）和属性宽（width）这两个值不包含 填充（padding）和边框（border　　）\nIE盒子模型——属性高（height）和属性宽（width）这两个值包含 填充（padding）和边框（border）\n　　我们在编写页面代码的时候应该尽量使用标准的W3C盒子模型（需要在页面中声明DOCTYPE类型）。\n\n　　各浏览器盒模型的组成结构是一致的，区别只是在\"怪异模式\"下宽度和高度的计算方式，而“标准模式”下则没有区别。组成结构以宽度为例：总宽度=marginLeft+borderLeft+paddingLeft+contentWidth+paddingRight+borderRight+marginRight（W3C标准盒子模型）。页面在“怪异模式”下，css中为元素的width和height设置的值在标准浏览器和ie系列(ie9除外)里的代表的含义是不同的（IE盒子模型）。 \n\n   　因而解决兼容型为题最简洁和值得推荐的方式是：下述的第一条。 \n\n将页面设为“标准模式”。添加对应的dtd标识\n使用hack或者在外面套上一层wrapper \n　　参考: 解释一下你对盒模型的理解，以及如何在 CSS 中告诉浏览器使用不同的盒模型来渲染你的布局。\n\n　　　　　MSDN：盒模型\n\n18. 伪类的用法：\n\n　　参考：CSS中伪类及伪元素用法详解\n\n19. 描述下\"reset\"css文件的作用和使用它的好处\n\n　　reset.css能够重置浏览器的默认属性。不同的浏览器具有不同的样式，重置能够使其统一。比如说ie浏览器和FF浏览器下button显示不同，通过reset能够统一样式，显示相同的效果。但是很多reset是没必要的，多写了会增加浏览器在渲染页面的负担。\n\n　　比如说，\n\n　　　　1）我们不应该对行内元素设置无效的属性，对span设置width和height，margin都不会生效的。\n\n　　　　2）对于absolute和fixed定位的固定尺寸（设置了width和height属性），如果设置了top和left属性，那么bottom和right，margin和float就没有作用。\n\n　　　　3）后面设置的属性将会覆盖前面重复设置的属性。 \n\n期待能够指出它的负面影响，或者提到它的一个更好的替换者\"normalize\"   normalize.css是一个可以定制的css文件，它让不同的浏览器在渲染元素时形式更统一。\n20. 请解释一下 * { box-sizing: border-box; } 的作用, 并且说明使用它有什么好处？\n\n　　说到 IE 的 bug，在 IE6以前的版本中，IE对盒模型的解析出现一些问题，跟其它浏览器不同，将 border 与 padding 都包含在 width 之内。而另外一些浏览器则与它相反，是不包括border和padding的。对于IE浏览器，当我们设置 box-sizing: content-box; 时，浏览器对盒模型的解释遵从我们之前认识到的 W3C 标准，当它定义width和height时，它的宽度不包括border和padding；对于非IE浏览器，当我们设置box-sizing: border-box; 时，浏览器对盒模型的解释与 IE6之前的版本相同，当它定义width和height时，border和padding则是被包含在宽高之内的。内容的宽和高可以通过定义的“width”和 “height”减去相应方向的“padding”和“border”的宽度得到。内容的宽和高必须保证不能为负，必要时将自动增大该元素border box的尺寸以使其内容的宽或高最小为0。\n\n　　使用 * { box-sizing: border-box; }能够统一IE和非IE浏览器之间的差异。\n\n21. block, inline和inline-block的区别\n\n答:\n\n起新行\nblock元素会独占一行, 多个block元素会各自新起一行. 默认情况下, block元素宽度自动填满其父元素宽度\ninline元素不会独占一行, 多个相邻的行内元素会排列在同一行里, 直到一行排列不下, 才会新换一行, 其宽度随元素的内容而变化\n设置宽高\nblock元素可以设置width, height属性. 块级元素即使设置了宽度, 仍然独占一行\ninline元素设置width, height无效\n内外边距\nblock元素可以设置margin和padding属性\ninline元素的margin和padding属性,水平方向的padding-left, padding-right, margin-left, margin-right都会产生边距效果. 但是数值方向的 margin/padding-top/bottom不会产生边距效果\n包含\nblock可以包含inline和block元素,而inline元只能包含inline元素\n而display: inline-block, 则是将对象呈现为inline对象, 但是对象的内容作为block对象呈现. 之后的内联对象会被排列到一行内. 比如我们可以给一个link(a元素)inline-block的属性, 使其既有block的高宽特性又有inline的同行特性\n22. css动画和js动画的优缺点\n\n　　CSS3的动画\n\n优点：\n1.在性能上会稍微好一些，浏览器会对CSS3的动画做一些优化（比如专门新建一个图层用来跑动画）\n2.代码相对简单\n缺点：\n1.在动画控制上不够灵活\n2.兼容性不好\n3.部分动画功能无法实现（如滚动动画，视差滚动等）\n　　JavaScript的动画\n\n优点：\n1.控制能力很强，可以单帧的控制、变换\n2.兼容性好，写得好完全可以兼容IE6，且功能强大。\n缺点：\n计算没有css快，另外经常需要依赖其他的库。\n　结论\n\n所以，不复杂的动画完全可以用css实现，复杂一些的，或者需要交互的时候，用js会靠谱一些~\n23. 你用过媒体查询，或针对移动端的布局/CSS 吗？\n\n答: \n\n　　通过媒体查询可以为不同大小和尺寸的媒体定义不同的css，适合相应的设备显示；即响应式布局\n\n@media screen and (min-width: 400px) and (max-width: 700px) { … }\n@media handheld and (min-width: 20em), screen and (min-width: 20em) { … }\n　　参考自: CSS3媒体查询  使用 CSS 媒体查询创建响应式网站  《响应式Web设计实践》学习笔记\n\n24. 有哪些隐藏内容的方法(同时还要保证屏幕阅读器可用)\n\n答：\n\n　　display:none或者visibility:hidden，overflow:hidden。\n\n \n\n1.display:none;的缺陷\n搜索引擎可能认为被隐藏的文字属于垃圾信息而被忽略\n屏幕阅读器（是为视觉上有障碍的人设计的读取屏幕内容的程序）会忽略被隐藏的文字。\n2.visibility: hidden ;的缺陷\n隐藏的内容会占据他所应该占据物理空间\n3.overflow:hidden;一个比较合理的方法\n例：.texthidden { display:block; overflow: hidden; width: 0; height: 0; }\n将宽度和高度设定为0，然后超过部分隐藏，就会弥补上述一、二方法中的缺陷，也达到了隐藏内容的目的\n 25. CSS选择器级别\n\n1.如果样式是行内样式（通过Style=””定义），那么a=1\n2.b为ID选择器的总数\n3.c为Class类选择器的数量。\n4.d为类型选择器的数量\n5.属性选择器，伪类选择器和class类选择器优先级一样，伪元素选择器和类型选择器一样\n6.!important 权重最高，比 inline style 还要高。\n　　一般来讲，越详细的级别越高。CSS优先级包含四个级别（文内选择符，ID选择符，Class选择符，元素选择符）以及各级别出现的次数。根据这四个级别出现的次数计算得到CSS的优先级\n\n　　参考： CSS样式选择器优先级\n\n 26. img设置属性title和alt的区别?\n\n答: \n\nAlt是img的特有属性, 或与<input type=\"image\">配合使用，规定图像的替代文本. 如果无法显示图像, 浏览器将显示替代文本. 用于图片无法加载显示、读屏器阅读图片，可提高图片可 访问性，搜索引擎会重点分析。最长可包含1024个字符,\n Title为元素提供附加的提示信息，用于鼠标滑到元素上的时候显示。其值可以比alt属性值设置的更长, 但是有些浏览器会截断过长的文字.\n　　\n\n27. 知道BFC吗? \n\n答: BFC指的是Block Formatting Context, 它提供了一个环境, html元素在这个环境中按照一定规则进行布局. 一个环境中的元素不会影响到其他环境中的布局. 决定了元素如何对其内容进行定位, 以及和其他元素的关系和相互作用.\n\n　　其中: FC(Formatting Context): 指的是页面中的一个渲染区域, 并且拥有一套渲染规则, 它决定了其子元素如何定位, 以及与其他元素的相互关系和作用.\n\n　　BFC: 块级格式化上下文, 指的是一个独立的块级渲染区域, 只有block-level box参与, 该区域拥有一套渲染规则来约束块级盒子的布局, 且与区域外部无关.\n\nBFC的生成:\n根元素\nfloat的值不为none\noverflow的值不为visible\ndisplay的值为 inline-block, table-cell, table-caption\nposition的值为absolute或fixed\nBFC的约束规则\n生成BFC元素的子元素会一个接一个的放置。垂直方向上他们的起点是一个包含块的顶部，两个相邻子元素之间的垂直距离取决于元素的margin特性。在BFC中相邻的块级元素外边距会折叠。\n生成BFC元素的子元素中，每一个子元素做外边距与包含块的左边界相接触，（对于从右到左的格式化，右外边距接触右边界），即使浮动元素也是如此（尽管子元素的内容区域会由于浮动而压缩），除非这个子元素也创建了一个新的BFC（如它自身也是一个浮动元素）。\n分解:\n内部的Box会在垂直方向上一个接一个的放置\n垂直方向上的距离由margin决定。（完整的说法是：属于同一个BFC的两个相邻Box的margin会发生重叠，与方向无关。）\n每个元素的左外边距与包含块的左边界相接触（从左向右），即使浮动元素也是如此。（这说明BFC中子元素不会超出他的包含块，而position为absolute的元素可以超出他的包含块边界）\nBFC的区域不会与float的元素区域重叠\n计算BFC的高度时，浮动子元素也参与计算\nBFC就是页面上的一个隔离的独立容器，容器里面的子元素不会影响到外面元素，反之亦然\nBFC在布局中的应用\n不和浮动元素重叠:\n如果一个浮动元素后面跟着一个非浮动元素, 就会产生覆盖\n防止margin重叠:\n同一个BFC中的两个相邻Box才会发生重叠与方向无关，不过由于上文提到的第一条限制，我们甚少看到水平方向的margin重叠。这在IE中是个例外，IE可以设置write-mode\n解决浮动相关问题\n父元素: overflow:hidden    IE: zoom:1(hasLayout)\n根本原因在于创建BFC的元素，子浮动元素也会参与其高度计算，即不会产生高度塌陷问题。实际上只要让父元素生成BFC即可，并不只有这两种方式。\n多栏布局\n比如左右两栏宽度固定, 中间栏自适应 则中间栏设置 overflow:hidden生成BFC\nIE中类似概念: hasLayout\n　　参考: 我对BFC的理解   CSS BFC和IE Haslayout介绍\n\n28. 行内元素有哪些？块级元素有哪些？空元素有哪些？\n\n答：\n\n行内元素有：span img input select strong\n块级元素有：div ul ol dl dt dd h1 h2 h3 h4 p...\n常见的空元素：br hr img input link meta  base area command embed keygen param source track wbr....\n三、html\n\n1. h5的改进:\n\n新元素\n画布canvas: HTML5 <canvas> 元素用于图形的绘制，通过脚本 (通常是JavaScript)来完成\n音频audio\n视频video\n语义性: article,  nav ,  footer, section, aside, hgroup等.\n时间time\n新属性\n拖放: draggable   <img draggable=\"true\">\n可编辑: contenteditable\n新事件\n拖放 ondrag ondrop\n关闭页面 onunload\n窗口大小改变 onresize\n取消了一些元素: font center等\n新的DOCTYPE声明  <!DOCTYPE html> \n完全支持CSS3\nVideo和Audio\n2D/3D制图\n本地存储\n本地SQL数据\nWeb应用\n　　参考自: 猿教程\n\n2. 什么是语义化的html?\n\n答: \n\nwhat?\n根据内容的结构（内容语义化），选择合适的标签（代码语义化）便于开发者阅读和写出更优雅的代码的同时让浏览器的爬虫和机器很好地解析。\nwhy?\n为了在没有CSS的情况下，页面也能呈现出很好地内容结构、代码结构:为了裸奔时好看；\n用户体验：例如title、alt用于解释名词或解释图片信息、label标签的活用；\n有利于SEO：和搜索引擎建立良好沟通，有助于爬虫抓取更多的有效信息：爬虫依赖于标签来确定上下文和各个关键字的权重\n方便其他设备解析（如屏幕阅读器、盲人阅读器、移动设备）以意义的方式来渲染网页；\n便于团队开发和维护，语义化更具可读性，是下一步吧网页的重要动向，遵循W3C标准的团队都遵循这个标准，可以减少差异化。\nhow?\n尽可能少的使用无语义的标签div和span；\n在语义不明显时，既可以使用div或者p时，尽量用p, 因为p在默认情况下有上下间距，对兼容特殊终端有利；\n不要使用纯样式标签，如：b、font、u等，改用css设置。\n需要强调的文本，可以包含在strong或者em标签中（浏览器预设样式，能用CSS指定就不用他们），strong默认样式是加粗（不要用b），em是斜体（不用i）；\n使用表格时，标题要用caption，表头用thead，主体部分用tbody包围，尾部用tfoot包围。表头和一般单元格要区分开，表头用th，单元格用td；\n表单域要用fieldset标签包起来，并用legend标签说明表单的用途；\n每个input标签对应的说明文本都需要使用label标签，并且通过为input设置id属性，在lable标签中设置for=someld来让说明文本和相对应的input关联起来。\n　　参考自: 理解HTML语义化\n\n \n\n3. 从前端角度出发谈谈做好seo需要考虑什么?\n\n答: \n\n语义化html标签\n合理的title, description, keywords;\n重要的html代码放前面\n少用iframe, 搜索引擎不会抓取iframe中的内容\n图片加上alt\n4. 文档类型(DOCTYPE)\n\n答:\n\n作用: doctype声明位于文档中最前面的位置，处于标签之前，告知浏览器使用的是哪种规范。\n类型: 三种: Strict、Transitional 以及 Frameset \n如果不声明: 不写doctype，浏览器会进入quirks mode （混杂模式）。即，如果不声明doctype，浏览器不引入w3c的标准，那么早期的浏览器会按照自己的解析方式渲染页面。浏览器采用自身方式解析页面的行为称为\"quirks mode（混杂模式也称怪异模式）\"；采用w3c方式解析就是\"strict mode（标准模式）\"。 如果完全采用strictmode就不会出任何的差错，但这样会降低程序的容错率，加重开发人员的难度\n用哪种: \n没有doctype声明的采用quirks mode解析\n对于有doctype的大多数采用standard mord。\n特殊情况：\n对于那些浏览器不能识别的doctype ,浏览器采用quirks mode；\n没有声明DTD或者html版本声明低于4.0采用quirks mode，其他使用standard mode；\nie6中，如果在doctype声明前有一个xml声明(比如:<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>)，则采用quirks mode解析\n标准模式与怪异模式的区别:\n标准模式：浏览器根据规范呈现页面\n混杂模式（怪异模式）：页面以一种比较宽松的兼容方式显示。\n他们最大的不同是对盒模型的解析。\n在strict mode中 ：width是内容宽度 ，也就是说,元素真正的宽度 = margin-left + border-left-width + padding-left + width + padding-right + border-right- width +  margin-right;\n在quirks mode中 ：width则是元素的实际宽度 ，内容宽度 = width - (margin-left + margin-right + padding-left + padding-right + border-left-width + 　border-right-width)\n5. 使用XHTML的局限有哪些?\n\n　　答：\n\nXHTML较为严格，标签必须闭合，必须要body，head等\n如果页面使用 'application/xhtml+xml' 一些老的浏览器并不兼容\n6. 如果网页内容需要多语言,要怎么做?\n\n　　答: 采用统一编码utf-8模式\n\n+ View Code\n　　参考自: 多语言的网站怎么做呀 多语言网站设计需要注意的问题  \n\n7. data-*属性的作用\n\nhtml5规范里增加了一个自定义data属性\n为前端开发者提供自定义的属性，这些属性集可以通过对象的dataset属性获取，不支持该属性的浏览器可以通过 getAttribute方法获取\n<div data-author=\"david\" data-time=\"2011-06-20\" data-comment-num=\"10\">...</div>\ndiv.dataset.commentNum; // 可通过js获取 10\ndata-为前端开发者提供自定义的属性，这些属性集可以通过对象的dataset属性获取，不支持该属性的浏览器可以通过 getAttribute方法获取。ppk提到过使用rel属性，lightbox库推广了rel属性，HTML5提供了data-做替代，这样可以更好 地使用自定义的属性\n需要注意的是，data-之后的以连字符分割的多个单词组成的属性，获取的时候使用驼峰风格。\n8. 如果把 HTML5 看作做一个开放平台，那它的构建模块有哪些？\n\n答: \n\n　　　　1）Web Storage API\n　　　　2）基于位置服务LBS\n　　　　3）无插件播放音频视频\n　　　　4）调用相机和GPU图像处理单元等硬件设备\n　　　　5）拖拽和Form API\n\n9. 请描述一下 cookies，sessionStorage 和 localStorage 的区别？\n\n答:\n\ncookie:\ncookie是网站为了标示用户身份而储存在用户本地终端（Client Side）上的数据（通常经过加密）。\ncookie数据始终在同源的http请求中携带（即使不需要），记会在浏览器和服务器间来回传递。\nsessionStorage和localStorage不会自动把数据发给服务器，仅在本地保存。\n存储大小：\ncookie数据大小不能超过4k。\nsessionStorage和localStorage 虽然也有存储大小的限制，但比cookie大得多，可以达到5M或更大。\n有期时间：\nlocalStorage    存储持久数据，浏览器关闭后数据不丢失除非主动删除数据；\nsessionStorage  数据在当前浏览器窗口关闭后自动删除。\ncookie          设置的cookie过期时间之前一直有效，即使窗口或浏览器关闭\n作用域不同:\nsessionStorage不在不同的浏览器窗口中共享，即使是同一个页面；\nlocalStorage 在所有同源窗口中都是共享的；cookie也是在所有同源窗口中都是共享的。\nWeb Storage 支持事件通知机制，可以将数据更新的通知发送给监听者。\nWeb Storage 的 api 接口使用更方便。\n View Code\n10. 浏览器本地存储与服务器端存储之间的区别\n\n其实数据既可以在浏览器本地存储，也可以在服务器端存储。\n浏览器端可以保存一些数据，需要的时候直接从本地获取，sessionStorage、localStorage和cookie都由浏览器存储在本地的数据。\n服务器端也可以保存所有用户的所有数据，但需要的时候浏览器要向服务器请求数据。\n1.服务器端可以保存用户的持久数据，如数据库和云存储将用户的大量数据保存在服务器端。\n2.服务器端也可以保存用户的临时会话数据。服务器端的session机制，如jsp的 session 对象，数据保存在服务器上。实现上，服务器和浏览器之间仅需传递session id即可，服务器根据session id找到对应用户的session对象。会话数据仅在一段时间内有效，这个时间就是server端设置的session有效期。\n服务器端保存所有的用户的数据，所以服务器端的开销较大，而浏览器端保存则把不同用户需要的数据分布保存在用户各自的浏览器中。\n浏览器端一般只用来存储小数据，而服务器可以存储大数据或小数据。\n服务器存储数据安全一些，浏览器只适合存储一般数据。\n11. sessionStorage和页面js数据对象的区别\n\n答:\n\n页面中一般的 js 对象或数据的生存期是仅在当前页面有效，因此刷新页面或转到另一页面这样的重新加载页面的情况，数据就不存在了。\n而sessionStorage 只要同源的同窗口（或tab）中，刷新页面或进入同源的不同页面，数据始终存在。也就是说只要这个浏览器窗口没有关闭，加载新页面或重新加载，数据仍然存在。\n 12. canvas和svg的区别?\n\n答: \n\nsvg: \nSVG是一种使用XML描述2D图形的语言\nSVG基于XML, 这意味着SVG DOM中的每个元素都是可用的. 所以可以为每个元素附加JavaScript事件处理器\n在SVG中, 每个被绘制的图像均被视为对象. 如果SVG对象的属性发生变化, 那么浏览器能够自动重现图像\nCanvas\nCanvas通过js来绘制2D图形\nCanvas是逐像素进行渲染的\n在Canvas中, 一旦图形被绘制完成, 它就不会继续得到浏览器的关注. 如果其位置发生变化, 那么整个场景也需要重新绘制, 包括任何或许已被图形覆盖的对象.\n区别\nCanvas支持分辨率, SVG不支持\nCanvas不支持事件处理器, SVG支持\nCanvas只有弱的文本渲染能力, 而SVG最适合带有大型渲染区域的应用程序(比如谷歌地图)\nCanvas能够以.png或.jpg格式保存结果图像\nSVG的复杂度过高的话会减慢渲染速度(任何过度使用DOM的应用都不快)\nCanvas最适合图像密集型的游戏, 其中的许多对象会被频繁重绘. 而SVG不适合游戏应用\nCanvas是基于位图的图像,它不能够改变大小, 只能缩放显示; SVG是基于矢量的, 所以它能够很好地处理图形大小的改变　　　　\nCanvas提供的功能更原始, 适合像素处理, 动态渲染和大数据量绘制; SVG功能更完善, 适合静态图片显示, 高保真文档查看和打印的应用场景\n绘制Canvas对象后, 不能使用脚本和CSS对它进行修改; 而SVG对象是文档对象模型的一部分, 所以可以随时使用脚本和CSS修改它们\n13. src和href的区别?\n\n答: \n\nsrc指向外部资源的位置, 用于替换当前元素, 比如js脚本, 图片等元素\nhref指向网络资源所在的位置, 用于在当前文档和引用资源间确定联系, 加载css\n四、js\n\n1. ajax, 跨域, jsonp\n\n　　参考： 《JavaScript》高级程序设计第21章：Ajax和Comet\n\n　　　　　js跨域\n\n　　　　　jQuery中Ajax操作\n\n2. apply和call的用法和区别:\n\n用法: 都能继承另一个对象的方法和属性,区别在于参数列表不一样\n区别:\nFunction.apply(obj, args) args是一个数组,作为参数传给Function\nFunction.call(obj, arg1, arg2,...)  arg*是参数列表\napply一个妙用: 可以将一个数组默认的转化为一个参数列表\n举个栗子: 有一个数组arr要push进一个新的数组中去, 如果用call的话需要把数组中的元素一个个取出来再push, 而用apply只有Array.prototype.push.apply(this, arr)\n　　参考自: 区别和详解：js中call()和apply()的用法\n\n3. bind函数的兼容性\n\n用法:\nbind()函数会创建一个新函数, 为绑定函数。当调用这个绑定函数时,绑定函数会以创建它时传入bind方法的第一个参数作为this,传入bind方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.\n一个绑定函数也能使用new操作符创建对象：这种行为就像把原函数当成构造器。提供的 this 值被忽略，同时调用时的参数被提供给模拟函数。　　\n4. 解释下事件代理\n\n答: 事件委托利用了事件冒泡, 只指定一个事件处理程序, 就可以管理某一类型的所有事件.\n\n例: \n\nhtml部分: 要点击li弹出其id\n\n复制代码\n<ul id=\"list\">\n    <li id=\"li-1\">Li 2</li>\n    <li id=\"li-2\">Li 3</li>\n    <li id=\"li-3\">Li 4</li>\n    <li id=\"li-4\">Li 5</li>\n    <li id=\"li-5\">Li 6</li>\n    <li id=\"li-6\">Li 7</li>\n</ul>\n复制代码\n \n\n复制代码\n//js部分\ndocument.getElementById(\"list\").addHandler(\"click\", function(e){\n    var e = e || window.event;\n    var target = e.target || e.srcElement;\n    if(target.nodeName.toUpperCase == \"LI\"){\n        console.log(\"List item\", e,target.id, \"was clicked!\");\n    }\n});\n复制代码\n \n\n \n\n5. 解释下js中this是怎么工作的?\n\n答: \n\n　　this 在 JavaScript 中主要由以下五种使用场景。\n\n作为函数调用，this 绑定全局对象，浏览器环境全局对象为 window 。\n内部函数内部函数的 this 也绑定全局对象，应该绑定到其外层函数对应的对象上，这是 JavaScript的缺陷，用that替换。\n作为构造函数使用，this 绑定到新创建的对象。\n作为对象方法使用，this 绑定到该对象。\n使用apply或call调用 this 将会被显式设置为函数调用的第一个参数。\n6. 继承\n\n　　参考：js怎么实现继承？\n\n \n\n7. AMD vs. CommonJS？\n\n答: AMD是依赖提前加载,CMD是依赖延时加载\n\n \n\n8. 什么是哈希表?\n\n答:  哈希表（Hash table，也叫散列表），是根据关键码值(Key value)而直接进行访问的数据结构。也就是说，它通过把关键码值映射到表中一个位置来访问记录，以加快查找的速度。这个映射函数叫做散列函数，存放记录的数组叫做散列表。\n\n　　使用哈希查找有两个步骤:\n\n使用哈希函数将被查找的键转换为数组的索引。在理想的情况下，不同的键会被转换为不同的索引值，但是在有些情况下我们需要处理多个键被哈希到同一个索引值的情况。所以哈希查找的第二个步骤就是处理冲突\n处理哈希碰撞冲突。有很多处理哈希碰撞冲突的方法，比如拉链法和线性探测法。\n \n\n元素特征转变为数组下标的方法就是散列法。散列法当然不止一种，下面列出三种比较常用的：\n\n1，除法散列法 \n最直观的一种，上图使用的就是这种散列法，公式： \n      index = value % 16 \n学过汇编的都知道，求模数其实是通过一个除法运算得到的，所以叫“除法散列法”。\n\n2，平方散列法 \n求index是非常频繁的操作，而乘法的运算要比除法来得省时（对现在的CPU来说，估计我们感觉不出来），所以我们考虑把除法换成乘法和一个位移操作。公式： \n      index = (value * value) >> 28   （右移，除以2^28。记法：左移变大，是乘。右移变小，是除。） \n如果数值分配比较均匀的话这种方法能得到不错的结果，但我上面画的那个图的各个元素的值算出来的index都是0——非常失败。也许你还有个问题，value如果很大，value * value不会溢出吗？答案是会的，但我们这个乘法不关心溢出，因为我们根本不是为了获取相乘结果，而是为了获取index。\n\n3，斐波那契（Fibonacci）散列法\n\n \n\n解决冲突的方法:\n\n1. 拉链法\n\n　　将大小为M 的数组的每一个元素指向一个条链表，链表中的每一个节点都存储散列值为该索引的键值对，这就是拉链法.\n\n　　对采用拉链法的哈希实现的查找分为两步，首先是根据散列值找到等一应的链表，然后沿着链表顺序找到相应的键。\n\n2. 线性探测法: \n\n　　使用数组中的空位解决碰撞冲突\n\n \n\n参考: 哈希表的工作原理  　浅谈算法和数据结构: 十一 哈希表\n\n \n\n9. 什么是闭包? 闭包有什么作用?\n\n答:\n\n闭包是指有权访问另一个函数作用域中的变量的函数. 创建闭包常见方式,就是在一个函数内部创建另一个函数.\n作用: \n匿名自执行函数  (function (){ ... })();   创建了一个匿名的函数，并立即执行它，由于外部无法引用它内部的变量，因此在执行完后很快就会被释放，关键是这种机制不会污染全局对象。\n缓存, 可保留函数内部的值\n实现封装\n实现模板\n　　参考自: js闭包的用途\n\n10. 伪数组: \n\n什么是伪数组: \n伪数组是能通过Array.prototype.slice 转换为真正的数组的带有length属性的对象\n比如arguments对象,还有像调用getElementsByTagName,document.childNodes之类的，它们都返回NodeList对象都属于伪数组\n我们可以通过Array.prototype.slice.call(fakeArray)将伪数组转变为真正的Array对象: 返回新数组而不会修改原数组　　\n　　参考: 伪数组\n\n11. undefined和null的区别, 还有undeclared:\n\nnull表示没有对象, 即此处不该有此值. 典型用法:\n（1） 作为函数的参数，表示该函数的参数不是对象。\n\n（2） 作为对象原型链的终点。\n\n ( 3 )   null可以作为空指针. 只要意在保存对象的值还没有真正保存对象,就应该明确地让该对象保存null值.\nundefined表示缺少值, 即此处应该有值, 但还未定义.\n（1）变量被声明了，但没有赋值时，就等于undefined。\n\n（2) 调用函数时，应该提供的参数没有提供，该参数等于undefined。\n\n（3）对象没有赋值的属性，该属性的值为undefined。\n\n（4）函数没有返回值时，默认返回undefined。\n\nundeclared即为被污染的命名, 访问没有被声明的变量, 则会抛出异常, 终止执行. 即undeclared是一种语法错误\n　　参考: undefined与null的区别\n\n12. 事件冒泡机制:　　\n\n从目标元素开始，往顶层元素传播。途中如果有节点绑定了相应的事件处理函数，这些函数都会被一次触发。如果想阻止事件起泡，可以使用e.stopPropagation()（Firefox）或者e.cancelBubble=true（IE）来组织事件的冒泡传播。\n\n13. 解释下为什么接下来这段代码不是 IIFE(立即调用的函数表达式)：function foo(){ }();?\n\n答: \n\n　　而函数定义（语句以function关键字开始）是不能被立即执行的，这无疑会导致语法的错误（SyntaxError）。当函数定义代码段包裹在括号内，使解析器可以将之识别为函数表达式，然后调用。IIFE:  (function foo(){})()　\n\n　　区分  (function(){})(); 和  (function(){}());  其实两者实现效果一样。\n　　函数字面量：首先声明一个函数对象，然后执行它。(function () { alert(1); })();\n\n　　优先表达式：由于Javascript执行表达式是从圆括号里面到外面，所以可以用圆括号强制执行声明的函数。(function () { alert(2); }());\n\n14. \"attribute\" 和 \"property\" 的区别是什么？\n\n答: \n\n　　DOM元素的attribute和property两者是不同的东西。attribute翻译为“特性”，property翻译为“属性”。\n　　attribute是一个特性节点，每个DOM元素都有一个对应的attributes属性来存放所有的attribute节点，attributes是一个类数组的容器，说得准确点就是NameNodeMap，不继承于Array.prototype，不能直接调用Array的方法。attributes的每个数字索引以名值对(name=”value”)的形式存放了一个attribute节点。<div class=\"box\" id=\"box\" gameid=\"880\">hello</div>\n\n　　property就是一个属性，如果把DOM元素看成是一个普通的Object对象，那么property就是一个以名值对(name=”value”)的形式存放在Object中的属性。要添加和删除property和普通的对象类似。\n\n　　很多attribute节点还有一个相对应的property属性，比如上面的div元素的id和class既是attribute，也有对应的property，不管使用哪种方法都可以访问和修改。\n\n　　总之，attribute节点都是在HTML代码中可见的，而property只是一个普通的名值对属性。\n\n15. 请指出 document load 和 document ready 两个事件的区别。\n\ndocument.ready和onload的区别——JavaScript文档加载完成事件。页面加载完成有两种事件:\n一是ready，表示文档结构已经加载完成（不包含图片等非文字媒体文件）\n二是onload，指示页面包含图片等文件在内的所有元素都加载完成。\njQuery中$(function(){/* do something*/});他的作用或者意义就是:在DOM加载完成后就可以可以对DOM进行操作。一般情况先一个页面响应加载的顺序是，域名解析-加载html-加载js和css-加载图片等其他信息。\n16. 什么是use strict? 其好处坏处分别是什么?\n\n答: \n\n　　在所有的函数 (或者所有最外层函数) 的开始处加入 \"use strict\"; 指令启动严格模式。\n\n　　\"严格模式\"有两种调用方法\n\n　　　　　　1）将\"use strict\"放在脚本文件的第一行，则整个脚本都将以\"严格模式\"运行。如果这行语句不在第一行，则无效，整个脚本以\"正常模式\"运行。如果不同模式的代码文件合并成一个文件，这一点需要特别注意。\n\n　　　　　　2）将整个脚本文件放在一个立即执行的匿名函数之中。\n\n 　　　　好处\n\n　　　　　　- 消除Javascript语法的一些不合理、不严谨之处，减少一些怪异行为;\n\n　　　　　　- 消除代码运行的一些不安全之处，保证代码运行的安全；\n\n　　　　　　- 提高编译器效率，增加运行速度；\n\n　　　　　　- 为未来新版本的Javascript做好铺垫。\n\n　　　　坏处　\n\n　　　　　　同样的代码，在\"严格模式\"中，可能会有不一样的运行结果；一些在\"正常模式\"下可以运行的语句，在\"严格模式\"下将不能运行　\n\n17. 浏览器端的js包括哪几个部分?\n\n　　答: 核心( ECMAScript) , 文档对象模型(DOM), 浏览器对象模型(BOM)\n\n18. DOM包括哪些对象?\n\n　　答: DOM是针对HTML和XML文档的一个API(应用程序编程接口). DOM描绘了一个层次化的节点树, 允许开发人员添加, 移除和修改页面的某一部分.\n\n常用的DOM方法:\ngetElementById(id)\ngetElementsByTagName()\nappendChild(node)\nremoveChild(node)\nreplaceChild()\ninsertChild()\ncreateElement()\ncreateTextNode()\ngetAttribute()\nsetAttribute()\n常用的DOM属性\ninnerHTML  节点(元素)的文本值\nparentNode  节点(元素)的父节点\nchildNodes \nattributes   节点(元素)的属性节点\n　　参考:  HTML DOM 方法\n\n19. js有哪些基本类型?\n\n　　答: Undefined, Null, Boolean, Number, String\n\n　　　　Object是复杂数据类型, 其本质是由一组无序的名值对组成的.\n\n20. 基本类型与引用类型有什么区别?\n\n　　答: \n\n基本类型如上题所示. 引用类型则有: Object, Array, Date, RegExp, Function\n存储\n基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中\n引用类型的值是对象, 保存在堆内存中. 包含引用类型的变量实际上包含的并不是对象本身, 而是一个指向改对象的指针　\n复制\n从一个变量向另一个变量复制基本类型的值, 会创建这个值的一个副本\n从一个变量向另一个变量复制引用类型的值, 复制的其实是指针,　因此两个变量最终都指向同一个对象\n检测类型\n确定一个值是哪种基本类型可以用typeof操作符,\n而确定一个值是哪种引用类型可以使用instanceof操作符　　　　　　　\n　　参考: 《JavaScript高级程序设计》\n\n21. 关于js的垃圾收集例程\n\n　　js是一门具有自动垃圾回收机制的编程语言,开发人员不必关心内存分配和回收问题\n\n离开作用域的值将被自动标记为可以回收, 因此将在垃圾收集期间被删除\n\"标记清除\"是目前主流的垃圾收集算法, 这种算法的思路是给当前不使用的值加上标记, 然后再回收其内存\n另一种垃圾收集算法是\"引用计数\", 这种算法的思想是跟踪记录所有值被引用的次数. js引擎目前都不再使用这种算法, 但在IE中访问非原生JS对象(如DOM元素)时, 这种算法仍然可能会导致问题\n当代码中存在循环引用现象时, \"引用计数\" 算法就会导致问题\n解除变量的引用不仅有助于消除循环引用现象, 而且对垃圾收集也有好处. 为了确保有效地回收内存, 应该及时解除不再使用的全局对象, 全局对象属性以及循环引用变量的引用\n22. ES5中, 除了函数,什么能够产生作用域?\n\n答: try-catch 和with延长作用域. 因为他们都会创建一个新的变量对象. \n\n　　这两个语句都会在作用域链的前端添加一个变量对象. 对with语句来说, 会将指定的对象添加到作用域链中. 对catch语句来说, 会创建一个新的变量对象, 其中包含的是被抛出的错误对象的声明.\n\n当try代码块中发生错误时，执行过程会跳转到catch语句，然后把异常对象推入一个可变对象并置于作用域的头部。在catch代码块内部，函数的所有局部变量将会被放在第二个作用域链对象中。请注意，一旦catch语句执行完毕，作用域链机会返回到之前的状态。try-catch语句在代码调试和异常处理中非常有用，因此不建议完全避免。你可以通过优化代码来减少catch语句对性能的影响。一个很好的模式是将错误委托给一个函数处理\nwith(object) {statement}。它的意思是把object添加到作用域链的顶端\ne.g.\n复制代码\nfunction buildUrl(){\n    var qs = \"?debug=true\";\n   \n    //with接收location对象, 因此其变量对象中就包含了location对象的所有属性和方法, 而这个变量对象被添加到了作用域链的前端\n    with(location){\n        //这里的href其实是location.href. 创建了一个名为url的变量, 就成了函数执行环境的一部分\n        var url = href + qs;\n    }\n       \n    return url;\n}\n复制代码\n \n\n　　参考: js try、catch、finally语句还有with语句  JavaScript 开发进阶：理解 JavaScript 作用域和作用域链\n\n23. js有几种函数调用方式?\n\n答: \n\n方法调用模型    var obj = { func : function(){};}    obj.func()\n函数调用模式　　var func = function(){}    func();\n构造器调用模式  \napply/ call调用模式\n24. 描述事件模型?IE的事件模型是怎样的？事件代理是什么？事件代理中怎么定位实际事件产生的目标？\n\n答: 　捕获->处于目标->冒泡，IE应该是只有冒泡没有捕获。\n　　  事件代理就是在父元素上绑定事件来处理，通过event对象的target来定位。\n\n25. js动画有哪些实现方法?\n\n答: 用定时器 setTimeout和setInterval\n\n26. 还有什么实现动画的方法?\n\n答:\n\njs动画: 使用定时器\nCSS : transition , animation\ntransition 包含4种属性：transition-delaytransition-durationtransition-propertytransition-timing-function，对应动画的4种属性： 延迟、持续时间、对应css属性和缓动函数，\ntransform 包含7种属性：animation-nameanimation-durationanimation-timing-functionanimation-delayanimation-directionanimation-iteration-countanimation-fill-modeanimation-play-state，它们可以定义动画名称，持续时间，缓动函数，动画延迟，动画方向，重复次数，填充模式。\n\nHTML5 动画\ncanvas\nsvg\nwebgl\n　　参考自: 前端动画效果实现的简单比较\n\n27. 面向对象有哪几个特点? \n\n答: 封装, 继承, 多态\n\n28. 如何判断属性来自自身对象还是原型链?\n\n答: hasOwnPrototype\n\n29. ES6新特性\n\n答: \n\n　　1) 箭头操作符 inputs=>outputs: 操作符左边是输入的参数，而右边则是进行的操作以及返回的值\n\n　　2) 支持类, 引入了class关键字. ES6提供的类实际上就是JS原型模式的包装\n\n　　3) 增强的对象字面量. \n\n　　　　1. 可以在对象字面量中定义原型  __proto__: xxx  //设置其原型为xxx,相当于继承xxx\n\n　　　　2. 定义方法可以不用function关键字\n\n　　　　3. 直接调用父类方法\n\n　　4) 字符串模板: ES6中允许使用反引号 ` 来创建字符串，此种方法创建的字符串里面可以包含由美元符号加花括号包裹的变量${vraible}。\n\n　　5) 自动解析数组或对象中的值。比如若一个函数要返回多个值，常规的做法是返回一个对象，将每个值做为这个对象的属性返回。但在ES6中，利用解构这一特性，可以直接返回一个数组，然后数组中的值会自动被解析到对应接收该值的变量中。\n\n　　6) 默认参数值: 现在可以在定义函数的时候指定参数的默认值了，而不用像以前那样通过逻辑或操作符来达到目的了。\n\n　　7) 不定参数是在函数中使用命名参数同时接收不定数量的未命名参数。在以前的JavaScript代码中我们可以通过arguments变量来达到这一目的。不定参数的格式是三个句点后跟代表所有不定参数的变量名。比如下面这个例子中，…x代表了所有传入add函数的参数。\n\n　　8) 拓展参数则是另一种形式的语法糖，它允许传递数组或者类数组直接做为函数的参数而不用通过apply。\n\n　　9) let和const关键字: 可以把let看成var，只是它定义的变量被限定在了特定范围内才能使用，而离开这个范围则无效。const则很直观，用来定义常量，即无法被更改值的变量。\n\n　　10) for of值遍历 每次循环它提供的不是序号而是值。\n\n　　11) iterator, generator\n\n　　12) 模块\n\n　　13) Map, Set, WeakMap, WeakSet\n\n　　14) Proxy可以监听对象身上发生了什么事情，并在这些事情发生后执行一些相应的操作。一下子让我们对一个对象有了很强的追踪能力，同时在数据绑定方面也很有用处。\n\n　　15) Symbols Symbol 通过调用symbol函数产生，它接收一个可选的名字参数，该函数返回的symbol是唯一的。之后就可以用这个返回值做为对象的键了。Symbol还可以用来创建私有属性，外部无法直接访问由symbol做为键的属性值。\n\n　　16) Math, Number, String, Object的新API\n\n　　17) Promises是处理异步操作的一种模式\n\n　　参考: ES6新特性概览\n\n30. 如何获取某个DOM节点，节点遍历方式\n\n答: getElementById()  getElementsByTagName()\n\n节点遍历: 先序遍历DOM树的5种方法\n\n31. 用LESS如何给某些属性加浏览器前缀？\n\n答:  可以自定义一个函数\n\n1\n2\n3\n4\n5\n6\n7\n8\n.border-radius(@values) {\n  -webkit-border-radius: @values;\n     -moz-border-radius: @values;\n          border-radius: @values;\n}\ndiv {\n  .border-radius(10px);\n}\n32.  js异步模式如何实现？\n\n答: JavaScript异步编程的Promise模式\n\n33. 事件机制，如何绑定事件处理函数。\n\n34. 图片预加载的实现\n\n答:\n\n使用jQuery图片预加载插件Lazy Load\n加载jQuery, 与jquery.lazyload.js\n设置图片的占位符为data-original, 给图片一个特别的标签,比如class=\".lazy\"\n然后延迟加载: $('img.lazy').lazyload();这个函数可以选择一些参数: \n图片预先加载距离：threshold，通过设置这个值，在图片未出现在可视区域的顶部距离这个值时加载。\n事件绑定加载的方式：event\n图片限定在某个容器内：container\n使用js实现图片加载: 就是new一个图片对象, 绑定onload函数, 赋值url\n用CSS实现图片的预加载\n写一个CSS样式设置一批背景图片，然后将其隐藏\n改进: 使用js来推迟预加载时间, 防止与页面其他内容一起加载\n用Ajax实现预加载\n其实就是通过ajax请求请求图片地址. 还可以用这种方式加载css,js文件等\n35. 如果在同一个元素上绑定了两个click事件, 一个在捕获阶段执行, 一个在冒泡阶段执行. 那么当触发click条件时, 会执行几个事件? 执行顺序是什么?\n\n答:\n\n 　　我在回答这个题的时候说是两个事件, 先执行捕获的后执行冒泡的. 其实是不对的.\n\n　　绑定在目标元素上的事件是按照绑定的顺序执行的!!!!\n\n　　即: 绑定在被点击元素的事件是按照代码顺序发生，其他元素通过冒泡或者捕获“感知”的事件，按照W3C的标准，先发生捕获事件，后发生冒泡事件。所有事件的顺序是：其他元素捕获阶段事件 -> 本元素代码顺序事件 -> 其他元素冒泡阶段事件 。\n\n　　参考: JavaScript-父子dom同时绑定两个点击事件，一个用捕获，一个用冒泡时执行顺序\n\n36. js中怎么实现块级作用域?\n\n答:\n\n使用匿名函数, (立即执行函数)\n(function(){...})()\n或者es6\n块级作用域引入了两种新的声明形式,可以用它们定义一个只存在于某个语句块中的变量或常量.这两种新的声明关键字为:\n\nlet: 语法上非常类似于var, 但定义的变量只存在于当前的语句块中\nconst: 和let类似,但声明的是一个只读的常量\n使用let代替var可以更容易的定义一个只在某个语句块中存在的局部变量,而不用担心它和函数体中其他部分的同名变量有冲突.在let语句内部用var声明的变量和在let语句外部用var声明的变量没什么差别,它们都拥有函数作用域,而不是块级作用域.\n\n37. 构造函数里定义function和使用prototype.func的区别？\n\n答：\n\n　　　　1. 直接调用function，每一个类的实例都会拷贝这个函数，弊端就是浪费内存（如上）。prototype方式定义的方式，函数不会拷贝到每一个实例中，所有的实例共享prototype中的定义，节省了内存。\n\n　　　　2. 但是如果prototype的属性是对象的话，所有实例也会共享一个对象（这里问的是函数应该不会出现这个情况），如果其中一个实例改变了对象的值，则所有实例的值都会被改变。同理的话，如果使用prototype调用的函数，一旦改变，所有实例的方法都会改变。——不可以对实例使用prototype属性，只能对类和函数用。\n\n \n\n38. js实现对象的深克隆\n\n答:\n\n因为js中数据类型分为基本数据类型(number, string, boolean, null, undefined)和引用类型值(对象, 数组, 函数). 这两类对象在复制克隆的时候是有很大区别的. 原始类型存储的是对象的实际数据, 而对象类型存储的是对象的引用地址(对象的实际内容单独存放, 为了减少数据开销通常放在内存中). 此外, 对象的原型也是引用对象, 它把原型的属性和方法放在内存中, 通过原型链的方式来指向这个内存地址. \n\n于是克隆也会分为两类:\n\n浅度克隆: 原始类型为值传递, 对象类型仍为引用传递\n深度克隆: 所有元素或属性均完全复制, 与原对象完全脱离, 也就是说所有对于新对象的修改都不会反映到原对象中\n深度克隆实现:\n\n复制代码\nfunction clone(obj){\n    if(typeof(obj)== 'object'){\n        var result = obj instanceof Array ? [] : {};\n        for(var i in obj){\n            var attr = obj[i];\n            result[i] = arguments.callee(attr);\n        }\n        return result;\n    } else {\n        return obj;\n    }\n};\n复制代码\n \n\n  参考: JavaScript深克隆   javascript中对象的深度克隆\n\n六、 jQuery问题\n\n七、 一些编程题\n\n1. var obj = {a : 1}; (function (obj) { obj = {a : 2}; })(obj); //问obj怎么变？\n答: 外部的obj不变. 因为匿名函数中obj传入参数等于是创建了一个局部变量obj, 里面的obj指向了一个新的对象 . 如果改成(function () { obj = {a : 2}; })(obj);  就会变了\n\n2. var obj = { a:1,  func: function() { (function () { a=2; }(); }} ; obj.fun()   //a 怎么变? 匿名函数里的this 是什么?\n\n答: obj里的a不会变. 匿名函数里的this指向全局对象window.  这等于是给window加了一个名为a的属性\n\n　　要改变obj中a的值 , 应当:\n\n　　(function() { this.a = 2}).call(this);\n\n　　或者obj中定义func :    func: function() { var self = this; (function(){self.a=2;})();}\n\n3. 要实现函数内每隔5秒调用自己这个函数，100次以后停止，怎么办\n\n \n\n4. 点击一个ul的五个li元素，分别弹出他们的序号，怎么做？\n\n想到了用闭包, 但是写错了...\n\n一开始是这么写的\n\n1\n2\n3\n4\n5\n6\n7\n　//注意!!这种写法是错误的!!!　　 for(var i=0; i<oLis.length; i++){\n    oLis[i].onclick = function(){\n        return (function(j){\n            alert(j);\n        })(i);\n    }\n}\n但是这样做的话, 点击所有的li都会弹出最后一个序号. 因为每个li对应的onclick事件的函数, 返回的那个函数的参数还是最后的那个i, 并没有改变. 应该是这么写\n\n方法1 : \n\n1\n2\n3\n4\n5\n6\n7\nfor(var i=0; i<oLis.length; i++){\n    oLis[i].onclick = (function(j){\n        return function(){\n            alert(j);\n        }\n    })(i);\n}\n这样的话, 给每个li绑定onclick事件时, 其实绑的是一个立即执行函数, 这个立即执行函数的参数是i, 因为它是立即执行的, 循环时已经把i的值赋给了li的onclick事件, 所以在外部函数里的i改变后并不会影响i的值.\n\n另一种实现方法:(立即执行函数)\n\n1\n2\n3\n4\n5\n6\n7\nfor(var i=0; i<oLi.length; i++){\n    (function(j){\n        oLi[j].onclick = function(){\n            alert(j);\n        };\n    })(i);\n}\n或者不用闭包\n\n方法2: \n\n复制代码\n    var oLi = document.getElementsByTagName('li');\n    function func(obj,i){\n        obj.onclick = function(){\n            alert (i);\n        }\n    }\n    for(var i = 0; i<oLi.length; i++){\n        func(oLi[i], i);\n    }\n复制代码\n \n\n方法3: 设置属性:\n\n1\n2\n3\n4\nvar oLi = document.getElementsByTagName('li');\nfor(var i=0; i<oLi.length; i++){\n    oLi[i].setAttribute(\"onclick\", \"alert(\"+i+\");\");\n}　\n方法4: 设置index保存\n\n1\n2\n3\n4\n5\n6\nfor(var i=0; i<oLi.length; i++){\n    oLi[i].index = i;\n    oLi[i].onclick = function(){\n        alert(this.index);\n    }\n}\n　　\n\n或者也可以用事件代理来做.\n\n参考: 原生js实现点击li弹出当前的索引值(原创)  \n\n \n\n八、js算法题\n\n1. js实现数组去重怎么实现?\n\n方法1. 创建一个新的临时数组来保存数组中已有的元素\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\nvar a = new Array(1,2,2,2,2,5,3,2,9,5,6,3);\nArray.prototype.unique1 = function(){\n    var n = [];     //一个新的临时数组\n    for(var i=0; i<this.length; i++){\n        //如果把当前数组的第i已经保存进了临时数组, 那么跳过\n        if(n.indexOf(this[i]) == -1){\n            n.push(this[i]);\n        }\n    }\n    return n;\n}\nconsole.log(a.unique1());\n方法2. 使用哈希表存储已有的元素\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\nArray.prototype.unique2 = function(){\n    var hash = {},\n        n = [];     //hash 作为哈希表, n为临时数组\n    for(var i=0; i<this.length; i++){\n        if(!hash[this[i]]){         //如果hash表中没有当前项\n            hash[this[i]] = true;   //存入hash表\n            n.push(this[i]);        //当前元素push到临时数组中\n        }\n    }\n    return n;\n}\n方法3. 使用indexOf判断数组元素第一次出现的位置是否为当前位置\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\nArray.prototype.unique3 = function(){\n    var n = [this[0]]; \n    for(var i=1; i<this.length; i++)    //从第二项开始遍历\n    {\n        //如果当前数组元素在数组中出现的第一次的位置不是i\n        //说明是重复元素\n        if(this.indexOf(this[i]) == i){\n            n.push(this[i]);\n        }\n    }\n    return n;\n}\n方法4. 先排序再去重\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\nArray.prototype.unique4 = function(){\n    this.sort(function(a, b){ return a - b;});\n    var n = [this[0]];\n    for(var i=1; i<this.length; i++){\n        if(this[i] != this[i-1]){\n            n.push(this[i]);\n        }\n    }\n    return n;\n}\n第一种方法和第三种方法都使用了indexOf(), 这个函数的执行机制也会遍历数组\n\n第二种方法使用了一个哈希表, 是最快的. \n\n第三种方法也有一个排序的复杂度的计算.\n\n然后做了个测试, 随机生成100万个0-1000的数组结果如下:\n\n\n\n第三种方法总是第二种方法的将近两倍, 而第四种方法与数组的范围有关,\n\n如果是0-100的数组\n\n\n\n而如果是0-10000, 方法四看着就效果还不错了\n\n\n\n而第二种方法永远是最好的, 但是是以空间换时间\n\n全部代码如下:\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n34\n35\n36\n37\n38\n39\n40\n41\n42\n43\n44\n45\n46\n47\n48\n49\n50\n51\n52\n53\n54\n55\n56\n57\n58\n59\n60\n61\n62\n63\n64\n65\n66\n67\n68\n69\n70\n71\nvar a = [];\nfor(var i=0; i<1000000; i++){\n    a.push(Math.ceil(Math.random()*10000));\n}\n \nArray.prototype.unique1 = function(){\n    var n = [];     //一个新的临时数组\n    for(var i=0; i<this.length; i++){\n        //如果把当前数组的第i已经保存进了临时数组, 那么跳过\n        if(n.indexOf(this[i]) == -1){\n            n.push(this[i]);\n        }\n    }\n    return n;\n}\n \nArray.prototype.unique2 = function(){\n    var hash = {},\n        n = [];     //hash 作为哈希表, n为临时数组\n    for(var i=0; i<this.length; i++){\n        if(!hash[this[i]]){         //如果hash表中没有当前项\n            hash[this[i]] = true;   //存入hash表\n            n.push(this[i]);        //当前元素push到临时数组中\n        }\n    }\n    return n;\n}\n \nArray.prototype.unique3 = function(){\n    var n = [this[0]]; \n    for(var i=1; i<this.length; i++)    //从第二项开始遍历\n    {\n        //如果当前数组元素在数组中出现的第一次的位置不是i\n        //说明是重复元素\n        if(this.indexOf(this[i]) == i){\n            n.push(this[i]);\n        }\n    }\n    return n;\n}\n \nArray.prototype.unique4 = function(){\n    this.sort(function(a, b){ return a - b;});\n    var n = [this[0]];\n    for(var i=1; i<this.length; i++){\n        if(this[i] != this[i-1]){\n            n.push(this[i]);\n        }\n    }\n    return n;\n}\nvar begin1 = new Date();\na.unique1();\nvar end1 = new Date();\n \nvar begin2 = new Date();\na.unique2();\nvar end2 = new Date();\n \nvar begin3 = new Date();\na.unique3();\nvar end3 = new Date();\n \nvar begin4 = new Date();\na.unique4();\nvar end4 = new Date();\n \nconsole.log(\"方法一执行时间:\" + (end1 - begin1));\nconsole.log(\"方法二执行时间:\" + (end2 - begin2));\nconsole.log(\"方法三执行时间:\" + (end3 - begin3));\nconsole.log(\"方法四执行时间:\" + (end4 - begin4));\n　　参考: js数组去重\n\n \n\n \n\n \n\n了解前后端分离吗（当时真不了解，但他细心地给我解释了一下，还建议我去看淘宝ued团队的文章）\n\n用什么版本控制工具\n在用mac吗\n用什么来管理各种依赖\n \n \n数组去重\nposition的几种值\ncss选择器优先级\n伪类的用法\n闭包\n闭包的实现举例\n浮动\n清除浮动\njsonp是啥\n \ngulp怎么用，用过啥\n你觉得你还有什么闪光点我没有问到的（这个太囧了，我居然反问了面试官你觉得我刚才闪不闪？面试官呵呵呵呵）\n \n \nrequire的读取顺序\n图片轮播（虽然很多面试题都有这个，但是我自己真的没有实现过，说了一个opacity与setTimeout的组合运用，最后没有实现到面试官要求的效果，但是他说还行）\nsass的伪类怎么嵌套\njsonp是啥\n怎么用jsonp\n发送jsonp时候的那个消息头长什么样子？（这个我直接跟他说没看过不知道）\n一个文本框，需要两个控件对里面的值进行控制，一个是＋1一个是－1，要求每点击一次就有个提示框冒出来。\n而且文本框是可修改的，每次修改也会冒出提示框。（这个我回答的很模糊，我说应该有个监听的事件的，但是我忘了是啥了，面试官说也行）\ncss定位\n \n百度\n（很惊奇的是面试我的三个面试官都是女生，简直了）\n一面\ncss position的几种值与区别\n闭包\n偏向于什么语言？html、css or javascript\n开发中偏于jQuery还是原生js\n为什么选择原生js？\n最近做过的项目\n那个系统还在用吗\n你是怎么学的？\n看英文有障碍吗（卧槽，当时我回答了完全没有问题，不知道哪里来的自信）\n \n二面\nhtml5的新功能了解多少\n说说离线存储\n它们与cookie的区别\nthis的理解\n怎么传入this\napply和call的区别\n项目您说说\n你github上面那个打字机的效果是怎么实现的（这是我fork了一位小伙伴的，但幸好我也花了一点时间弄懂了他的做法，要不然肯定囧死。插播一个很硬的广告 github.com/sidkwok/typing）\n前端优化\n你用过哪些优化（我说了css sprite和减少http请求，顺便引出了webpack，但没想到她也没有问）\ngulp你是怎么用的\njsonp是啥\n怎么跨域\n为什么要跨域\n怎么学习的\nposition几种值\n具体运用是什么\ndisplay几种值\n兼容性你懂多少（我就说了Ajax的还有盒子模型，但她说还有很多，但之前我说了没有系统地去学过css，都是不会的时候查文档的，估计因为这样所以她也没有追问下去）\n说说你了解的框架（我说了react和vue）\nreact你觉得怎样（我说了一些组件化和虚拟dom树的东西）\nAngularJS呢（我说没有学过，但了解过一点，我把我了解的都说了给她听）\n两个的比较\n为什么你会觉得AngularJS笨重？（也是自己的看法，mvc框架嘛，一整套什么都有）\njQuery还是原生js（百度的面试官都问了这个问题，我直接说对于jQuery我并不是很熟悉，因为我更喜欢研究原生js。）\n为什么选择原生js（我认为要把原生吃透再去理解其他类库的话会更好一点，她说也是）\n \n三面\n（二面问面试官我表现怎样的时候她说，我可以跟你讲你已经过了，我的助理会跟你通知的。以为是hr面没想到还是技术面）\n介绍自己\n说说框架\n比较一下框架\n你打算怎么去学这些框架\n听说你在浏览器兼容性这边学习的不是很好（不慌，她只是建议我这方面其实也很重要）\n对自己的规划\n喜欢用什么系统\n来，我们聊聊人生\n \ndaocloud\n一面\nhtml语义化\nh5新标签\nes6\npromise\npromise解决了你什么问题\n跨域的方法\njsonp怎么用\n用过什么库\ngulp的插件用过啥\nwebpack\n为什么要打包 (我说了http请求那点事)\n介绍一下react\n组件化是啥\n你觉的react的优点\n为什么没有选择学习AngularJS\nflex\n响应式布局是啥\n响应式布局是根据什么进行响应\ncss中用什么进行屏幕的判定\ncss中实现阴影的有什么\n \n二面\n点击a标签发生了啥\nhttp2您说说\n说说各种框架\n聊聊AngularJS（说了我的理解，但不怎么了解）\n那你熟悉哪个框架（正在玩React，vue也懂一点）\n你给我说说你那个博客生成器（github.com/sidkwok/tech-dairy）（我知道我知道，很多广告！）\n聊聊项目\n聊聊模块化吧\nsass你怎么用\ngulp用过啥\n介绍一下webpack\n实现sum(2,3);sum(2,3,4);sum(2,3,4,5);(我用了比较原始的方法，if来判断)\n那如果sum里面的参数不确定呢（提取arguments的长度，用for循环）\n你知道伪数组吗？\nok， 再来一个数组去重吧（这太经典了吧）\n \n \nMVC理解\n\n\n二面问\n项目中你解决过哪些比较困难的问题\n\n```\n"
  },
  {
    "path": "5.1.2 vuejs 前端面试题.md",
    "content": "# 前端面试题\n\n* Vue原理解析之Virtual Dom: https://joeray61.com/2017/02/08/Vue%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90%E4%B9%8BVirtual-Dom/\n  [Vue原理解析之Virtual Dom](https://joeray61.com/2017/02/08/Vue%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90%E4%B9%8BVirtual-Dom/)\n\n```\n1.vue中的MVVM模式  即Model-View-ViewModel。  \nVue是以数据为驱动的，Vue自身将DOM和数据进行绑定，一旦创建绑定，DOM和数据将保持同步，每当数据发生变化，DOM会跟着变化。  \nViewModel是Vue的核心，它是Vue的一个实例。Vue实例时作用域某个HTML元素上的，这个HTML元素可以是body，也可以是某个id所指代的元素。 \nDOM Listeners和Data Bindings是实现双向绑定的关键。DOM Listeners监听页面所有View层DOM元素的变化，当发生变化，Model层的数据随之变化；\nData Bindings监听Model层的数据，当数据发生变化，View层的DOM元素随之变化。 \n\n2.v-show指令，v-if的区别  条件渲染指令，与v-if不同的是，无论v-show的值为true或false，元素都会存在于HTML代码中；\n而只有当v-if的值为true，元素才会存在于HTML代码中。v-show指令只是设置了元素CSS的style值  \n\n3.如何让css只在当前组件中起作用  在每一个vue组件中都可以定义各自的css，js，如果希望组件内写的css只对当前组件起作用，\n只需要在style中写入scoped，即： <style scoped></style> \n\n4.指令keep-alive  在vue-router写着keep-alive，\nkeep-alive的含义： 如果把切换出去的组件保留在内存中，可以保留它的状态或避免重新渲染。\n为此可以添加一个keep-alive指令  <component :is='curremtView' keep-alive></component> 5.Vuejs组件 \nvuejs构建组件使用  Vue.component('componentName',{ /*component*/ })；  \n这里注意一点，组件要先注册再使用  Vue.component('mine',{           \ntemplate:'#mineTpl',            \nprops:['name','title','city','content']        \n});   \nvar v=new Vue({       \nel:'#vueInstance',\ndata:{           \nname:'zhang',            \ntitle:'this is title',          \ncity:'Beijing',           \ncontent:'these are some desc about Blog'     \n} });  \n6.路由嵌套  路由嵌套会将其他组件渲染到该组件内，而不是进行整个页面跳转router-view本身就是将组件渲染到该位置，想要进行页面跳转，\n就要将页面渲染到根组件，在起始配置路由时\n\n```\n"
  },
  {
    "path": "5.1.3 面试经验之谈.md",
    "content": "# 面试经验之谈\n\n- [hire-me](https://fvcproductions.github.io/hire-me/)\n- [九章算法 - 帮助更多中国人找到好工作，硅谷顶尖IT企业工程师实时在线授课为你传授面试技巧](https://www.jiuzhang.com/)\n- [怎么去海外工作](http://dingyu.me/blog/how-to-get-a-job-overseas)\n- [面试过阿里等互联网大公司，我知道了这些套路](https://mp.weixin.qq.com/s?__biz=MzIxMTE0ODU5NQ%3D%3D&mid=2650236979&idx=1&sn=71f07d1741a57f8fd429d76d37fd8a07)\n- [Bridge The Right Way to Accept a Job Offer - Bridge](https://designerfund.com/bridge/right-way-accept-job-offer/)\n- [lietoumai/Hunter: 关于职位/面试/谈Offer/程序员职场生涯等](https://github.com/lietoumai/Hunter)\n"
  },
  {
    "path": "5.1.4 前端面试.md",
    "content": "切记：不要一问一答，善于沟通。不要被动的回答问题，可以将知识延伸，聊到自己擅长的领地\n自己的职业规划：认真想想，已3-5年为界\t\n想想最后问面试官的问题\t\t\t\t\n\t\t\n\t\t\t\t\tcss面试\t\n```\n一、css盒模型\n\tcss中的盒子模型包括IE盒子模型和标准的W3C盒子模型。\nbox-sizing: border-box, inherit, content-box\n标准盒子模型： 左右border+左右padding+contentwidth\nIE盒子模型border-box: width = content+padding+border 元素指定的任何内边距和边框都将在已设定的宽度和高度内进行绘制\ninherit: 从父类继承box-sizing的值\n\n二、前端一像素问题（画一条0.5px的线）\n* transform: scaleY（0.5） 使用伪元素设置1px的边框，然后对边框进行缩放(s\ncaleY)\n　实现思路：\n     1、设定目标元素的参考位置\n     2、给目标元素添加一个伪元素before或者after，并设置绝对定位\n     3、给伪元素添加1px的边框\n     4、用box-sizing: border-box 属性把边框都包进宽和高里面\n     5、宽和高设置为 200%\n     6、整个盒子模型缩小为0.5\n     7、调整盒子模型的位置，以左上角为基准 transform-origin: 0 0;\n* border-image 设置图片的边框\n\n三、4.transition和animation的区别\n\tAnimation和transition大部分属性是相同的，他们都是随时间改变元素的属性值，他们的主要区别是transition需要触发一个事件才能改变属性，\n\t而animation不需要触发任何事件的情况下才会随时间改变属性值，并且transition为2帧，从from .... to，而animation可以一帧一帧的。\n\t\n\ttransition 规定动画的名字  规定完成过渡效果需要多少秒或毫秒  规定速度效果  定义过渡效果何时开始\n\tanimation  指定要绑定到选择器的关键帧的名称\n\t\n\n四、不定宽高的DIV居中\t\n\t1.使用flex  在父盒子设置display: flex; justify-content: center;align-items: center\n\t2.使用css的transform  \t父盒子设置:display:relative\n\t\t\t\tDiv 设置: transform: translate(-50%，-50%);position: absolute;top: 50%;left: 50%;\n\t3.display：table-cell   父盒子设置:display:table-cell; text-align:center;vertical-align:middle;\n\t\t\t\tDiv 设置: display:inline-block;vertical-align:middle;\n\t\t\n五、浮动 https://juejin.im/post/5a954add6fb9a06348538c0d\n\t特性：浮动元素影响的不仅是自己，他会影响周围元素对其进行环绕\n      为什么要清除浮动？（解决父元素高度坍陷问题）\n\t一个块级元素如果没有设置height,其height由子元素撑开，对子元素使用了浮动之后，子元素就会脱离文档流\n\t也就是说，父及元素中没有内容可以撑开其高度，这样父级元素height就会被忽略。这就是所谓的高度坍塌\n      如何清除浮动\n\t1.给父级元素定义高度 2.让父级元素也浮动 3.父级定义display:table 4.父元素设置overflow:hidden \n\tclearfix:使用内容生成的方式清除浮动\n\t\t.clearfix:after {  // :after选择器向选定的元素之后插入内容\n   \t\t\tcontent:\"\"; // 生成内容为空\n   \t\t\tdisplay: block; // 块级元素显示\n   \t\t\tclear:both; // 清除前面元素\n\t\t\t}\n\t不破坏文档流，没有副作用\n\n七、position\n\t值：relative,static（默认值）,absolute,sticky,fixed\n\tabsolute会根据上一级position的值不为static进行定位，如果向上一直没有找到position，则相对整个body进行定位\n\tfixe相对的是视图的窗口，或者frame框架（setFram的子框架，一种html标签）\n\n八、css选择器分类：\n\t   基本的：\n\t\t1.id选择器（id=\"name\"）\n\t\t2.类选择器（class=\"head\"）\n\t\t3.标签选择器（body, div, ul, li）\n\t\t4.全局选择器（*）\n\t   复杂的：\n\t\t1.组合选择器（.head .head_logo）\n\t\t2.后代选择器 （#head .nav ul li 从父集到子孙集）\n\t\t3.群组选择器 (div, span, img {color:Red} 具有相同样式的标签分组显示)\n\t\t4.继承选择器\n\t\t5.伪类选择器（链接样式，a元素的伪类）\n\t\t6.子选择器（div>p, 带大于号>）\n\t\t7.CSS相邻相邻兄弟选择器（h1+p, 带加号+）\n\n九、CSS优先级\n\t不同级别：总结排序：!important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性\n\t\t1.属性后面加!import 会覆盖页面内任何位置定义的元素样式\n\t\t2.作为style属性写在元素内的样式\n\t\t3.id选择器\n\t\t4.类选择器\n\t\t5.标签选择器\n\t\t6.通配符选择器（*）\n\t\t7.浏览器自定义或继承\n\t同一级别：后写的会覆盖先写的\n\n css选择器的解析原则：选择器定位DOM元素是从右往左的方向，这样可以尽早的过滤掉一些不必要的样式规则和元素\n\t\n十、对于行内元素，font-size指定 他们的content area的高度，由于inline box = 上下的helf-leading，如果leading为0，在这种情况下，font-size指定了inline box的高度\n\t\tfont-size 指的是字体的高度，但是不能指定每个字形给定字体高度下的实际高度，导致了span的高度大于line-height\n\n十一、z-index属性  z-index 属性设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。\n\t\t\tposition的值的属性大于z-index   Z-index 仅能在定位元素上奏效（例如 position:absolute;）\n\t\t\t元素可拥有负的 z-index 属性值\n\n十二、块元素和行内元素\n\t1.块元素会独占一行，默认情况下，其宽度自动填满父元素宽度 行元素不会占据一行，会一直排在一行，直到一行排不下\n\t2.行元素没有宽度和高度属性，块级元素即使设置了宽度，还是会独占一行\n\t块级元素： div  p forn ul li h1-h6\n\t行内元素：span img input a i\n\n十三、如何画一个三角形：  设置宽高，然后用border去画\n  \t\t\t   width: 0;\n        \t\t   height: 0;\n        \t\t   border-bottom: 100px solid cyan;\n        \t\t   border-left: 50px solid transparent;\n        \t\t   border-right: 50px solid transparent;\n\n十四、伪类：link 表示链接正常情况下（即页面加载完成时）显示的颜色\n\t    hover:表示鼠标悬停时显示的颜色\n\t    visited:链接被点击时显示的位置\n\t    focus：元素获得光标焦点时的颜色\n\t    active: 元素处于激活状态\n\tlink -> visited -> hover -> focus -> active\n\n十五、雪碧图：多个图片集成在一个图片中的图\n\t\t使用雪碧图可以减少网络请求的次数，加快允许的速度\n\t\t通过background-position，去定位图片在屏幕的哪个位置\n\n十六、使元素消失的方法\n\tvisibility:hidden、display:none、z-index=-1、opacity：0\n\t1.opacity：0,该元素隐藏起来了，但不会改变页面布局，并且，如果该元素已经绑定了一些事件，如click事件也能触发\n\t2.visibility:hidden,该元素隐藏起来了，但不会改变页面布局，但是不会触发该元素已经绑定的事件\n\t3.display:node, 把元素隐藏起来，并且会改变页面布局，可以理解成在页面中把该元素删掉\n\t\n\n\t\t\t\t\t\t\t布局面试\n一、flex弹性布局， \n\t可以简单的使一个元素居中（包括水平和垂直居中）\n栅格式系统布局，bootstrap grid\n\n二、圣杯和双飞翼布局 三栏是布局（两边两栏宽度固定，中间栏宽度自适应）\n\t方案一：position（绝对定位法） center的div需要放在最后面\n\t\t绝对定位法原理将左右两边使用absolute定位，因为绝对定位使其脱离文档流，后面的center会自然流动到他们的上卖弄，然后margin属性，留出左右两边的宽度。就可以自适应了。\n\t方案二：float 自身浮动法 center的div需要放到后面\n\t\t自身浮动法的原理就是对左右使用float:left和float：right，float使左右两个元素脱离文档流，中间的正常文档流中，使用margin指定左右外边距对其进行一个定位。\n\t圣杯布局：原理就是margin负值法。使用圣杯布局首先需要在center元素外部包含一个div,包含的div需要设置float属性使其形成一个BFC，并且这个宽度和margin的负值进行匹配\n\n三、左边定宽，右边自适应\n\t方案一：左边设置浮动，右边宽度设置100%  .left{float:left}  .right:{width:100%}\n\t方案二：左设置浮动，右用cacl去补宽度计算 .left{float:left} .right:{width:cacl(100vw-200px}\n\t方案三：父容器设置display：flex  right部分是设置flex：1\n\t方案四：右边div套个包裹、并前置、左及包裹 双浮动\n\n四、水平居中\n\t行内元素居中（父元素text-align:center）\n\t块状元素居中（块状元素没发用text-align）\n\t\t1.宽度一定：margin:auto\n\t\t2.宽度不定：块级变行内，然后在父上text-aligin\n\t\t\t    float\n\n四、BFC https://juejin.im/post/5909db2fda2f60005d2093db\n\t理解：BFC是css布局的一个概念，是一块独立的渲染区域，一个环境，里面的元素不会影响到外部的元素\n\t如何生成BFC：（脱离文档流）\n\t\t     【1】根元素，即HTML元素（最大的一个BFC）\n\t\t     【2】float的值不为none\n\t\t     【3】position的值为absolute或fixed\n\t\t     【4】overflow的值不为visible（默认值。内容不会被修剪，会呈现在元素框之外）\n\t\t     【5】display的值为inline-block、table-cell、table-caption\n\tBFC布局规则：1.内部的Box会在垂直方向，一个接一个地放置。\n\t\t     2.属于同一个BFC的两个相邻的Box的margin会发生重叠\n\t\t     3.BFC就是页面上的一个隔离的独立容器，容器里面的子元素不会影响到外面的元素。反之也如此, 文字环绕效果，设置float\n\t\t     4.BFC的区域不会与float box重叠。\n\t\t     5.计算BFC的高度，浮动元素也参与计算\n\tBFC作用：1.自适应两栏布局\n\t\t 2.可以阻止元素被浮动元素覆盖\n\t\t 3.可以包含浮动元素---清除内部浮动 原理:：触发父div的BFC属性，使下面的子div都处在父div的同一个BFC区域之内\n\t\t 4.分属于不同的BFC时，可以阻止margin重叠\n\t\n\t\t\t\t\t\t\tjs面试\n一、 this的指向\n\t1.当函数作为对象的方法被调用时，this就会指向该对象。\n\t2.作为普通函数，this指向window。\n\t3.构造器调用，this指向返回的这个对象。\n\t4.this的隐式丢失\n\t5.箭头函数  箭头函数的this绑定看的是this所在函数定义在哪个对象下，就绑定哪个对象\n      \t\t    如果有嵌套的情况，则this绑定到最近的一层对象上\n\n\t\tthis指向的固定化，并不是因为箭头函数内部有绑定this的\n\t\t机制，实际原因是箭头函数根本没有自己的this，导致内部的this就是外\n\t\t层代码块的this。正是因为它没有this，所以也就不能用作构造函数。\n\n\t怎么改变this的指向呢？ 1.使用es6的箭头函数；2.在函数内部使用that = this；3.使用apply，call，bind； 4.new实例化一个对象\n\n二、什么是闭包和原型链\n\t内部函数可以访问定义他们外部函数的参数和变量。（作用域链的向上查找，把外围的作用域中的变量值存储在内存中而不是在函数调用完毕后销毁）设计私有的方法和变量，避免全局变量的污染\n\t函数嵌套函数\n\t本质是将函数内部和外部连接起来。优点是可以读取函数内部的变量，让这些变量的值始终保存在内存中，不会在函数被调用之后自动清除\n   闭包的缺陷：\n\t\t1.闭包的缺点就是常驻内存会增大内存使用量，并且使用不当容易造成内存泄漏\n\t\t2.如果不是因为某些特殊任务而需要闭包，在没有必要的情况下，在其它函数中创建函数是不明智的，因为闭包对脚本性能具有负面影响，包括处理速度和内存消耗。   \n\n\n   内存溢出和内存泄漏（给的不够用| 用了不归还）\n\t内存溢出：在程序中申请内存时，没有足够的内存空间供其使用，出现out of memory；比如申请了一个integer,但给它存了long才能存下的数，那就是内存溢出\n\t内存泄漏：在程序申请内存后，无法释放已申请的内存空间，一次内存泄漏危害可以忽略，但内存泄漏堆积后果很严重，无论多少内存，迟到会被占光\n\n   举列子：闭包中的this,对象函数。匿名函数返回函数return function\n \n   作用域：(由当前环境与上层环境一系列的变量对象组成！！！保证 当先执行环境里，有权访问的变量和函数是有序的，作用域链变量只能被向上访问)\n\t定义：由当前环境与上层环境的一系列变量对象组成(函数嵌套函数，内部一级级往上有序访问变量或对象)\n\t作用是：保证当前执行环境里，有权访问的变量和函数时有序的，作用域链的变量只能被向上访问\n\t\t变量访问到window对象及被终止，作用域链向下访问是不允许的\n\t\t1.改变作用域有 with try..中的catch，\n\t\t2.所有为定义的直接赋值的变量自动声明为全局作用域\n\n\t作用域：一套规则，管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称\n\t\t查找变量（标识符就是变量或者函数名）（只用全局作用域和局部作用域）（作用域在它创建的时候就存在了）\n\n\t代码执行分为两个阶段：\n\t\t1.代码编译阶段：有编译器完成，将代码翻译可执行的代码，这个阶段会被确定\n\t\t2.代码执行阶段：有js引擎完成，主要执行可执行的大妈，这个阶段执行上下文被创建（对象被创建）\n\t\n\t执行上下文：一个看不见得对象，存在若干个属性和变量，它被调用的时候创建的。函数被调用查看的this指向的object，object就是上下文（只有被调用的时候创建）\n\n   作用域链: https://blog.csdn.net/yooungt13/article/details/20581635\n\t    · 当代码在一个环境中执行时，会创建变量对象的一个作用域链,\n\t\t举例子：var name =\"Tom\"\n\t\t\tfunction sayHi () {\n\t\t\t    alert('Hi,'+name)\n\t\t\t}\n\t\t\tsayHi()  //Hi, Tom\n\t\t函数sayHi()的执行环境为全局环境，所以它的变量对象为window。当函数执行到name时，先查找局部环境，找到则换回，否则顺着作用域查找，在全局环境中，\n\t\t找到name返回，这一查找变量的有序过程的依据就是作用域。\n\t\n\t    · 作用域链是保证执行环境有权访问的所有变量和函数的有序访问\n\n   原型链：函数的原型链对象constructor默认指向函数本身，原型对象除了有原型属性外，为了实现继承，还有一个原型链指针_proto_,\n\t   该指针是指向上一层的原型对象，而上一层的原型对象的结构依然类似。因此可以利用_proto_一直指向Object的原型对象上，而Object\n\t   原型对象用Object.prototype._proto_  = null表示原型链顶端。如此形成了js的原型链继承。同时所有的js对象都有Object的基本防范\n\t\n三、类的创建和继承\n\t（es5）new 一个function，在这个function的prototype里增加属性和方法, 类里面有方法和属性\n\t (es6)中class, extends\n\t1. 继承：\n \t\t原型链继承： function Cat(){ } Cat.prototype = new Animal(); Cat.prototype.name = 'cat'; 无法实现多继承\n\t\t构造继承：使用父类的构造函数来增强子类实例。function Cat(name){Animal.call(this);this.name = name || 'Tom';} 无法继承父类原型链上的属性跟方法  installof去检验\n\t\t实例继承：为父类实例添加新特性，作为子类实例的返回\n\t\t拷贝继承：拷贝父类元素上的属性跟方法\n\t\t组合继承：构造继承 + 原型继承的组合体\n\t\t寄生组合继承：通过寄生方式，在构造继承上加一个Super函数(没有实例和方法) 让他的原型链指向父类的原型链 \n\t\t\t      砍掉父类的实例属性，这样，在调用两次父类的构造的时候，就不会初始化两次实例方法/属性\n\t2. 如何判断是那种类型？ \n\n\t3.给两个构造函数A和B，如何实现A继承B （Object.prototype）\n\t\tfunction A(....){} A.prototype...\n\t\tfunction B(....){} B.prototype...\n\t\tA.prototype = Object.create(B.prototype)  再A的构造函数里new B(props)\t\t\n\t\n\t   使用new一个函数的话，函数里的构造函数的参数就为undefined，里面的一些函数可能执行错误，因为this改变了\t\n\t\tObject.create =  function (o) {\n   \t\t\tvar F = function () {};\n   \t\t\tF.prototype = o;\n   \t\t\treturn new F();\n\t\t\t};\n\n\t\t\n四、异步回调（如何解决回调地狱）\n\tpromise、generator、async/await\n\t\n\tpromise： 1.是一个对象，用来传递异步操作的信息。代表着某个未来才会知道结果的时间，并未这个事件提供统一的api，供进异步处理\n\t\t  2.有了这个对象，就可以让异步操作以同步的操作的流程来表达出来，避免层层嵌套的回调地狱\n\t\t  3.promise代表一个异步状态，有三个状态pending（进行中），Resolve(以完成），Reject（失败）\n\t\t  4.一旦状态改变，就不会在变。任何时候都可以得到结果。从进行中变为以完成或者失败\n\t\t\tpromise.all() 里面状态都改变，那就会输出，得到一个数组\n\t\t\tpromise.race() 里面只有一个状态变为rejected或者fulfilled即输出\n\t\t\tpromis.finally()不管指定不管Promise对象最后状态如何，都会执行的操作（本质上还是then方法的特例）\n \n五、前端事件流\n\t事件流描述的是从页面中接受事件的顺序，事件 捕获阶段 处于目标阶段 事件冒泡阶段 addeventListener 最后这个布尔值参数如果是true，表示在捕获阶段调用事件处理程序；如果是false，表示在冒泡阶段调用事件处理程序。\n\t  1、事件捕获阶段：实际目标div在捕获阶段不会接受事件，也就是在捕获阶段，事件从document到<html>再到<body>就停止了。\n          2、处于目标阶段：事件在div发生并处理，但是事件处理会被看成是冒泡阶段的一部分。\n          3、冒泡阶段：事件又传播回文档\n       阻止冒泡事件event.stopPropagation()\n \t\t  function stopBubble(e) {\n        \t\tif (e && e.stopPropagation) { // 如果提供了事件对象event 这说明不是IE浏览器\n          \t\te.stopPropagation()\n        \t\t} else {\n          \t\twindow.event.cancelBubble = true //IE方式阻止冒泡\n        \t      }\n      \t\t   }\n       阻止默认行为event.preventDefault()\n\t function stopDefault(e) {\n        if (e && e.preventDefault) {\n          e.preventDefault()\n        } else {\n          // IE浏览器阻止函数器默认动作的行为\n          window.event.returnValue = false\n        }\n      }\n事件如何先捕获后冒泡？\n  在DOM标准事件模型中，是先捕获后冒泡。但是如果要实现先冒泡后捕获的效果，\n\t对于同一个事件，监听捕获和冒泡，分别对应相应的处理函数，监听到捕获事件，先暂缓执行，直到冒泡事件被捕获后再执行捕获事件。\n\t\n哪些事件不支持冒泡事件：鼠标事件：mouserleave  mouseenter\n\t\t\t焦点事件：blur focus\n\t\t\tUI事件：scroll resize\n\n\n六、事件委托（提高性能）\n\t简介：事件委托指的是，不在事件的（直接dom）上设置监听函数，而是在其父元素上设置监听函数。通过事件冒泡，父元素可以监听到子元素上事件的触发\n\t      通过判断事件发生元素DOM的类型，来做出不同的响应。\n\t举例子： 最经典的就是ui和li标签的事件监听，比如我们在添加事件的时候，采用事件委托机制，不会在li标签上直接添加，而是在ul父元素上添加\n\t好处：可以比较合适动态元素的绑定，新添加的子元素也会监听函数，也可以有事件触发机制\n\t\n\n七、js的new操作符做了什么？\n\tnew操作符创建了一个空对象，这个对象原型指向构造函数的prototype，执行构造函数后返回这个对象（return this）。\n\t如果不要父类的属性跟方法，在函数的prototype上去new这个父类。\n\n八、改变函数内部this指针的指向函数(bind,apply,call)\n\t通过apply和call改变函数的this指向，他们两个函数的第一个参数都是一样的表示要改变指向的那个对象，第二个参数，apply是数组，而call则是arg1,arg2...这种形式。\n\n\n\n\tbind 一个是返回一个函数，并不会立即执行 第二个是带参数（第一个参数要指向的this，后面的的参数用来传递\n\n九、深拷贝和浅拷贝 https://juejin.im/post/5b00e85af265da0b7d0ba63f 从堆和栈都是内存中划分出来用来存储的区域开始讲起\n\t基本类型：undefined,null,Boolean,String,Number,Symbol 在内存中占据固定大小，保存在栈内存中\n\t引用类型：Object,Array,Date,Function,RegExp等\t引用类型的值是对象 保存在堆内存中，栈内存存储的是对象的变量标识符以及对象在堆内存中的存储地址。\n\t 基本类型的复制: 其实就是创建了一个新的副本给将这个值赋值给新变量， 改变值旧对象不会改变\n\t 引用类型的复制： 其实就是复制了指针，这个最终都将指向同一个对象，改变其值新对象也会改变\n\t基本类型的比较 == 会进行类型转换\n\n\t浅拷贝：仅仅就是复制了引用，彼此操作不影响，slice() concat()  object.assign\n\t深拷贝：在堆中重新分配内存，不同的地址，相同的值,互不影响的 JSON.parse()将一个js对象序列化为一个json字符串  JSON.stringify()将json字符串反序列化为一个js对象  es6的展开 {...}\n\t深拷贝和浅拷贝的主要区别是：在内存中的存储类型不同\n\t\t浅拷贝：重新在堆栈中创建内存，拷贝前后对象的基本类型互不影响。只拷贝一层，不能对对象进行子对象进行拷贝\n\t\t深拷贝：对对象中的子对象进行递归拷贝，拷贝前后两个对象互不影响\n\t\n十、跨域\n\t同源策略（协议+端口号+域名要相同）\n\t1、jsonp跨域(只能解决get）\n\t\t原理：动态创建一个script标签。利用script标签的src属性不受同源策略限制，因为所有的src属性和href属性都不受同源策略的限制，可以请求第三方服务器资源内容\n\t\t步骤：1.去创建一个script标签\n\t\t      2.script的src属性设置接口地址\n\t\t      3.接口参数，必须要带一个自定义函数名，要不然后台无法返回数据\n\t\t      4.通过定义函数名去接受返回的数据    \t\n\t\t\n\t2、document.domain 基础域名相同 子域名不同\n    \t3、window.name 利用在一个浏览器窗口内，载入所有的域名都是共享一个window.name\n    \t4、服务器设置对CORS的支持\n\t\t原理：服务器设置Access-Control-Allow-Origin HTTP响应头之后，浏览器将会允许跨域请求\n   \t5、利用h5新特性window.postMessage()\n\n\tiframe元素创建包含另外一个文档的内联框架（行内框架）(setTimeout进行异步加载)\n      解释：浏览器中的浏览器！用于设置文本或者图形的浮动图文框或容器\n      它和跨域\n        1、document.domain 实现主域名相同，子域名不同的网页通信\n          都设置为超域：document.domain = 'demo.com'\n        2、window.postMessageht(data, url)，h5的API，启动跨域通信\n\n\n十一、图片的懒加载和预加载\n\t预加载：提前加载图片，当用户需要查看是可以直接从本地缓存中渲染\n\t  为什么要使用预加载：在网页加载之前，对一些主要内容进行加载，以提供用户更好的体验，减少等待时间。\n\t\t\t      否则，如果一个页面的内容过于庞大，会出现留白。\n\t\t解决页面留白的方案：1.预加载  2.使用svg站位图片，将一些结构快速搭建起来，等待请求的数据来了之后，替换当前的占位符\n\t实现预加载的方法：\n\t \t\t1.使用html标签\n\t\t\t2.使用Image对象\n\t\t\t3.使用XMLHTTPRequest对像，但会精细控制预加载过程\n\n\n\n\t懒加载（lazyload）：客户端优化，减少请求数和延迟请求数\n\t\t提升用户体验，\n\t\t减少无效资源的加载\n\t\t防止并发加载的资源过多会阻塞js的加载，影响网站的正常使用\n\t  原理：首先将页面上的图片的src属性设置为空字符串，而图片的真是路经则设置带data-original属性中，\n\t\t当页面滚动的时候需要去监听scroll事件，在scroll事件的回调中，判断我们的懒加载的图片是否进入到可视区域\n\t\t，如果图片在可视区域将图片的src属性设置为data-original的值，这样就可以实现延迟加载。\t\n\n十二、函数节流防抖\n\t什么是防抖：短时间内多次触发同一个事件，只执行最后一次，或者在开始时执行，中间不执行。比如公交车上车，要等待最后一个乘客上车\n        什么是节流：节流是连续触发事件的过程中以一定时间间隔执行函数。节流会稀释你的执行频率，比如每间隔1秒钟，只会执行一次函数，无论这1秒钟内触发了多少次事件\n\t都为解决高频事件而来， scroll mousewhell mousemover touchmove onresize\n\n十三、将arguments类数组转化为数组的方法\n\tArray.apply(null, arguments)\n\tArray.prototype.slice.apply(arguments)\n\tArray.from(arguments)\n\n十四、高阶函数\n\t一、函数作为参数传递 抽离出一部分容易变化的业务逻辑，把这部分业务逻辑放在函数参数中。这样一来可以分离业务代码中变化与不变的部分\n\t\t回调函数\n\t二、函数作为返回值传递\n\n十五、如何判断一个变量是对象还是数组（prototype.toString.call()）。\n\t千万不要使用typeof来判断对象和数组，因为这种类型都会返回object。\n\ttypeOf()是判断基本类型的Boolean,Number，symbol, undefined, String。\n\t\t对于引用类型：除function，都返回object   null返回object。\n\tinstallOf() 用来判断A是否是B的实例，installof检查的是原型。\n\ttoString() 是Object的原型方法，对于 Object 对象，直接调用 toString()  就能返回 [Object Object] 。而对于其他对象，则需要通过 call / apply 来调用才能返回正确的类型信息。\n\t\n\thasOwnProperty()方法返回一个布尔值，指示对象自身属性中是否具有指定的属性，该方法会忽略掉那些从原型链上继承到的属性。\n\t\n\tisProperty()方法测试一个对象是否存在另一个对象的原型链上。\n\n\t\n十六、setTimeout 和 setInterval的机制\n\t因为js是单线程的。浏览器遇到etTimeout 和 setInterval会先执行完当前的代码块，在此之前会把定时器推入浏览器的\n\t待执行时间队列里面，等到浏览器执行完当前代码之后会看下事件队列里有没有任务，有的话才执行定时器里的代码\n\t\t\t\t\t\t\t\n十七、var let const\n\tconst：定义的变量不可修改，必须初始化 ，\n\tvar：定义的变量可以修改，如果不初始化输出undefined，不会报错\n\tlet：块级作用域，函数内部使用let定义后，会函数外部无影响\n\tlet const 不会造成变量的提升\n\n十八、js垃圾回收机制\n\t1.JS具有自动垃圾收集的机制\n\t2.JS的内存生命周期（变量的生命）\n\t\t1.分配你所需要的空间 var a = 20\n\t\t2.使用分配带的内存（读写） alert（a + 10）\n\t\t3.不适用的时候，释放内存空间 a = null \n\t3.JS的垃圾收集器每隔固定的时间就执行一次释放操作，通用的是通过标记清除的算法\n\t4.在局部作用域中，垃圾回收器很容易做出判断并回收，全局比较难，因此应避免全局变量\n\n\t   标记清除算法：js最常见的垃圾回收方式，当变量进入执行环境的时候，比如函数中声明一个变量，垃圾回收器将他标记为'进入环境'，\n\t\t\t 当变量离开（函数执行完后），就其标记为'离开环境'。垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记，\n\t\t\t 然后去掉环境中的变量以及被环境中该变量所引用的变量（闭包）。在这些完成之后仍存在标记的就是要删除的变量了\n\n十九、渐进增强和优雅降级\n\t1.渐进增强就是针对低版本浏览器进行构建页面，保证最基本的功能，然后对高级浏览器进行效果、交互等改进和最佳功能达到更好的用户体验\n\t2.优雅降级：一开始构建完整的功能，然后对低版本的进行兼容\n\n二十、undefined 和 null\n\t1.undefined类型只要一个，即undefined，当声明变量还未被初始化时就是undefined\n\t2.null类型也只有一个值，即null。null用来表示尚未存在的对象，常用来表示函数企图返回一个不存在的对象\n\t3.NaN 与任何值都是相比较的结果都是false\n\n二十一、valueof和tostring\n\tvalueof：所有对象都有valueof，如果存在任意原始值，他就默认将对象转化为表示它的原始值。\n\t\t\t\t      如果对象是复合值，而却大部分对象无法真正表示一个原始值，因此默认的valueof()方法简单的返回对象本身，而不是返回原始值。\n\t\t\t\t      数组、函数和正则表达式简单的继承了这个more方法，返回对象本身\n\t\n二十二、输入框的change和input事件\n\tonchange事件：要在input失去焦点的时候才触发\n\toninput事件：要在用户输入的时触发，他是元素值发生变化时立即触发\n\n二十三、同步和异步\n\t同步：由于js单线程，同步任务都在主线程上排队执行，前面任务没有执行完成，后面的任务会一直等待\n\t异步：不进入主线程，进入任务队列，等待主线程任务执行完成，开始执行。最基本的异步操作SetTimemot和SetInterval,等待主线程任务执行完，在开始执行里面的函数\n\n\n二十四、函数的柯里化\n\t 概念：一个函数接受函数A作为参数，运行后返回return function一个新的函数，并且可以处理A中的参数（只接受单一参数的函数）\n\t 意义：将函数完全变成了接受一个参数，返回一个参数的固定形式，便于讨论和优化\n\n二十五、while\n\twhile循环会在指定条件为真时循环执行代码\n\n二十六、TypeScript的优点：\n      1、编译时的强类型，变成了强类型语言，还是编译成js 编译的时候就可以检验\n      2、更好的模块化\n      3、更好的是实现面向对象的编程，类、接口、模块\n\n二十七、js的阻塞特性：所有浏览器在下载JS的时候，会阻止一切其他活动，比如其他资源的下载，内容的呈现等等。\n\t\t      直到JS下载、解析、执行完毕后才开始继续并行下载其他资源并呈现内容。\n\t\t      为了提高用户体验，新一代浏览器都支持并行下载JS，但是JS下载仍然会阻塞其它资源的下载（例如.图片，css文件等）。\n\tcss阻塞：因为浏览器会维持html中css和js的顺序，样式表必须在嵌入的JS执行前先加载、解析完。\n\t\t 而嵌入的JS会阻塞后面的资源加载，所以就会出现上面CSS阻塞下载的情况。\n\t\n二十八、meta元素可提供有关页面的元信息，比如针对搜索引擎和更新频度的描述和关键词\n\tmeta name=\"keyword\" 告诉搜素引擎网页的关键词\n\tmeta name=\"description\" 告诉搜素引擎站点的内容\n\tmata name=\"author\" content=\"name\"站点制作望着\n\tmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0 响应式页面\t\t\t\t\t\n\n二十九、splice和slice、map和forEach、 filter()、reduce()的区别\n\t 1.slice(start,end):方法可以从已有数组中返回选定的元素，返回一个新数组，包含从start到end（不包含该元素）的数组方法\n\t\t注意：该方法不会更新原数组，而是返回一个子数组\n\t 2.splice():该方法想或者从数组中添加或删除项目，返回被删除的项目。（该方法会改变原数组）\n\t\tsplice(index, howmany,item1,...itemx)\n\t\t\t·index参数：必须，整数规定添加或删除的位置，使用负数，从数组尾部规定位置\n\t\t\t·howmany参数：必须，要删除的数量，\n\t\t\t·item1..itemx:可选，向数组添加新项目\n\t3.map()：会返回一个全新的数组。使用于改变数据值的时候。会分配内存存储空间数组并返回，forEach（）不会返回数据\n\t4.forEach(): 不会返回任何有价值的东西，并且不打算改变数据，单纯的只是想用数据做一些事情，他允许callback更改原始数组的元素\n\t5.reduce(): 方法接收一个函数作为累加器，数组中的每一个值（从左到右）开始缩减，最终计算一个值，不会改变原数组的值\n\t6.filter(): 方法创建一个新数组，新数组中的元素是通过检查指定数组中符合条件的所有元素。它里面通过function去做处理\t\n\n\n\n\t\t\t\t\t\t\tnode面试\n一、koa中间件执行机制\n\t1.添加中间件的方式是使用Koa实例的use方法，并传入一个generator函数，这个generator函数接受一个next参数\n\t2.use的原理：function Application () {this.middleware = [] // 这个数组就是用来装一个个中间间的}\n\t3.每次执行use方法，就把外面传进来的generator函数push到middleware数组中\n\t\tapp.use = function (fn) {this.middleware.push(fn)}\n\t4.koa中是预先通过use方法，将请求可能会经过的中间间装在一个数组中。\n\t5.callback函数就是请求到来的时候执行的回调。把装着中间件middleware的数组作为参数传递为compose这个方法。\n\t6.componse把毫无关系的一个个中间件给收尾串起来了，就好比我们平常的烤面筋\n\t7.componse将中间件从最后一个开始处理，并一直往前知道第一个中间件。其实最关键的就是将最后一个中间件得到generator\n\t   作为参数传递给前一个中间件。当最后一个中间件的参数next是空的generator函数生成对象\n\n\t中间件是怎么跑起来的：https://juejin.im/post/591c8b4544d904006c90a2cb\n\n\n二、koa和express的区别\n\t1.异步流程的控制。express采用callback来处理异步，koa2采用的是async/await\n\t2.错误处理。express采用callback捕获异常，对深层次的异常捕获不了。koa采用try/catch\t\t\t\t\t\t\t\n\n\n\t\t\t\t\t\t\t\tvue面试\n一、介绍下MVVM(数据的双向绑定）\n\tM: model数据模型\n\tV: view 界面\n\tMV:作为桥梁负责沟通view跟model\n     只关心数据的流传，减少强耦合性。最关键的就是数据的双向绑定\n 关键步骤：1.实现数据监听器Observer，用object.defineProperty()重写数据的get/set。值更新就在set中通知订阅者更新数据\n\t  2.实现模板编译compile，深度遍历dom树，对每个元素节点的指令模板替换数据以及订阅数据\n\t  3.实现watch用于连接Observer和compile，能够订阅并接受每一个属性的变动的通知，执行指令绑定的相应的回调函数，从而更新数据\n\nmvc和mvvm其实区别并不大。都是一种设计思想。主要就是mvc中Controller演变成mvvm中的viewModel。mvvm主要解决了mvc中大量的DOM 操作使页面渲染性能降低，\n加载速度变慢，影响用户体验。和当 Model 频繁发生变化，开发者需要主动更新到View 。\n\n二、 eventBus vuex\n \t原理：eventbus 解决了兄弟组件之间事件传递问题,本质是订阅发布者模式，从而摆脱了兄弟之间需要父组件转而传递的复杂。还有一种方法是vuex数据流，单一状态树,rootState树根\n              名词，专车。订阅者跟发布者都引用专车，这个vue实例，来完成订阅发布者。 emit（发布）  on(订阅一个组件)\n npm包\tvue-event-proxy\n\n\tvuex 是将数据单独的抽离出来，一种状态管理工具，它借鉴的是Flux、redux的基本思想，将转态抽离到全局形成一个store\n\n三、watch:\n\t对属性进行监听，允许我们执行异步操作，限制我们执行该操作的频率（debounce），并在我们得到结果前，设置中间转态。\n\n四、Vue的双向数据绑定实现原理\n\t1.核心就是数据劫持 + 发布/订阅者模式：vue使用的是Object.defineProperty()通过监听他的get/set事件，监听对数据的操作，从而触发数据同步\n\n Object.defineProperty缺陷的：\n\t1.只能对属性进行数据劫持，并且需要深度遍历整个对象\n\t2.对于数组不能监听数据的变化\n    而proxy原生支持监听数组的变化，并且可以直接对整个对象进行拦截，所有Vue在下个版本中用proxy替换object.defineProperty\n\n\n五、nextTick原理\n \n六、生命周期函数  https://juejin.im/post/5b41bdef6fb9a04fe63765f1\n\t\tnew Vue（创建一个Vue对象）--> beforeCreate --> observer Data(开始监控data对象数据变化） --> init event(vue内部初始化事件）\n\n\t\t --> created()  --> compile(编译模板,把data里面的数据和模板生成html)  -->  beforeMount(还没有生成HTML到元素上)  -->\n\t\t\n\t\t mounted(挂载完成，也就是模板中的html渲染到了html页面中）  -->  beforeUpdate (Vritual Dom)  --> updated  --> beforeDestroy --> destroyed\t\n\n\t1.ajax请求最好放在created里面，页面可以访问到this了\n\t2.关于dom的操作要放在mounted里面，在mounted前面还没有生成dom\n\t3.每次进入/离开组件都要做一些事情，用什么钩子函数：\n\t\t不缓存：进入的时候可以用created和mounted钩子，离开的时候可以使用beforedDestory（可以访问this）和destoryed\n\n\n\t\t缓存：缓存了组件之后，再次进入组件不会触发beforeCreate，created, beforeMount,mounted\n\t\t      如果你想每次进入组件都做一些事情的话，你可以放在activated进入缓存组件的钩子中\n七、keep-alive\n\t在被keep-alive包含的组件/路由，会多出两个生命周期：activated 和 deactivated\n\tactived在组件第一次渲染时会被调用，之后再每次缓存组件被激活时调用 调用机制：第一次进入缓存路由/组件，在mounted后面，beforeRouteEnter守卫传给 next 的回调函数之前调用：\n\n八、Vue的SPA 如何优化加载速度\n\t1.减少入口文件体积\n\t2.静态资源本地缓存\n\t3.开启Gzip压缩\n\t4.使用SSR,nuxt.js\n\n九、模块化\n\t基本概念： 1.在js中，一个模块就是实现特定功能的文件(js文件) \n\t\t   2.遵循模块的机制，想要什么就加载什么模块\n\t\t   3.模块化开发需要遵循规范\n\n\tjs实现模块化规范\n\t\t    1.AMD 浏览器  requirejs  模块被异步加载，模块加载不影响后面语句的运行 默认使用baseURL+ paths的路经解析方式\n\t\t    2.CommonJS  nodejs  \n\t\t    3.ES6的import/export\n\t\t    4.CMD 浏览器端 \n\n\t解决的问题：1.命名冲突 2.文件依赖 3.模块的复用 4.统一规范和开发方式\n\n十、谈谈Vue和React组件化的思想\n\t1.我们在各个页面开发的时候，会产生很多重复的功能，比如element中的xxxx。像这种纯粹非页面的UI，便成为我们常用的UI组件，最初的前端组件也就仅仅指的是UI组件\n\t2.随着业务逻辑变得越来多是，我们就想要我们的组件可以处理很多事，这就是我们常说的组件化，这个组件就不是UI组件了，而是包具体业务的业务组件\n\t3.这种开发思想就是分而治之。最大程度的降低开发难度和维护成本的效果。并且可以多人协作，每个人写不同的组件，最后像撘积木一样的把它构成一个页面\t\n\n十一、vue的依赖收集和watch原理\n\t\n\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tReact\n一、react和vue的区别\n       =>  相同点：\n\t\t1.数据驱动页面，提供响应式的试图组件\n\t\t2.都有virtual DOM,组件化的开发，通过props参数进行父子之间组件传递数据，都实现了webComponents规范\n\t\t3.数据流动单向，都支持服务器的渲染SSR\n\t\t4.都有支持native的方法，react有React native， vue有wexx\n\t=>  不同点：\n\t\t1.数据绑定：Vue实现了双向的数据绑定，react数据流动是单向的\n\t\t2.数据渲染：大规模的数据渲染，react更快\n\t\t3.使用场景：React配合Redux架构适合大规模多人协作复杂项目，Vue适合小快的项目\n\t\t4.开发风格：react推荐做法jsx + inline style把html和css都写在js了\n\t\t\t    vue是采用webpack + vue-loader单文件组件格式，html, js, css同一个文件\n\n二、redux中的reducer（纯函数）\n\tRedux数据流里，reduces其实是根据之前的状态（previous state）和现有的action（current action）更新state(这个state可以理解为上下累加器的结果）\n\t每次redux reducer被执行时，state和action被传入，这个state根据action进行累加或者是'自身消减'(reduce),进而返回最新的state,这也就是典型reduce函数的用法：state ->  action ->  state\n\n三、react的refs\n\trefs就想一个逃生窗，允许我们之间访问dom元素或者组件实例，可以向组件添加一个ref属性的值是一个回调函数，\n\t它将接受地城dom元素或组件的已挂在实例，作为第一个参数\n\n四、react中的keys\n\t帮组我们跟踪哪些项目已更改、添加、从列表中删除，key是独一无二的，可以让我们高效的去定位元素，并且操作它\n\n五、React的生命周期\n\t三个状态：Mounting(已插入真实的DOM）\n\t\t  Updating(正在被重新渲染)\n\t\t  Unmounting(已移除真实的DOM)\n\tcomponentDIdMount 在第一次渲染后调用，只在客服端。之后组件已经生成对应的DOM结构，\n\tcomponentDidUpdate 在组件完成更新后立即调用，在出初始化是不会调用\n\n六、React子组件向父组件传值\n\t父组件通过props 给子组件传递数据，子组件则是通过调用父组件传给它的函数给父组件传递数据。\n\n七、React数据流\n\n八、为什么虚拟DOM会提高性能 https://www.zhihu.com/question/29504639?sort=created\n\t虚拟DOM相当于在js和真实dom中间加了一个缓存，利用dom diff算法避免了没有必要的doom操作，从而提高性能\n\t具体实现步骤：\n\t\t·用JavaScript对象结构表示DOM树的结构；然后用这个树构建一个真正的DOM树，插到文档中\n\t        ·当状态变更的时候，重新构造一棵树的对象树，然后用新的树和旧的树进行对比，记录两棵树差异\n\t\t·把2所记录的差异应用到步骤1所构建的真正的DOM树上，试图就更新了。\n\n九、diff算法\n\t1.把树形结构按照层级分解，只比较同级元素\n\t2.给列表结构的每个单元添加key属性，方便比较。在实际代码中，会对新旧两棵树进行一个深度优先的遍历，这样每个节点都会有一个标记\n\t3.在深度优先遍历的时候，每遍历到一个节点就把该节点和新的树进行对比。如果有差异的话就记录到一个对象里面\n\tVritual DOM 算法主要实现上面步骤的三个函数：element， diff， patch。然后就可以实际的进行使用\n\treact只会匹配相同的class的component（这里的class指的是组件的名字）\n\t合并操作，条用component的setState方法的时候，React将其标记为dirty.到每一个时间循环借宿，React检查所有标记dirty的component重新绘制\n\t4.选择性子树渲染。可以重写shouldComponentUpdate提高diff的性能\t\n\n十、super\n\n十一、简述下flux的思想\n\tflux的最大特点，就是数据的‘单向流动’\n\t1.用户访问View\n\t2.View发出用户的Action\n\t3.Dispatcher收到Action,要求state进行相应的更新\n\t4.store更新后，发出一个‘change’事件后，更新页面\n\n十二、reac性能优化是哪个周期函\n\tshouldComponentUpdate 这个方法用来判断是否需要调用render方法重新描绘dom.因为dom的描绘非常消耗性能，\n\t如果我们在shouldComponentUpdate方法中能够写出更优化的dom diff算法，可以极大的提高性能\n\n十三、react怎么划分业务组件和技术组件\n\t根据组件的职责通常把组件分为UI组件和容器组件\n\tUI组件负责UI的呈现，容器组件负责管理数据和逻辑\n\t两者通过React-redux提供connect方法联系起来\n\n十四、setState\n\tsetState通过一个队列机制实现state更新，当执行setState时，会将需要更新的state很后放入状态队列\n\t而不会立即更新this.state，队列机制可以高效地批量更新state。如果不通过setState而直接修改this.state的值\t\n\t那么该state将不会被放入状态队列中。当下次调用setState并对状态队列进行合并时，就会忽略之前修改的state，造成不可预知的错误\n\t\n\t同时，也利用了队列机制实现了setState的异步更新，避免了频繁的重复更新state\n\n\t同步更新state:\n\t\tsetState 函数并不会阻塞等待状态更新完毕，因此 setNetworkActivityIndicatorVisible 有可能先于数据渲染完毕就执行。第二个参数是一个回调函数，在setState的异步操作结束并且组件已经重新渲染的时候执行\n\t\t也就是说，我们可以通过这个回调来拿到更新的state的值，实现代码的同步\n\t\n\t例子：componentDidMount() {\n    \n\t\tfetch('https://test.com')\n        \n\t\t.then((res) => res.json())\n        \n\t\t.then(\n(data) => {\nthis.setState({ data:data });\n\n\t\t\t\tStatusBar.setNetworkActivityIndicatorVisible(false);\n            }\n   \n\t\t\t\t\t\t\t\n\t\t\t\t\t\t性能优化\n\n一、webpack打包文件体积过大？（最终打包为一个js文件）\n\t1.异步加载模块\n\t2.提取第三库\n\t3.代码压缩\n\t4.去除不必要的插件\n\n如何优化webpack构建的性能\n\t一、减少代码体积 1.使用CommonsChunksPlugin 提取多个chunk之间的通用模块，减少总体代码体积\n\t\t\t 2.把部分依赖转移到CDN上，避免每次编译过程都由Webpack处理\n\t\t\t 3.对一些组件库采用按需加载，避免无用的代码\n\t二、减少目录检索范围\n\t\t\t ·在使用loader的时候，通过制定exclude和include选项，减少loader遍历的目录范围，从而加快webpack编译速度\n\t\t\n\t三、减少检索路经：resolve.alias可以配置webpack模块解析的别名，对于比较深的解析路经，可以对其配置alias\n\n\n二、我们把开发中的所有资源（图片，js、css文件）都看成模块，通过loader和plugins来对资源进行处理，打包成符合生产环节部署的前端资源。\n\t\t\t\t\t\t\t\n三、移动端的性能优化\n      1、首屏加载和按需加载，懒加载\n      2、资源预加载\n      3、图片压缩处理，使用base64内嵌图片\n      4、合理缓存dom对象\n      5、使用touchstart代替click（click 300毫秒的延迟）\n      6、利用transform:translateZ(0)，开启硬件GUP加速\n      7、不滥用web字体，不滥用float（布局计算消耗性能），减少font-size声明\n      8、使用viewport固定屏幕渲染，加速页面渲染内容\n      9、尽量使用事件代理，避免直接事件绑定\n\n四、Vue的SPA 如何优化加载速度\n\t1.减少入口文件体积\n\t2.静态资源本地缓存\n\t3.开启Gzip压缩\n\t4.使用SSR,nuxt.js\n\n五、移动端300ms延迟\n\t由来：300毫米延迟解决的是双击缩放。双击缩放，手指在屏幕快速点击两次。safari浏览器就会将网页缩放值原始比例。\n\t     由于用户可以双击缩放或者是滚动的操作，当用户点击屏幕一次之后，浏览器并不会判断用户确实要打开至这个链接，还是想要进行双击操作\n\t    因次，safair浏览器就会等待300ms，用来判断用户是否在次点击了屏幕\n\t解决方案：1.禁用缩放，设置meta标签 user-scalable=no\n\t\t  2.fastclick.js\n\t\t\t原理：FastClick的实现原理是在检查到touchend事件的时候，会通过dom自定义事件立即\n\t\t\t      发出click事件，并把浏览器在300ms之后真正的click事件阻止掉\n\tfastclick.js还可以解决穿透问题\n\n六、页面的重构；在不改变外部行为的前提下，简化结构、添加可读性\n\n\t\t\t\t\t\t\t服务器端\n一、状态码：\n\n      2XX（成功处理了请求状态）\n          200 服务器已经成功处理请求，并提供了请求的网页\n          201 用户新建或修改数据成功\n          202 一个请求已经进入后台\n          204 用户删除成功\n      3XX（每次请求使用的重定向不要超过5次）\n          304 网页上次请求没有更新，节省带宽和开销\n      4XX（表示请求可能出错，妨碍了服务器的处理）\n          400 服务器不理解请求的语法\n          401 用户没有权限（用户名，密码输入错误）\n          403 用户得到授权（401相反），但是访问被禁止\n          404 服务器找不到请求的网页，\n      5XX（表示服务器在处理请求的时候发生内部错误）\n          500 服务器遇到错误，无法完成请求\n          503 服务器目前无法使用（超载或停机维护）\n\n二、304的缓存原理（添加Etag标签.last-modified） 304 网页上次请求没有更新，节省带宽和开销\n\t1.服务器首先产生Etag,服务器可在稍后使用它来判断页面是否被修改。本质上，客户端通过该记号传回服务器要求服务器验证（客户端）缓存）\n\t2.304是\tHTTP的状态码，服务器用来标识这个文件没有被修改，不返回内容，浏览器接受到这个状态码会去去找浏览器缓存的文件\n\t3.流程：客户端请求一个页面A。服务器返回页面A，并在A上加一个Tage客服端渲染该页面，并把Tage也存储在缓存中。客户端再次请求页面A\n\t\t并将上次请求的资源和ETage一起传递给服务器。服务器检查Tage.并且判断出该页面自上次客户端请求之后未被修改。直接返回304\n\t\n\tlast-modified: 客服端请求资源，同时有一个last-modified的属性标记此文件在服务器最后修改的时间\n\t\t\t客服端第二次请求此url时，根据http协议。浏览器会向服务器发送一个If-Modified-Since报头，\n\t\t\t询问该事件之后文件是否被修改，没修改返回304\n\n\t 有了Last-Modified，为什么还要用ETag？\n      1、因为如果在一秒钟之内对一个文件进行两次更改，Last-Modified就会不正确（Last—Modified不能识别秒单位的修改）\n      2、某些服务器不能精确的得到文件的最后修改时间\n      3、一些文件也行会周期新的更改，但是他的内容并不改变（仅仅改变修改的事件），这个时候我们并不希望客户端认为文件被修改，而重新Get\n    \n    ETag，为什么还要用Last-Modified？\n      1、两者互补，ETag的判断的缺陷，比如一些图片等静态文件的修改\n      2、如果每次扫描内容都生成ETag比较，显然要比直接比较修改时间慢的多。\n\n\n    ETag是被请求变量的实体值（文件的索引节，大小和最后修改的时间的Hash值）\n      1、ETag的值服务器端对文件的索引节，大小和最后的修改的事件进行Hash后得到的。\n\n三、get/post的区别\n\t1.get数据是存放在url之后，以？分割url和传输数据，参数之间以&相连； post方法是把提交的数据放在http包的Body中\n\t2.get提交的数据大小有限制，（因为浏览器对url的长度有限制），post的方法提交的数据没有限制\n\t3.get需要request.queryString来获取变量的值，而post方式通过request.from来获取变量的值\n\t4.get的方法提交数据，会带来安全问题，比如登录一个页面，通过get的方式提交数据，用户名和密码就会出现在url上\n\n四、http协议的理解\n\t1.超文本的传输协议，是用于从万维网服务器超文本传输到本地资源的传输协议\n\t2.基于TCP/IP通信协议来传递数据（HTML，图片资源）\n\t3.基于运用层的面向对象的协议，由于其简洁、快速的方法、适用于分布式超媒体信息系统\n\t4.http请求信息request：\n\t\t请求行（request line）、请求头部（header）,空行和请求数据四部分构成\n\n\t\t请求行，用来说明请求类型,要访问的资源以及所使用的HTTP版本.\n\t\t请求头部，用来说明服务器要使用的附加信息\n\t\t空行，请求头部后面的空行是必须的\n\t\t请求数据也叫主体，可以添加任意的其他数据。\n\t5.http相应信息Response\n\t\t状态行、消息报头、空行和响应正文\n\n\t\t状态行，由HTTP协议版本号， 状态码， 状态消息 三部分组成\n\t\t消息报头，用来说明客户端要使用的一些附加信息\n\t\t空行，消息报头后面的空行是必须的\n\t\t响应正文，服务器返回给客户端的文本信息。\n\n\n五、http和https\n\thttps：是以安全为目标的HTTP通道，简单讲是HTTP的安全版本，通过SSL加密\n\thttp：超文本传输协议。是一个客服端和服务器端请求和应答的标准（tcp）,使浏览器更加高效，使网络传输减少\n\n五、http1.0 1.1 2.0的区别\n\t长连接：HTTP1.0需要使用keep-alive参数来告知服务器建立一个长连接，而HTP1.1默认支持长连接\n\t节约宽带：HTTP1.1支持只发送一个header信息（不带任何body信息）\n\thost域（设置虚拟站点，也就是说，web server上的多个虚拟站点可以共享同一个ip端口）：HTTP1.0没有host域\n\n\t1.http2采用的二进制文本传输数据，而非http1文本格式，二进制在协议的解析和扩展更好\n\t2.数据压缩：对信息头采用了HPACK进行压缩传输，节省了信息头带来的网络流量\n\t3.多路复用：一个连接可以并发处理多个请求\n\t4.服务器推送：我们对支持HTTP2.0的web server请求数据的时候，服务器会顺便把一些客户端需要的资源一起推送到客户端，免得客户端再次创建连接发送请求到服务器端获取。这种方式非常合适加载静态资源\n\n七、web缓存\n\t1.web缓存就是存在于客户端与服务器之间的一个副本、当你第一个发出请求后，缓存根据请求保存输出内容的副本\n\t2.缓存的好处\n            （1）减少不必要的请求\n\t    （2）降低服务器的压力，减少服务器的消耗\n\t    （3）降低网络延迟，加快页面打开速度（直接读取浏览器的数据）\n\n八、常见的web安全及防护原理\n\t1.sql注入原理：通郭sql命令插入到web表单递交或者输入活命，达到欺骗服务器执行的恶意sql命令\n\t\t\t防范：1.对用户输入进行校验\n\t\t\t       2.不适用动态拼接sql\n\t2.XSS（跨站脚本攻击）：往web页面插入恶意的html标签或者js代码。\n\t\t\t        举例子：在论坛放置一个看是安全的链接，窃取cookie中的用户信息\n\t\t\t\t防范：1.尽量采用post而不使用get提交表单\n\t\t\t\t      2.避免cookie中泄漏用户的隐式\n\t3.CSRF(跨站请求伪装）：通过伪装来自受信任用户的请求\n\t\t\t\t举例子：黄轶老师的webapp音乐请求数据就是利用CSRF跨站请求伪装来获取QQ音乐的数据\n\t\t\t\t防范：在客服端页面增加伪随机数，通过验证码\n\tXSS和CSRF的区别：\n\t   1.XSS是获取信息，不需要提前知道其他用户页面的代码和数据包\n\t   2.CSRF代替用户完成指定的动作，需要知道其他页面的代码和数据包\n\n九、CDN（内容分发网络）\n\t1.尽可能的避开互联网有可能影响数据传输速度和稳定性的瓶颈和环节。使内容传输的更快更稳定。\n\t2.关键技术：内容存储和分发技术中\n\t3.基本原理：广泛采用各种缓存服务器，将这些缓存服务器分布到用户访问相对的地区或者网络中。当用户访问网络时利用全局负载技术\n\t\t    将用户的访问指向距离最近的缓存服务器，由缓存服务器直接相应用户的请求（全局负载技术）\n\n十、TCP三次握手\t(客服端和服务器端都需要确认各自可收发）\n\t客服端发c起请求连接服务器端s确认，服务器端也发起连接确认客服端确认。\n\t第一次握手：客服端发送一个请求连接，服务器端只能确认自己可以接受客服端发送的报文段\n\t第二次握手： 服务端向客服端发送一个链接，确认客服端收到自己发送的报文段\n\t第三次握手： 服务器端确认客服端收到了自己发送的报文段\n\n十一、从输入url到获取页面的完整过程  https://blog.csdn.net/samjustin1/article/details/52650520\n\t1.查询NDS(域名解析),获取域名对应的IP地址  查询浏览器缓存\n\t2.浏览器与服务器建立tcp链接（三次握手）\n\t3.浏览器向服务器发送http请求(请求和传输数据）\n\t4.服务器接受到这个请求后，根据路经参数，经过后端的一些处理生成html代码返回给浏览器\n\t5.浏览器拿到完整的html页面代码开始解析和渲染，如果遇到外部的css或者js，图片一样的步骤\n\t6.浏览器根据拿到的资源对页面进行渲染，把一个完整的页面呈现出来\n\n十二、浏览器渲染原理及流程 DOM -> CSSOM -> render -> layout -> print\n\t流程：解析html以及构建dom树 -> 构建render树 ->  布局render树 -> 绘制render树\n\t概念：1.构建DOM树： 渲染引擎解析HTML文档，首先将标签转换成DOM树中的DOM node(包括js生成的标签)生成内容树\n\t      2.构建渲染树： 解析对应的css样式文件信息（包括js生成的样式和外部的css）\n\t      3.布局渲染树：从根节点递归调用，计算每一个元素的大小，位置等。给出每个节点所在的屏幕的精准位置\n\t      4.绘制渲染树：遍历渲染树，使用UI后端层来绘制每一个节点\n   \n\t重绘：当盒子的位置、大小以及其他属性，例如颜色、字体大小等到确定下来之后，浏览器便把这些颜色都按照各自的特性绘制一遍，将内容呈现在页面上\n\t\t触发重绘的条件：改变元素外观属性。如：color，background-color等\n\t\t重绘是指一个元素外观的改变所触发的浏览器行为，浏览器会根据元素的新属性重新绘制，使元素呈现新的外观\n\t注意：table及其内部元素需要多次计算才能确定好其在渲染树中节点的属性值，比同等元素要多发时间，要尽量避免使用table布局\n\t\n\t重排（重构/回流/reflow）： 当渲染书中的一部分（或全部）因为元素的规模尺寸，布局，隐藏等改变而需要重新构建，这就是回流。\n\t\t每个页面都需要一次回流，就是页面第一次渲染的时候\n\n\t重排一定会影响重绘，但是重绘不一定会影响重排\n\n\n十三、为什么css放在顶部而js写在后面\n\t1.浏览器预先加载css后，可以不必等待HTML加载完毕就可以渲染页面了\n\t2.其实HTML渲染并不会等到完全加载完在渲染页面，而是一边解析DOM一边渲染。\n\t3.js写在尾部，主要是因为js主要扮演事件处理的功能，一方面很多操作是在页面渲染后才执行的。另一方面可以节省加载时间，使页面能够更加的加载，提高用户的良好体验\n\n\t但是随着JS技术的发展，JS也开始承担页面渲染的工作。比如我们的UI其实可以分被对待，把渲染页面的js放在前面，时间处理的js放在后面\n\n十四、存储方式与传输方式\n\t1.indexBD: 是h5的本地存储库，把一些数据存储到浏览器中，没网络，浏览器可以从这里读取数据，离线运用。5m\n\t2.Cookie: 通过浏览器记录信息确认用户身份，最大4kb,这也就限制了传输的数据，请求的性能会受到影响\n\t3.Session: 服务器端使用的一种记录客户状态的机制（session_id存在set_cookie发送到客服端，保存为cookie）\n\t4.localStroage: h5的本地存储，数据永久保存在客服端\n\n    1、cookie，sessionStorage，localStorage是存放在客户端，session对象数据是存放在服务器上\n       实际上浏览器和服务器之间仅需传递session id即可，服务器根据session-id找到对应的用户session对象\n        session存储数据更安全一些，一般存放用户信息，浏览器只适合存储一般的数据\n    2、cookie数据始终在同源的http请求中携带，在浏览器和服务器来回传递，里面存放着session-id\n       sessionStorage，localStorage仅在本地保存\n    3、大小限制区别，cookie数据不超过4kb，localStorage在谷歌浏览中2.6MB\n    4、数据有效期不同，cookie在设置的（服务器设置）有效期内有效，不管窗口和浏览器关闭\n      sessionStorage仅在当前浏览器窗口关闭前有效，关闭即销毁（临时存储）\n      localStorage始终有效\t\n\n\nSessionStorage和localStorage区别：\n\t1.sessionStorage用于本地存储一个会话（session）中的数据，这些数据只有在用一个会话的页面中才能被访问（也就是说在第一次通信过程中）\n\t   并且在会话结束后数据也随之销毁，不是一个持久的本地存储，会话级别的储存\n\t2.localStorage用于持久化的本地存储，除非主动删除数据，否则不会过期\n\ntoken、cookie、session三者的理解？？？！！！\n    1、token就是令牌，比如你授权(登录)一个程序时,他就是个依据,判断你是否已经授权该软件（最好的身份认证，安全性好，且是唯一的）\n        用户身份的验证方式    \n\n    2、cookie是写在客户端一个txt文件，里面包括登录信息之类的，这样你下次在登录某个网站，就会自动调用cookie自动登录用户名\n        服务器生成，发送到浏览器、浏览器保存，下次请求再次发送给服务器（存放着登录信息）\n\n    3、session是一类用来客户端和服务器之间保存状态的解决方案，会话完成被销毁（代表的就是服务器和客户端的一次会话过程）\n        cookie中存放着sessionID，请求会发送这个id。sesion因为request对象而产生。\n  \n\n    基于Token的身份验证：（最简单的token: uid用户唯一的身份识别 + time当前事件戳 + sign签名）\n      1、用户通过用户名和密码发送请求\n      2、服务器端验证\n      3、服务器端返回一个带签名的token，给客户端\n      4、客户端储存token，并且每次用于发送请求\n      5、服务器验证token并且返回数据\n      每一次请求都需要token\n\n    cookie与session区别\n      1、cookie数据存放在客户的浏览器上，session数据放在服务器上。\n      2、cookie不是很安全，别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。\n      3、session会在一定时间内保存在服务器上。当访问增多，会比较占用你服务器的性能考虑到减轻服务器性能方面，应当使用COOKIE。\n      4、单个cookie保存的数据不能超过4K，很多浏览器都限制一个站点最多保存20个cookie。\n\n    session与token区别\n      1、session认证只是把简单的User的信息存储Session里面，sessionID不可预测，一种认证手段。只存在服务端，不能共享到其他的网站和第三方App\n      2、token是oAuth Token，提供的是认证和授权，认证针对用户，授权是针对App，目的就是让某APP有权访问某用户的的信息。Token是唯一的，\n         token不能转移到其他的App，也不能转到其他用户上。（适用于App）\n      3、session的状态是存在服务器端的，客户端只存在session id， Token状态是存储在客户端的\n\n    Cookie的弊端有哪些？？？（优势：保存客户端数据，分担了服务器存储的负担）\n      1、数量和长度的限制。每个特定的域名下最多生成20个cookie（chorme和safari没有限制）\n      2、安全性问题。\n\n\t\t\t\t\t\t设计模式\n\n一、观察者模式：https://juejin.im/post/5a14e9edf265da4312808d86   https://juejin.im/post/5af05d406fb9a07a9e4d2799\n\t在软件开发设计中是一个对象(subject)，维护一系列依赖他的对象（observer），当任何状态发生改变自动通知他们。强依赖关系\n\t简单理解：数据发生改变时，对应的处理函数就会自动执行。一个Subjet,用来维护Observers,为某些event来通知（notify）观察者\n\n二、发布-订阅者  有一个信息中介，过滤 耦合性低\n\t它定义了一种一对多的关系，可以使多个观察者对象对一个主题对象进行监听，当这个主题对象发生改变时，依赖的所有对象都会被通知到。\n\n两者的区别：\n\t1.观察者模式中，观察者知道Subject ,两者是相关联的，而发发布订阅者只有通过信息代理进行通信\n\t2.在发布订阅模式中，组件式松散耦合的。正好和观察者模式相反。\n\t3.观察者大部分是同步的，比如事件的触发。Subject就会调用观察者的方法。而发布订阅者大多数是异步的（）\n\t4.观察者模式需要在单个应用程序地址空间中实现，而发布订阅者更像交叉应用模式。\n\n1004001111\n\t\t\t\t\t\t \t数据结构和算法\n\n一、两个栈实现一个队列，两个队列实现一个栈 https://www.cnblogs.com/MrListening/p/5697459.html\n\n二、红黑树（解决二叉树依次插入多个节点时的线型排列） https://juejin.im/post/5a27c6946fb9a04509096248\n\n三、最小栈的实现（查找最小元素，用两个栈配合栈内元素的下标）https://juejin.im/post/5a2ff8c651882533d0230a85\n\n四、十大排序 \n\t1.冒泡排序：重复走访过要排序的数列，一次比较两个元素，如果他们的顺序错误就把它们交换过来。\n\t  实现过程：1.比较相邻的元素。如果第一个比第二个大，就交换他们两个\n\t\t    2.对每一对相邻元素作同样的工作，从开始第一对到结尾的最后一对，这样在最后的元素应该会是最大的数\n\t\t    3.针对所有的元素重复以上的步骤，除了最后一个\n\t\t    4.重复步骤1-3，直到排序完成。\n\t2.选择排序：首先在未排序序列中找到最小值，放在排序序列的起始位置，然后，在从剩下未排序元素中继续寻找最小值，然后放在与排序序列的末尾\n\t  实现过程：\n\n\t3.插入排序：构建有序序列，对于未排序数据，在已排序序列中冲后向前扫描，找到相应位置并插入\n\t  实现过程：1.从第一个元素开始，该元素可以认为已经被排序\n\t\t    2.取出下一个元素，在已排序的元素序列中冲后向前扫描\n\t\t    3.如果该元素（以排序）大于新元素，将元素向后移一位\n\t\t    4.在取出一个元素，比较之前的，直到找到自己合适的位置\n\n\t4.桶排序：将数据分布到有限数量的桶里，每个桶在分别排序\n\n\t1.快速排序：快速排序使用分治法把一个串（list）分为两个子串（sub-lists）.具体算法实现\n\t  实现过程：1.从数组中挑出一个元素，成为一个基准\n\t\t    2.重新排列数组，所有元素比基准小的摆在基准前面，所有元素比基准大的摆在基准后面（相同的可以摆在一边）\n\t\t\t这个分区退出之后，该基准就处于数列的中间位置。成为分区操作。\n\t\t    3.递归的把小于基准值的子数列和大于基准值元素的子数列排序\n\t算法实现： function quickSort (arr) {\n\t\t\tif （arr.length <= 1） {return arr}\n\t\t\tvar destIndex = Math.floor(arr.length/2)\n\t\t\tvar left = [], right = [];\n\t\t\tvar dest = arr.splice(destIndex,1)[0];\n\t\t\tfor (var i =0;i<arr.length;i++){\n\t\t\t\tif (arr[i]<dest) {\n\t\t\t\tleft.push(arr[i])\n\t\t\t\t} else {\n\t\t\t\tright.push(arr[i]) }\n\t\t\treturn quickSort(left).concat([dest],quickSort(right)\n\t\t\t\t\n\n\t2.堆排序：利用对这种数据结构所涉及的一种排序算法，堆积是一个近乎完全二叉树的结构，并同时满足堆积的性质：即子节点的键值或索引总是小于（或大于）它的父节点。\n\t  实现过程：1.\n\n五、数组去重 https://juejin.im/post/5aed6110518825671b026bed#heading-6 \n\t1.双重循环\n\t2.indexOf\n\t3.数组排序去重 最快你Olong\n\t\n六、字符串\n\t判断回文字符串：（递归的思想）\n\t\t1.字符串分隔，倒转，聚合[...obj].reverse().join('')\n\t\t2.字符串头部和尾部，逐次向中间检测 \n\t\t\t实现：function isPalindrome(line) {\n\t\t\t\tline += '';\n\t\t\t\tfor (var i=0,j=line.length-1;i<j;i++,j--) {\n\t\t\t\t\tif (line.chartAt(i) !== line.chartAt(j) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\t\n\t\t3.递归\n\n七、二分查找（有序数组的查找）\n\t 二分查找可以解决已排序数组的查找问题，即只要数组中包含T(要查找的值)，那么通过不断的缩小包含T的数据范围，就可以最终要找到的数\n\t (1) 一开始,数据范围覆盖整个数组。\n\t (2) 将数组的中间项与T进行比较，如果T比数组的中间项小，则到数组的前半部分继续查找，反之，则到数组的后半部分继续查找。\n\t (3) 就这样，每次查找都可以排除一半元素，相当于范围缩小一半。这样反复比较，反复缩小范围，最终会在数组中找到T\n\t代码实现：function binarySearch (data, dest, start, end){\n\t\t\tvar end = end || data.length-1;\n\t\t\tvar start = start || 0;\n\t\t\tvar m = Math.floor((start+end)/2);\n\t\t\tif (dest<data[m]){\n\t\t\t\treturn binarySearch(data, dest, 0, m-1)\n\t\t\t} else {\n\t\t\t\treturn binarySearch(data, dest, m+1, end)\n\t\t\t}}\n\t\t\treturn false\n\n\n\n\t\t\t\t\t\t\t手写代码\n\n一、动手实现一个bind（原理通过apply，call）\n\t一句话概括：1.bind()返回一个新函数，并不会立即执行。\n\t\t    2.bind的第一个参数将作为他运行时的this，之后的一系列参数将会在传递的实参前传入作为他的参数\n\t\t    3.bind返回函数作为构造函数，就是可以new的，bind时指定的this值就会消失，但传入的参数依然生效\nFunction.prototype.bind = function (obj, arg) {\n   var arg = Array.prototype.slice.call(arguments, 1);\n   var context = this;\n   var bound = function (newArg) {\n   arg = arg.concat(Array.prototype.slice.call(newArg);\n   return context.apply(obj, arg)\n}\n  var F =  function () {}  // 在new一个bind会生成新函数，必须的条件就是要继承原函数的原型，因此用到寄生继承来完成我们的过程\n  F.prototype = context.prototype;\n  bound.prototype =  new F();\n  return bound;\n}\t\n\n二、 AJAX （异步的javascript和xml）\n\tajax的原理：相当于在用户和服务器之间加一个中间层（ajax引擎),使用户操作与服务器响应异步化。\n\t优点：在不刷新整个页面的前提下与服务器通信维护数据。不会导致页面的重载\n\t      可以把前端服务器的任务转嫁到客服端来处理，减轻服务器负担，节省宽带\n\t劣势：不支持back。对搜索引擎的支持比较弱；不容易调试\t\n\t怎么解决呢？通过location.hash值来解决Ajax过程中导致的浏览器前进后退按键失效，\n\t解决以前被人常遇到的重复加载的问题。主要比较前后的hash值，看其是否相等，在判断是否触发ajax\nfunction getData(url) {\n    var xhr = new XMLHttpRequest();  // 创建一个对象，创建一个异步调用的对象\n    xhr.open('get', url, true)  // 设置一个http请求，设置请求的方式，url以及验证身份\n    xhr.send() //发送一个http请求\n    xhr.onreadystatechange = function () {  //设置一个http请求状态的函数\n      if (xhr.readyState == 4 && xhr.status ==200) {\n        console.log(xhr.responseText)  // 获取异步调用返回的数据\n      }\n    }\n  }\n  Promise(getData(url)).resolve(data => data)\n\n\t AJAX状态码：0 - （未初始化）还没有调用send()方法\n\t\t     1 - （载入）已调用send方法，正在发送请求\n\t\t     2 - （载入完成呢）send()方法执行完成\n\t\t     3 - （交互）正在解析相应内容\n\t\t     4 - （完成）响应内容解析完成，可以在客户端调用了\n\n\n三、函数节流（throttle）\n function throttle (func, wait) {\n        var timeout;\n        var previous = 0;\n        return function () {\n            context = this;\n            args = arguments;\n            if (!timeout) {\n                timeout = setTimeout(() => {\n                    timeout = null;\n                    func.apply(context,args)\n                }, wait);\n            }\n        }\n    }\n     \n}\n\n四、函数防抖（dobounce）\n function debounce (func, wait) {\n         var timeout;\n         return function() {\n             var context = this;\n             var args = arguments;\n             clearTimeout(timeout);\n             timeout = setTimeout(() => {\n                 func.apply(context,args)\n             }, wait);\n         }\n     }\n\n五、实现一个函数clone，可以对JavaScript中的5种主要的数据类型（包括Number、String、Object、Array、Boolean）进行值复制\n    \n    Object.prototype.clone = function() {\n      var newObject = this.constructor === Array ? [] : {}  //对象的深拷贝 获取对应的构造函数 [] 或者 {}\n      for (let e in this) { //遍历对象的属性 in  this[e]\n        newObject[e] = typeof this[e] === 'object' ? this[e].clone() : this[e]  //对象中的属性如果还是对象 那就继续递归 否则就返回基本的数据类型\n      }\n      return newObject\n    }\n \n六、实现一个简单的Promise https://juejin.im/post/5b2f02cd5188252b937548ab\nclass Promise {\n  constructor (executor) {   // executor里面有两个参数，一个叫resolve（成功），一个叫reject（失败）。\n    this.status = 'pending',\n    this.value = undefined;\n    this.reason = undefined;\n    // 成功存放的数组\n    this.onResolvedCallbacks = [];\n     // 失败存放法数组\n     this.onRejectedCallbacks = [];\n    let resolve = (value) => {\n      if (this.status == 'pending') {\n        this.status = 'resolve';\n        this.value = value;\n        this.onResolvedCallbacks.forEach(fn => fn())\n      }\n    }\n\n    let reject = (reason) => {\n      if (this.status == 'pending') {\n        this.status = 'reject';\n        this.reason = reason;\n        this.onRejectedCallbacks.forEach(fn => fn())\n      }\n    }\n    try{\n      executor(resolve, reject);\n    } catch (err) {\n      reject(err);\n    }\n  } \n  then (onFullFilled,onRejected) {\n    if (this.status == 'resolved') {\n      onFullFilled(this.value)\n    }\n    if (this.status == 'rejectd') {\n      onRejected(this.reason);\n    }\n    if (this.status == 'pending') {\n      this.onResolvedCallbacks.push(()=>{\n        onFullFilled(this.value);\n      })\n      this.onRejectedCallbacks.push(()=> {\n          onRejected(this.reason);\n      })\n  }\n   \n  }\n}\n\nconst p = new Promise((resolve, reject) => {\n  setTimeout(() => {\n      resolve('hello world')\n  }, 1000);\n})\np.then((data) =>{\n  console.log(data)\n},(err) =>{\n  console.log(err);\n})\n\n七、发布订阅者模式（观察者模式）\n\nvar event = {}; // 发布者\nevent.clientList = [] //发布者的缓存列表\n\nevent.listen = function (fn) {  // 增加订阅者函数\n  this.clientList.push(fn)\n}\n\nevent.trigger = function () {  // 发布信息\n  for (var i =0;i<this.clientList.length;i++) {\n    var fn = this.clientList[i];\n    fn.apply(this, arguments);\n  }\n}\n\nevent.listen (function(time) {\n  console.log('正式上班时间为：' +time)\n})\nevent.trigger ('2018/7')\n\n八、手动写一个node服务器\nconst http = require('http');\nconst fs = require('fs');\nconst server = http.createServer((req,res) => {\n\tif (reu.url == '/') {\n\tconst indexFile = fs.createReadStream('./index.html')\n\treq.writeHead(200,{'context-Type':'text/html;charset = utf8})\n\tindexFile.pipe(res)\n}\nserver.listen(8080)\n```\n## 参考\n- https://github.com/WsmDyj/Interview/edit/master/%E5%89%8D%E7%AB%AF%E6%AD%A6%E6%9E%97%E7%A7%98%E7%B1%8D.txt\n- https://juejin.im/post/5b68f384f265da0fa00a3df0\n"
  },
  {
    "path": "5.1.5 剑指Offer.md",
    "content": "#  剑指Offer\n\n1.一个二维数组中，每一行都按照从左到右递增的顺序排序，每一列都按照从上到下递增的顺序排序。请完成一个函数，输入这样的一个二维数组和一个整数，判断数组中是否含有该整数。\n\n>思想：根据二维数组的结构，从左到右递增，从上到下递归，选择一个比较适合的切入点，进行数组遍历，比较。这个选择的切入点需要满足，当要查找的数据与它作比较时，有唯一的出路可走，如当要查找的数据大于这个切入点时，下面该往哪里继续查找，不能同时几条路都可以选择，那判断起来就麻烦了。这里我们以第n行第1列数据为切入点，即二维数组最左下角的元素，这样当要查找的数据大于该切入点时，直接j++向右进行遍历，如果要查找的数据小于该切入点时，直接i--向上进行遍历，否则，那切入点即为要查找的数据，如此循环，直到找到为止。\n\n```\nfunction Find(target, array) \n{\n    var row=array.length;\n    if(row==0){\n        return false;\n   }\n    var col=array[0].length;\n    var i=row-1,j=0;  //以最后一行第一列为基准\n    while(i>=0&&j<col){ //循环\n        if(array[i][j]<target){ //待查找值大，继续向右查找\n            j++;\n        }else if(array[i][j]>target){ //待查找值小，向上继续查找\n            i--;\n        }else{   //找到\n            return true;\n        }\n    }\n```\n\n2.请实现一个函数，将一个字符串中的空格替换成“%20”。例如，当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。\n\n```\nfunction replaceSpace(str) \n{\n    var newstr;\n    newstr=str.replace(/\\s/g,\"%20\"); //正则表达式匹配，替换\n    return newstr;\n}\n或\nfunction replaceSpace(str)  \n{\n    if(str.length==0){\n        return str;\n    }\n    var len=str.length; //将输入字符串长度存储起来\n    var str1=\"\"; //定义一个新的空字符串\n    for(var i=0;i<len;i++){\n        if(str[i]==' '){  //如果读取到str的空格处，用“%20”赋值到str1\n            str1+=\"%20\";\n        }else{\n            str1+=str[i];//如果读取到str的非空格处，将str该处的字符赋值给str1\n        }\n    }\n    return str1;  //返回str1\n}\n```\n\n3.输入一个链表，从尾到头打印链表每个节点的值。\n\n思想：新创建一个数组，将链表中的数据依次push进数组，然后将数组反转，并输出反转后的数组。\n\n```\nfunction printListFromTailToHead(head)  \n{\n    var lst=[]; //定义一个数组\n    while(head!=null){  //将链表中的元素push进数组\n        lst.push(head.val); //将链表的头指针指向的元素push进数组\n        head=head.next;  //指针指向后移\n    }\n    lst.reverse();  //利用reverse()方法将数组颠倒顺序\n    return lst;  //返回颠倒后的数组\n}\n```\n\n \n\n5.用两个栈来实现一个队列，完成队列的Push和Pop操作。 队列中的元素为int类型。\n\n思想：将数据push进栈1，将栈1中的数据pop进栈2，将栈2中的数据pop出去。当栈2为空时，再将栈1中的数据pop进栈2，即将栈2中的数据一次性pop完。\n\n```\nfunction Stack(){       //定义栈及栈中方法\n \n    this.dataStore =[]\n \n    this.top =0\n \n    this.psh=psh\n \n    this.empty=empty\n \n    this.pp=pp\n \n}\n \nfunction empty(){       \n \n    return this.top<=0\n \n}\n \nfunction psh(element){\n \n    this.dataStore[this.top++] = element\n \n}\n \nfunction pp(){\n \n    if(this.empty()){\n \n        return 0;\n \n    }\n \n    return this.dataStore[--this.top]\n \n}\n \nvar stack1 = new Stack()   \n \nvar stack2 = new Stack()\n \nfunction push(node){     //将节点push进栈1中\n \n    stack1.psh(node)\n \n}\n \nfunction pop()\n \n{\n \n    if(stack2.empty()){        //判断栈2是否为空，当栈2为空时，再将栈1里的数据push进栈2，每次将栈2中的数据全部pop出去\n \n        while(!stack1.empty()){\n \n            stack2.psh(stack1.pp())\n \n        }\n \n    }\n \n    return stack2.pp()               //返回栈2pop出去的数据\n \n  \n}\n```\n \n\n6.把一个数组最开始的若干个元素搬到数组的末尾，我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转，输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转，该数组的最小值为1。NOTE：给出的所有元素都大于0，若数组大小为0，请返回0。\n\n思想：元素查找，遍历\n\n```\nfunction minNumberInRotateArray(rotateArray) \n{\n    var len=rotateArray.length;\n    if(len==0){\n        return 0;\n    }else if( len==1){\n       return rotateArray[0];\n    }\n    for(var i=len-1;i>=1;i--){\n        if(rotateArray[i]<rotateArray[i-1]){\n            return rotateArray[i];\n        }\n    }\n    return rotateArray[0];\n}\n```\n\n7.大家都知道斐波那契数列，现在要求输入一个整数n，请你输出斐波那契数列的第n项。n<=39\n\n思想：递归\n\n```\nfunction Fibonacci(n) \n{\n    if(n==0){\n        return 0;\n    }\n    var fn=[1,1];  //最开始的两个数\n    for(var i=2;i<n;i++){\n        fn[i]=fn[i-1]+fn[i-2] //后一个数为前两个数之和，递归\n    }\n    return fn[n-1];  //返回数组的第n个数\n}\n``` \n\n8.-一只青蛙一次可以跳上1级台阶，也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。\n\n思想：给出n，第一次有两种情况，跳2那么剩下f(n-2)种跳法，跳1剩下f(n-1)种跳法，总的是f(n-1)+f(n-2)可以往下一直递归。\n\n```\njumpFloor(number) \n{ function\n    if(number<=0){\n        return 0;\n    }\n    var jump=[1,2];\n    for(var i=2;i<number;i++){\n        jump[i]=jump[i-1]+jump[i-2];\n    }\n    return jump[number-1];\n}\n```\n\n9.一只青蛙一次可以跳上1级台阶，也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。\n\n思想：\n\nf(1)=1,f(2)=2，f(3)=1+f(2)+f(1)...\n\nf(3)可以这样考虑：分跳3(本身值直接+1)，跳1，跳2三种情况，跳1之后还剩f(3-1)种跳法，跳2之后\n\n还有f(3-2)种跳法，所以f(3)可以等于这三种分法相加。类推f(n)=1+f(1)+f(2)+...+f(n-1)。\n\n2的n-1次方\n\n```\nfunction jumpFloorII(number) \n{\n    if(number <= 0){\n        return 0;\n    }\n    var jump=1;\n    for(var i=1;i<number;i++){\n        jump*=2;\n    }\n    return jump;\n}\n<br>\n\n或\n\nfunction jumpFloorII(number) {  \n   if(number<0){\n       return 0;\n   }\n    return Math.pow(2,number-1);\n}\n```\n \n\n10.--我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形，总共有多少种方法？\n\n思想：n=1时，1种情况，n=2时，横着或竖着覆盖，两种情况。n=3时，横着、竖着、横竖混着，共3种情况。可以依次递推。\n\n```\nfunction rectCover(number) \n{\n    if(number<=0){\n        return 0;\n    }\n    var Fn=[1,2];\n    for(var i=2;i<number;i++){\n        Fn[i]=Fn[i-1]+Fn[i-2];\n    }\n    return Fn[number-1];\n}\n```\n\n11.输入一个整数，输出该数二进制表示中1的个数。其中负数用补码表示。\n\n```\nfunction getNum(n){\n    var count=0;\n    if(n<0){\n        n=n&0x7FFFFFFF;  //负数的补码取出来\n        count++;//最高位的符号位1，加到count中\n    }\n    while(n){\n      count+=n%2  //取余，同时将该二进制数各位相加，获得1的个数\n      n>>=1  //右移1位，相当于除以2\n    }\n    return count\n}\nconsole.log(getNum(10));\n```\n\n12.给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。\n\n```\nfunction Power(base, exponent) \n{\n    // write code here\n    var position=true;\n    if(exponent==0){\n        return 1;\n    }else if(exponent<0){\n        exponent*=-1;\n        position=false; \n    }\n    var pow=1.0;\n    for(var i=1;i<=exponent;i++){\n        pow*=base\n    }\n    if(!position){\n        pow=1.0/pow;\n    }\n    return pow\n}\n```\n\n\n- 13.输入一个整数数组，实现一个函数来调整该数组中数字的顺序，使得所有的奇数位于数组的前半部分，所有的偶数位于位于数组的后半部分，并保证奇数和奇数，偶数和偶数之间的相对位置不变。\n\n```\nfunction reOrderArray(array) \n{\n    var left=[];\n    var right=[];\n    for(var i=0;i<array.length;i++){\n        if(array[i]%2==0){   //right数组用来存放偶数\n            right.push(array[i]);\n        }else{\n            left.push(array[i]);  //left数组用来存放奇数\n        }\n    }\n    for(var i=0,len=right.length;i<len;i++){\n        left.push(right[i]);\n    }\n    return left;\n}\n```\n\n- 14.输入一个链表，输出该链表中倒数第k个结点\n\n```\nfunction FindKthToTail(head, k) \n{\n    // write code here\n    var p=head;\n    var q=head; //p、q开始都指向头指针\n    for(var i=0;p&&i<k;i++){   //将p向后移动k个节点\n        p=p.next;\n    }\n    if(i<k){   //数据错误，即还没遍历到第k个位置，数据就结束了\n        return null;\n    }\n    while(p!=null){  //p、q同时向后移动，当p移动到链表最后时，q正好移动//到倒数第k个节点，p与q之间相差k\n        p=p.next;\n        q=q.next;\n    }\n    return q;\n}\n```\n\n## 参考\n- https://www.cnblogs.com/haimengqingyuan/p/6917433.html\n- https://blog.csdn.net/column/details/16574.html?&page=1\n- https://zhuanlan.zhihu.com/imweb\n"
  },
  {
    "path": "5.1.6 写好一份简历.md",
    "content": "# 你为什么写不好一份简历?\n\n## 禅与写简历的艺术\n\n- 1.简历格式\n>程序员请用PDF格式的简历，可以先用Word进行编辑排版，完了之后再转格式。 具体原因：\n\n①部分看简历的人使用Mac，不一定安装了office系列工具，而Mac自带的预览工具对Word的支持效果很一般；\n\n②Word本身也分很多版本，不同版本之间兼容性不好，打开之后有时会出现排版混乱；\n\n③部分技术人员（也许是果粉）对windows没什么好感，连带着对office系列软件也持保守态度。\n\nPDF的文件没有上述问题;\n\n\n- 2.文件命名\n\n>使用**应聘岗位+姓名+电话**的格式来命名。 \n\n\n- 3.重要的个人信息要突出，方便HR浏览\n\n除了简历命名上不要出现”个人简历.pdf“这种情况，  \n\n简历的排版也应该避免在最上方出现”个人简历“四个字，因为这四个字提供不了任何有效信息。\n\n可以把姓名、电话、邮箱、应聘岗位这几个东西放在最上面以突出重点。\n\n- 4.简历页数和内容\n\n简历尽量保持一页，如果是工作年限比较长、经历的项目比较多，或者一些技术大拿、leader，可以写两页，但绝对不要超过两页\n\n## 参考\n- https://zhuanlan.zhihu.com/p/28454432\n- [如何写好一份简历](https://github.com/Wscats/CV/issues/7)\n"
  },
  {
    "path": "5.1.7 leetCode 算法题.md",
    "content": "#  leetCode 算法题\n\n- [labuladong 的算法小抄](https://labuladong.gitbook.io/algo/)\n  - https://github.com/labuladong/fucking-algorithm\n\n- [awesome-algorithm: ](https://github.com/apachecn/awesome-algorithm)\n\n- 测试 js 代码\n  - [leetcode-cn playground](https://leetcode-cn.com/playground/new/empty)\n  \n1. 两数之和\n\n给定一个整数数组和一个目标值，找出数组中和为目标值的两个数。\n\n你可以假设每个输入只对应一种答案，且同样的元素不能被重复利用。\n\n示例:\n\n```\n给定 nums = [2, 7, 11, 15], target = 9\n\n因为 nums[0] + nums[1] = 2 + 7 = 9\n所以返回 [0, 1]\n```\njs 实现：\n\n```\n/**\n * @param {number[]} nums\n * @param {number} target\n * @return {number[]}\n */\nvar twoSum = function(nums, target) {\n    var arr = [];\n    for(var i = 0; i < nums.length; i++){\n        for(var j = i+1; j < nums.length; j++){\n            if (nums[i] + nums[j] == target){\n                arr.push(i);\n                arr.push(j);\n            }\n        }\n    }\n    return arr;\n};\n\n// 测试 \ntwoSum([3,2,4],6)\n```\n\n- 参考\n  - [two-sum/description](https://leetcode-cn.com/problems/two-sum/description/)\n  \n  \n\n2.7.整数反转\n\n\n## 一、题目描述：\n>给你一个 32 位的有符号整数 x ，返回将 x 中的数字部分反转后的结果。\n\n>如果反转后整数超过 32 位的有符号整数的范围 [−2^31,  2^31 − 1] ，就返回 0。\n\n>假设环境不允许存储 64 位整数（有符号或无符号）。\n \n```\n示例 1：\n\n输入：x = 123\n输出：321\n\n示例 2：\n\n输入：x = -123\n输出：-321\n\n示例 3：\n\n输入：x = 120\n输出：21\n```\n\n\n\n## 二、思路分析：\n\n- 依次对 x 的末位进行处理,即通过 x%10 取余获得个位数, 通过 x/10 替代现有 x,\n- 位运算符：| ,两个位只要有一个为1，那么结果都为1,否则就为0\n\n\n## 三、AC 代码\n\n\n```\n/**\n * @param {number} x\n * @return {number}\n */\nvar reverse = function(x) {\n    let result = 0;\n    while(x !== 0) {\n        result = result * 10 + x % 10;\n        x = (x / 10) | 0;\n    }\n    return (result | 0) === result ? result : 0;\n};\n执行用时：92 ms\n\n内存消耗：39.2 MB\n\n\n```\n\n## 四、总结\n- 当然不止一种方法，也看到很多方法实现，大概思路很类似；\n\n```\n/**\n * @param {number} x\n * @return {number}\n */\nvar reverse = function(x) {\n    let res = 0;\n    while(x){\n        res = res * 10 + x % 10;\n        if(res > Math.pow(2, 31) - 1 || res < Math.pow(-2, 31)) return 0;\n        x = ~~(x / 10); // 向下取整\n    }\n    return res;\n\n};\n```\n\n执行用时：108 ms\n内存消耗：39.3 MB\n\n也看到有些使用字符串反转，再加判断\n\n```\n/**\n * @param {number} x\n * @return {number}\n */\nvar reverse = function(x) {\n    let sign = Math.sign(x)\n    let res = (Math.abs(x) + '').split('').reverse().join('') * sign\n    if (res > Math.pow(2, 31) - 1 || res < Math.pow(2, 31) * -1) res = 0\n    return res\n\n};\n```\n\n\n>>>仅供学习参考\n\n\n## \n\n## 参考题目\n- [力扣（LeetCode）](https://leetcode-cn.com/problems/reverse-integer)\n\n\n\n\n\n9.回文数\n\n\n## 一、题目描述：\n>给你一个整数 x ，如果 x 是一个回文整数，返回 true ；否则，返回 false 。\n\n>回文数是指正序（从左向右）和倒序（从右向左）读都是一样的整数。例如，121 是回文，而 123 不是。\n\n\n## 二、思路分析：\n\n- 整数转换为字符串-->数组-->数组反转-->字符串  ，然后比较是否相等\n- 参考整数反转的方法\n\n\n## 三、AC 代码\n\n\n```\n/**\n * @param {number} x\n * @return {boolean}\n */\nvar isPalindrome = function(x) {\n  return String(x) === String(x).split('').reverse().join('');\n};\n```\n\n## 四、总结\n- 当然不止一种方法，也看到很多方法实现，\n\n- 可以利用整数反转的逻辑来实现\n\n```\n/**\n * @param {number} x\n * @return {boolean}\n */\nvar isPalindrome = function(x) {\n    if(x < 0 || (!(x % 10) && x)) return false;\n    const num = x;\n    let result = 0;\n    while(x !== 0) {\n        result = result * 10 + x % 10;\n        x = (x / 10) | 0;\n    }\n    return (result | 0) === result ? result === num: 0;\n\n};\n```\n当然了也看到别的方法,觉得可能有点繁琐，比如：\n\n```\n1、先算出数字总位数\n2、每次比较首位和末尾\n3、去掉首位和末尾继续比较\n```\n\n>>>仅供学习参考\n\n## 参考题目\n- [力扣（LeetCode）](https://leetcode-cn.com/problems/palindrome-number)\n\n\n\n\n\n\n\n\n\n\n## 参考\n- [Tcdian/tcdian_leetcode](https://github.com/Tcdian/tcdian_leetcode)\n- [Kexin-Li/LintCode](https://github.com/Kexin-Li/LintCode)\n- [LeetCode with Python](https://github.com/HuberTRoy/leetCode)\n"
  },
  {
    "path": "5.1.8 招聘过程中询问未来的雇主的问题集合.md",
    "content": "# 招聘过程中询问未来的雇主的问题集合\n\n## 职位\n\n- 你为什么要招聘这个职位？\n\n- 我在公司的角色是什么？\n\n- 我将在哪里工作？\n\n- 我期望在这个职位上呆多久？\n\n- 我每天的职责是什么？\n\n- 你希望我花多少时间在每一个时间上？\n\n- 什么样的编程语言会让我投入工作？\n\n- 公司会准许我学习任何我需要使用但不知道的语言吗？\n\n- 你有目标薪资范围吗？\n\n\n## 开发人员协调/团队互动\n\n- 你的团队结构如何？\n\n- 你现在有多少开发？\n\n- 你的团队有多大？\n\n- 团队坐在一起吗？\n\n- 团队是否有与其他员工/其他团队隔离的区域？\n\n- 团队成员在会议中有多频繁？\n\n- 你的开发人员定期配对程序吗？\n\n- 你的开发人员使用屏幕共享或协作编码工具吗？\n\n- 你（或我未来的老板）的领导风格是什么？\n\n- 您是否遵循项目管理的敏捷方法（看板、Scrum等）？\n\n- 你如何分配工作？\n\n- 你预分配基于熟练的任务，还是所有成员都期望同样熟练？\n\n- 你怎么评价工作？\n\n- 单一产品，还是我将定期在不同的项目上工作？\n\n- 贵公司/团队多久启动一个新项目？\n\n- 贵公司与公司其他群体的最好和最坏的工作关系是什么？\n\n-你的团队经常与其他团队互动吗？\n\n- 你是否发现自己经常被其他团队的依赖所阻断？\n\n- 团队工作几小时？\n\n\n\n## 文化\n\n\n- 这里的工作节奏是什么？一年中有没有这样的时候，所有的人都在甲板上，我们通宵达旦地工作，还是全年都很一致？周/月怎么样？在整个星期/月内是否有相当均匀的传播，或者是有危机的日子吗？\n\n- 什么使你（面试官）选择加入这个公司？\n\n- 在这里工作，你最喜欢的是什么？\n\n- 你公司的英雄是谁？\n\n- 最有名的人有什么共同之处？\n\n- 公司是否有员工业绩奖励制度？\n\n- 什么类型的人在这里成功？什么类型的人不是？\n\n- 我是被允许还是被期望把我的工作带回家？\n\n- 对工作时间、截止日期和加班有什么期望？\n\n- 你们提供多少假期？\n\n- 预计假期需要多少时间？\n\n- 开放办公室、私人办公室或小隔间？\n\n- 有服装规范吗？\n\n- 你的开发部与你的销售部门有什么关系？谁设定最后期限？\n\n- 公司提供零食和饮料吗？\n\n- 你对开发者每天有多少生产时间有什么期望？\n\n\n\n\n## 总结\n\n- 公司的团队，技术\n- 问一下面试官对自己的评价或者看法\n- 对于团队成员的成长，您有哪些方案\n- 最好留下每一位面试官的微信，面试结束后，发一段感谢的话，感谢这次面试，肯定面试的内容很有水平，希望以后可以多多交流。一是表现自己的素养，二是可以看出面试官对于自己的态度。实在不去这家公司，也方便以后挖人\n- 福利什么的\n\n\n## 参考\n- https://github.com/Twipped/InterviewThis\n"
  },
  {
    "path": "5.1.9 2018大厂高级前端面试题汇总.md",
    "content": "# 2018大厂高级前端面试题汇总\n\n>面试的公司分别是：\n阿里、网易、滴滴、今日头条、有赞、挖财、沪江、饿了么、携程、喜马拉雅、兑吧、微医、寺库、宝宝树、海康威视、蘑菇街、酷家乐、百分点和海风教育。\n\n## 阿里\n\n- 使用过的koa2中间件\n\n- koa-body原理\n\n- 介绍自己写过的中间件\n\n- 有没有涉及到Cluster\n\n- 介绍pm2\n\n- master挂了的话pm2怎么处理\n\n- 如何和MySQL进行通信\n\n- React声明周期及自己的理解\n\n- 如何配置React-Router\n\n- 路由的动态加载模块\n\n- 服务端渲染SSR\n\n- 介绍路由的history\n\n- 介绍Redux数据流的流程\n\n- Redux如何实现多个组件之间的通信，多个组件使用相同状态如何进行管理\n\n- 多个组件之间如何拆分各自的state，每块小的组件有自己的状态，它们之间还有一些公共的状态需要维护，如何思考这块\n\n- 使用过的Redux中间件\n\n- 如何解决跨域的问题\n\n- 常见Http请求头\n\n- 移动端适配1px的问题\n\n- 介绍flex布局\n\n- 其他css方式设置垂直居中\n\n- 居中为什么要使用transform（为什么不使用marginLeft/Top）\n\n-   使用过webpack里面哪些plugin和loader\n\n-   webpack里面的插件是怎么实现的\n\n- dev-server是怎么跑起来\n\n- 项目优化\n\n- 抽取公共文件是怎么配置的\n\n- 项目中如何处理安全问题\n\n- 怎么实现this对象的深拷贝\n\n## 网易\n\n- 介绍redux，主要解决什么问题\n- 文件上传如何做断点续传\n- 表单可以跨域吗\n- promise、async有什么区别\n- 搜索请求如何处理（防抖）\n- 搜索请求中文如何请求\n- 介绍观察者模式\n- 介绍中介者模式\n- 观察者和订阅-发布的区别，各自用在哪里\n- 介绍react优化\n- 介绍http2.0\n- 通过什么做到并发请求\n- http1.1时如何复用tcp连接\n- 介绍service worker\n- 介绍css3中position:sticky\n- redux请求中间件如何处理并发\n- 介绍Promise，异常捕获\n- 介绍position属性包括CSS3新增\n- 浏览器事件流向\n- 介绍事件代理以及优缺点\n- React组件中怎么做事件代理\n- React组件事件代理的原理\n- 介绍this各种情况\n- 前端怎么控制管理路由\n- 使用路由时出现问题如何解决\n- React怎么做数据的检查和变化\n- \n## 滴滴\n\n- react-router怎么实现路由切换\n- react-router里的<Link>标签和<a>标签有什么区别\n- <a>标签默认事件禁掉之后做了什么才实现了跳转\n- React层面的性能优化\n- 整个前端性能提升大致分几类\n-   import { Button } from 'antd' ，打包的时候只打包button，分模块加载，是怎么做到的\n-   使用import时，webpack对node_modules里的依赖会做什么\n- JS异步解决方案的发展历程以及优缺点\n- Http报文的请求会有几个部分\n- cookie放哪里，cookie能做的事情和存在的价值\n- cookie和token都存放在header里面，为什么只劫持前者\n- cookie和session有哪些方面的区别\n- React中Dom结构发生变化后内部经历了哪些变化\n- React挂载的时候有3个组件，textComponent、composeComponent、domComponent，区别和关系，Dom结构发生变化时怎么区分data的变化，怎么更新，更新怎么调度，如果更新的时候还有其他任务存在怎么处理\n- key主要是解决哪一类的问题，为什么不建议用索引index（重绘）\n- Redux中异步的请求怎么处理\n- Redux中间件是什么东西，接受几个参数（两端的柯里化函数）\n- 柯里化函数两端的参数具体是什么东西\n- 中间件是怎么拿到store和action，然后怎么处理\n- state是怎么注入到组件的，从reducer到组件经历了什么样的过程\n- koa中response.send、response.rounded、response.json发生了什么事，浏览器为什么能识别到它是一个json结构或是html\n- koa-bodyparser怎么来解析request\n-   webpack整个生命周期，loader和plugin有什么区别\n-   介绍AST（Abstract Syntax Tree）抽象语法树\n- 安卓Activity之间数据是怎么传递的\n- 安卓4.0到6.0过程中WebView对js兼容性的变化\n- WebView和原生是如何通信\n- 跨域怎么解决，有没有使用过Apache等方案\n## 今日头条\n- 对async、await的理解，内部原理\n- 介绍下Promise，内部实现\n-   清除浮动\n-   定位问题（绝对定位、相对定位等）\n- 从输入URL到页面加载全过程\n- tcp3次握手\n- tcp属于哪一层（1 物理层 -> 2 数据链路层 -> 3 网络层(ip)-> 4 传输层(tcp) -> 5 应用层(http)）\n- redux的设计思想\n- 接入redux的过程\n- 绑定connect的过程\n- connect原理\n-   webpack介绍\n- == 和 ===的区别，什么情况下用相等==\n- bind、call、apply的区别\n- 动画的了解\n- 介绍下原型链（解决的是继承问题吗）\n- 对跨域的了解\n\n## 有赞\n\n- Linux 754 介绍\n- 介绍冒泡排序，选择排序，冒泡排序如何优化\n- transform动画和直接使用left、top改变位置有什么优缺点\n- 如何判断链表是否有环\n- 介绍二叉搜索树的特点\n- 介绍暂时性死区\n- ES6中的map和原生的对象有什么区别\n- 观察者和发布-订阅的区别\n- react异步渲染的概念,介绍Time Slicing 和 Suspense\n- 16.X声明周期的改变\n- 16.X中props改变后在哪个生命周期中处理\n- 介绍纯函数\n- 前端性能优化\n- pureComponent和FunctionComponent区别\n- 介绍JSX\n- 如何做RN在安卓和IOS端的适配\n- RN为什么能在原生中绘制成原生组件（bundle.js）\n- 介绍虚拟DOM\n- 如何设计一个localStorage，保证数据的实效性\n- 如何设计Promise.all()\n- 介绍高阶组件\n- sum(2, 3)实现sum(2)(3)的效果\n- react性能优化\n- 两个对象如何比较\n## 挖财\n\n- JS的原型\n- 变量作用域链\n- call、apply、bind的区别\n- 防抖和节流的区别\n- 介绍各种异步方案\n- react生命周期\n- 介绍Fiber\n- 前端性能优化\n- 介绍DOM树对比\n- react中的key的作用\n- 如何设计状态树\n- 介绍css，xsrf\n- http缓存控制\n- 项目中如何应用数据结构\n- native提供了什么能力给RN\n- 如何做工程上的优化\n- shouldComponentUpdate是为了解决什么问题\n- 如何解决props层级过深的问题\n- 前端怎么做单元测试\n-   webpack生命周期\n-   webpack打包的整个过程\n-   常用的plugins\n- pm2怎么做进程管理，进程挂掉怎么处理\n- 不用pm2怎么做进程管理\n\n## 沪江\n\n- 介绍下浏览器跨域\n- 怎么去解决跨域问题\n- jsonp方案需要服务端怎么配合\n- Ajax发生跨域要设置什么（前端）\n- 加上CORS之后从发起到请求正式成功的过程\n- xsrf跨域攻击的安全性问题怎么防范\n- 使用Async会注意哪些东西\n- Async里面有多个await请求，可以怎么优化（请求是否有依赖）\n- Promise和Async处理失败的时候有什么区别\n- Redux在状态管理方面解决了React本身不能解决的问题\n- Redux有没有做过封装\n- react生命周期，常用的生命周期\n- 对应的生命周期做什么事\n- 遇到性能问题一般在哪个生命周期里解决\n- 怎么做性能优化（异步加载组件...）\n- 写react有哪些细节可以优化\n- React的事件机制（绑定一个事件到一个组件上）\n- 介绍下事件代理，主要解决什么问题\n- 前端开发中用到哪些设计模式\n- React/Redux中哪些功能用到了哪些设计模式\n- JS变量类型分为几种，区别是什么\n- JS里垃圾回收机制是什么，常用的是哪种，怎么处理的\n- 一般怎么组织CSS（Webpack）\n\n## 饿了么\n\n- 小程序里面开页面最多多少\n- React子父组件之间如何传值\n- Emit事件怎么发，需要引入什么\n- 介绍下React高阶组件，和普通组件有什么区别\n- 一个对象数组，每个子对象包含一个id和name，React如何渲染出全部的name\n- 在哪个生命周期里写\n- 其中有几个name不存在，通过异步接口获取，如何做\n- 渲染的时候key给什么值，可以使用index吗，用id好还是index好\n-   webpack如何配sass，需要配哪些loader\n-   配css需要哪些loader\n- 如何配置把js、css、html单独打包成一个文件\n-   div垂直水平居中（flex、绝对定位）\n- 两个元素块，一左一右，中间相距10像素\n- 上下固定，中间滚动布局如何实现\n- [1, 2, 3, 4, 5]变成[1, 2, 3, a, b, 5]\n- 取数组的最大值（ES5、ES6）\n- apply和call的区别\n- ES5和ES6有什么区别\n- some、every、find、filter、map、forEach有什么区别\n- 上述数组随机取数，每次返回的值都不一样\n- 如何找0-5的随机数，95-99呢\n- 页面上有1万个button如何绑定事件\n- 如何判断是button\n- 页面上生成一万个button，并且绑定事件，如何做（JS原生操作DOM）\n- 循环绑定时的index是多少，为什么，怎么解决\n- 页面上有一个input，还有一个p标签，改变input后p标签就跟着变化，如何处理\n- 监听input的哪个事件，在什么时候触发\n## 携程\n- 对React看法，有没有遇到一些坑\n- 对闭包的看法，为什么要用闭包\n- 手写数组去重函数\n- 手写数组扁平化函数\n- 介绍下Promise的用途和性质\n- Promise和Callback有什么区别\n- React生命周期\n\n## 喜马拉雅\n\n- ES6新的特性\n- 介绍Promise\n- Promise有几个状态\n- 说一下闭包\n- React的生命周期\n- componentWillReceiveProps的触发条件是什么\n- React16.3对生命周期的改变\n- 介绍下React的Filber架构\n- 画Filber渲染树\n- 介绍React高阶组件\n- 父子组件之间如何通信\n- Redux怎么实现属性传递，介绍下原理\n- React-Router版本号\n- 网站SEO怎么处理\n- 介绍下HTTP状态码\n- 403、301、302是什么\n- 缓存相关的HTTP请求头\n- 介绍HTTPS\n- HTTPS怎么建立安全通道\n- 前端性能优化（JS原生和React）\n- 用户体验做过什么优化\n- 对PWA有什么了解\n- 对安全有什么了解\n- 介绍下数字签名的原理\n- 前后端通信使用什么方案\n- RESTful常用的Method\n- 介绍下跨域\n- Access-Control-Allow-Origin在服务端哪里配置\n- csrf跨站攻击怎么解决\n- 前端和后端怎么联调\n- \n## 兑吧\n\n- localStorage和cookie有什么区别\n- CSS选择器有哪些\n- 盒子模型，以及标准情况和IE下的区别\n- 如何实现高度自适应\n- prototype和——proto——区别\n- `_construct是`什么\n- new是怎么实现的\n- promise的精髓，以及优缺点\n- 如何实现H5手机端的适配\n- rem、flex的区别（root em）\n- em和px的区别\n- React声明周期\n- 如何去除url中的#号\n- Redux状态管理器和变量挂载到window中有什么区别\n- webpack和gulp的优缺点\n- 如何实现异步加载\n- 如何实现分模块打包（多入口）\n- 前端性能优化（1js css；2 图片；3 缓存预加载； 4 SSR； 5 多域名加载；6 负载均衡）\n- 并发请求资源数上限（6个）\n- base64为什么能提升性能，缺点\n- 介绍webp这个图片文件格式\n- 介绍koa2\n- Promise如何实现的\n- 异步请求，低版本fetch如何低版本适配\n- ajax如何处理跨域\n- CORS如何设置\n- jsonp为什么不支持post方法\n- 介绍同源策略\n- React使用过的一些组件\n- 介绍Immuable\n- 介绍下redux整个流程原理\n- 介绍原型链\n- 如何继承\n\n## 微医\n\n- 介绍JS数据类型，基本数据类型和引用数据类型的区别\n- Array是Object类型吗\n- 数据类型分别存在哪里\n- var a = {name: \"前端开发\"}; var b = a; a = null那么b输出什么\n- var a = {b: 1} 存放在哪里\n- var a = {b: {c: 1}}存放在哪里\n- 栈和堆的区别\n- 垃圾回收时栈和堆的区别\n- 数组里面有10万个数据，取第一个元素和第10万个元素的时间相差多少\n- 栈和堆具体怎么存储\n- 介绍闭包以及闭包为什么没清除\n- 闭包的使用场景\n- JS怎么实现异步\n- 异步整个执行周期\n- Promise的三种状态\n- Async/Await怎么实现\n- Promise和setTimeout执行先后的区别\n- JS为什么要区分微任务和宏任务\n- Promise构造函数是同步还是异步执行，then呢\n- 发布-订阅和观察者模式的区别\n- JS执行过程中分为哪些阶段\n- 词法作用域和this的区别\n- 平常是怎么做继承\n- 深拷贝和浅拷贝\n- loadsh深拷贝实现原理\n- ES6中let块作用域是怎么实现的\n- React中setState后发生了什么\n- setState为什么默认是异步\n- setState什么时候是同步的\n- 为什么3大框架出现以后就出现很多native（RN）框架（虚拟DOM）\n- 虚拟DOM主要做了什么\n- 虚拟DOM本身是什么（JS对象）\n- 304是什么\n- 打包时Hash码是怎么生成的\n- 随机值存在一样的情况，如何避免\n-   使用webpack构建时有无做一些自定义操作\n  webpack做了什么\n- a，b两个按钮，点击aba，返回顺序可能是baa，如何保证是aba（Promise.then）\n- node接口转发有无做什么优化\n- node起服务如何保证稳定性，平缓降级，重启等\n- RN有没有做热加载\n- RN遇到的兼容性问题\n- RN如何实现一个原生的组件\n- RN混原生和原生混RN有什么不同\n- 什么是单页项目\n- 遇到的复杂业务场景\n- Promise.all实现原理\n\n## 寺库\n\n- 介绍Promise的特性，优缺点\n- 介绍Redux\n- RN的原理，为什么可以同时在安卓和IOS端运行\n- RN如何调用原生的一些功能\n- 介绍RN的缺点\n- 介绍排序算法和快排原理\n- 堆和栈的区别\n- 介绍闭包\n- 闭包的核心是什么\n- 网络的五层模型\n- HTTP和HTTPS的区别\n- HTTPS的加密过程\n- 介绍SSL和TLS\n- 介绍DNS解析\n- JS的继承方法\n- 介绍垃圾回收\n- cookie的引用为了解决什么问题\n- cookie和localStorage的区别\n- 如何解决跨域问题\n- 前端性能优化\n\n## 宝宝树\n\n- 使用canvas绘图时如何组织成通用组件\n- formData和原生的ajax有什么区别\n- 介绍下表单提交，和formData有什么关系\n- 介绍redux接入流程\n- rudux和全局管理有什么区别（数据可控、数据响应）\n- RN和原生通信\n- 介绍MVP怎么组织\n- 介绍异步方案\n- promise如何实现then处理\n- koa2中间件原理\n- 常用的中间件\n- 服务端怎么做统一的状态处理\n- 如何对相对路径引用进行优化\n- node文件查找优先级\n- npm2和npm3+有什么区别\n\n## 海康威视\n\n- knex连接数据库响应回调\n- 介绍异步方案\n- 如何处理异常捕获\n- 项目如何管理模块\n- 前端性能优化\n- JS继承方案\n- 如何判断一个变量是不是数组\n- 变量a和b，如何交换\n- 事件委托\n- 多个\n- 标签生成的Dom结构是一个类数组\n- 类数组和数组的区别\n- dom的类数组如何转成数组\n- 介绍单页面应用和多页面应用\n- redux状态树的管理\n- 介绍localstorage的API\n\n## 蘑菇街\n\n- html语义化的理解\n- <b>和<strong>的区别\n- 对闭包的理解\n- 工程中闭包使用场景\n- 介绍this和原型\n- 使用原型最大的好处\n- react设计思路\n- 为什么虚拟DOM比真实DOM性能好\n- react常见的通信方式\n- redux整体的工作流程\n- redux和全局对象之间的区别\n- Redux数据回溯设计思路\n- 单例、工厂、观察者项目中实际场景\n- 项目中树的使用场景以及了解\n- 工作收获\n\n## 酷家乐\n\n- react生命周期\n- react性能优化\n- 添加原生事件不移除为什么会内存泄露\n- 还有哪些地方会内存泄露\n- setInterval需要注意的点\n- 定时器为什么是不精确的\n- setTimeout(1)和setTimeout(2)之间的区别\n- 介绍宏任务和微任务\n- promise里面和then里面执行有什么区别\n- 介绍pureComponet\n- 介绍Function Component\n- React数据流\n- props和state的区别\n- 介绍react context\n- 介绍class和ES5的类以及区别\n- 介绍箭头函数和普通函数的区别\n- 介绍defineProperty方法，什么时候需要用到\n- for..in 和 object.keys的区别\n- 介绍闭包，使用场景\n- 使用闭包特权函数的使用场景\n- get和post有什么区别\n- 百分点\n- React15/16.x的区别\n- 重新渲染render会做些什么\n- 哪些方法会触发react重新渲染\n- state和props触发更新的生命周期分别有什么区别\n- setState是同步还是异步\n- 对无状态组件的理解\n- 介绍Redux工作流程\n- 介绍ES6的功能\n- let、const以及var的区别\n- 浅拷贝和深拷贝的区别\n- 介绍箭头函数的this\n- 介绍Promise和then\n- 介绍快速排序\n- 算法：前K个最大的元素\n-  \n## 海风教育\n\n- 对react看法，它的优缺点\n\n- 使用过程中遇到的问题，如何解决的\n\n- react的理念是什么（拿函数式编程来做页面渲染）\n\n- JS是什么范式语言(面向对象还是函数式编程)\n\n- koa原理，为什么要用koa(express和koa对比)\n\n- 使用的koa中间件\n\n- ES6使用的语法\n\n- Promise 和 async/await 和 callback的区别\n\n- Promise有没有解决异步的问题（promise链是真正强大的地方）\n\n- Promise和setTimeout的区别（Event Loop）\n\n- 进程和线程的区别（一个node实例就是一个进程，node是单线程，通过事件循环来实现异步）\n\n- 介绍下DFS深度优先\n\n- 介绍下观察者模式\n\n- 观察者模式里面使用的数据结构(不具备顺序 ，是一个list)- \n\n\n## 参考\n- [yyg blog](https://github.com/yygmind/blog)- \n- [2018大厂高级前端面试题汇总](https://juejin.im/post/5bc92e9ce51d450e8e777136)\n"
  },
  {
    "path": "5.2.0 这是一份能让你更好准备下一次面试的图谱.md",
    "content": "# 这是一份能让你更好准备下一次面试的图谱\n\n\n## 参考\n- [这是一份能让你更好准备下一次面试的图谱](https://github.com/InterviewMap/CS-Interview-Knowledge-Map)\n- [doc](https://yuchengkai.cn/docs/zh/frontend/#proxy)\n"
  },
  {
    "path": "5.2.1 经典面试100题.md",
    "content": "# 经典面试100题\n\n## 经典面试题1：图片占多少内存\n一个图片占多少内存在于图片的尺寸大小。\n\n以一张尺寸为900 × 600的图片为例，图片共有像素数：\n\n900 × 600 = 540,000像素(Pixel)。\n\n如果图片是RGB 色彩模式，占用的内存是：\n\n900 × 600 × 3 = 1,620,000 字节(bytes).\n\n后面\"× 3\"表示每个像素内RGB 颜色的信息需要3字节，也是24比特(bit)。 \n\n也可以说每个像素中 3 RGB值，每一个RGB值需要一个 8 比特，也是一个字节，总计24个比特。\n\n存储不同的色彩模式需要不同的内存，具体如下：\n```\n\n图片类型                            |      每像素多少字节 \n\n1 比特 数据图(Line art)        |      每像素1/8字节，也是一个比特。\n\n8 比特灰度(Grayscale)         |      每像素1字节。\n\n16 比特灰度(Grayscale)        |     每像素2字节。\n\n24 比特 RGB                        |     每像素3字节，这是图片中最常用的，如JPG格式。\n\n32 比特 印刷色彩模式(CMYK) |   每像素4字节\n\n48 比特 RGB                        |    每像素6字节\n```\n\n内存单位\n\n关于兆字节 （Megabytes)\n\n大部分程序系统使用兆来衡量图片大小，下面解释一下字节和兆的关系。\n\n1兆(MB) = 1024 × 1024 = 1,048,576 字节， 也就是2的20次方。\n\n1,000,000字节不等于 1兆， 10,000,000 / (1024x1024) = 9.54 兆。\n\n回到本文开始时的题目：\n\n一张尺寸为900×600的RGB图片占的内存大小：\n\n900 × 600 × 3 = 1,620,000 字节(bytes) = 1.582兆（M）\n\n再来一个例子，一个1024 x 768大小 24位色(24 bit RGB)的图像占多少内存？\n\n1024 × 768 × 3 = 2,359,296 字节(bytes) =2,359,296/(1024 x 1024)兆（M）=2.25M\n\n\n\n注意：\n\n* 图片编辑器一般用MB衡量尺寸大小。(没压缩之前的原始尺寸除以1024x1024)\n\n* 操作系统里显示的尺寸一般是压缩后的大小，会小于原始尺寸。\n\n总体来说一张图片占多少内存，在于图片的尺寸大小和色彩模式。\n\n\n\n另外，该题目结合一些设备知识有一些变种：\n\n1. 如iPhone 6 上设备分辨率大小的RGB类型图片占用内存多少？\n\n由于iPhone 6的设计分辨率是750x1334，计算所有像素点 = 750X1334X3 。\n\n图片占用内存大小 =  (750X1334X3) / (1024X1024) , 也就是 2.86M.\n\n2. 一张6x4寸的图片在150dpi设备上，占用多少内存？\n\n首先是计算像素点， 像素点 = (6 x 150) x(4 x 150) = 540,000像素\n\n如果图片是RGB类型，则占用内存为(540,000 x 3)/(1024 x 1024) = 1.545M。\n\n\n\n\n## 参考\n- [经典面试100题](https://www.jianshu.com/p/a445d4e83e9d)\n"
  },
  {
    "path": "5.2.1 高级前端进阶.md",
    "content": "# 高级前端进阶\n\n- 1、前端面试图谱\nhttps://github.com/InterviewMap/CS-Interview-Knowledge-Map\n\n- 2、2017年8月web前端跳槽面试技巧视频教程\n链接：https://pan.baidu.com/s/1FvUT9X11BIR-d6WkvPaTVw 提取码：bqn6\n\n- 3、React.js入门与实战 开发适配PC端及移动端新闻头条平台\n链接：https://pan.baidu.com/s/1JwGxLBDUOtiErocZfvA4vg 提取码：cc6j\n\n- 4、React16+React-Router4 从零打造企业级电商后台管理系统\n链接：https://pan.baidu.com/s/1GRBnR_xHagExjZ9LBlH01A 提取码：lag6\n\n- 5、前端 Vue+Node+MongoDB高级全栈开发（完结）\n链接：https://pan.baidu.com/s/17FRldTCAXazccXuzBSpXVg 提取码：n5oy\n\n- 6、全网首发mpvue课程小程序全栈开发\n链接：https://pan.baidu.com/s/1UC_lBhc2jZ9ZD6xwzCjfqg 提取码：8ij1\n\n\n## 参考\n- [网易高级前端工程师进阶群]()\n"
  },
  {
    "path": "5.2.2 如何面试前端面试者.md",
    "content": "# 如何面试前端面试者\n\n>我们去面试一个前端开发者，需要了解这个开发者的那些方面\n\n\n- 曾经看到别人说的维度：\n\n  - 通用能力：考察其沟通表达、学习成长等\n\n  - 专业知识：考察其知识的掌握、深度、广度等\n\n  - 专业能力：考察其技能应用的能力和结果\n\n  - 工作业绩：考察其工作成果、产出、创新点等\n\n  - 价值观：考察其认知、理解、行为等\n\n\n曾经面试阿里的时候，面试官问的很细，很重视基础，一问就是问一个小时左右，\n\n我们面试前端开发者同样也要看基础能力，还要看一个人的品质，\n\n总结起来要考察一个前端的综合素养：\n- 1.基础知识掌握能力\n- 2.学习成长能力，求知欲，持续学习能力（GitHub，常去的论坛社区，博客之类）\n- 3.对一个领域的深度与广度（原理性的东西）\n- 4.未来技能提升能力 （未来的打算，计划）\n- 5.沟通表达能力\n- 6.团队协作能力，人品等（离职原因）\n"
  },
  {
    "path": "5.2.3 ScriptOJ-国人开发的前端题库，可以用作评测系统.md",
    "content": "# 国人开发的前端题库，可以用作评测系统 - ScriptOJ\n>yhq147258369\n\n## 1.完成 extname 函数，它会接受一个文件名作为参数，你需要返回它的扩展名。例如，输入 emoji.png，返回 .png。\n```\nconst extname = (filename) => {\n  /* TODO */\n  if(filename.indexOf('.') != -1){\n    let arr = filename.split('.');\n    if(!!arr[0]){\n      return `.${arr[arr.length-1]}`;\n    }else {\n      return '';\n    }\n  }else {\n    return '';\n  }\n}\n\n```\n\n## 2.\n\n\n\n## 参考\n- [website-题目](http://scriptoj.mangojuice.top/problems?tag=all)\n"
  },
  {
    "path": "5.2.4 线性顺序存储结构和链式存储结构有什么区别.md",
    "content": "# 线性顺序存储结构和链式存储结构有什么区别\n\n\n## 定义\n\n>顺序存储结构就是用一组地址连续的存储单元依次存储该线性表中的各个元素。  \n由于表中各个元素具有相同的属性，所以占用的存储空间相同。\n\n>线性表按链式存储时，每个数据元素 (结点)的存储包括数据区和指针区两个部分。  \n数据区存放结点本身的数据，  \n指针区存放其后继元素的地址只要知道该线性表的起始地址表中的各个元素就可通过其间的链接关系逐步找到\n\n>链表存储结构的内存地址不一定是连续的，但顺序存储结构的内存地址一定是连续的；  \n链式存储适用于在较频繁地插入、删除、更新元素时，而顺序存储结构适用于频繁查询时使用。\n\n## 优缺点\n顺序存储需要开辟一个定长的空间，读写速度快，缺点不可扩充容量(如果要扩充需要开辟一个新的足够大的空间把原来的数据重写进去)\n\n链式存储无需担心容量问题，读写速度相对慢些，由于要存储下一个数据的地址所以需要的存储空间比顺序存储大。\n\n\n\n\n## 参考\n- [线性顺序存储结构和链式存储结构有什么区别？以及优缺点。](https://www.jianshu.com/p/9bc2f63da883)\n"
  },
  {
    "path": "5.2.5 编程之法-面试和算法心得.md",
    "content": "# 编程之法：面试和算法心得\n\n- [目录 章节](https://github.com/julycoding/The-Art-Of-Programming-By-July/tree/master/ebook/zh)\n\n##目录\n### 第一部分 数据结构\n* **第一章 字符串**\n - [1.0 本章导读](01.00.md)\n - [1.1 旋转字符串](01.01.md)\n - [1.2 字符串包含](01.02.md)\n - [1.3 字符串转换成整数](01.03.md)\n - [1.4 回文判断](01.04.md)\n - [1.5 最长回文子串](01.05.md)\n - [1.6 字符串的全排列](01.06.md)\n - [1.10 本章习题](01.10.md)\n* **第二章 数组**\n - [2.0 本章导读](02.00.md)\n - [2.1 寻找最小的 k 个数](02.01.md) \n - [2.2 寻找和为定值的两个数](02.02.md)\n - [2.3 寻找和为定值的多个数](02.03.md)\n - [2.4 最大连续子数组和](02.04.md)\n - [2.5 跳台阶](02.05.md)\n - [2.6 奇偶排序](02.06.md)\n - [2.7 荷兰国旗](02.07.md)\n - [2.8 矩阵相乘](02.08.md)\n - [2.9 完美洗牌](02.09.md)\n - [2.15 本章习题](02.15.md)\n* **第三章 树**\n - [3.0 本章导读](03.00.md)\n - [3.1 红黑树](03.01.md)\n - [3.2 B树](03.02.md)\n - [3.3 最近公共祖先LCA](03.03.md)\n - [3.10 本章习题](03.10.md)\n \n### 第二部分 算法心得\n* **第四章 查找匹配**\n - [4.1 有序数组的查找](04.01.md)\n - [4.2 行列递增矩阵的查找](04.02.md)\n - [4.3 出现次数超过一半的数字](04.03.md)\n* **第五章 动态规划**\n - [5.0 本章导读](05.00.md)\n - [5.1 最大连续乘积子串](05.01.md)\n - [5.2 字符串编辑距离](05.02.md)\n - [5.3 格子取数](05.03.md)\n - [5.4 交替字符串](05.04.md)\n - [5.10 本章习题](05.10.md)\n \n### 第三部分 综合演练\n* **第六章 海量数据处理**\n - [6.0 本章导读](06.00.md)\n - [6.1 关联式容器](06.01.md)\n - [6.2 分而治之](06.02.md)\n - [6.3 simhash算法](06.03.md)\n - [6.4 外排序](06.04.md)\n - [6.5 MapReduce](06.05.md)\n - [6.6 多层划分](06.06.md)\n - [6.7 Bitmap](06.07.md)\n - [6.8 Bloom filter](06.08.md)\n - [6.9 Trie树](06.09.md)\n - [6.10 数据库](06.10.md)\n - [6.11 倒排索引](06.11.md)\n - [6.15 本章习题](06.15.md)\n* **第七章 机器学习**\n - [7.1 K 近邻算法](07.01.md)\n - [7.2 支持向量机](07.02.svm.md)\n* **附录 更多题型**\n - [附录A 语言基础](08.00.md)\n - [附录B 概率统计](08.01.md)\n - [附录C 智力逻辑](08.02.md)\n - [附录D 系统设计](08.03.md)\n - [附录E 操作系统](08.04.md)\n - [附录F 网络协议](08.05.md)\n\n\n\n\n## 参考\n- [The-Art-Of-Programming-By-July](https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/Readme.md)\n"
  },
  {
    "path": "5.2.6 观察者模式.md",
    "content": "# 观察者模式 \n\n\n>当对象间存在一对多关系时，则使用观察者模式（Observer Pattern）。\n比如，当一个对象被修改时，则会自动通知它的依赖对象。\n\n意图：定义对象间的一种一对多的依赖关系，当一个对象的状态发生改变时，所有依赖于它的对象都得到通知并被自动更新。\n\n主要解决：一个对象状态改变给其他对象通知的问题，而且要考虑到易用和低耦合，保证高度的协作。\n\n何时使用：一个对象（目标对象）的状态发生改变，所有的依赖对象（观察者对象）都将得到通知，进行广播通知\n\n\n有时候观察者模式也称为：发布-订阅者模式\n\n\n\n## 优点： \n- 1、观察者和被观察者是抽象耦合的。 \n- 2、建立一套触发机制。\n\n## 缺点： \n- 1、如果一个被观察者对象有很多的直接和间接的观察者的话，将所有的观察者都通知到会花费很多时间。 \n- 2、如果在观察者和观察目标之间有循环依赖的话，观察目标会触发它们之间进行循环调用，可能导致系统崩溃。 \n- 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的，而仅仅只是知道观察目标发生了变化。\n"
  },
  {
    "path": "5.2.7 大数据数组对分页.md",
    "content": "# 5.2.7 大数据数组对分页\n\n\n```\n  const arr = [{}, ...{}]; 假如有11111条数据，一页1000条数据进行分页\n  \n  let pagesData = [];\n  arr.forEach((ele,index) => {\n    // 先计算可以分多少页\n    const page = Math.floor(index / 1000)\n    if (!pagesData[page]) {\n     pagesData[page] = [];\n    }\n    // 每页中加数据\n    pagesData[page].push(ele) \n  })\n```\n"
  },
  {
    "path": "5.2.8 平均分配问题.md",
    "content": "# 5.2.8 平均分配问题\n\n>多个会员分配给多个导购，选择导购后平均分配给已选的导购，余下的再向已选的最开始导购里加\n\n```\n# demo\n\nlet checkedClerks = [2,3,4,5];\nlet clerkOptions= [\n    {\n     clerkId: 1,\n     clerkName: '1',\n     memberCount: '1'\n    },\n{\n     clerkId: 2,\n     clerkName: '2',\n     memberCount: '1'\n    },\n{\n     clerkId: 3,\n     clerkName: '3',\n     memberCount: '1'\n    },\n{\n     clerkId: 4,\n     clerkName: '4',\n     memberCount: '1'\n    },\n{\n     clerkId: 5,\n     clerkName: '5',\n     memberCount: '1'\n    }\n\n];\nconst averageNum = Math.floor(15 / checkedClerks.length);\nconst leftNum = 15 % checkedClerks.length;\nlet arr = [];\nclerkOptions.forEach((ele, index) => {\n  ele.memberCount = averageNum;\n  if (checkedClerks.includes(ele.clerkId)) {\n    arr.push(index);\n  }\n});\nclerkOptions.forEach((ele, index) => {\n  arr.forEach((el, key) => {\n    if (el == index && key < leftNum) {\n      ele.memberCount = ele.memberCount + 1;\n    }\n  });\n});\n```\n"
  },
  {
    "path": "5.3.0 js判断两个数组对象是否相同的方式 .md",
    "content": "# 5.3.0 js判断两个数组对象是否相同的方式 \n\n\n```\n# 1、js 比较两个对象数组是否完全相同\n\nlet obj1 = [{\"id\" : 1, \"name\" : \"lisi\"}];\nlet obj2 = [{\"id\" : 1, \"name\" : \"lisi\"}];\nconst isEqual = JSON.stringify(obj1) === JSON.stringify(obj2);\n\n\n# js判断两个对象是否相同\n\nObject.entries(object1).toString() === Object.entries(object2).toString();\n\n\nor \n\n// 列出所有的键，接着遍历数组\nfunction ifCompare(object1, object2) {\n  let o1keys = Object.keys(object1);\n  let o2keys = Object.keys(object2);\n  if (o2keys.length !== o1keys.length) return false;\n  for (let i = 0; i <= o1keys.length - 1; i++) {\n    let key = o1keys[i];\n    if (!o2keys.includes(key)) return false;\n    if (object2[key] !== object1[key]) return false;\n  }\n  return true;\n}\n```\n\n\n"
  },
  {
    "path": "5.6.6 前端面试准备.md",
    "content": "# 前端面试准备\n\n\n## 简历方面\n- [关于程序员求职简历](https://mdluo.com/2016-02-27/about-resume/)\n- [罗礼权的个人简历：罗礼权，56网，前端](https://cv.mrluo.life/)\n- [张雯莉的简历：张雯莉，上海交通大学，前端](http://zhangwenli.com/cv/cn.html)\n- [范文杰的简历：范文杰，丁香园，前端](http://html.love/)\n\n\n## 前端知识方面\n- [前端面试图谱](https://yuchengkai.cn/docs/zh/frontend/#%E5%86%85%E7%BD%AE%E7%B1%BB%E5%9E%8B)\n- [前端面试题---很全](https://github.com/fairyly/front-end-summary/blob/gh-pages/5.1.4%20%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95.md)\n- [前端知识总结](https://github.com/fairyly/front-end-summary)\n- [冴羽博客](https://github.com/mqyqingfeng/Blog)\n\n\n## 1.实现一个 `storage`，对 `localstorage` 封装设置值 `setItem(key,value)` 和 `getItem(key)`\n```\n//封装操作localstorage本地存储的方法\n \nvar storage = {\n \n    //存储\n    set(key, value) {\n        localStorage.setItem(key, JSON.stringify(value));\n    },\n    //取出数据\n    get(key) {\n        return JSON.parse(localStorage.getItem(key));\n    },\n    // 删除数据\n     remove(key) {\n        localStorage.removeItem(key);\n    }\n \n}\n \n// 暴露给外部访问\nexport default storage;\n \n--------------------- \n作者：无悔_一叶扁舟 \n原文：https://blog.csdn.net/u011662320/article/details/80447882 \n\n## 增加数据有效期限制\nconst newStorage = {\n    storage: \"\",\n    length: 0,\n    limit: 1000,\n    init(t) {\n        this.storage = localStorage;\n        this.length = this.storage.length;\n        this.limit = t;\n        // 不够完善，考虑了 key 不在localstorage,还要考虑 key 已经存在的情况\n        for(var a in localStorage){           \n            newStorage[a] = localStorage[a];\n        }\n        this.setItem = (key,val) => {\n            localStorage.setItem(key,val);\n            if(t) setTimeout(()=>{localStorage.removeItem(key)},t) \n        }\n    },\n    setItem(key,val) {\n        this.storage.setItem(key,val);\n        let limit = this.limit;\n        if(limit) setTimeout(()=>{this.removeItem(key)},limit) \n    },\n    getItem(key) {\n        this.storage.getItem(key)\n    },\n    removeItem(key){\n        this.storage.removeItem(key)\n    },\n    clear() {\n        this.storage.clear();\n    }\n}\n--------------------- \n作者：Mr.Cool \n原文：https://blog.csdn.net/qq_39300332/article/details/79511572 \n```\n\n## 2.实现一个函数，输入的数组是2n项，则把这数组以2个为一组分割，并使每个分割后的数组的最小值相加最大，并输出。\n\n例如：【1，4，3，2】输出为4；\n\n【1，5，7，9，4，12】输出为15.\n\n>思路：把一个数组两个两个一块成一组，再在每个数组中找最小的数相加和是最大的，   \n即是要让每个小的数和只比它大一点的数一组    \n那么只要对输入的数组进行从小到大排序，之后分割就行  \n\n取数组中 第 1,3,5,7,9... 奇数号数 相加即可\n\n```\nfunction group(arr){\n    arr.sort();\n    return arr.reduce((c,n,i)=>{\n        if(i%2!=0) n=0;\n        return c+n\n    })\n}\n\ngroup([1,4,5,6])\n6\n\n- reduce 为数组中的每一个元素依次执行callback函数，不包括数组中被删除或从未被赋值的元素，接受四个参数：\n\naccumulator 累计器\ncurrentValue 当前值\ncurrentIndex 当前索引\narray 数组\n\n```\n- [MDN---reduce](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)\n\n- DEMO: 计算数组中每个元素出现的次数\n```\n\nvar names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];\n\nvar countedNames = names.reduce(function (allNames, name) { \n  if (name in allNames) {\n    allNames[name]++;\n  }\n  else {\n    allNames[name] = 1;\n  }\n  return allNames;\n}, {});\n// countedNames is:\n// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }\n```\n- DEMO: 将二维数组转化为一维\n\n```\n\nvar flattened = [[0, 1], [2, 3], [4, 5]].reduce(\n  function(a, b) {\n    return a.concat(b);\n  },\n  []\n);\n// flattened is [0, 1, 2, 3, 4, 5]\n```\n\n## 3.实现一个 promise\n>思路：    \n我们用的 `new Promise(function(resolve, reject) {})` 回调函数中有两个 参数 `resolve，reject`  \n1.要有一个状态参数 `state，pending, fulfilled,rejected`  \n2.一个 `then` 方法，一个 `resolve` 方法，一个 `reject` 方法  \n3.`then` 中根据状态判断执行不同的操作，需要一个成功时的回调  \n\n```\nfunction newPromise(fn) {\n  \n  let doneList = [];\n  let state = 'pending';\n  // 加入链式，成功回调的方法就得变成数组才能存储\n  this.then(function(done){\n    // 加入状态机制\n    switch(state) {\n      case 'pending':\n         doneList.push(done);\n         return this;\n         break;\n      case 'fulfilled':\n        done();\n        return this;\n        break;\n    }\n  })\n\n\n  function resolve(newVal) {\n    \n    state = 'fulfilled';\n    setTimeout( function{\n      let value = newVal;\n      // 加上异步结果的传递\n      //doneList.forEach(function(fulfill){\n      // value = fulfill(value)\n      //})\n      =====================\n      // 支持串行\n      //执行resolve时，我们会尝试将doneList数组中的值都执行一遍\n      //当遇到正常的回调函数的时候，就执行回调函数\n      //当遇到一个新的promise的时候，就将原doneList数组里的回调函数推入新的promise的doneList，以达到循环的目的\n      for (let i = 0;i<doneList.length;i++){\n        let temp = doneList[i](value)\n        if(temp instanceof newPromise){\n            let newP =  temp;\n            for(i++;i<doneList.length;i++){\n                newP.then(doneList[i]);\n            }\n        }else{\n            value = temp;\n        }\n      }\n     },0)\n  }\n  \n  // 加入reject\n  function reject(newValue){\n    state = \"rejected\";\n    setTimeout(function(){\n      var value = newValue;\n      var tempRe = failList[0](value);\n      //如果reject里面传入了一个promise，那么执行完此次的fail之后，将剩余的done和fail传入新的promise中\n      if(tempRe instanceof newPromise){\n        var newP = tempRe;\n        for(i=1;i<doneList.length;i++){\n            newP.then(doneList[i],failList[i]);\n        }\n      }else{\n        //如果不是promise，执行完当前的fail之后，继续执行doneList\n        value =  tempRe;\n        doneList.shift();\n        failList.shift();\n        resolve(value);\n      }\n    },0);\n  }\n  fn(resolve,reject)\n}\n```\n\n- [Promise简单实现（正常思路版）](https://www.jianshu.com/p/473cd754311f)\n\n\n\n## 4.实现一个 mvvm\n vue  原理解析：\n>1、实现一个数据监听器Observer，能够对数据对象的所有属性进行监听，如有变动可拿到最新值并通知订阅者   \n2、实现一个指令解析器Compile，对每个元素节点的指令进行扫描和解析，根据指令模板替换数据，以及绑定相应的更新函数   \n3、实现一个Watcher，作为连接Observer和Compile的桥梁，能够订阅并收到每个属性变动的通知，执行指令绑定的相应回调函数，从而更新视图  \n4、mvvm入口函数，\n\n![](https://github.com/DMQ/mvvm/blob/master/img/2.png)\n\n```\ninput id=\"input\"/>\n\nconst data = {};\nconst input = document.getElementById('input');\nObject.defineProperty(data, 'text', {\n  set(value) {\n    input.value = value;\n    this.value = value;\n  }\n});\ninput.onChange = function(e) {\n  data.text = e.target.value;\n}\n\n作者：麦乐丶\n链接：https://juejin.im/post/5befeb5051882511a8527dbe\n```\n\n- [剖析vue实现原理，自己动手实现mvvm](https://github.com/DMQ/mvvm)\n\n## 5.Event Loop\n\n![](https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1542445578551&di=e61315b4edf8e20d0fa77327d31d237e&imgtype=0&src=http%3A%2F%2Faliyunzixunbucket.oss-cn-beijing.aliyuncs.com%2Fpng%2F20180617192118792387.png)\n\n![](https://user-gold-cdn.xitu.io/2018/3/25/1625bc9652ff030e?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)\n```\n\nNode.js的运行机制如下：\n\n1.写的JavaScript脚本会交给V8引擎解析\n2.解析后的代码，调用Node API，Node会交给Libuv库处理\n3.Libuv库将不同的任务分配给不同的线程，形成一个Event Loop（事件循环），以异步的方式将任务的执行结果返回给V8引擎\n4.V8引擎再将结果返回给用户\n\n```\n\n\n## 6.canvas 图片为什么会跨域，绘制一个canvas,随机布着黑块，实现计算canvas上有多少黑块\n\n- 跨域问题： 同源策略\n\n```\nlet img = new Image()\n// 加入这句\nimg.setAttribute('crossOrigin', 'Anonymous')\n\n\n<img id=\"scream\" src=\"\" crossOrigin=\"anonymous\" alt=\"The Scream\" width=\"220\">\n```\n- 实现计算canvas上有多少黑块： 使用getImageData获取像素数组\n```\n\n```\n  - [](https://www.jianshu.com/p/f54d265f7aa4)\n\n\n\n## 7.webpack对项目的优化\n\n- [Vue SPA 项目webpack打包优化指南](https://juejin.im/post/5bd2b60e6fb9a05d27794c5e)\n\n\n## 8.事件流\n\n>- 冒泡型事件流：事件的传播是从最特定的事件目标到最不特定的事件目标。即从DOM树的叶子到根。  \n  即事件开始时由最具体的元素（文档中嵌套层次最深的那个节点）接收，然后逐级向上传播到较为不具体的节点（文档）。所有现代浏览器都支持事件冒泡，并且会将事件一直冒泡到window对象\n  \n- 捕获型事件流：事件的传播是从最不特定的事件目标到最特定的事件目标。即从DOM树的根到叶子。  \n事件捕获的思想就是不太具体的节点应该更早接收到事件，而最具体的节点最后接收到事件。\n\n\n- dom 事件流： 事件捕获，目标阶段 ，事件冒泡\n\n\n## 9.递归（数组全排列，）\n\n- Fibonacci数列第N项\n```\nvar fib = function (n){\n  if(n<=2){\n    return 1;\n  }\n  return fib(n-1) + fib(n-2);  \n}\nconsole.log(fib(5))\n```\n\n\n- 一共10级楼梯，每次可以走一步或两步，求一共多少种走法。\n```\n第0阶台阶：0\n\n第1阶台阶：1\n\n第2阶台阶：2（1+1或者2）\n\n得到公式，也就是斐波那契数列\n\nvar fib = function (n){\n  if(n == 1){\n    return 1;\n  }else if(n==2){\n    return 2;\n  }else if(n>2){\n    return fib(n-1) + fib(n-2);\n  }\n}\nconsole.log(fib(10));\n\n```\n\n- 1 个细胞，一个小时分裂一次，生命周期是3小时，求n小时后容器内，有多少细胞。\n```\n思路：\n\n细胞的生存周期是3个小时，那我们就可以把细胞在题目中状态分为以下几个状态：\n\na：刚分裂态——由前一小时的a,b,c分裂出\nb：分裂1小时态——由前一小时a长成\nc：分裂2小时态——由前一小时b长成\nd：分裂3小时态——死亡的细胞。由前一小时c长成,和之前的d一起组成。\n那么，我们就可以根据细胞状态设定函数。分析每一个状态的来源是哪里即可。\n\n\n容器中存活的细胞数目就是a、b、c三种状态数量的总和。\n\nvar afib = function (n){\n  if(n===0){return 1;} //初始的那个细胞\n  return afib(n-1)+bfib(n-1)+cfib(n-1);\n}\nvar bfib = function(n){\n  if(n===0){return 0;} //一个小时之后才会生成\n  return afib(n-1);\n}\nvar cfib = function(n){\n  if(n===0||n===1){return 0;} //前两小时还没生成\n  return bfib(n-1);\n}\n \nvar time = 3;\nconsole.log(afib(time)+bfib(time)+cfib(time))\n```\n\n\n## 虚拟 dom diff 算法\n\n>\n\n## 面试参考\n- [2018 百度、有赞、阿里面试总结](https://juejin.im/post/5befeb5051882511a8527dbe)\n\n\n\n## 面试中需要问的问题\n- 1.公司基本情况，公司做的什么产品，以后的发展方向\n- 2.公司技术团队情况，技术栈\n- 3.如果入职，我的主要工作是做什么？\n- 4.我们是如何团队协作的\n- 5.我们经常加班吗?\n- 6.问一下面试官对自己的评价或者看法\n- 7.对于团队成员的成长，您有哪些方案\n- 8.什么使你（面试官）选择加入这个公司？\n- 9.我们上班时间假期，公司的上班时间，上班地点，试用期情况，薪资发放时间\n- 10.我们的福利待遇（五险一金入职就交吗，其他补助）\n\n\n## 参考\n- [GitHub最全前端资源汇总](https://helloqingfeng.github.io/front-end-index/index.html)\n- [mdluo's blog](https://mdluo.com/)\n"
  },
  {
    "path": "6.0 你不知道的五险一金.md",
    "content": "# 6.0 你不知道的五险一金\n\n>以前好像不是特别关注五险一金，按多少比例缴纳的也不清楚，怎么算的好像也没注意，所以这块好像是很模糊的，最近面试的时候 HR 提到了就开始计算一下，仅供参考\n\n\n## 税前： 13000\n\n税前： 13000\n\n社保汇缴基数：13000\n公积金汇缴基数：13000\n\n\n|  1  | 个人应缴部分：|\t单位应缴部分：|\n|-- | -- | --  |\n|养老保险金：\t| 1040.00\t(8%)\t|  1820.00\t(14%)       |\n|医疗保险金：\t|260.00\t(2%)\t|1365.00\t(10.5%)        |\n|失业保险金：|\t65.00\t(0.5%)\t|65.00\t(0.5%)        |\n|基本住房公积金：\t|1560.00\t(12%) \t| 1560.00\t(12%) |\n|补充住房公积金：\t|0.00\t(0%)\t  |      0.00\t(0%)   |\n|工伤保险金：\t\t|\t0   |    26.00\t(0.2%)         |\n|生育保险金：\t\t|\t0 |    156.00\t(1.2%)         |\n|共计支出：\t   | 2925.00\t       |\t4992.00\t    |\n|扣除四金后月薪：|\t10075.00\t\t|  -- |\t\n|个人所得税：|\t297.50\t\t| 760.00(老税法)\t|\n|税后月薪：\t|  9777.50\t|\t9315.00(老税法)\t|\n\n\n\n## 税前： 16000\n\n税前： 16000\n\n社保汇缴基数：16000\n公积金汇缴基数：16000\n\n\n|  1  | 个人应缴部分：|\t单位应缴部分：|\n|-- | -- | --  |\n|养老保险金：\t| 1221.98\t(8%)\t|  2138.46\t(14%)       |\n|医疗保险金：\t|305.49\t(2%)\t  |   1603.85\t(10.5%)        |\n|失业保险金：\t| 76.37\t(0.5%)\t|76.37\t(0.5%)\n|基本住房公积金：\t| 1920.00\t(12%)\t|1920.00\t(12%)\n|补充住房公积金：\t| 0.00\t(0%)\t|0.00\t(0%)\n|工伤保险金：\t\t\t| 0 |30.55\t(0.2%)\n|生育保险金：\t\t\t|0 |183.30\t(1.2%)\n|共计支出：\t| 3523.84\t\t|5952.53\t\n|扣除四金后月薪：\t| 12476.16\t|\t\t\n|个人所得税：\t| 537.62\t\t|1240.23(老税法)\t\n|税后月薪：\t| 11938.54\t|\t11235.93(老税法)\t\n\n\n## 参考\n- []()\n"
  },
  {
    "path": "6.0 双向数据绑定原理.md",
    "content": "# 双向数据绑定原理\n\n* 脏值检测  \nAngularJS 采用“脏值检测”的方式，数据发生变更后，对于所有的数据和视图的绑定关系进行一次检测，识别是否有数据发生了改变，有变化进行处理，  \n可能进一步引发其他数据的改变，所以这个过程可能会循环几次，一直到不再有数据变化发生后，将变更的数据发送到视图，更新页面展现。  \n如果是手动对 ViewModel 的数据进行变更，为确保变更同步到视图，需要手动触发一次“脏值检测”。  \n\n\n* 数据劫持  \nVueJS 则使用 ES5 提供的 Object.defineProperty() 方法，监控对数据的操作，从而可以自动触发数据同步。  \n并且，由于是在不同的数据上触发同步，可以精确的将变更发送给绑定的视图，而不是对所有的数据都执行一次检测。\n```\nvar obj = {};  \n            Object.defineProperty(obj,'a',{  \n                set:function(newVal){  \n                    document.getElementById('a').value = newVal;  \n                    document.getElementById('b').innerHTML = newVal;  \n                }  \n            });  \n              \n            document.addEventListener('keyup',function(e){  \n                obj.a = e.target.value;  \n            });  \n```\n\n* 观察机制  \n博主之前有一篇转载翻译的文章， Object.observe()带来的数据绑定变革 ，说的就是使用ECMAScript7中的 Object.observe 方法对对象    \n（或者其属性）进行监控观察，一旦其发生变化时，将会执行相应的handler。    \n这是目前监控属性数据变更最完美的一种方法，语言（浏览器）原生支持，没有什么比这个更好了。唯一的遗憾就是目前支持广度还不行，有待全面推广。   \n\n\n"
  },
  {
    "path": "6.0.1 如何实现增量部署发布.md",
    "content": "# 6.0.1 如何实现增量部署发布\n\n\n>痛点：每次发布更新项目的时候常常需要等到半夜凌晨几点更新，因为白天怕影响用户使用，那么怎么做到发布的时候还不影响用户的使用呢;\n从面试中了解到，可以使用增量发布，\n\n\n\n\n\n## 参考\n- [diff ](https://github.com/starkwang/Incremental/blob/master/index.js)\n\n"
  },
  {
    "path": "README.md",
    "content": "# front-end-summary\n\n* https://github.com/markyun/My-blog/tree/master/Front-end-Developer-Questions/Question\n\n* https://github.com/h5bp/Front-end-Developer-Interview-Questions\n\n* ApacheCN: https://github.com/apachecn\n\n\n（注：:+1: 表示推荐，:+1::+1: 表示强烈推荐，:x: 表示差评）\n\n<details>\n  \n  - [License](#license)\n  \n</details>\n\n## markdown 索引\n\n- [独行](#独行)\n- [传记](#传记)\n\n\n## 独行\n   &emsp;&emsp;也不知道什么时候开始，爬山，去图书馆成了每周的习惯；**宝石山**，**浙江图书馆**；**老和山**，**将军山**，**灵隐山**，不知怎么的就到了**北高峰**，**天下第一财神庙** ， **浙江图书馆**； \n\n   &emsp;&emsp;万事开头难，每次爬山时最开始的时候总觉得晕晕乎乎的，爬了一段就感觉突然有劲了，不在觉得很难很累，走起路来都是大步前进，山路也不是一直平坦，有时上坡，有时下坡，有时是平坦大道，有时是凹凸不平的石道，在爬山的路上，你也会遇到各种各样的人，有的也是和我一样独行，有的一家三口，有的一对对情侣，也有的是老夫老妻，也会经常见到什么登山队，什么公司团队，也会经常看到有老外等，有的在向北高峰赶，有的在归来，走了几次发现到达的时间有所提升，原本一个半小时的路程，今天过去用了一个小时，本来今天没打算爬到全程的，想着去的时候已经是两点了，回来不想赶夜路，也不知道怎么就来了兴致，就决心一定爬完全程再回来；\n\n   &emsp;&emsp;突然觉得爬山的路就是人生的路，不可能一直上坡，也不会一直是平坦大道，总会遇到坎坎坷坷的小道，最开始的时候也是最困难的时候，爬过去了也没什么了，人生的起点也可能是终点，终点也是起点   \n   \n   &emsp;&emsp;\"总的来看，下一代青年不太可能像上一代有那么多机会。经济增长已经开始放缓，还将继续放缓；人口增长高峰已经过去；除了高科技，几乎所有行业都不会有以前那么高的增长率。  \n    &emsp;&emsp;现在是穷人，未来极大可能还是穷人，能翻身的只能是少数，而且难度越来越大。\"  \n    &emsp;&emsp;\"没有学历、技术的人，为了活下去，不是住笼屋就是要工作到半夜，对于他们，最重要事情是下一顿吃什么，怎么会有时间和精力去思考未来怎么发展？来来去去都在死胡同！\"  \n     &emsp;&emsp;\"穷忙族是指那些薪水不多，整日奔波劳动，却始终无法摆脱贫穷的人。最早出现于上世纪90年代的美国，指拼命工作仍然无法摆脱最低水准生活的人们。日本经济学家门仓贵史在《穷忙族》一书中，他对\"穷忙族\"下的定义是：每天繁忙地工作却依然不能过上富裕生活的人。\"\n     \n\n## 传记\n  &emsp;&emsp;不要像驴一样的生活  \n  &emsp;&emsp;经常爬北高峰的人路上都会遇到一群的驴或者马，有人说是骡子，每次看到的都是这些驴驮着沙子从上下爬到山上，也有些时候驮着很大的砖块，每群驴都有一个人看着，有时候看到有的驴爬到一半不走了，或许是太累了吧，无数次爬山，看到北高峰的台阶已经被踏的碎石满台阶，这些台阶被驴走过了无数次，回头想想这些驴真的可伶，一天到晚都在驮着沙拖着石头被人们使唤，如果人们不去修什么庙，建什么东西，这些驴也不会那么累，没有买卖真的就没有伤害，不过仔细一想，驴的一生又何尝不是人的一生，我们上班听从老板的使唤，老板让加班我们就得加班，可我们又想想我们为什么加班，我们做的到底有没有意义呢，看到阮一峰的未来世界的幸存者，感觉很贴近现实，我们每天一直忙碌，有没有停下了思考一下，我们忙碌的有没有意义，或许有人说我们要去生存，我们要去挣钱，这些是没错的；  \n\n  &emsp;&emsp;关键我们要明白，我们真正需要的是什么，不能别人怎么做，自己也跟着做，就像驴群中最前面的驴，一直背着东西前行，后面的驴也都跟着爬，人生不过百年，过的是生活，不能成为金钱的奴隶，一辈子都为了钱忙碌，那样生活也就没什么意思了；  \n\n  &emsp;&emsp;做自己喜欢做的事，忙碌的时候抬头看看天空，自己到底需要什么，问问自己最想过什么样的生活！\n\n\n\n"
  },
  {
    "path": "html5与css3权威指南学习笔记.md",
    "content": "# html5与css3权威指南学习笔记\n\n* 访问原始二进制数据\n  - new ArrayBuffer(32)\n  \n* blob 对象\n  - new Blob([]) //第一个参数是个数组，第二个参数 {type: \"text/plain:charset=UTF-8\"}\n  - URL.createObjectURL(blob)\n"
  },
  {
    "path": "你不知道的JavaScript(上).md",
    "content": "# 你不知道的JavaScript(上)\n\n\n## 作用域\n\n>编译原理：\n- 词法分析：把字符串分解成有意义的代码块（词法单元）；\n- 语法分析：将词法单元流转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树，就是抽象语法树（AST）\n- 代码生成：将AST转换成可执行代码的过程\n\n\n### 引擎： 从头到尾负责整个js程序的编译和执行过程\n### 编译器： 负责语法分析和代码生成；\n### 作用域：负责收集和维护由所有声明的标识符（变量）组成的一系列查询，确定当前执行的代码对这些标识符的访问权限；\n\n\n### LHS查询：赋值操作\n### RHS查询： 取到它的源值，\n"
  },
  {
    "path": "你不知道的JavaScript(下).md",
    "content": "# 你不知道的JavaScript(下)\n\n\n## 1\n\n## 1.1\n>语句有一个或多个表达式组成；\n\n- 表达式： 如：a = b * 2;\n  - 1.字面表达式： 2 就是\n  - 2.变量表达式： b 就是\n  - 3.算术表达式： b * 2 就是\n  - 4.赋值表达式：a = b * 2;\n\n\n\n## 参考\n- [你不知道的JavaScript(下)]()\n"
  }
]