-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsearch.json
1 lines (1 loc) · 255 KB
/
search.json
1
[{"title":"git操作-合并和暂存篇","url":"/2019/12/24/git操作-1/","content":"\n因为业务需求,有个工程开辟很多分支导致每个分支的开发进度不一致。有时候需要在自己的分支上合并其他分支的代码,某个文件夹或者具体到某次提交。\n\ngit的操作先从功能展示,在深入其原理\n\n\n## 1 合并\n\n场景: 把B分支的代码合并到A分支,当前在A分支\n\n### 1.1 git merge:合并整个分支 \n\ngit merge 可以合并代码\n\n#### 1.1.1 fastforward模式\n\n这种模式下,删除分支后,会丢掉分支信息。(简言而知在A上看不到B上commit信息都没有)\n\n``` javascript\ngit merge B\n\n// 没有冲突\ngit push origin A\n\n//有冲突,解决完之后\nGit add .\nGit commit -m’fix 冲突’\ngit push origin A\n\n```\n\n\n用git log --graph命令可以看到分支合并图\n\n#### 1.1.2 --no-ff模式\n\n如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。(可以在A上查看到B的commit信息)\n\n``` javascript\ngit merge --no-ff -m \"merge with no-ff\" B\n\n```\n因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。\n合并后,我们用git log看看分支历史:\n\n\n### 1.2 git cherry-pick :合并某次提交\n\n合并某次提交\n\n``` javascript\ngit cherry-pick \n例如\ngit cherry-pick 4c805e2\n\n```\n\n合并某次提交记录\n\n\n### 1.3 git checkout: 合并某些文件或文件夹\n\n合并某些文件或文件夹\n\n``` javascript\ngit checkout B 某些文件 某些目录/**\n\n```\n\n注意:上面这个并不好,应该改成\n\n``` javascript\ngit checkout -b A_temp\n//当前分支A_temp\ngit checkout B `某些文件` `某些目录/**`\n// 注意目录后面的两个*表示深层拷贝\n\ngit checkout A\ngit merge A_temp\n\n```\n\n原因:使用checkout会把B分支的代码原原本本合并到A_temp,哪怕A_temp也有改动代码\n\n\n\n## 2 暂存代码\n\n\n场景:比如在develop开发着新功能,突然急需解决其他分支如master的代码的bug\n\n解决方案:暂存develop代码,切到其他分支,\n\n```javascript\n// 当前分支develop \ngit stash\ngit status //本次操作无意义\n//此时 git status 肯定是空的\ngit checkout master\nGit pull\nGit checkout -b bugfix_master\n// 修改完\ngit add .\nGit commit -m’bugfix’\nGit checkout master\nGit merge —no-diff -m \"merged bug fix \" bugfix_master\n\n```\n\n回到develop,怎么取出\n\n``` javascript\n\ngit stash list\n\ngit stash pop\n\n```\n一是用`git stash apply`恢复,但是恢复后,stash内容并不删除,你需要用`git stash drop`来删除;\n另一种方式是用`git stash pop`,恢复的同时把stash内容也删了:\n\n你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:\n\n```javascript\n\n$ git stash apply stash@{0}\n\n```\n\n\n## 3 git push 前\n\ngit push操作顺利推到远程没什么好说的,如果该分支线上有人提前提交了代码,git push应该会失败,需要先git pull.\n\n此时 你确定直接 git pull \n\n\n\n","tags":["git"]},{"title":"Less(2)-colorless生成","url":"/2019/12/24/Less(2)-colorless生成/","content":"\n前面讲了在线修改样式调用的方法是window.less.modifyVars,这边讲一下color.less的生成。\n下面的代码来源于我的项目组\n\n## 1 color.less\n\n下面展示一下实际项目中现在使用的less的是什么样子的\n\n```less\n\n@primary-color: #F53C32;\n@primary-color-dark: #E60012;\n@primary-color-light: #E60012;\n@secondary-color: #E0E0E0;\n@secondary-color-dark: #BDBDBD;\n@secondary-color-light: #BDBDBD;\n@text-color-base: #FFFFF1;\n@border-radius: 3px;\n@border-color: #A5ADBA;\n@button-secondary-text-color: #212121;\n@item-hover-bg-color-base: #EBECF0;\n@item-selected-bg-color-base: #F7F7F7;\n@table-header-background-color: #f7f7f7;\n@table-header-text-color: #666666;\n@table-border-color-base: #e9e9e9;\n@table-row-hover-bg-color: #ebecf0;\n//vars.less\n\n@palette-grey-100: \t#F5F5F5;\n@palette-grey-200: \t#EEEEEE;\n@palette-grey-300: #E0E0E0;\n@palette-grey-400: #BDBDBD ;\n@palette-grey-900: #212121;\n\n@palette-cyan-500: #00BCD4;\n\n@color-black: #000001;\n@color-white: #FFFFF1;\n\n//colors.scss\n@styleguide-generate-template: false ;\n\n// The two possible colors for overlayed text.\n@color-dark-contrast: @color-white ;\n@color-light-contrast: @color-black ;\n\n// 主题色\n\n\n\n\n// 默认色\n@default-color: @palette-grey-300;\n@default-color-dark: @palette-grey-400;\n@default-color-light: @palette-grey-200;\n\n// 字体\n@font-family-primary: \"Open Sans\", \"Helvetica Neue\", Arial, \"Hiragino Sans GB\", \"Microsoft YaHei\", sans-serif ;\n// 主字号\n@font-size-base: 14px ;\n// 主文本色\n \n\n// 圆角,包括:button、select等\n\n// 边框色,包括按钮、输入框、分页\n\n// 条目hover背景色,包括:select、dropdown、table、datepicker、tree、menu、calendar\n\n// 条目selected背景色,包括:select、menu等\n\n// Button 细化样式变量:\n// 次按钮背景色\n\n// 次按钮文本色\n\n\n// Table 细化样式变量:\n// 表头背景色\n\n// 表头文字颜色\n\n// 表格分割线颜色\n\n// 表格行hover背景色\n\n@unit: 10px ;\n\n@gray-light: @palette-grey-400 ;\n@gray-lighter: @palette-grey-300 ;\n@gray-lightest: @palette-grey-200 ;\n\n// 边框圆角\n@border-radius-base: @border-radius;\n\n//-- Indexes\n@zindex-menubar: 1400;\n\n// hover时的背景色,包括select、dropdown、table、datepicker、tree、menu等组件\n@hover-bg-color-base: @item-hover-bg-color-base ;\n// // selected背景色,包括:select、menu等\n@selected-bg-color-base: @item-selected-bg-color-base ;\n\n// 品牌色\n@brand-default: @gray-lighter;\n@brand-default-hover: @gray-lightest;\n@brand-default-active: @gray-light;\n\n@brand-primary : @primary-color ;\n@brand-primary-hover: @primary-color-light ;\n@brand-primary-active: @primary-color-dark ;\n\n@brand-secondary : @secondary-color ;\n@brand-secondary-hover: @secondary-color-light ;\n@brand-secondary-active: @secondary-color-dark ;\n\n@brand-info: @palette-cyan-500 ;\n\n@brand-light : @color-dark-contrast ;\n\n//不同背景下对应的文字颜色\n@color-info: @palette-cyan-500 ;\n\n//全局不同状态颜色\n\n//disable颜色\n@disabled-color-base: @gray-light;\n@disabled-border-color: @gray-lighter;\n@disabled-bg-color: @gray-lightest;\n\n@border-color-base: @border-color ;\n\n// UButton\n\n// Button 基础背景色.\n// 默认按钮(<Button></Button>)\n@button-default-color: @default-color;\n@button-default-color-IE8: @default-color;\n\n// Button 不同状态下的背景色 :hover、active、focus状态.\n@button-hover-color: @default-color-light ;\n@button-active-color: @default-color-dark ;\n@button-focus-color: @default-color-light;\n\n// Button 配置不同colors属性时的背景色.\n@button-primary-color: @brand-primary;\n@button-primary-active-color: @brand-primary-active;\n@button-primary-hover-color: @brand-primary-hover;\n@button-secondary-color: @brand-secondary;\n@button-secondary-active-color: @brand-secondary-active;\n@button-secondary-hover-color: @brand-secondary-hover;\n// Button 文字颜色.\n// 主按钮(colors:'primary')\n@button-primary-text-color: @text-color-base;\n@button-text-color: @button-primary-text-color;\n// 次按钮(colors:'secondary')\n@button-second-text-color: @button-secondary-text-color;\n// 默认按钮(<Button></Button>)\n@button-default-text-color: @palette-grey-900;\n\n// Button 边框样式及颜色.\n@button-border-style: solid;\n@button-border-color: @border-color-base;\n@button-default-border-color: @button-default-color ;\n\n// Button 不同状态下的边框颜色 :hover、active、focus状态.\n@button-hover-border-color: @brand-default-hover;\n@button-active-border-color: @brand-default-active;\n@button-focus-border-color: @brand-default-active;\n\n// Button 最小宽度、高度、内边距、外边距、行高、边框粗细、圆角.\n@button-border-width: 1/10 * @unit ;\n\n// UText\n\n@form-control-border-radius: @border-radius-base;\n@form-control-color: #424242;\n@form-control-bg-color: #fff;\n@form-control-border-color: @border-color-base;\n\n//progresbar\n@progress-primary-bg: @brand-primary;\n\n//timeline\n@timeline-info-color: @color-info;\n\n// bee-tree\n@tree-checkbox-color: @primary-color ;\n@tree-node-bg-color: @hover-bg-color-base ;\n\n//dropdown\n\n@dropdown-item-hover-bg-color: @palette-grey-100 ;\n@dropdown-item-divier-bg-color: @gray-lighter;\n@dropdown-border-color: @border-color-base;\n@dropdown-border-radius:3px;\n\n//select\n@select-bg-color: #fff;\n@select-border-color: @border-color-base;\n@select-border-radius: @border-radius-base;\n\n//switch\n//color\n@switch-border-color: @gray-lighter;\n@switch-back-color: @gray-lighter;\n@switch-checked-borColor: @brand-primary;\n@switch-checked-backColor: @brand-primary;\n\n// border-radius of different state switch\n@switch-border-radius: 20px;\n@switch-border-radius-after: 18px;\n\n@switch-primary-bg: @brand-primary;\n\n//rate\n@rate-star-default-color: @gray-lightest;\n @rate-star-active-color: @brand-primary;\n\n//Checkbox\n\n@checkbox-color: @primary-color ;\n\n@checkbox-primary-bg: @brand-primary;\n@checkbox-info-bg: @brand-info;\n\n//progressbar\n@progress-primary-bg: @brand-primary;\n\n// Radio\n\n@radio-color: @primary-color ;\n\n@radio-primary-bg: @brand-primary;\n\n@radio-border-color: #d9d9d9;\n@radio-bg-color: #fff;\n@radio-color: rgba(0, 0, 0, 0.65);\n\n@radio-checked-bg-color: #fff;\n@radio-checked-color: @brand-primary;\n@radio-checked-border-color: @brand-primary;\n\n//select\n\n@select-dropdown-selected-bg: @selected-bg-color-base;\n\n//下面是对单个组件样式\n//bage\n.customed .u-badge-primary.u-badge .badge-single,\n.customed .u-badge-primary.u-badge .data-icon {\n background: @primary-color;\n}\n.customed .u-breadcrumb a {\n color: @primary-color;\n}\n.customed .u-breadcrumb li {\n color: @primary-color;\n}\n...省略\n//button\n.customed .u-button {\n background: @secondary-color;\n border: 1px solid @secondary-color;\n border-radius: @border-radius;\n color: @button-secondary-text-color;\n}\n.customed .u-button:hover {\n background-color: #EEEEEE;\n border-color: #EEEEEE;\n}\n\n\n```\n\n\ncolor.less的难点在于需要将整个组件库(50多个组件)的样式放到这里,而且组件库使用的scss的语法,所以需要这部分的工程量较大。后续可能会上传如何从scss生成less\n\n","tags":["less"]},{"title":"javascript书籍推荐1","url":"/2019/09/22/javascript书籍推荐1/","content":"\n有些书籍看了才知道是真的好\n\n\n## 1 目录\n\n\n### 1.1 你不知道的javascript 上中下\n\n上卷第一章就是讲的编译原理,这简直超级有用。不同于的别的书籍,一上来就是数据类型\n\n上卷,只涉及作用域和闭包,this和对象原型两部分。\n\n不过其深入程度是其他js书籍难以企及的。\n\n可以说,这已经是大部分前端程序员对js语法可以深入了解的最底层了,再往下就直面编译原理了。\n\n> 阅读建议:\n用于提升自己的js语法的理解等级。\n对于进入工作第二年的想深入理解js语法的同学来说特别有效。\n\n### 1.2 JavsScript高级程序设计\n\n无论何时,这都是学习js最好的书。\n\n**如果说其他的书都是在合适的阶段会有不一般的体验。**\n\n这本书就是无论什么阶段,都够你感悟一番的。\n\n当你看完了市场上js相关(不包括框架类库的最佳实践类)的所有书籍。**你再回去看这本书,也会发现其实95%的内容早就写在这本书里了,只不过你当时等级不够,根本没有意识到。**\n\n> 阅读建议:\n人们喜欢把他和犀牛书拿一起推荐,不过就我看来,他们完全不是一个可读性上的。\n一个是课本一个是字典。而且还是佶屈聱牙的字典。\n所以无论何时我都会把高程三排在js推荐书籍的第一位。\n\n### 1.3 JavaScript设计模式与开发实践\n我认为腾讯AlloyTeam的这本书对我帮助媲美高程三。\n\n那时候我恰好换公司,从原来的开发方式切换成另一种完全不同的开发方式,让我很不适应。\n\n这本书及时的蹦出来,他拿实际的应用场景举例,告诉你不同js项目里如何共通设计模式,还极其详细的介绍了函数的高级用法,能让你对js高阶函数的认知上升一个台阶。\n\n难能可贵的是他还拿java作为静态语言的类比。从语言统一高度来给你分析这些问题。\n\n和上本介绍数据结构的书一样,这本书有的放矢,能完美的解决对js设计模式有疑问的同学的实际问题。\n\n他比上本书更深刻,更易读,也更贴近实践。\n\n> 阅读建议:\n> 这是本适合反复阅读的书籍。\n> 如果你的思路能根据应用场景自动切换到最适合的设计模式,说明你已吃透这本书了。\n\n\n","tags":["javascript"]},{"title":"前端模块化-总结Commonjs和AMD和CMD","url":"/2019/08/19/前端模块化-总结Commonjs和AMD和CMD/","content":"\n主要代码粘贴,说那么多语法不如直接看使用\n\n\n\n## 1 Commonjs\n\n\n```javascript\n// 定义模块math.js\nvar basicNum = 0;\nfunction add(a, b) {\n return a + b;\n}\nmodule.exports = { //在这里写上需要向外暴露的函数、变量\n add: add,\n basicNum: basicNum\n}\n\n// 引用自定义的模块时,参数包含路径,可省略.js\nvar math = require('./math');\nmath.add(2, 5);\n\n// 引用核心模块时,不需要带路径\nvar http = require('http');\nhttp.createService(...).listen(3000);\n\n\n```\n\n\n## 2 AMD 和requirejs \n\n\n\n```javascript\n\n/** 网页中引入require.js及main.js **/\n<script src=\"js/require.js\" data-main=\"js/main\"></script>\n\n/** main.js 入口文件/主模块 **/\n// 首先用config()指定各模块路径和引用名\nrequire.config({\n baseUrl: \"js/lib\",\n paths: {\n \"jquery\": \"jquery.min\", //实际路径为js/lib/jquery.min.js\n \"underscore\": \"underscore.min\",\n }\n});\n// 执行基本操作\nrequire([\"jquery\",\"underscore\"],function($,_){\n // some code here\n});\n\n\n\n```\n\n\n\n## 3 CMD和seajs\n\n\n```javascript \n\n/** AMD写法 **/\ndefine([\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"], function(a, b, c, d, e, f) { \n // 等于在最前面声明并初始化了要用到的所有模块\n a.doSomething();\n if (false) {\n // 即便没用到某个模块 b,但 b 还是提前执行了\n b.doSomething()\n } \n});\n\n/** CMD写法 **/\ndefine(function(require, exports, module) {\n var a = require('./a'); //在需要时申明\n a.doSomething();\n if (false) {\n var b = require('./b');\n b.doSomething();\n }\n});\n\n/** sea.js **/\n// 定义模块 math.js\ndefine(function(require, exports, module) {\n var $ = require('jquery.js');\n var add = function(a,b){\n return a+b;\n }\n exports.add = add;\n});\n// 加载模块\nseajs.use(['math.js'], function(math){\n var sum = math.add(1+2);\n});\n复制代码\n\n\n```\n\n\n","tags":["javascript"]},{"title":"JS作用域系列-apply/bind/call","url":"/2019/07/21/JS作用域系列-apply-bind-call/","content":"\n之前对于这块非常的模糊,经常看到但是不是很理解,百度完了看完之后就忘记了。结果现在做的项目中处处用到这几个方法,原因是因为现在的项目嵌套层级太深了,希望使用这几个方法,能够让几个文件共享变量。\n\n<!--more-->\n\n\n# 1 摘要\n我们都知道`call` `apply` `bind`都可以改变函数调用的this指向。\n\n## 1.1 apply语法\n```javascript\nfun.apply(thisArg, [argsArray])\n```\n`thisArg` 在 `fun` 函数运行时指定的 this 值。需要注意的是,指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 `null` 或 `undefined` 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。\n\n`argsArray` 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。\n\n## 1.2 call语法\n```javascript\nfun.call(thisArg, arg1, arg2, ...)\n\n```\n`thisArg`: 在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。\n\n`arg1`, `arg2`, ... 指定的参数列表\n\n\n## 1.3 bind语法\n```javascript\nfun.bind(thisArg[, arg1[, arg2[, ...]]])\n\n```\n`thisArg` 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。\n\n`arg1`, `arg2`, `...`当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。\n\n\n\n# 2 总结\n\n\n- `call`跟`apply`的用法几乎一样,唯一的不同就是传递的参数不同,\n\n - `call`只能一个参数一个参数的传入。\n\n - `apply`则只支持传入一个数组,哪怕是一个参数也要是数组形式。最终调用函数时候这个数组会拆成一个个参数分别传入。\n\n- bind方法,他是直接改变这个函数的this指向并且返回一个新的函数,之后再次调用这个函数的时候this都是指向bind绑定的第一个参数。bind传餐方式跟call方法一致。\n\n\n\n>1当我们使用一个函数需要改变this指向的时候才会用到`call`,`apply`,`bind`\n>\n>2如果你要传递的参数不多,则可以使用fn.call(thisObj, arg1, arg2 ...)\n>\n>3如果你要传递的参数很多,则可以用数组将参数整理好调用fn.apply(thisObj, [arg1, arg2 ...])\n>\n>4如果你想生成一个新的函数长期绑定某个函数给某个对象使用,则可以使用const newFn = fn.bind(thisObj); newFn(arg1, arg2...)\n\n\n# 3 小技巧\n\n## 3.1 apply的小技巧\n```javascript\n// 如果一个数组我们已知里面全都是数字,想要知道最大的那个数,由于Array没有max方法,Math对象上有\n// 我们可以根据apply传递参数的特性将这个数组当成参数传入\n// 最终Math.max函数调用的时候会将apply的数组里面的参数一个一个传入,恰好符合Math.max的参数传递方式\n// 这样变相的实现了数组的max方法。min方法也同理\nconst arr = [1,2,3,4,5,6]\nconst max = Math.max.apply(null, arr)\nconsole.log(max) // 6\n\n\n```\n\n\n## 3.2 bind的小技巧\n\n```javascript\n// 如果你想将某个函数绑定新的`this`指向并且固定先传入几个变量可以在绑定的时候就传入,之后调用新函数传入的参数都会排在之后\nconst obj = {}\nfunction test(...args) {console.log(args)}\nconst newFn = test.bind(obj, '静态参数1', '静态参数2')\nnewFn('动态参数3', '动态参数4')\n\n```\n\n输出\n\n```javascript\n(4) [\"静态参数1\", \"静态参数2\", \"动态参数3\", \"动态参数4\"]\n```\n\n\n# 4 模拟实现 call 和 apply\n\n```javascript\n\nlet a = {\n value: 1\n}\nfunction getValue(name, age) {\n console.log(name)\n console.log(age)\n console.log(this.value)\n}\ngetValue.call(a, 'yck', '24')\ngetValue.apply(a, ['yck', '24'])\n\n\n```\n\n输出\n\n```javascript\n\nyck\n24\n1\nyck\n24\n1\n```\n\n\n目的是为了更能理解\n\n可以从以下几点来考虑如何实现\n\n- 不传入第一个参数,那么默认为 window\n- 改变了 this 指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?\n\n## 4.1 模拟实现 call \n\n\n```javascript\n\nFunction.prototype.myCall = function (context) {\n var context = context || window\n // 给 context 添加一个属性\n // getValue.call(a, 'yck', '24') => a.fn = getValue\n context.fn = this //ps 请注意这个this是调用myCall的函数指定的this,如上面的demo this应该指定的是getValue(.myCall(a,...))\n // 将 context 后面的参数取出来\n var args = [...arguments].slice(1)//除了第一个\n // getValue.call(a, 'yck', '24') => a.fn('yck', '24')\n var result = context.fn(...args)\n // 删除 fn\n delete context.fn\n return result\n}\n\n\n```\n## 4.2 模拟实现 apply\n\n\n\n```javascript\n\n\nFunction.prototype.myApply = function (context) {\n var context = context || window\n context.fn = this\n\n var result\n // 需要判断是否存储第二个参数\n // 如果存在,就将第二个参数展开\n if (arguments[1]) {\n result = context.fn(...arguments[1])\n } else {\n result = context.fn()\n }\n\n delete context.fn\n return result\n}\n\n```\n\n## 4.3 模拟实现bind\n\n\n\n```javascript\nFunction.prototype.myBind = function (context) {\n if (typeof this !== 'function') {\n throw new TypeError('Error')\n }\n var _this = this\n var args = [...arguments].slice(1)\n // 返回一个函数\n return function F() {\n // 因为返回了一个函数,我们可以 new F(),所以需要判断\n if (this instanceof F) {\n return new _this(...args, ...arguments) //超级注意ps这里解释上面bind的小技巧\n }\n return _this.apply(context, args.concat(...arguments))\n }\n}\n\n```\n\n\n\n\n# 5 参考文献\n\n[文章1](https://yuchengkai.cn/docs/frontend/#%E6%A8%A1%E6%8B%9F%E5%AE%9E%E7%8E%B0-call-%E5%92%8C-apply)","tags":["javascript"],"categories":["总结"]},{"title":"vscode-快捷键集合","url":"/2019/07/21/vscode-快捷键集合/","content":"\n\n目前开发前端使用的工具是vscode,虽然网上有很多的文章介绍快捷键但是实际上自己用到的就那么几个,记录下来就不用去文章搜索啦\n<!--more-->\n\n## 1 vscode打开快捷命令的面板\n\n常用的快捷键搜索面板,`commond+shif+p`。\n\n\n## 2 折叠\n\n最近看别人的代码,有个代码4千行,希望折叠看整体的结构。\n\n### 2.1 fold\n(1)方法1\n\n`command+k` + `command+1` = 折叠一级;\n这个命令只保留每个函数的最外层大括号。\n> 提示,光标放到最上层才会有效,不然只会折叠光标所在的那行函数\n\n`command+k` + `command+2` = 折叠二级;\n\n(2)方法2\n\n在上面1所打开的命令搜索面板中,搜索`fold`就会看到快捷键的命令是什么了\n\n\n### 2.2 unfold\n(1)方法1\n\n`command+k` + `command+]` = 打开全部代码\n\n\n(2)方法2\n\n在上面1所打开的命令搜索面板中,搜索`unfold`就会看到快捷键的命令是什么了","tags":["vscode"]},{"title":"事件循环-浏览器","url":"/2019/07/18/事件循环-浏览器/","content":"\n浏览器的事件循环机制可以理解成js的事件循环机制,本篇博客只是简单地介绍一下整个事件循环的机制,具体的原因之后的会详细的讲\n\n<!--more-->\n\n# 1 操作系统的概念\n\n这里有几个操作系统的概念,异步 并行 并发等介绍一下\n\n ## 1.1 异步 vs 并行\n术语“异步”和“并行”常常被混为一谈,但实际上它们的意义完全不同。记住,\n\n异步是关于现在和将来的时间间隙,\n\n并行是关于能够同时发生的事情。 \n\n区别很大,异步和并行会导致运行结果不一致,但是程度上不一样,异步的这种不确定性是在函数(事 件)顺序级别上,多线程(并行)情况下的不确定性是语句顺序级别(或者说,表达式运算顺序级别)。 换句话说,这异步确定性要高于多线程(并行)情况。 \n\n ## 1.2 并发\n现在让我们来设想一个展示状态更新列表(比如社交网络新闻种子)的网站,其随着用户向下滚动列表而逐渐加载更多内容。要正确地实现这一特性,需要(至少)两个独立的 “进程”同时运行(也就是说,是在同一段时间内,并不需要在同一时刻)。 \n\n>注意\n这里的“进程”之所以打上引号,是因为这并不是计算机科学意义上的真正 操作系统级进程。这是虚拟进程,或者任务,表示一个逻辑上相关的运算序 列。 \n\n# 2 事件循环event loop\n\n## 2.1 概述\n\nJavaScript是单线程(只有一个主线程)的语言\n\nEvent Loop是JavaScript的执行机制\n\nJavaScript的任务分为同步任务和异步任务。异步任务分为宏任务和微任务\n\n## 2.2 常见的概念:\n\n- 堆和栈。当javascript代码执行的时候会将不同的变量存于内存中的不同位置:堆(heap)和栈(stack)中来加以区分。其中,堆里存放着一些对象。而栈中则存放着一些基础类型变量以及对象的指针。\n \n- 执行栈。但是我们这里说的执行栈和上面这个栈的意义却有些不同。当一个脚本第一次被执行的时候,js引擎会解析这段代码,并将其中的**同步代码按照执行顺序加入执行栈中**,然后从头开始执行。如果当前执行的是一个方法,那么js会向执行栈中**添加**这个方法的执行环境,**然后进入**这个执行环境继续执行其中的代码。当这个执行环境中的代码 **执行完毕**并返回结果后,js会**退出这个执行环境**并把这个执行环境销毁,**回到上一个方法的执行环境**。这个过程反复进行,直到执行栈中的代码全部执行完毕。\n \n- 同步任务(synchronous):**指在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。**\n\n- 异步任务(asynchronous):指不进入主线程、而进入\"任务队列\"的任务\n\n- 宏任务(macrotask):包括setTimeOut 、 setInterval 、setImmediate 、 I/O 、 各种callback、UI渲染等\n> 优先级如下:主代码块 > setImmediate >MessageChannel > setTimeOut/setInterval\n\n- 微任务(microtask):包括process.nextTick 、Promise、MutationObserver 、async(实质上也是promise)\n> 优先级如下:process.nextTick > Promise >MutationOberser\n\n- 任务队列(task queue/callback queue):包括1个microtask队列和1个或多个macrotask队列\n\n\n## 2.3 事件循环的简单描述\n\n> 主代码块->微任务->宏任务->微任务->宏任务...(主代码块也是一个宏任务)\n详细解释如下:\n\n- 1、主代码块入栈\n- 2、顺序执行代码块中的*同步任务*,遇到异步任务则交由浏览器内核的其模块处理,处理完后将异步任务的回调函数加入到任务队列中(**一般不同异步任务的回调函数会放入不同的任务队列之中**)\n- 3、当函数执行栈为空时,若microtask队列中有任务则先将microtask队列中的所有微任务入栈执行,当函数执行栈为空,再从macrotask队列中取出一个宏任务入栈。\n- 4、2~3步骤循环,直到所有任务执行完\n\n\n## 2.4 事件循环的图片\n\n这张图片应该是最全的图片了\n\n异步任务先去执行micro的任务之后再去找macro任务\n\n![此处输入图片的描述][1]\n\n\n\n# 3 demo展示\n\n可以直接参考一篇文章,写的事件循环机制非常的不错。\n\n[事件循环机制demo](https://juejin.im/post/5a6309f76fb9a01cab2858b1?utm_medium=fe&utm_source=weixinqun#heading-5)\n\n\n[1]: https://raw.githubusercontent.com/XYooo/image/master/el1.png","tags":["浏览器"]},{"title":"window.location.search","url":"/2019/07/16/url-location/","content":"\n有时候需要从window.location.search去掉的值是空的,配合一些方法就会报错。甚至有时候不是空有的时候是空的,那么原因是因为是什么?\n\n<!-- more -->\n\n\n## 1. 定义 ?\n\nURL:http://b.a.com:88/index.php?name=kang&when=2016#first\n\n|属性 |含义 |值 |\n| -------- | :----------- | :---- |\n|protocol:\t|协议\t| \"http:\"\n|hostname: |\t服务器的名字|\t\"b.a.com\"\n|port: |\t端口|\t\"88\"\n|pathname: | URL中主机名后的部分\t|\"/index.php\"\n|search: |\t\"?\"后的部分,又称为查询字符串|\t\"?name=kang&when=2016\"\n|hash: |\t返回\"#\"之后的内容\t| \"#first\"\n|host: |\t等于hostname + port\t|\"b.a.com:88\"| \n|href: |\t当前页面的完整URL\t|\"http://www.a.com:88/index.php?name=kang&when=2016#first\"\n\n\nlocation的8个属性都是可读写的,但是只有href与hash的写才有意义。\n\n例如改变location.href会重新定位到一个URL,\n\n而修改location.hash会跳到当前页面中的anchor(`<a id=\"name\">或者<div id=\"id\">`等)名字的标记(如果有),而且页面不会被重新加载\n\n\n## 2 为什么为空?\n答: **查询字符串search只能在取到“?”后面和“#”之前的内容,如果“#”之前没有“?”search取值为空。**\n\n注意上面的search和hash的区别,如果URL中“?”之前有一个“#”,比如:`http://localhost:63342/index.html#/version?type=35&id=5`。\n那么使用`window.location.search`得到的就是空(“”)。\n\n因为`?type=35&id=5`串字符是属于`#/version?type=35&id=5`这个串字符的,","tags":["window"]},{"title":"javascript语言的历史","url":"/2019/07/15/JS语言的历史/","content":"\n刚接触前端的时候,在搜集资料的时候会看见es5和javascript还是很迷糊。下面就介绍一下js和\nes的关系,以及js的历史\n\n<!-- more -->\n\n\n## 1 JavaScript 与 ECMAScript 的关系\n\n1996年8月,微软模仿 JavaScript 开发了一种相近的语言,取名为JScript(JavaScript 是 Netscape 的注册商标,微软不能用),首先内置于IE 3.0。Netscape 公司面临丧失浏览器脚本语言的主导权的局面。\n\n1996年11月,Netscape 公司决定将 JavaScript 提交给国际标准化组织 ECMA(European Computer Manufacturers Association),希望 JavaScript 能够成为国际标准,以此抵抗微软。\n\n\n1997年7月,ECMA 组织发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript。这个版本就是 ECMAScript 1.0 版。\n\n\n因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。在日常场合,这两个词是可以互换的。\n\n\nECMAScript 只用来标准化 JavaScript 这种语言的基本语法结构,与部署环境相关的标准都由其他标准规定,比如 DOM 的标准就是由 W3C组织(World Wide Web Consortium)制定的。\n\nECMA-262 标准后来也被另一个国际标准化组织 ISO(International Organization for Standardization)批准,标准号是 ISO-16262。\n\n\n\n\n##2 JavaScript 的版本\n\n- 1997年7月,ECMAScript 1.0发布。\n\n- 1998年6月,ECMAScript 2.0版发布。\n\n- 1999年12月,ECMAScript 3.0版发布,成为 JavaScript 的通行标准,得到了广泛支持。\n\n- 2007年10月,ECMAScript 4.0版草案发布,对3.0版做了大幅升级。但是争论出现\n\n- 2008年7月,由于对于下一个版本应该包括哪些功能,各方分歧太大,争论过于激进,ECMA 开会决定,中止 ECMAScript 4.0 的开发(即废除了这个版本),将其中涉及现有功能改善的一小部分,发布为 ECMAScript 3.1,没过多久,ECMAScript 3.1 就改名为 ECMAScript 5。\n\n- 2009年12月,ECMAScript 5.0版 正式发布。ECMAScript 5 与 ECMAScript 3 基本保持兼容。\n\n- 2011年6月,ECMAScript 5.1版发布,并且成为 ISO 国际标准(ISO/IEC 16262:2011)。到了2012年底,所有主要浏览器都支持 ECMAScript 5.1版的全部功能。\n\n- 2013年3月,ECMAScript 6 草案冻结,不再添加新功能。新的功能设想将被放到 ECMAScript 7。\n\n- 2013年12月,ECMAScript 6 草案发布。\n\n- 2015年6月,ECMAScript 6 正式发布,并且更名为“ECMAScript 2015”。","tags":["javascript"]},{"title":"IE9-坑","url":"/2019/06/29/IE9-坑/","content":"\n陆续完善下工作中遇见的ie9的坑。当然这都是初级的坑\n\n## 1 上传\n\n上传一直是很麻烦的事情,还好我有源码\n\n\n### 1.1 非IE9,利用input的upload来上传图片\n\n[上传图片](/2018/12/25/IE9-上传图片/)\n\n\n## 2 ie9下css加载不全\n\n - [ie9css加载不全](/2018/12/25/IE9-CSS的限制/)\n - [ie9css加载不全2](/2018/12/25/IE9-CSS的限制2/)\n\n\n## 3 ie判断汇总\n\n### 3.1 是否是ie环境\n\n1.在html上\n```html\n\n<!--[if IE]>\n<p>Work in IE browser</p>\n<![endif]-->\n\n```\n2.在js中\n\n```javascript\n(!!window.ActiveXObject || 'ActiveXObject' in window) ?\n<p>Work in IE browser</p> : '' }\n\n```\n### 3.2 ie的事件绑定\n\n暂定\n\n### 3.3 其他\n\n- IE9不支持formdata\n- IE9不支持文件File接口\n- IE9不支持application/json格式\n- IE9不支持通过e.target.value获取dom组件的value值(input、select等)\n- IE9不支持250kcss\n- IE9不支持很多css3,如animation\n- IE9不支持h5 Input控件(select等)\n- IE9react下不支持BrowerRouter、支持HashRouter\n\n\n### 3.3 window.location.origin\n\n有时候需要拼接url,注意window.location.origin在ie9\\8 下并找不到。因此用下面的方式进行替换window.location.origin\n\n```javascript\n\nconst windowLocationOrigin = window.location.protocol + \"//\" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');//ie8-ie10不兼容的原因\n\n\n```","tags":["IE"]},{"title":"chrome--调试技巧1","url":"/2019/06/29/chrome-调试技巧/","content":"\n## 1 前提\n\n前端开发调试很重要。除了一下简单的调试,本篇博客会汇总一些从其他地方看来的chrome小知识点\n\n\n### 1.1 $家族\n\n没有看错,不需要引入jquery.js文件就可以使用$\n\n\n#### 1.1.1 $_\n\n返回上一个被执行过的值\n\n如果之前的值没有保存在变量里,可以通过这个方法临时访问(为什么说临时,因为当你执行完下一个表达式后,$_已经更新了哈)\n\n#### 1.1.2 $\n`document.querySelector()`方法的别名。不过比较少为人知的应该是它的第二个参数。指定从哪个节点开始选择。有时候想减少范围时,尤其管用!\n\n> P.S. 函数签名`$(selector, [startNode])`。\n\n#### 1.1.3 $$\n`document.querySelectorAll()`方法的别名,可参考同上。\n\n> P.S. 函数签名`$$(selector, [startNode])`\n\n\n### 1.2 monitor/unmonitor\n\n用来观察函数调用的工具方法。在函数调用的时候,可以同步输出函数名以及参数。\n\n```console\n\n> function add(x) {return x+1}\n< undefined\n> monitor(add);\n< undefined\n> add(99);\n function add called with arguments : 99\n< 100\n\n> unmonitor(add);\n< undefined\n> add(99)\n< 100\n\n\n```\n\n当不再需要观察该函数时,调用unmonitor取消即可。\n> 但是匿名函数不会生效,因为获取不到名字.\n\n\n### 1.3 monitorEvents/unmonitorEvents\n\n可以观察对像的事件~\n\neg\n```javascript\nmonitorEvents(window,'resize');\nmonitorEvents(window,'click');\n\n//也可以同时观察对象的多个事件~\nmonitorEvents(window,['click','resize']);\nunmonitorEvents(window,'resize');\nunmonitorEvents(window,'click');\n\n\n```\n\n> P.S. 函数签名:`monitorEvents(object[, events])`\n\n### 1.4 copy\n\n快速拷贝一个对象为字符串表示方式到剪切板~\n\n```console\n\n> const cord = {x:1,y:2}\n< undefined\n> copy(cord);\n< undefined\n\n\n```\n\n\n### 1.5 getEventListeners\n获取注册到一个对象上的所有事件监听器~\n\n```javascript\n\ngetEventListeners(dom);\n\n```\n\n## 2 断点调试\n\n### 2.1.1 DOM breakpoint\n在Elements面板,右键点击节点唤出菜单,添加对应的DOM断点,可以监测指定节点的子树修改(substree modifications)、属性修改(attribute modifications)、以及节点的移除(node removal)。\n\n> 知道,不再解释\n\n### 2.1.2 Source breakpoint\n\n有时候无需在源码中添加debugger。直接在Source面板添加断点即可调试。见下图行号上的小蓝色箭头!\n\n> 常用,不解释\n\n\n### 2.1.3 Conditional breakpoint\n\n条件断点。只有符合条件时,才会触发断点。见下图行号上的小橙色箭头!\n\n> 常用,不解释\n\n## 3 小技巧\n\n\n### 3.1 查找命令\n\n如果找不到对应的指令,可以在控制台使用快捷键Ctrl + Shift + P。MacOS的话就是Command + Shift + P(这个和编辑器是一样的道理)。快速搜索你想要的控制面板工具\n\n\n## 4 文献\n\n[谷歌官方文档](https://jsproxy.ga/-----https://developers.google.com/web/tools/chrome-devtools/console/utilities)","tags":["浏览器"]},{"title":"less浏览器端的用法","url":"/2019/06/26/Less(1)-浏览器端应用/","content":"\n\n## 1 前言\n\n这里不讲less的用法,也不讲如何使用gulp、webpack编译less生成css文件,而是less的浏览器端用法。\n目的是为了动态修改css样式\n\n## 2 摘要\n实现的原理:通过html中使用link引入less文件,使用script引入less.js文件,使用less.modifyVars,动态修改less变量值,从而修改css样式\n\n** 用途:实现在线样式定制 **\n\n## 4 项目中实际使用\n原理在section3中有讲到,这里直接先看实际使用。...cdnPath是公司cdn路径,隐私去掉。\n\n在线主题定制功能使用这个了,实现代码如下:\n\n### 4.1 html\n\n```html\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"description\" content=\"主题定制面板\" />\n <meta name=\"keywords\" content=\"ynpm ynpm-tool yonyou\" />\n <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n <meta http-equiv=\"Content-Security-Policy\" content=\"upgrade-insecure-requests\">\n <link id=\"tinper-bee-theme\" href=\"...cdnPath/tinper-bee.css\" rel=\"stylesheet\">\n <title>主题定制面板</title>\n</head>\n<body>\n <div id=\"theme-app\"></div>\n <!-- start: 关键的代码在这里 -->\n <link id='color-less' rel=\"stylesheet/less\" type=\"text/css\" href=\"...cdnPath/color.less\">\n <script>\n window.less = {\n async: false,\n env: 'production'\n };\n </script>\n <script type=\"text/javascript\" src=\"https://cdnjs.cloudflare.com/ajax/libs/less.js/2.7.2/less.min.js\"></script>\n <!-- end: 关键的代码在这里 -->\n</body>\n</html>\n```\n\n> 关键点有两个: color.less的生成和调用window.less.modifyVars 修改color.less中的使用到的参数。color.less的内容在Less(2)中讲解\n\n注意\n- link的 rel=\"stylesheet/less\"\n- 两个script\n\n\n### 4.2 js\n\n动态修改css样式\n\n```javascript\n\nwindow.less.modifyVars(\n this.lessChangeStyle\n )\n .then(() => { console.log('change success') })\n .catch(error => {\n console.log(`Failed to update theme`, error);\n });\n\n```\n\n this.lessChangeStyle的格式\n \n```json\n\n{\"@primary-color\":\"#e1bee7\",\n\"@border-radius\":\"10px\",\n\"@border-color\":\"#c62828\",\n} \n```\n\n\n\n## 3 官网上浏览器端的用法\n\n### 3.1 官网建议\n这部分的资料来自于[less中文网][1]-浏览器端用法。\n> Using less.js in the browser is great for development, but it's not recommended for production\n> \n> 最好在development模式下使用less.js,不推荐在production模式使用\n\n很可惜,为了实现动态修改css样式,这个功能需要在production下使用。\n让我们看下官网解释为什么不推荐在production模式下使用less.js\n[不推荐理由][2]\n\n> We recommend using less in the browser only for development or when you need to dynamically compile less and cannot do it serverside. This is because less is a large javascript file and compiling less before the user can see the page means a delay for the user. In addition, consider that mobile devices will compile slower. For development consider if using a watcher and live reload (e.g. with grunt or gulp) would be better suited.\n>\n> 我们推荐仅在development模式下或者在服务器端不能动态编译less文件的时候需要动态编译less文件的情况下使用less。因为less是一个大体积js文件,编译less需要一定时间,因此给用户造成延迟。另外在移动端,这个时间会需要更久。在development模式下考虑使用watcher或着grunt、gulp等工具的重加载。\n\n很显然,我们的需求动态生成css样式满足第二点,因此请使用less。\n\n在渲染HTML页面时,less文件需要编译成css文件。我们可以有很多种方法。在服务器端,如Node.js,我们有专门的less编译模块。如果是在客户端,需要从LESS官网下载less.js文件,然后在HTML页面中引入。\n\n### 3.2 使用方式\n\n- To start off, link your .less stylesheets with the rel attribute set to \"stylesheet/less\":\n\n```html\n<link rel=\"stylesheet/less\" type=\"text/css\" href=\"styles.less\" />\n```\n\n- Next, download less.js and include it in a <script></script> tag in the <head> element of your page:\n\n```html\n<script src=\"less.js\" type=\"text/javascript\"></script>\n```\n\n- options\n\n你可以引入`<script src=\"less.js\"></script>`之前通过创建一个全局less对象的方式来指定参数,\n```html\n<script>\n less = {\n env: \"development\",\n logLevel: 2,\n async: false,\n fileAsync: false,\n poll: 1000,\n functions: {},\n dumpLineNumbers: \"comments\",\n relativeUrls: false,\n globalVars: {\n var1: '\"string value\"',\n var2: 'regular value'\n },\n rootpath: \":/a.com/\"\n };\n</script>\n```\n但是这影响所有初始链接标记。你也可以在指定的脚本标签的增加选项,如下:\n```html\n<script src=\"less.js\" data-env=\"development\"></script>\n```\n\n或者,你也可以在链接配置参数覆盖某些选项,如下:\n```html\n\n<link data-dump-line-numbers=\"all\" data-global-vars='{ myvar: \"#ddffee\", mystr: \"\\\"quoted\\\"\" }' rel=\"stylesheet/less\" type=\"text/css\" href=\"less/styles.less\">\n\n```\n\n> 注意:\n> 1. 以上三种配置参数的优先级为:link标签的>script标签>全局对象\n> 2. 对象属性名称不驼峰 (e.g logLevel -> data-log-level)\n> 3. link标签的配置只和时间选项有关,其他不起作用(e.g verbose, logLevel ... are not supported)\n\n### 3.3 注意事项\n\n 1. 确保包涵.less样式表在less.js脚本之前\n 2. 当你引入多个.less样式表时,它们都是独立编译的。所以,在每个文件中定义的变量、混合、命名空间都不会被其它的文件共享。\n 3. Due to the same origin policy of browsers loading external resources requires enabling CORS.同源策略,小心跨域问题\n\n### 3.4 参数解析\n#### 3.4.1 async(重点)\n```\nType: Boolean\nDefault: false\n```\nWhether to request the import files with the async option or not. See fileAsync.\n是否异步加载重要文件\n\n\n#### 3.4.3 env(重点)\n```\nType: String Default: depends on page URL\n\n```\n运行环境,如果是production,你的css文件将被缓存到本地并且信息不会输出到控制台。如果url以file://开头或者在你本地或者没有标准的端口,这都将被认为是development模式。\n例如:\n```js\nless = { env: 'production' };\n```\n\n\n\n#### 3.4.9 globalVars(重点)\n```\nType: Object\n\nDefault: undefined\n\n```\n\n全局变量列表注入代码。“字符串”类型的变量必须显式地包含引号。\n\n```js\nless.globalVars = { myvar: \"#ddffee\", mystr: \"\\\"quoted\\\"\" };\n```\n\nThis option defines a variable that can be referenced by the file. Effectively the declaration is put at the top of your base Less file, meaning it can be used but it also can be overridden if this variable is defined in the file.(翻译:这个选项定义了一个可以被文件引用的变量。这个变量也可以在文件中重新定义。)\n\n#### 3.4.10 modifyVars(重点)\n```\nType: Object\n\nDefault: undefined\n```\n\nSame format as globalVars.\n\nAs opposed to the globalVars option, this puts the declaration at the end of your base file, meaning it will override anything defined in your Less file.(翻译:与 globalVars参数含义相反,它将会在你文件最后定义,这意味着它将重写你在文件定义的变量)\n\n\n#### 3.4.2 dumpLineNumbers\n\n```\nType: String Options: ''| 'comments'|'mediaquery'|'all' Default: ''\n\n```\n如果设置了,这增加了源代码行信息输出的CSS文件。这有助于您调试,分析其中一个特定的规则是从哪里来的。\ncomments 选项用于输出user-understandable内容,\nmediaquery 选项用于使用火狐插件解析css文件信息.\n\n在未来我们希望这个选项被sourcemaps取代。\n\n#### 3.4.4 errorReporting\n```\nType: String\n\nOptions: html|console|function\n\nDefault: html\n\n```\n设置编译失败时错误报告的方法。\n\n#### 3.4.5 fileAsync\n```\nType: Boolean\n\nDefault: false\n```\n\n当以file协议访问页面,是否异步引入文件\n\n#### 3.4.6 functions\n\n```\nType: object\n```\n\n用户自定义函数\n```less\ne.g.\n\nless = {\n functions: {\n myfunc: function() {\n return new(less.tree.Dimension)(1);\n }\n }\n};\n```\n可以像Less函数一样使用它。\n```css\n.my-class {\n border-width: unit(myfunc(), px);\n}\n\n```\n#### 3.4.7 logLevel\nType: Number\n\nDefault: 2\n\n在控制台输出日志的数量。如果是production环境,将不会输出任何信息。\n2 - Information and errors1 - Errors0 - Nothing\n\n\n#### 3.4.8 relativeUrls\n```\nType: Boolean\n\nDefault: false\n```\n\n使用相对路劲。如果设置FALSE,则url是相对根目录文件。\n\n\n#### 3.4.11 rootpath\n```\nType: String\n\nDefault: false\n```\n\n设置根目录,所有的Less文件都会以这个目录开始。\n\n#### 3.4.12 useFileCache\n```\nType: Boolean\n\nDefault: true (previously false in before v2)\n```\n\n#### 3.4.13 poll\n```\nType: Integer\n\nDefault: 1000\n```\n在观察模式下,测试的时间。\n\n是否要使用每个会话文件缓存。缓存文件可以使用modifyVars,并且它不会再次检索所有文件。如果您使用观察模式或调用刷新加载设置为true,那么运行之前缓存将被清除。\n\n\n## 4 参考文档\n\n[浏览器端Less - 夕阳白雪 - 博客园](https://www.cnblogs.com/xiyangbaixue/p/4128075.html)\n\n[CSS系列——前端进阶之路:初涉Less](https://www.cnblogs.com/landeanfen/p/6047031.html#_label0)\n\n [1]: http://lesscss.cn/\n [2]: http://lesscss.cn/usage/#using-less-in-the-browser"},{"title":"react的context","url":"/2019/06/11/react的context/","content":"\n## 1.摘要\n\n 前几天学习了rc-tree-select的组件源码,学习一些之前不知道的写法。其中一点就是:在父组件(index.js->selector.js)中定义了一系列的方法,然后在下面子组件中(singleSelector.js、multipleSelector.js、popup.js)中直接使用父组件的写的这些方法\n\n\n其实不用context写法也可以实现这个功能,只要将这个方法作为props传进去就可以,但是多传几层(如子组件下还有子子组件...),处理起来很麻烦。\n\n### 1.1 对比props和context\n\nReact可以轻松地通过组件来跟踪数据流。\n\n1.使用props在组件之间传递数据,props传递数据简单清晰,很容易看到正在传递数据信息如图一所示。组件A与组件C之间通信,C想获得A中的data数据,则需要通过props先将数据传递到组件B,B再通过props将数据传递到C。\n\n\n使用props传递数据可以清晰看到数据流向,但是跨级传递非常麻烦。随着应用结构越来越复杂,组件嵌套层次越来越深,有时甚至需要将数据从最外层传递到最里层,使用props则需要一层一层地逐层传递数据。不过这样也太麻烦了,\n\n2.如果有一种方法实现数据穿越就方便多啦。context能够做到这一点,它使得props不用流经组件树的每一层,将数据跨级传递到你想要传递到的深层次组件,如图二所示.\n\n\n![props和context](https://raw.githubusercontent.com/XYooo/image/master/xuexiliangsanshi_context1.png'')\n\n## 2.context使用\n\n\n### 2.1 具体使用方法\n\n- childContextTypes: 用于说明所传递的数据类型。\n\n- getChildContext: 该方法表示该组件使用context传递数据,该方法返回的对象就是context需要传递的数据。getChildContext指定的传递给子组件的属性需要先通过childContextTypes来执行,不然会报错\n\n- contextTypes: 在子组件中用于说明context接收的数据类型。任何想访问context里面的属性的组件都必须显示的指定一个contextTypes的属性,如果没有指定该属性,那么组件通过this.context访问属性将会出错。\n\n### 2.2 项目中代码\n先看一下项目中是如何使用的 \n\n#### 2.2.1 父组件中使用context\n\n```javascript\nimport React from 'react';\nimport { selectorContextTypes } from './Base/BaseSelector';\nimport { popupContextTypes } from './Base/BasePopup';\nimport { multipleSelectorContextTypes } from './Selector/MultipleSelector';\nclass Select extends React.Component {\n static propTypes = {\n }\n static defaultProps = {\n \n };\n **//关键1:需要先指明,childContextTypes必须使用这个名字**\n static childContextTypes = {\n rcTreeSelect: PropTypes.shape({\n ...selectorContextTypes,\n ...multipleSelectorContextTypes,\n ...popupContextTypes,\n\n onSearchInputChange: PropTypes.func,\n onSearchInputKeyDown: PropTypes.func,\n }),\n };\n\n constructor(props) {\n super(props);\n this.state = {\n };\n }\n **//关键2:使用getChildContext**\n getChildContext() {\n return {\n rcTreeSelect: {\n onSelectorFocus: this.onSelectorFocus,\n onSelectorBlur: this.onSelectorBlur,\n onSelectorKeyDown: this.onComponentKeyDown,\n onSelectorClear: this.onSelectorClear,\n onMultipleSelectorRemove: this.onMultipleSelectorRemove,\n\n onTreeNodeSelect: this.onTreeNodeSelect,\n onTreeNodeCheck: this.onTreeNodeCheck,\n onPopupKeyDown: this.onComponentKeyDown,\n\n onSearchInputChange: this.onSearchInputChange,\n onSearchInputKeyDown: this.onSearchInputKeyDown,\n },\n };\n }\n\n // ===================== Render =====================\n\n render() {\n return (\n //省略...\n );\n }\n}\n\n```\n\n\n下面是上面用到的\n selectorContextTypes,\n multipleSelectorContextTypes,\n popupContextTypes,\n\n ```javascript\n//base/baseSelector.js\nexport const selectorContextTypes = {\n onSelectorFocus: PropTypes.func.isRequired,\n onSelectorBlur: PropTypes.func.isRequired,\n onSelectorKeyDown: PropTypes.func.isRequired,\n onSelectorClear: PropTypes.func.isRequired,\n};\n//selector/multipleSelector/index.js\nexport const multipleSelectorContextTypes = {\n onMultipleSelectorRemove: PropTypes.func.isRequired,\n};\n\n//base/basePopup.js\nexport const popupContextTypes = {\n onPopupKeyDown: PropTypes.func.isRequired,\n onTreeNodeSelect: PropTypes.func.isRequired,\n onTreeNodeCheck: PropTypes.func.isRequired,\n};\n\n ```\n\n可见,全是一些方法,并且指定都是必须。\n\n#### 2.2.2 子组件中使用context\n\n那么如何使用呢?下面是baseSelector为例\n```js\n\nexport default function (modeName) {\n class BaseSelector extends React.Component {\n static propTypes = {\n //省略...\n };\n **//关键1,在这里指明,contextTypes必须使用这个名字**\n static contextTypes = {\n rcTreeSelect: PropTypes.shape({\n ...selectorContextTypes,\n }),\n };\n\n static defaultProps = {\n }\n\n constructor() {\n super();\n\n }\n //如何使用\n onFocus = (...args) => {\n const { rcTreeSelect: { onSelectorFocus } } = this.context;\n onSelectorFocus();\n \n };\n\n onBlur = (...args) => {\n const { rcTreeSelect: { onSelectorBlur } } = this.context;\n onSelectorBlur();\n };\n \n render() {\n const {\n //省略...\n } = this.props;\n const { rcTreeSelect: { onSelectorKeyDown } } = this.context;\n\n return (\n <span\n //...省略\n onFocus={this.onFocus}\n onBlur={this.onBlur}\n onKeyDown={onSelectorKeyDown}\n >\n {/*省略...*/}\n </span>\n );\n }\n }\n\n return BaseSelector;\n}\n\n```\n\n## 3.参考文献\n\n1.[React 中使用context实现数据穿越](http://www.sohu.com/a/197697519_575744)\n\n完结","tags":["react"]},{"title":"css动画-字体模糊","url":"/2019/05/17/css动画-字体模糊/","content":"\n## 1.摘要\n\n当使用bee-modal弹框组件的时候,发现modal上面的内容发生迷糊现象甚至直线变曲线。\n\n\n## 2.原因\n\n后来看了bee-modal的样式,发现bee-modal使用了动画translate和transform属性,目的是为了让modal弹框居中展示。\n\n### 2.1 原因\n\n发现 transform: translate(-50%,-50%); 当使用translate进行位移的时候,如果选择的值是不接近整数的小数(测试时,整数和接近整数的小数比如:1.89、1.9+的数不会出现模糊的情况),位移的元素字体和背景等都会出现模糊的情况,这行居中css代码会导致字体模糊,直接去掉居中效果没了。\n\n具体为什么会导致模糊,这是因为,transform变换会在浏览器上单独创建一个绘画层并重新进行渲染,rotate、transfor等渲染的时候,由于图层渲染的时候也处理了周围的文字,如果width和height的数值为奇数的文字可能会存在半个像素的计算量,浏览器对这半个像素会进行优化渲染,所以边缘会出现模糊的情况。\n\n\n可以通过chrome的compute来查看具体的数值。\n\n- 使用 transform 后出现效果模糊的情况,先查看 width height 属性是否为偶数,将元素的高度设置为偶数可解决; \n- 将transform: translate(-50%, -50%)改成transform: translate3d(-50%, -50%, 0)可以解决抖动,但仍然模糊。 \n- 将元素的高度设置为偶数可解决; \n- 将transform: translate(-50%, -50%)中的y轴单位改成px也可以解决 \n- 改成transform: translate(-50%, -52%)也可以解决(如果52%不行则从51%每个百分比尝试) \n\n","tags":["css"]},{"title":"css动画-变形","url":"/2019/05/02/css动画-变形/","content":"\n## 1 摘要\n\n我们正式踏入CSS3的神奇领域:CSS3动画。在CSS3动画出现之前,对于动画效果(如渐显、移动等)大部分都是使用JavaScript制作。而现在这些动画效果仅仅使用几句CSS代码即可轻松实现。\n\nCSS3动画效果共3大部分:\n\n### 1.1 CSS3变形;\n### 1.2 CSS3过渡;\n### 1.3 CSS3动画;\n\n## 2 CSS3变形\n\n在CSS3中,我们可以使用**transform属性**来实现文字或图像的的各种变形效果,如位移、缩放、旋转、倾斜等。\n\n|方法或属性|说明|\n|translate()|\t位移|\n|scale()\t|缩放|\n|rotate()|\t旋转|\n|skew()\t|倾斜|\n|transform-origin|\t中心原点|\n\n### 2.1 translate\n\n对于位移translate()方法,我们分为3种情况:\n\n(1)translateX(x):元素仅在水平方向移动(X轴移动);\n\n(2)translateY(y):元素仅在垂直方向移动(Y轴移动);\n\n(3)transklate(x,y):元素在水平方向和垂直方向同时移动(X轴和Y轴同时移动);\n\n#### 2.1.1 translateX(x)\n\nx表示元素在水平方向(X轴)的移动距离,单位为px、em或百分比等。\n\n当x为正时,表示元素在水平方向向右移动(X轴正方向);当x为负时,表示元素在水平方向向左移动(X轴负方向)。\n\n#### 2.1.2 translateY(y)\n\ny表示元素在水平方向(y轴)的移动距离,单位为px、em或百分比等。\n\n当y为正时,表示元素在垂直方向向下移动;当y为负时,表示元素在垂直方向向上移动。\n\n> 在W3C规定中,出于人的习惯是从上到下阅读,所选取的坐标系为下图中的第2种坐标系,因此x轴正方向向右,而y轴正方向向下。与‘数学坐标系’区分开来\n\n#### 2.1.3 translate(x,y)\n\nx表示元素在水平方向(x轴)的移动距离,y表示元素在水平方向(y轴)的移动距离。\n\n注意,Y是一个可选参数,如果没有设置Y值,则表示元素仅仅沿着X轴正方形移动。\n\n### 2.2 scale()\n\n缩放,指的是“缩小”和“放大”。在CSS3中,我们可以使用scale()方法来将元素根据中心原点进行缩放。\n\n跟translate()方法一样,缩放scale()方法也有3种情况:\n\n(1)scaleX(x):元素仅水平方向缩放(X轴缩放);\n\n(2)scaleY(y):元素仅垂直方向缩放(Y轴缩放);\n\n(3)scale(x,y):元素水平方向和垂直方向同时缩放(X轴和Y轴同时缩放);\n\nx表示元素沿着水平方向(X轴)缩放的倍数,如果大于1就代表放大;如果小于1就代表缩小。\ny表示元素沿着水平方向(Y轴)缩放的倍数,如果大于1就代表放大;如果小于1就代表缩小。\n\n### 2.3 rotate()\n\n在CSS3中,我们可以使用rotate()方法来将元素相对中心原点进行旋转。这里的旋转是二维的,不涉及三维空间的操作\n\n语法:\n\ntransform:rotate(度数);\n\n说明:\n\n度数指的是元素相对中心原点进行旋转的度数,单位为deg。其中,deg是degree(度数)的缩写。\n\n如果度数为正,则表示元素相对原点中心顺时针旋转;如果度数为负,则表示元素相对原点中心进行逆时针旋转。\n\n### 2.4 倾斜skew()方法\n\n在CSS3中,我们可以使用skew()方法将元素倾斜显示。\n\nskew()方法跟translate()方法、scale()方法一样,也有3种情况:\n\n(1)skewX(x):使元素在水平方向倾斜(X轴倾斜);\n\n(2)skewY(y):使元素在垂直方向倾斜(Y轴倾斜);\n\n(3)skew(x,y):使元素在水平方向和垂直方向同时倾斜(X轴和Y轴同时倾斜);\n\nx表示元素在X轴倾斜的度数,单位为deg。如果度数为正,表示元素沿水平方向(X轴)顺时针倾斜;如果度数为负,表示元素沿水平方向(X轴)逆时针倾斜。\n\ny表示元素在Y轴倾斜的度数,单位为deg。如果度数为正,表示元素沿垂直方向(Y轴)顺时针倾斜;如果度数为负,表示元素沿垂直方向(Y轴)逆时针倾斜。\n\n> 由此我们可以总结:\n> - (1)skewX()方法会保持高度,沿着X轴倾斜;\n> - (2)skewY()方法会保持宽度,沿着Y轴倾斜;\n> - (3)skew(x,y)方法会先按照skewX()方法倾斜,然后按照skewY()方法倾斜;\n\n### 2.5 中心原点transform-origin属性\n\n任何一个元素都有一个中心原点,默认情况下,元素的中心原点位于X轴和Y轴的50%处。\n\n默认情况下,CSS3变形进行的位移、缩放、旋转、倾斜都是以元素的中心原点进行变形。\n\n假如我们要使得元素进行位移、缩放、旋转、倾斜这些变形操作的中心原点不是原来元素的中心位置,那该怎么办呢?\n\n在CSS3中,我们可以通过transform-origin属性来改变元素变形时的中心原点位置。\n\n语法:\n\ntransform-origin:取值;\n\n说明:\n\ntransform-origin属性取值有2种:一种是采用长度值,另外一种是使用关键字。长度值一般使用百分比作为单位,很少使用px、em等作为单位。\n\n\n\n## 3 CSS3过渡","tags":["css"]},{"title":"前端模块化-6webpack下的libraryTarget","url":"/2019/03/28/前端模块化-6webpack下的libraryTarget/","content":"\n\n> 上述代码是返回值,区别在于\n\n### 具体栗子\n\n## 1.1 libraryTarget ='var';library=\"PapRefer\";\n\n```javascript\nvar PapRefer = function(e){var t={};.....\n\n```\n\n## 1.2 libraryTarget ='this';library=\"PapRefer\";\n \n```javascript\nthis.PapRefer = function(e){var t={};.....\n\n```\n## 1.3 libraryTarget ='window';library=\"PapRefer\";\n```javascript\nwindow.PapRefer = function(e){var t={};.....\n\n```\n\n## 1.4 libraryTarget ='global';library=\"PapRefer\";\n\n```javascript\nwindow.PapRef=function(e){var t={};.....\n```\n\n## 1.5 libraryTarget='commonjs';library=\"PapRefer\";\n\n```javascript\nexports.PapRef=function(e){var t={};.....\n\n```\n\n## 1.6 libraryTarget='commonjs2';library=\"PapRefer\";\n```javascript\nmodule.exports=function(e){var t={};......\n\n```\n## 1.7 libraryTarget='amd';library=\"PapRefer\";\n```javascript\ndefine(\"PapRef\",[\"react\",\"prop-types\",\"tinper-bee\",\"react-dom\"],function(__WEBPACK_EXTERNAL_MODULE__1__,__WEBPACK_EXTERNAL_MODULE__2__,__WEBPACK_EXTERNAL_MODULE__5__,__WEBPACK_EXTERNAL_MODULE__7__).....\n\n\n ```\n \n \n## 1.8 libraryTarget='umd';library=\"PapRefer\";\n\n看代码可以看出来使用几个if判断,因此umd的规范编译出来的代码可以适用于 commonjs和amd\n```javascript\n!function(e,t){\"object\"==typeof exports&&\"object\"==typeof module.......\n\n```\n","tags":["webpack"]},{"title":"前端模块化-5webpack下的libraryTarget","url":"/2019/03/28/前端模块化-5webpack下的libraryTarget/","content":"output.library和output.libraryTarget属性可能大家都会比较陌生,因为一般如果只在项目中使用 webpack 不需要关注这两个属性,但是如果是开发类库,那么这两个属性就是必须了解的。说白了就是发包\n\n## 1简介\n\n回想一下,当我们引入别人开发的类库时有几种方式?下面假设我们引入一个demo方法:\n\n### 1.1.传统方式:script标签\n\n\n```javascript\n\n<script src=\"demo.js\"></script>\n<script>demo();</script>\n\n```\n\n\n### 1.2.AMD\n\n```javascript\ndefine(['demo'], function(demo) {\n\n demo();\n\n});\n\n```\n\n\n### 1.3.commonjs 方式\n\n```javascript\nconst demo = require('demo');\n\ndemo();\n```\n\n\n### 1.4.ES6 module\n\n\n```\nimport demo from 'demo';\n\ndemo();\n\n```\n\n大家思考一下,为什么这个类库能支持不同方式的引入?如何实现的?\n\n> 这就是 webpack 配置output.library和output.libraryTarget提供的功能。\n\n## 2.output.library\n\n### 2.1 语法\n支持输入string或者object(从 webpack 3.1.0 版本开始支持; 限于 libraryTarget: “umd” 时使用)类型的值。\n\n### 2.2 output.library vs output.libraryTarget\noutput.library的值被如何使用会根据output.libraryTarget的取值不同而不同。 而默认output.libraryTarget的取值是var,如果如下配置:\n\n```\noutput: {\n library: \"myDemo\"\n}\n\n```\n\n如果在 `HTML` 页面中使用`script`标签引入打包结果文件,那么变量`myDemo`对应的值将会是入口文件(`entry file`)的返回值。\n\n\n## 3.output.libraryTarget\n\n### 3.1 语法\n支持输入string类型的值。默认值:var\n\n### 3.2 用途\n1. 此配置的作用是控制 webpack 打包的内容是如何暴露的。\n \n2. 请注意这个选项需要和output.library所绑定的值一起产生作用。\n\n\n## 4分类\n\n在以下的 demo 中,假设`output.library`值是`myDemo`。`_entry_return_`表示入口点返回的值。在`bundle`中,它是`webpack`从入口点生成的函数的输出。\n\n4.1.暴露一个变量\n\n4.2.通过对象属性暴露\n\n4.3.模块定义系统\n\n4.4.其他类型\n\n### 4.1 暴露一个变量\n\n以下选项会把打包返回的值(无论暴露的是什么)**绑定到一个由output.library指定的变量上**,无论包是被如何引用。\n\n#### 4.1.1 libraryTarget: \"var\"- (default)\n\n使用这个配置,当库被加载时,那么库的返回值会被分配到使用用var申明的变量上。\n\n\n```javascript\nvar myDemo = _entry_return_;\n\n// In a separate script...\nmyDemo();\n\n```\n\n如果没有设置output.library值,那么将不会发生赋值行为。\n\n亲测:可以通过script标签音如,library挂载在window上\n\n#### 4.1.2 libraryTarget: \"assign\"\n\n使用这个设置,会把库返回值分配给一个没使用var申明的变量中,如果这个变量没有在引入作用域中提前申明过,**那么将会挂载在全局作用域中**。(注意,这个行为有可能会覆盖全局作用域中的已有变量)\n\n\n```javascript\nmyDemo = _entry_return_;\n```\n\n### 4.2 通过对象属性暴露\n\n以下选项将库的返回值(无论返回值是什么)分配给特定对象的指定属性,属性由`output.library`指定,对象由`output.libraryTarget`指定。\n\n当`output.library`没有指定为非空字符串,那么默认行为是将库返回值的所有属性(properties)都分配到对象中,代码如下:\n\n\n```javascript\n(function(e, a) { for(var i in a) e[i] = a[i]; }(${output.libraryTarget}, _entry_return_)\n\n```\n\n**注意,发生这个行为的时候 webpack 并不会检查对象中是否已经存在这些属性值,也就是会发生覆盖行为。**\n\n#### 4.2.1libraryTarget: \"this\" \n\n> 将库的返回值分配给`this`对象的由`output.library`指定的属性。其中`this`的意义由用户决定。\n\n\n```javascript\nthis[\"myDemo\"] = _entry_return_;\n//使用时\nthis.myDemo();\n或者\nmyDemo(); // if this is window\n\n```\n\n\n#### 4.2.2 libraryTarget: \"window\" \n\n将库的返回值分配给`window`对象的由output.library指定的属性。\n\n\n```javascript\nwindow[\"myDemo\"] = _entry_return_;\n\n//使用时\n\nwindow.myDemo.doSomething();\n```\n\n#### 4.4.3 libraryTarget: \"global\" \n\n将库的返回值分配给global对象的由output.library指定的属性。\n\n\n```javascript\nglobal[\"myDemo\"] = _entry_return_;\n\n//使用时\n\nglobal.myDemo();\n\n```\n> 实际经测试与window是一样的,不会出现global\n\n#### 4.4.4 libraryTarget: \"commonjs\" \n\n将库的返回值分配给exports对象的由output.library指定的属性。正如名字所指,这个选项可以使用在 CommonJS 环境。\n\n\n```javascript\n\nexports[\"myDemo\"] = _entry_return_;\n\nrequire(\"myDemo\").doSomething();\n```\n> myDemo是webpack中library的值\n\n### 4.3模块定义系统\n\n以下选项将产生一个**包含更完整兼容代码的包**,以确保与各种模块系统的兼容性。 此时`output.library`选项在不同的`output.libraryTarget`选项下具有不同的含义。\n\n#### 4.3.1 libraryTarget: \"commonjs2\" \n\n> 将库的返回值分配给module.exports。正如名字所指,这个选项可以使用在 CommonJS 环境。\n\n```javascript\n//实际的代码\nmodule.exports = _entry_return_;\n\n//使用\nconst myDemo = require(\"myDemo\");\nmyDemo();\n\n```\n\n> 注意,在这个情况下output.library不是必须的,因为此时output.library选项将会被忽略。\n> \n> 有没有注意到 CommonJS 和 CommonJS2 长的非常像?他们确实很相似,但是其中有微妙的区别,想了解更多可以参考这个issue\n\n> 注意:如果使用的方式不对,如不是require进来的,会报错module is not defined\n\n\n#### 4.3.2 libraryTarget: \"umd\" \n\n这个选项会尝试把库暴露给前使用的模块定义系统,这使其和CommonJS、AMD兼容或者暴露为全局变量。 \n\n**umd打出来的包,commonjs amd规范下可以使用,或者暴露成全局变量挂载window上,详情看最下面**\n\n**output.library 选项在这里是必须的。**\n\n最终代码输出如下:\n\n\n```javascript\n\n(function webpackUniversalModuleDefinition(root, factory) {\nif(typeof exports === 'object' && typeof module === 'object')\n module.exports = factory(); //Commonjs的语法\nelse if(typeof define === 'function' && define.amd)\n define([], factory);\nelse if(typeof exports === 'object')\n exports[\"MyLibrary\"] = factory();// 其他语法cmd\nelse\n root[\"MyLibrary\"] = factory();\n})(typeof self !== 'undefined' ? self : this, function() {\nreturn _entry_return_;\n});\n```\n\n如果 output.library 没有输入有效值,那么对于全局变量的处理会和上面提到的 暴露一个变量 一致。代码输出如下:\n\n\n```javascript\n\n(function webpackUniversalModuleDefinition(root, factory) {\nif(typeof exports === 'object' && typeof module === 'object')\n module.exports = factory();\nelse if(typeof define === 'function' && define.amd)\n define([], factory);\nelse {\n var a = factory();\n for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];\n}\n})(this, function() {\nreturn _entry_return_;\n});\n```\n\n\n从 webpack 3.10.0 版本开始,我们可以通过把 output.library 定义为对象来控制不同目标环境的输出值。详情可参考这个Demo\n\n\n```javascript\noutput: {\nlibrary: {\n root: \"myDemo\",\n amd: \"my-demo\",\n commonjs: \"my-common-demo\"\n},\nlibraryTarget: \"umd\"\n}\n```\n\n#### 4.3.3 libraryTarget: \"amd\" \n\n这个选项会把库作为 AMD 模块导出。 \n\nAMD模块要求输入脚本(例如由 `<script>`标签加载的第一个脚本)被定义为具有特定属性,例如通常由 RequireJS 或任何兼容的加载器(诸如almond)提供的require和define属性。**否则,直接加载生成的 AMD 捆绑包将导致类似define is not defined的错误。 \n由此定义生成的代码会如下:**\n\n\n```javascript\n\ndefine(\"myDemo\", [], function() {\nreturn _entry_return_;\n});\n\n```\n\n以上的代码可以作为script标签引入代码的一部分被包含,然后在通过以下代码调用:\n\n```javascript\n\nrequire(['myDemo'], function(myDemo) {\n// Do something with the library...\nmyDemo();\n});\n\n```\n\n如果output.library没有定义有效值,那么生成的代码将如下:\n\n\n```javascript\n\ndefine([], function() {\nreturn _entry_return_;\n});\n\n```\n\n如果直接使用`<script>`标签加载,该库将无法按预期生效,或者根本无法生效(在 almond 加载器的情况下)。它只能通过与 RequireJS 兼容的异步模块加载器通过该文件的实际路径进行引入,因此在这种情况下,如果这些由服务器直接提供,那么output.path和output.filename配置可能变得非常重要。\n\n> **非常重要的一件事情那就是umd和amd配置,打出来的包都可以共amd使用,换句话说可以让requirejs使用**\n\n\n\n### 4.4 其他类型\n\n#### 4.4.1libraryTarget: \"jsonp\" - \n> 这个方法会使用 jsonp 的方式把结果包裹起来。 \njs \n\n```javascript\n\nmyDemo(_entry_return_);\n\n```\n\n库的依赖由 externals 定义。\n\n\n## 5 ouput.library和output.libraryTarget\n\n**总结一下ouput.library和output.libraryTarget的区别**\n\n前者是文件id或者编译出来的代码块id\n\n后面是指定规范,按照什么规范编译出文件","tags":["webpack"]},{"title":"常见方法-数组","url":"/2019/02/13/常见方法-数组/","content":"\n每次都是用完就忘记的方法,印象中数组有很多方法,系统的整理一下,方便回头查~\n\n## 1 循环\n### 1.1 Array.map()\n反正这个是绝对不会忘的,但是有时候会使用错误,对象Object不可以使用这个方法。\n\n> 此方法是将数组中的每个元素调用一个提供的函数,结果作为一个新的数组返回,并没有改变原来的数组\n> 关键几点:1.不会改变原来的数组 2.但是会返回新数组(经常会忘记这点)\n\n```javascript\n\nlet arr = [1, 2, 3, 4, 5]\nlet newArr = arr.map(x => x*2)\n//arr= [1, 2, 3, 4, 5] 原数组保持不变\n//newArr = [2, 4, 6, 8, 10] 返回新数组\n \n```\n### 1.2 Array.forEach()\n反正这个是绝对不会忘的,但是有时候会使用错误,对象Object不可以使用这个方法。\n\n> 此方法是将数组中的每个元素执行传进提供的函数,没有返回值,不会改变原数组,注意和map方法区分\n> 相同点:不会改变原数组 \n> 不同点:有无返回值\n\n```javascript\n arr = [1, 2, 3, 4, 5]\n arr.forEach(x => x*2);\n //undefined\n //arr还是[1,2,3,4,5]\n\n```\n### 1.3 Array.filter()\n\n> 此方法是将所有元素进行判断,将满足条件的元素作为一个新的数组返回\n\n```javascript\n let arr = [1, 2, 3, 4, 5]\n const isBigEnough => value => value >= 3\n let newArr = arr.filter(isBigEnough )\n //newNum = [3, 4, 5] 满足条件的元素返回为一个新的数组\n \n```\n### 1.4 Array.every()\n\n> 此方法是将所有元素进行判断返回一个布尔值,如果所有元素都满足判断条件,则返回true,否则为false:\n\n```javascript\n let arr = [1, 2, 3, 4, 5]\n const isLessThan4 => value => value < 4\n const isLessThan6 => value => value < 6\n arr.every(isLessThan4 ) //false\n arr.every(isLessThan6 ) //true\n\n```\n\n### 1.5 Array.some()\n\n> 此方法是将所有元素进行判断返回一个布尔值,如果存在元素都满足判断条件,则返回true,若所有元素都不满足判断条件,则返回false:\n\n```javascript\n let arr= [1, 2, 3, 4, 5]\n const isLessThan4 => value => value < 4\n const isLessThan6 => value => value > 6\n arr.some(isLessThan4 ) //true\n arr.some(isLessThan6 ) //false\n```\n\n> Array.every 和 Array.some可以用来求差集(相对补集),交集,和并集\n\n> 关键就在于差集(相对补集)。若A和B 是集合,则A 在B 中的相对补集是这样一个集合:其元素属于B但不属于A,B - A = { x| x∈B且x∉A}。 \n\n```javascript\nlet 差集 = B.filter(itemB=>{\n return A.every(itemA=> itemA.key!== itemB.key)\n})\n```\n> 交集。若A和B 是集合,则A ,B交集这样一个集合:其元素属于B也属于A,B A = { x| x∈B且x∈A}。\n\n```javascript\nlet 交集 = B.filter(itemB=>{\n return A.some(itemA=> itemA.key === itemB.key)\n})\n```\n\n### 1.6 Array.reduce()\n\n> reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始缩减,最终为一个值。 reduce\n\n> 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值,当前元素值,当前索引,调用 reduce的数组。\n\n语法:\n\n arr.reduce(callback,[initialValue])\n\ncallback (执行数组中每个值的函数,包含四个参数)\n\n previousValue (看下面结束)\n currentValue (数组中当前被处理的元素)\n index (当前元素在数组中的索引)\n array (调用 reduce 的数组)\ninitialValue (作为第一次调用 callback 的第一个参数。)\n\n> 上面解释又一个地方不合适,那就是previousValue值是什么,就两种情况\n\n - 1.传了initialValue,那么 previousValue= initialValue, currentValue= array[0], index从0开始\n - 2.没有传initialValue,那么 previousValue = array[0], currentValue = array[1], index从1开始\n\n```javascript\n let arr = [1, 2, 3, 4, 5]\n const add = (a, b) => a + b\n let sum = arr.reduce(add)\n //sum = 15 相当于累加的效果\n //与之相对应的还有一个 Array.reduceRight() 方法,区别是这个是从右向左操作的\n```\n\n## 2 操作数组和转化数组\n### 2.1 Array.concat()\n\n \n\n> 此方法是一个可以将多个数组拼接成一个数组:\n\n```javascript\n let arr1 = [1, 2, 3]\n arr2 = [4, 5]\n let arr = arr1.concat(arr2)\n console.log(arr)//[1, 2, 3, 4, 5]\n```\n\n### 2.2 Array.toString()\n\n> 此方法将数组转化为字符串:\n\n```javascript\n let arr = [1, 2, 3, 4, 5];\n let str = arr.toString()\n console.log(str)// 1,2,3,4,5\n```\n\n \n\n### 2.3 Array.join()\n\n \n\n> 此方法也是将数组转化为字符串:\n\n```javascript\n let arr = [1, 2, 3, 4, 5];\n let str1 = arr.toString()\n let str2 = arr.toString(',')\n let str3 = arr.toString('##')\n console.log(str1)// 12345\n console.log(str2)// 1,2,3,4,5\n console.log(str3)// 1##2##3##4##5\n```\n\n> 通过例子可以看出和toString的区别,可以设置元素之间的间隔~ \n\n### 2.4 Array.splice(开始位置, 删除的个数,元素)\n\n> 万能方法,可以实现增删改:\n\n> 注意,会修改原数组\n\n```javascript\nlet arr = [1, 2, 3, 4, 5];\nlet arr1 = arr.splice(2, 0 'haha')\n//新增\nconsole.log(arr) //[1, 2, 'haha', 3, 4, 5]新增一个元素\nconsole.log(arr1) //[]\n//删\nlet arr2 = arr.splice(2, 3)\nconsole.log(arr) //[1, 2, 5]删除三个元素\nconsole.log(arr2) //[\"haha\", 3, 4]截取到三个元素\n//替换\nlet arr3 = arr.splice(2, 1 'haha')\nconsole.log(arr) //[1, 2, \"haha\"] 替换的一份元素\nconsole.log(arr3) //[5]替换掉的元素\n\n```\n\n### 2.5 Array.slice(start,end)\n> 返回从原数组中指定开始下标到结束下标之间的项组成的新数组 \n\n> **注意,不会修改原数组,但是splice会修改原数组**\n\n```javascript\n var a = [1,2,3,4,5]; \n var b = a.slice(2,5); //a:[1,2,3,4,5] b:[3,4,5] \n\n```\n## 3 插入和删除\n\n### 3.1 Array.push()\n\n> 此方法是在数组的**后面添加新加元素**,此方法改变了数组的长度:\n```javascript\nvar arr = [1,2,3];\narr.push(4);\nconsole.log(arr); //[ 1, 2, 3, 4 ]\narr.push(5,6);\nconsole.log(arr); //[ 1, 2, 3, 4, 5, 6 ]\n```\n \n### 3.2 Array.pop()\n\n> 此方法在数组后面**删除最后一个元素**,并返回数组,此方法改变了数组的长度:\n\n```javascript\n let arr = [1, 2, 3, 4, 5]\n arr.pop()\n console.log(arr) //[1, 2, 3, 4]\n console.log(arr.length) //4\n```\n\n\n### 3.3 Array.shift()\n\n> 此方法在数组后面**删除第一个元素**,并返回数组,此方法改变了数组的长度\n\n```javascript\n let arr = [1, 2, 3, 4, 5]\n arr.shift()\n console.log(arr) //[2, 3, 4, 5]\n console.log(arr.length) //4 \n```\n\n \n\n### 3.4 Array.unshift()\n\n> 此方法是将一个或多个元素**添加到数组的开头**,并返回新数组的长度:\n\n```javascript\n let arr = [1, 2, 3, 4, 5]\n arr.unshift(6, 7)\n console.log(arr) //[6, 7, 2, 3, 4, 5]\n console.log(arr.length) //7 \n```\n\n### 3.5 Array.reverse()\n\n> 将数组反序\n```javascript\n var a = [1,2,3,4,5]; \n var b = a.reverse(); //a:[5,4,3,2,1] b:[5,4,3,2,1] \n```\n\n## 4 判断\n### 4.1 Array.isArray()\n 判断一个对象是不是数组,返回的是布尔值\n\n ","tags":["javascript"]},{"title":"常见方法-对象","url":"/2019/02/01/常见方法-对象/","content":"\n下面是Obeject上面的常见操作\n## 1 修改Object的操作\n### 1.1 Object.assign(target,source1,source2,...)\n\n该方法主要用于对象的合并,将源对象source的所有可枚举属性合并到目标对象target上,此方法只拷贝源对象的自身属性,不拷贝继承的属性。\n\n- Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。同名属性会替换。\n\n- Object.assign只能进行值的复制,如果要复制的值是一个取值函数,那么将求值后再复制。\n\n- Object.assign可以用来处理数组,但是会把数组视为对象。\n\n#### 1.1.1 同名属性被覆盖,拷贝对象是引用\n\n```javascript\n\nconst target = {\n x : 0,\n y : 1\n };\n const source = {\n x : 1,\n z : 2 ,\n fn : {\n number : 1\n }\n };\n Object.assign(target, source); \n // target {x : 1, y : 1, z : 2, fn : {number : 1}} \n // 同名属性会被覆盖\n // source {x : 1, z : 2, fn : {number : 1}}\n \n target.fn.number = 2; \n // 拷贝为对象引用\n // source {x : 1, z : 2, fn : {number : 2}}\n \n```\n\n#### 1.1.2 只能拷贝自身的属性,不能拷贝prototype\n\n\n```javascript\n\nfunction Person(){\n this.name = 1\n};\nPerson.prototype.country = 'china';\nlet student = new Person();\nstudent.age = 29 ;\nconst young = {insterst : 'sport'};\nObject.assign(young,student);\n\n// young {instest : 'sport' , age : 29, name: 1}\n```\n\n#### 1.1.3 把数组当作对象来处理\n\n\n```javascript\n\nObject.assign([1, 2, 3], [4, 5]) \n// 把数组当作对象来处理\n// [4, 5, 3]\n\n```\n\n### 1.2 Object.defineProperties(obj,props)\n\n直接在一个对象上定义新的属性或修改现有属性,并返回该对象。\n\n```javascript\n\nvar obj = {};\nObject.defineProperties(obj, {\n 'property1': {\n value: true,\n writable: true\n },\n 'property2': {\n value: 'Hello',\n writable: false\n }\n // etc. etc.\n});\nconsole.log(obj) \n// {property1: true, property2: \"Hello\"}\n```\n\n### 1.3 Object.defineProperty(obj,prop,descriptor)\n在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。\n\n```javascript\n\nObject.defineProperty(Object, 'is', {\n value: function(x, y) {\n if (x === y) {\n // 针对+0 不等于 -0的情况\n return x !== 0 || 1 / x === 1 / y;\n }\n // 针对NaN的情况\n return x !== x && y !== y;\n },\n configurable: true,\n enumerable: false,\n writable: true \n}); \n \n// 注意不能同时设置(writable,value) 和 get,set方法,否则浏览器会报错 :\n//Invalid property descriptor. Cannot both specify accessors and a value or writable attribute\n```\n### 1.4 toLocaleString()\ntoLocalString方法返回对象的字符串表示,和代码的执行环境有关。\n\n\n## 2 获取Obeject属性的操作\n### 2.1 Object.getOwnPropertySymbols()\n返回一个给定对象自身的所有 Symbol 属性的数组。\n\n### 2.2 Object.getOwnPropertyNames()\n返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。\n\n```javascript \n\n \nvar obj = {\n x : 1,\n y : 2\n}\n \nObject.defineProperty(obj,'z',{\n enumerable : false\n})\nconsole.log(Object.getOwnPropertyNames(obj)) // [\"x\", \"y\", \"z\"] 包含不可枚举属性 。\nconsole.log(Object.keys(obj)) // [\"x\", \"y\"] 只包含可枚举属性 。\n\n```\n\n### 2.3 getOwnPropertyDescripto和getOwnPropertyDescriptors\n- Object.getOwnPropertyDescriptor(obj,prop)\n - 返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性).\n\n - 如果指定的属性存在于对象上,则返回其属性描述符对象(property descriptor),否则返回 undefined。\n\n\n\n```javascript\nvar obj = {name:'haha',age:17}\nvar arr = ['name','age'] ;\narr.forEach(val => console.log(Object.getOwnPropertyDescriptor(obj,val)))\n \n//{value: \"haha\", writable: true, enumerable: true, configurable: true}\n//{value: 17, writable: true, enumerable: true, configurable: true}\n\n```\n- Object.getOwnPropertyDescriptors(obj)\n - 获取一个对象的所有自身属性的描述符。\n\n```javascript\n\nvar obj = {\n name : 'js',\n age : 20\n}\nconsole.log(Object.getOwnPropertyDescriptors(obj))\n//{name: {…}, age: {…}}\n//age: {value: 20, writable: true, enumerable: true, configurable: true}, \n//name: {value: \"js\", writable: true, enumerable: true, configurable: true}\n\n```\n\n\n```javascript\nvar obj = {};\nconsole.log(obj.toLocaleString());//[object Object] \n\nvar date = new Date();\nconsole.log(date.toLocaleString());//2016/2/28 下午1:39:27 输出的就是本地的时间\n\n```\n## 3 循环遍历Object的操作\n### 3.1 Object.keys(obj)\n 返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 (两者的主要区别是 一个 for-in 循环还会枚举其原型链上的属性)。\n\n```javascript\nlet arr = [\"a\", \"b\", \"c\"];\nconsole.log(Object.keys(arr));\n// ['0', '1', '2']\n \n/* Object 对象 */\nlet obj = { foo: \"bar\", baz: 42 },\n keys = Object.keys(obj);\nconsole.log(keys);\n// [\"foo\",\"baz\"]\n```\n\n\n### 3.2 Object.values()\n\n方法返回一个给定对象自己的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。\n\nObject.values会过滤属性名为 Symbol 值的属性。\n\n\n```javascript\nvar an_obj = { 100: 'a', 2: 'b', 7: 'c' };\nconsole.log(Object.values(an_obj)); // ['b', 'c', 'a']\n \nvar obj = { 0: 'a', 1: 'b', 2: 'c' };\nconsole.log(Object.values(obj)); // ['a', 'b', 'c']\n```\n\n \n### 3.3 Object.entries()\n\n返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。\n\n```javascript\n\nconst obj = { foo: 'bar', baz: 42 };\nconsole.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]\n \nconst simuArray = { 0: 'a', 1: 'b', 2: 'c' };\nconsole.log(Object.entries(simuArray)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]\n```\n\n## 4 Object的判断\n### 4.1 hasOwnProperty()\n判断对象自身属性中是否具有指定的属性。\n\n\n```javascript\n\nobj.hasOwnProperty('name')\n\n```\n\n### 4.2 isPrototypeOf()\n判断一个对象是否存在于另一个对象的原型链上。\n\n### 4.3 Object.isExtensible()\n \n 判断对象是否是可扩展的,Object.preventExtensions,Object.seal 或 Object.freeze 方法都可以标记一个对象为不可扩展(non-extensible)\n\n\n\n### 4.4 Object.is()\n判断两个值是否相同。\n如果下列任何一项成立,则两个值相同:\n\n- 两个值都是 undefined\n- 两个值都是 null\n- 两个值都是 true 或者都是 false\n- 两个值是由相同个数的字符按照相同的顺序组成的字符串\n- 两个值指向同一个对象\n- 两个值都是数字并且\n - 都是正零 +0\n - 都是负零 -0\n - 都是 NaN\n - 都是除零和 NaN 外的其它同一个数字\n\n```javascript \n\nObject.is('foo', 'foo'); // true\nObject.is(window, window); // true\n \nObject.is('foo', 'bar'); // false\nObject.is([], []); // false\n \nvar test = { a: 1 };\nObject.is(test, test); // true\n \nObject.is(null, null); // true\n \n// 特例\nObject.is(0, -0); // false\nObject.is(-0, -0); // true\nObject.is(NaN, 0/0); // true\n```\n\n\n## 5 其他方法\n### 5.1 Object.create(prototype[,propertiesObject])\n### 5.2 Obeject.getPrototypeOf()\n### 5.3 Object.setPrototypeOf(obj,prototype)\n### 5.4 Object.freeze()\n### 5.5 Object.isFrozen()\n### 5.6 Object.preventExtension()\n### 5.7 Object.isExtensible()\n### 5.8 Object.seal()\n### 5.9 Object.isSealed()\n\n\n自己去百度吧\n\n","tags":["javascript"]},{"title":"常见方法-字符串","url":"/2019/01/29/常见方法-字符串/","content":"\n字符串的操作在js中非常频繁,也非常重要。以往看完书之后都能记得非常清楚,但稍微隔一段时间不用,便会忘得差不多,记性不好是硬伤啊。。。今天就对字符串的一些常用操作做个整理,一者加深印象,二者方便今后温习查阅。\n引用某网友的话。\n\n## 1 String对象属性\n\n注意,这里是**属性**,不是方法\n### 1.1 length属性\n\nlength算是字符串中非常常用的一个属性了,它的功能是获取字符串的长度。当然需要注意的是js中的中文每个汉字也只代表一个字符,这里可能跟其他语言有些不一样。\n\n \n var str = 'abc';\n console.log(str.length);//3\n\n \n\n### 1.2 prototype属性\n\nprototype在面向对象编程中会经常用到,用来给对象添加属性或方法,并且添加的方法或属性在所有的实例上共享。因此也常用来扩展js内置对象,如下面的代码给字符串添加了一个去除两边空格的方法:\n\n\n String.prototype.trim = function(){\n return this.replace(/^\\s*|\\s*$/g, '');\n }\n\n\n## 2 查找类的方法\n### 2.1 indexOf()\n\n stringObject.indexOf(searchvalue,fromindex)\n\n \n\nindexOf()用来检索指定的字符串值在字符串中首次出现的位置。它可以接收两个参数,searchvalue表示要查找的子字符串,fromindex表示查找的开始位置,省略的话则从开始位置进行检索。\n\n \n\n var str = 'abcdeabcde';\n console.log(str.indexOf('a')); // 返回0\n console.log(str.indexOf('a', 3)); // 返回5\n console.log(str.indexOf('bc')); // 返回1\n\n\n### 2.2 lastIndexOf()方法\n\n \n\n stringObject.lastIndexOf(searchvalue,fromindex)\n\n \n\nlastIndexOf()语法与indexOf()类似,它返回的是一个指定的子字符串值最后出现的位置,其检索顺序是从后向前。\n\n \n\n var str = 'abcdeabcde';\n console.log(str.lastIndexOf('a')); // 返回5\n console.log(str.lastIndexOf('a', 3)); // 返回0 从第索引3的位置往前检索\n console.log(str.lastIndexOf('bc')); // 返回6\n\n \n\n### 2.3 search()方法\n\n \n\n stringObject.search(substr) \n stringObject.search(regexp)\n\n \n\nsearch()方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。它会返回第一个匹配的子字符串的起始位置,如果没有匹配的,则返回-1。\n\n \n\n var str = 'abcDEF';\n console.log(str.search('c')); //返回2\n console.log(str.search('d')); //返回-1\n console.log(str.search(/d/i)); //返回3\n\n \n\n### 2.4 match()方法\n\n \n \n stringObject.match(substr) \n stringObject.match(regexp)\n\n \n\nmatch()方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。\n\n如果参数中传入的是子字符串或是没有进行全局匹配的正则表达式,那么match()方法会从开始位置执行一次匹配,如果没有匹配到结果,则返回null。否则则会返回一个数组,该数组的第0个元素存放的是匹配文本,除此之外,返回的数组还含有两个对象属性index和input,分别表示匹配文本的起始字符索引和stringObject 的引用(即原字符串)。\n\n \n\n var str = '1a2b3c4d5e';\n console.log(str.match('h')); //返回null\n console.log(str.match('b')); //返回[\"b\", index: 3, input: \"1a2b3c4d5e\"]\n console.log(str.match(/b/)); //返回[\"b\", index: 3, input: \"1a2b3c4d5e\"]\n \n\n如果参数传入的是具有全局匹配的正则表达式,那么match()从开始位置进行多次匹配,直到最后。如果没有匹配到结果,则返回null。否则则会返回一个数组,数组中存放所有符合要求的子字符串,并且没有index和input属性。\n\n \n\n var str = '1a2b3c4d5e';\n console.log(str.match(/h/g)); //返回null\n console.log(str.match(/\\d/g)); //返回[\"1\", \"2\", \"3\", \"4\", \"5\"]\n\n \n\n## 3.截取类方法\n\n### 3.1 substring()\n\n \n\n stringObject.substring(start,end)\n\n \n\nsubstring()是最常用到的字符串截取方法,它可以接收两个参数(参数不能为负值),分别是要截取的开始位置和结束位置,**它将返回一个新的字符串**,**其内容是从start处到end-1处的所有字符**。若结束参数(end)省略,则表示从start位置一直截取到最后。\n\n \n\n var str = 'abcdefg';\n console.log(str.substring(1, 4)); //返回bcd\n console.log(str.substring(1)); //返回bcdefg\n console.log(str.substring(-1)); //返回abcdefg,传入负值时会视为0\n\n \n\n###3.2 slice()\n\n \n\n stringObject.slice(start,end)\n\n \n\nslice()方法与substring()方法非常类似,它传入的两个参数也分别对应着开始位置和结束位置。而区别在于,**slice()中的参数可以为负值,如果参数是负数,则该参数规定的是从字符串的尾部开始算起的位置。**也就是说,-1 指字符串的最后一个字符。\n\n \n\n var str = 'abcdefg';\n console.log(str.slice(1, 4)); //返回bcd\n console.log(str.slice(-3, -1)); //返回ef\n console.log(str.slice(1, -1)); //返回bcdef\n console.log(str.slice(-1, -3)); //返回空字符串,若传入的参数有问题,则返回空\n \n\n### 3.3 substr()\n\n \n\n stringObject.substr(start,length)\n\n \n\nsubstr()方法可在字符串中抽取从start下标开始的指定数目的字符。其返回值为一个字符串,包含从 stringObject的start(包括start所指的字符)处开始的length个字符。如果没有指定 length,那么返回的字符串包含从start到stringObject的结尾的字符。另外如果start为负数,则表示从字符串尾部开始算起。\n\n\n var str = 'abcdefg';\n console.log(str.substr(1, 3)) //返回bcd\n console.log(str.substr(2)) //返回cdefg\n console.log(str.substr(-2, 4)) //返回fg,目标长度较大的话,以实际截取的长度为准\n \n### 3.4 replace()方法\n\n \n\n stringObject.replace(regexp/substr,replacement)\n\n \n\nreplace()方法用来进行字符串替换操作,它可以接收两个参数,前者为被替换的子字符串(可以是正则),后者为用来替换的文本。\n\n如果第一个参数传入的是子字符串或是没有进行全局匹配的正则表达式,那么replace()方法将只进行一次替换(即替换最前面的),返回经过一次替换后的结果字符串。\n\n \n \n var str = 'abcdeabcde';\n console.log(str.replace('a', 'A'));\n console.log(str.replace(/a/, 'A'));\n\n \n\n如果第一个参数传入的全局匹配的正则表达式,那么replace()将会对符合条件的子字符串进行多次替换,最后返回经过多次替换的结果字符串。\n\n \n\n var str = 'abcdeabcdeABCDE';\n console.log(str.replace(/a/g, 'A')); //返回AbcdeAbcdeABCDE\n console.log(str.replace(/a/gi, '$')); //返回$bcde$bcde$BCDE\n\n \n\n### 3.5 split()方法\n\n stringObject.split(separator,howmany)\n\n \n\nsplit()方法用于把一个字符串分割成字符串数组。第一个参数separator表示分割位置(参考符),第二个参数howmany表示返回数组的允许最大长度(一般情况下不设置)。\n \n var str = 'a|b|c|d|e';\n console.log(str.split('|')); //返回[\"a\", \"b\", \"c\", \"d\", \"e\"]\n console.log(str.split('|', 3)); //返回[\"a\", \"b\", \"c\"]\n console.log(str.split('')); //返回[\"a\", \"|\", \"b\", \"|\", \"c\", \"|\", \"d\", \"|\", \"e\"]\n\n \n\n也可以用正则来进行分割\n\n \n\n var str = 'a1b2c3d4e';\n console.log(str.split(/\\d/)); //返回[\"a\", \"b\", \"c\", \"d\", \"e\"]\n \n> arrayObject.splice(index,howmany,item1,.....,itemX) ,splice操作是针对数组对象的,不是字符串对象\n\n### 3.6 toLowerCase()和toUpperCase()\n\n \n\n stringObject.toLowerCase() \n stringObject.toUpperCase()\n\n \n\ntoLowerCase()方法可以把字符串中的大写字母转换为小写,toUpperCase()方法可以把字符串中的小写字母转换为大写。\n\n \n\nvar str = 'JavaScript';\nconsole.log(str.toLowerCase()); //返回javascript\nconsole.log(str.toUpperCase()); //返回JAVASCRIPT\n\n## 4 其他\n### 4.1 charAt()\n\n stringObject.charAt(index)\n\n \ncharAt()方法可用来获取指定位置的字符串,index为字符串索引值,从0开始到string.length-1,若不在这个范围将返回一个空字符串。如:\n\n\n var str = 'abcde';\n console.log(str.charAt(2)); //返回c\n console.log(str.charAt(8)); //返回undefined\n console.log(str.charAt(-1)); //返回undefined\n \n### 4.2 charCodeAt()\n\n stringObject.charCodeAt(index)\n\n \n\ncharCodeAt()方法可返回指定位置的字符的Unicode编码。charCodeAt()方法与charAt()方法类似,都需要传入一个索引值作为参数,区别是前者返回指定位置的字符的编码,而后者返回的是字符子串。\n\n \n\n var str = 'abcde';\n console.log(str.charCodeAt(0)); //返回97\n \n### 4.3 fromCharCode()\n\n \n\n String.fromCharCode(numX,numX,…,numX)\n\n \n\nfromCharCode()可接受一个或多个Unicode值,然后返回一个字符串。另外该方法是String 的静态方法,字符串中的每个字符都由单独的数字Unicode编码指定。\n\n String.fromCharCode(97, 98, 99, 100, 101) //返回abcde\n\n\n\n","tags":["javascript"]},{"title":"http-cookie简介1","url":"/2019/01/04/http-cookie简介1/","content":"了解一下cookie\n<!-- more -->\n\n\n## 1.前言\n\n首先了解一下什么是cookie?\n\n**cookie是一种WEB服务器通过浏览器在访问者的硬盘上存储信息的手段**。IE浏览器把Cookie信息保存在类似于C://windows//cookies的目录下。\n\n\n当用户再次访问某个站点时,服务端将要求浏览器查找并返回先前发送的Cookie信息,来识别这个用户。\n\n\ncookies给网站和用户带来的好处非常多:\n\n- 1、Cookie能使站点跟踪特定访问者的访问次数、最后访问时间和访问者进入站点的路径\n\n- 2、Cookie能告诉在线广告商广告被点击的次数,从而可以更精确的投放广告\n\n- 3、Cookie有效期限未到时,Cookie能使用户在不键入密码和用户名的情况下进入曾经浏览过的一些站点\n\n- 4、Cookie能帮助站点统计用户个人资料以实现各种各样的个性化服务\n\n## 2. cookie 存在哪里?\n> **cookie是一种WEB服务器通过浏览器在访问者的硬盘上存储信息的手段**\n\n一般存在浏览器目录中的文本文件中\n\n## 3.cookie分为多少种?\n\n- 1.带失效时间expires的,在下次访问之前,如果失效时间到期,会自动清除对应cookie,expires和maxAge的概念差不多,通常两个时间都是一样的,一个是时间,一个时间长度,前者是HTTP/1.0 protocol,后者是HTTP/1.1 protocol,为了向下兼容而已,所以,最好是两个参数都设置\n\n- 2.跟随session结束就自动清除的,这种cookie很常见。通常你会在console看到它的expires时间是1969年的或者所边当前时间还要早的时间或者session,这种cookies会在会话结束的同时清除掉\n\n## 4.Cookie的工作原理是什么?\n\n前面我们说了Cookie一般存在浏览器目录中的文本文件中,并且会根据domain分开存放,比如,当你输入jianshu.com的时候,浏览器会向jianshu发送一个request,然后server根据request来返回response,把结果在显示器中显示,当你发送这个request的时候,浏览器会寻找当前浏览器目录中是否存在jianshu.com的相关cookie,如果有,浏览器会把Cookie文件中的数据放在request header中一起向server发送,服务器收到Cookie数据,服务器会根据你的cookie信息做一些相应的处理,比如第一次访问的话,会为你创建一个新的session id,否者来检测你是否需重新登录等等操作。\n\n## 5. Cookie包含哪些字段?\n\n### 5.1 查看cookie\n\n比如在chrome下打开jianshu.com,F12 -> Application -> Cookies -> https://www.jianshu.com\n\n![chrome下cookie查看](https://upload-images.jianshu.io/upload_images/5342565-24141bac572d3c5f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1000/format/webp)\n\n\n> 一个域名下面可能存在着很多个cookie对象。\n\n> **也可以在Console里边输入document.cookies,但是只显示非HTTP的cookie name和value,没有其他信息**\n\n### 5.2 cookie字段解析\n\n**name**字段为一个cookie的名称。\n\n**value**字段为一个cookie的值。\n\n**domain**字段为可以访问此cookie的域名。\n\n> 1、~~非顶级域名,如二级域名或者三级域名,设置的cookie的domain只能为顶级域名或者二级域名或者三级域名本身,不能设置其他二级域名的cookie,否则cookie无法生成。~~\n\n> 2、顶级域名只能设置domain为顶级域名,不能设置为二级域名或者三级域名,否则cookie无法生成。\n\n> 3、二级域名能读取设置了domain为顶级域名或者自身的cookie,不能读取其他二级域名domain的cookie。\n>\n> 所以要想cookie在多个二级域名中共享,需要设置domain为顶级域名,这样就可以在所有二级域名里面或者到这个cookie的值了。\n\n> 4、顶级域名只能获取到domain设置为顶级域名的cookie,其他domain设置为二级域名的无法获取。\n\n **path**字段为可以访问此cookie的页面路径。 比如domain是abc.com,path是/test,那么只有/test路径下的页面可以读取此cookie。\n> domain+path =可以读取此cookie的url\n\n**expires/Max-Age**字段为此cookie超时时间。\n若设置其值为一个时间,那么当到达此时间后,此cookie失效。**不设置的话默认值是Session,意思是cookie会和session一起失效**。当浏览器关闭(不是浏览器标签页,而是整个浏览器) 后,此cookie失效。\n\n**Size**字段为此cookie大小。\n\n**http**字段为cookie的httponly属性。若此属性为true,则只有在http请求头中会带有此cookie的信息,而不能通过document.cookie来访问此cookie。\n\n**secure**字段为设置是否只能通过https来传递此条cookie\n\n### 5.3 cookie设置过期时间\n\n\n```javascript\ncookie.setMaxAge(0);//不记录cookie\n\n\ncookie.setMaxAge(-1);//会话级cookie,关闭浏览器失效\n\n\ncookie.setMaxAge(60*60);//过期时间为1小时\n\n```\n### 5.4 cookie的其他操作\n\n 主要是两种方式\n - 前端操作\n - 后端操作\n \n#### 5.4.1 前端操作cookie\n\n以chrome为例子,可以直接在console里边对cookie进行修改,例如我们将default_font改成Simplified\n> **当然这只是举个例子,至于修改完重新发送请求到jianshu server之后会不会有作用,这个取决于jianshu server**\n\nF12 -> Console,输入:\n\n```javascript\ndocument.cookie = \"default_font=Simplified;path=/\"\n\n```\n#### 5.4.2 后端服务器操作\n可以直接在response上面进行操作,你可以直接抹掉某个cookie\n\n```javascript\nresponse.clearCookie('default_font')\n```\n\n这个时候回来的response header就不会带default_font这个cookie了\n也可以修改已存在的cookie过期时间或者加入新的cookie,nodejs的application可以使用cookie.js\n\n```javascript\nres.setHeader('Set-Cookie', cookie.serialize('my_cookie', 'my_value', {\n httpOnly: false,\n expires: \"Mon, 11 Jun 2018 05:47:25 GMT\"\n maxAge: 60 * 60 * 24 * 7 // 1 week\n}));\n\n```\n\n这个时候浏览器就会接受到一个包含cookie为my_cookie=my_value,并且过期时间为一个星期的response。\n\n## 参考文献\n[关于Cookie的一些思考和理解](https://www.jianshu.com/p/64c0f5d073bb)\n\n[cookie过期时间设置](https://www.aliyun.com/jiaocheng/829186.html)\n\n\n## 遇见的状况\n\n- 1.如果cookie设置了一个已经过期的字段,那么在chrome下的cookie则会不展示这个字段;document.cookie也不会展示这个字段,等于白写;","tags":["http"]},{"title":"IE9-CSS的限制2","url":"/2018/12/25/IE9-CSS的限制2/","content":"\n[Stylesheet Limits in Internet Explorer\n](https://blogs.msdn.microsoft.com/ieinternals/2011/05/14/stylesheet-limits-in-internet-explorer/)\n\n## 1.1 前言\n\n\n[ie css坑的翻译1](https://support.microsoft.com/zh-cn/help/262161/a-webpage-that-uses-css-styles-does-not-render-correctly-in-internet-e) outlines the maximum number of stylesheets and rules supported by Internet Explorer 6 to 9.\n\n```\nA sheet may contain up to 4095 rules\nA sheet may @import up to 31 sheets\n@import nesting supports up to 4 levels deep\n\n```\n\n\nSome folks have wondered about the math that underlies these numbers. The root of the limitations is that Internet Explorer uses a 32bit integer to identify, sort, and apply the cascading rules. The integer’s 32bits are split into five fields: four sheetIDs of 5 bits each, and one 12bit ruleID. The 5 bit sheetIDs results in the 31 @import limit, and the 12 bit ruleID results in the 4095 rules-per-sheet limitation. While these limits are entirely sufficient for most sites, there are some sites (particularly when using frameworks and controls) that can encounter the limits, requiring workarounds.\n\n> 翻译\n\n有些人对这些数字感到疑惑。limitations的根本在于,Internet Explorer使用32位整数来标识、排序和应用这些级联规则。\n\n32位整数被分成五个部分:\n1. 5bit用来表示SeigesID;\n2. 12bit的RuleID;\n\n5位SHITEDS导致31个@导入限制,12位RuleID导致每张表限制的4095条规则。虽然这些限制对于大多数站点来说已经完全足够了,但是有些站点(尤其是使用框架和控制时)可能遇到这些限制,需要解决办法。\n\n\n\n### 1.2示例 There’s a simple test page for the limits here.\n\n\n### 1.3 ie 升级\n\nUpdate: IE10 Platform Preview #2 significantly raises the limits described above. In IE10 (any browser/document mode):\n\n1. A sheet may contain up to 65534 rules\n2. A document may use up to 4095 stylesheets\n3. @import nesting is limited to 4095 levels (due to the 4095 stylesheet limit)","tags":["IE"]},{"title":"IE9-CSS的限制","url":"/2018/12/25/IE9-CSS的限制/","content":"\n## 1 前提\n\n在ie下css渲染不正确\n\n在《ie9记事(1)》中1.2提到ie9下css的长度限制,下面有具体的示例展示。\n具体参考microsoft网站上所写的[A webpage that uses CSS styles does not render correctly in Internet Explorer](https://support.microsoft.com/zh-cn/help/262161/a-webpage-that-uses-css-styles-does-not-render-correctly-in-internet-e)\n\n\n2.1-2.4是microsoft网站上的英文翻译,2.5是针对3条规则的示例展示\n\n---\n\n## 2 官方文档翻译\n\n### 2.0 项目中的问题描述\n\n同一个页面在chrome下面显示正常,但是在ie9下面显示不正常。后来定位到是样式没加载全乎。\n\n在chrome下面的main.css(工程的全部css样式文件)中关于.u-checkbox的样式有四处,但是ie9下main.css确只有三处,比对之后发现,在ie9的css中有个少了一个文件的样式,所以\n> 猜测:\n> \n> (1)因为在main.css通过import XXX.css方式引入文件,难道webpack打出来的dist文件没有main.css\n> \n> 解答:并不是,因为chrome,火狐下面是好使的,所以XXXcss肯定是打进来了\n> \n> (2)ie9下文件没有加载全\n> \n> 解答:是的,ie9下css文件没有加载全,有两种情况,1.文件本身没有加载全,比如415k的文件,只加载了250k;2.文件加载全了,但是样式没有起效,跟selector的个数有关。\n>\n>\n> 最终结论:ie9下的css长度有限制\n\n\n\n### 2.1 症状Symptoms\n\n在“Applies to”部分中(style、computed)列出的Microsoft Internet Explorer加载网页时,网页上的样式丢失或看起来不正确。\n\n注意,不管页面是使用内联样式或级联样式表,都会出现出现这样的问题。\n\n您也可能收到以下错误信息:\n> The page you are looking for might have been moved or had its name changed.\n\n\n### 2.2 起因cause\n\n此问题发生在Internet Explorer中,当是下面的情况的时候:\n\n1. All style tags after the first 31 style tags are not applied.前31个样式标签之后的所有样式标签都不被应。\n2. All style rules after the first 4,095 rules are not applied.前4095(不包含4095)个规则之后的所有样式规则不起效。\n3. 在使用@import规则连续导入导入其他样式表的外部样式表的页面上,忽略超过三个级别深的样式表。On pages that uses the @import rule to continously import external style sheets that import other style sheets, style sheets that are more than three levels deep are ignored.\n\n或者是\n\n1. A sheet may contain up to 4095 rules\n2. A sheet may @import up to 31 sheets\n3. @import nesting supports up to 4 levels deep\n\n\n> 总结下来就是:ie9下的css长度有限制\n> \n> 另外也有一些文章提到单个css文件的大小不能超过250k。官方规则中并没有提到这一条。\n\n\n\n### 2.3 更多信息More Information\n\n当Internet Explorer中加载的页面本身出现问题,网络监视工具可以指示出tcp请求重置了。然后,如果原始请求是POST请求,Internet Explorer会生成另一个POST请求。或者,Internet Explorer可以发送GET请求。\n\n这种style tag limitation也可能会影响使用.xsL文件。当.xsl文件在文档中嵌入了样式标记时,您试图查看.xml文件时,将收到以下错误消息:\n\n```javascript\n\nInternet Explorer could not open the Internet Site:\nfile://c:\\aaa.xml\n\n```\n\n\nWhen you click\nOK, you receive the following error message:\n\n```javascript\n\nThe page cannot be displayed\n\n```\n\n该代码示例,设置在“步骤来重现问题”部分可以动态创建样式表,并且生成以下错误消息:\n\n```javascript\nA Runtime Error has occurred.\nDo you wish to Debug?\n\nLine: 8\nError: Invalid argument.\n\n```\n\n\n\nIf the style sheets are not applied dynamically but are, instead, applied through` <Style> `tags or through .css files, the \"Invalid argument\" error message is not generated. In this case, all style sheets after the thirty-first style sheet are ignored.\nSteps to reproduce the problem\n\n如果不动态应用样式表,而是通过`<Style>`标记或通过.css文件应用,则不会生成“Invalid argument”错误消息。在这种情况下,忽略第三十一样式表之后的所有样式表。\n\n### 2.4 重现问题的步骤Steps to reproduce the problem\n\nPaste the following code sample in an HTML page. Run the code sample. An error is generated after the thirty-first style tag is applied.\n\n\n``` html\n<html>\n <head>\n <script>\n function fnCreateStyleSheets()\n {\n for (i=1 ; i <= 32; i++)\n {\n document.createStyleSheet()\n StyleSheetCount.innerText = \"Total Style Sheets = \" + i\n }\n }\n </script>\n </head>\n <body onLoad=\"fnCreateStyleSheets()\">\n <div id=\"StyleSheetCount\"></div>\n </body>\n</html>\n```\n### 2.5 示例\n\n#### 2.5.1 All style tags after the first 31 style tags are not applied.前31个样式标签之后的所有样式标签都不被应。\n\n\n\n```\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n\n<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n <title>\n\n </title>\n <style type=\"text/css\">\n div {\n color: Red;\n }\n </style>\n <link href=\"./31selectors.aspx_files/Stylesheet01.css\" type=\"text/css\" rel=\"stylesheet\">\n <link href=\"./31selectors.aspx_files/Stylesheet02.css\" type=\"text/css\" rel=\"stylesheet\">\n <link href=\"./31selectors.aspx_files/Stylesheet03.css\" type=\"text/css\" rel=\"stylesheet\">\n <link href=\"./31selectors.aspx_files/Stylesheet04.css\" type=\"text/css\" rel=\"stylesheet\">\n <link href=\"./31selectors.aspx_files/Stylesheet05.css\" type=\"text/css\" rel=\"stylesheet\">\n <link href=\"./31selectors.aspx_files/Stylesheet06.css\" type=\"text/css\" rel=\"stylesheet\">\n ...\n <link href=\"./31selectors.aspx_files/Stylesheet32.css\" type=\"text/css\" rel=\"stylesheet\">\n <link href=\"./31selectors.aspx_files/Stylesheet33.css\" type=\"text/css\" rel=\"stylesheet\">\n <link href=\"./31selectors.aspx_files/Stylesheet34.css\" type=\"text/css\" rel=\"stylesheet\">\n</head>\n\n<body>\n <span>If stylesheet is loaded, it should be colored green. IE loads only 30 because we have a style tag.</span>\n <div>\n <div class=\"class1\">Test Stylesheet 01</div>\n <div class=\"class2\">Test Stylesheet 02</div>\n <div class=\"class3\">Test Stylesheet 03</div>\n <div class=\"class4\">Test Stylesheet 04</div>\n <div class=\"class5\">Test Stylesheet 05</div>\n ...\n <div class=\"class29\">Test Stylesheet 29</div>\n <div class=\"class30\">Test Stylesheet 30</div>\n <div class=\"class31\">Test Stylesheet 31</div>\n <div class=\"class32\">Test Stylesheet 32</div>\n <div class=\"class33\">Test Stylesheet 33</div>\n <div class=\"class34\">Test Stylesheet 34</div>\n </div>\n</body>\n</html>\n\n```\n// css\n\n```\n// Stylesheet01.css\n.class1 \n{\n\tcolor: green;\n}\n\n// Stylesheet02.css\n.class2 \n{\n\tcolor: green;\n}\n...\n\n// Stylesheet30.css\n.class30\n{\n\tcolor: green;\n}\n// Stylesheet31.css\n.class31 \n{\n\tcolor: green;\n}\n...\n```\n\n> Test Stylesheet 30 还是link中的样式,字体颜色是绿色,\n\n>但是Test Stylesheet 31的样式才不是link 31.css中样式了,是红色的。说明Stylesheet31.css没起效\n\n\n#### 2.5.2 All style rules after the first 4,095 rules are not applied.前4095(不包含4095)个规则之后的所有样式规则不起效。\n\n\n\n```\n<html>\n <title></title>\n <link type=\"text/css\" rel=\"Stylesheet\" href=\"./4095issues_files/4095issues.css\">\n</head>\n<body>\n All rows should be with red background if the corresponding selector has any effect. <br>\n In IE only 4094 rows are with red background because we have two selectors in rule 4094 (one dummy).\n <div class=\"redClass0001\">Test 0001</div>\n <div class=\"redClass0002\">Test 0002</div>\n <div class=\"redClass0003\">Test 0003</div>\n <div class=\"redClass0004\">Test 0004</div>\n <div class=\"redClass0005\">Test 0005</div>\n <div class=\"redClass0006\">Test 0006</div>\n ...\n <div class=\"redClass4092\">Test 4092</div>\n <div class=\"redClass4093\">Test 4093</div>\n <div class=\"redClass4094\">Test 4094</div>\n <div class=\"redClass4095\">Test 4095</div>\n <div class=\"redClass4096\">Test 4096</div>\n <div class=\"redClass4097\">Test 4097</div>\n <div class=\"redClass4098\">Test 4098</div>\n <div class=\"redClass4099\">Test 4099</div>\n <div class=\"redClass4100\">Test 4100</div>\n <div class=\"redClass4011\">Test 4011</div>\n</body>\n</html>\n\n```\n// css\n\n```\n.redClass0001 { background: red }\n.redClass0002 { background: red }\n.redClass0003 { background: red }\n.redClass0004 { background: red }\n.redClass0005 { background: red }\n.redClass0006 { background: red }\n.redClass0007 { background: red }\n.redClass0008 { background: red }\n...\n.redClass4093 { background: red }\n.redClass4094,\n.pinkClass4099 { background: red } /* The limit is obviously 4095 selectors which is much easier to hit than 4095 rules. */\n.redClass4095 { background: red }\n.redClass4096 { background: red }\n.redClass4097 { background: red }\n.redClass4098 { background: red }\n.redClass4099 { background: red }\n.redClass4100 { background: red }\n```\n> 4094行是红色背景,但是4099行并不是,说明看着选择器个数起效果\n\n\n\n### 1.6 参考文献References\n\n常见的解决回答\n\n[IE9引发的血案-如何处理webpack打包后体积依然过大的css文件](https://blog.csdn.net/Napoleonxxx/article/details/80292006)\n\n[does-ie9-have-a-file-size-limit-for-css](https://stackoverflow.com/questions/11080560/does-ie9-have-a-file-size-limit-for-css/11080846)\n\nMiscrosoft官网上的规则[Rule](https://support.microsoft.com/zh-cn/help/262161/a-webpage-that-uses-css-styles-does-not-render-correctly-in-internet-e) \n\n\n\nFor more information, visit the following Microsoft Developer Network (MSDN) websites:\n\n\n[\"STYLE element | style Object\"](http://msdn.microsoft.com/en-us/library/ms535898(VS.85).aspx)\n\n[\"addRule Method\"](http://msdn.microsoft.com/en-us/library/aa358796(VS.85).aspx)\n\n[\"@import Rule\"](http://msdn.microsoft.com/en-us/library/ms530768(VS.85).aspx)\n\n","tags":["IE"]},{"title":"IE9-上传图片","url":"/2018/12/25/IE9-上传图片/","content":"\n## 前言\n这里记录一下上传功能,,不仅仅适用于ie9\n\n## 1 上传\n\n上传一直是很麻烦的事情,还好我有源码\n\n\n### 1.1 非IE9,利用input的upload来上传图片\n在chrome firefox下面的上传图片可以采用input的upload操作.代码如下\n\n\n```javascript\n<span className=\"iconOuter\" onClick={e => uploadImage(e)}>\n <input type=\"file\" id=\"btn_file2\" accept=\"image/x-png,image/gif,image/jpeg,image/bmp\" onChange={imgChange} style={{ display: 'none' }} />\n <Icon type=\"add\" className=\"iconAdd\"></Icon>\n</span>\n```\n> 其实上面的代码只显示icon 加号➕\n\n\n```javascript\n \n uploadImage = () => {\n this.actualInput = document.getElementById('btn_file2'); //上面input的id\n document.getElementById('btn_file2').click();\n }\n \n imgChange = (e) => {\n let _this = this;\n if (e.target.value.trim().length === 0) {\n this.setState({\n imgWarning: '请上传图片',\n });\n }\n const val = e.target.value && e.target.value.substr(e.target.value.lastIndexOf('.'));\n if (val && !val.match(/.jpg|.gif|.png|.bmp|.svg/i)) {\n this.setState({\n imgWarning: '必须是一个图片',\n });\n return false;\n }\n this.setState({\n imgWarning: false,\n });\n if (navigator.userAgent.indexOf('MSIE 9.0') > 0) {\n //ie9的时候逻辑,此处省略;具体见下面\n }\n //start:关键代码\n const [obj] = document.getElementById('btn_file2').files; //这里的上传的图片是在dialog中的\n const imgUrl = window.URL.createObjectURL(obj);\n // document.getElementById('imgSrc2').src = imgUrl;\n // 创建对象\n const imgObj = new Image();\n // 改变图片的src\n imgObj.src = imgUrl;\n // 加载完成执行\n imgObj.onload = function () {\n if (imgObj.width !== imgObj.height || imgObj.width !== 120 || obj.size > 5000000) {\n openMess({\n title: '上传图标失败!',\n content: '图标尺寸须为120*120,且不大于5M!',\n duration: 6,\n type: 'danger',\n closable: false,\n });\n return false;\n }\n const { uploadApplication, requestError } = _this.props;\n const from = new FormData();\n from.append('btn_file2', obj);\n //end:关键代码\n uploadApplication(from).then(({ error, payload }) => {\n if (error) {\n _this.actualInput.value = null;\n requestError(payload);\n } else if (payload && payload.url) {\n // document.getElementById('imgSrc2').src = payload.url;\n _this.setState({\n newApplicationIcon: payload.url,\n });\n }\n });\n return true;\n };\n\n }\n```\n\n\n### 1.2 IE9,使用input+form+iframe,\n\ninput的onchange触发form的submit,然后监听iframe的onload事件,获取ifame中内容,其实就是上传后图片的地址\n\n具体代码如下(es6+react)\n\n```javascript\n<div className={iconDialogContent}>\n {\n navigator.userAgent.indexOf('MSIE 9.0')> 0 &&\n (\n <div className=\"ie9_form\" style={{ position: 'relative' }}>\n <div className=\"hidden_form\" >\n <form\n id=\"upload-form\"\n name=\"myform\"\n action=\"/manager/file/upload/oss/workbench-image-path-applicationIcon\"\n method=\"post\"\n target=\"frameUpload\"\n acceptCharset=\"utf-8\"\n encType=\"multipart/form-data\"\n >\n <input id=\"btn_file2\" className={formBtnFile} type=\"file\" name=\"file\" accept=\"image/x-png,image/gif,image/jpeg,image/bmp\" onChange={e => imgChange(e)} />\n </form>\n <iframe id=\"frameUpload\" title=\"frameUpload\" name=\"frameUpload\" style={{ width: 0, height: 0, opacity: 0 }} />\n </div>\n <span className=\"iconOuter\">\n <Icon type=\"add\" className=\"iconAdd\"></Icon>\n </span>\n </div>\n )\n }\n\n</div>\n```\n\n> 其实上面的代码只显示icon 加号➕\n\n\n```javascript\n\n// 下面是关键的三个方法,\n// 第一个是chrome等下的spanclick方法:uploadImage\n// 第二个是input的imgchange(),,\n// 最后一个是ie9下的iframe的onload方法:\n\nuploadImage = () => {\n this.actualInput = document.getElementById('btn_file2');\n this.actualInput.click();\n}\n imgChange = (e) => {\n let _this = this;\n if (e.target.value.trim().length === 0) {\n this.setState({\n imgWarning: '请上传图片',\n });\n }\n const val = e.target.value && e.target.value.substr(e.target.value.lastIndexOf('.'));\n if (val && !val.match(/.jpg|.gif|.png|.bmp|.svg/i)) {\n this.setState({\n imgWarning: '必须是一个图片',\n });\n return false;\n }\n this.setState({\n imgWarning: false,\n });\n\n if (navigator.userAgent.indexOf('MSIE 9.0') > 0) {\n const formVal = document.getElementById('upload-form');\n formVal.submit();\n if (window.attachEvent) {\n document.getElementById('frameUpload').attachEvent('onload', this.handleOnLoad);\n } else {\n document.getElementById('frameUpload').addEventListener('load', this.handleOnLoad);\n }\n return true;// 后面的不再执行了\n }\n ....省略\n }\n\n handleOnLoad = () => {\n let _this = this;\n const frame = document.getElementById('frameUpload');\n const resp = {};\n const content = frame.contentWindow ?\n frame.contentWindow.document.body :\n frame.contentDocument.document.body;\n \n if (!content) throw new Error('Your browser does not support async upload');\n resp.responseText = content.innerHTML || 'null innerHTML';\n resp.json = JSON.parse(resp.responseText) || eval(`(${resp.responseText})`);\n \n const dataBack = resp.json || resp.responseText;\n \n if (dataBack) {\n // document.getElementById('imgSrc2').src = dataBack.data.url;\n // 创建对象\n const imgObj = new Image();\n // 改变图片的src\n // alert('dataBack.data.url'+dataBack.data.url)\n imgObj.src = dataBack.data.url;\n // 加载完成执行\n imgObj.onload = function () {\n if (imgObj.width !== imgObj.height || imgObj.width !== 120) {\n openMess({\n title:'上传图标失败!',\n content: '图标尺寸须为120*120,且不大于5M!',\n duration: 6,\n type: 'warning',\n closable: false,\n });\n return false;\n }\n _this.setState({\n newApplicationIcon: dataBack.data.url,\n });\n }\n }\n }\n\n```\n\nform+input+iframe针对的场景\n- 1.可以使跨域的,比如这里的上传图片的url是\n\n```\nhttps://pubapi.yonyoucloud.com/file/upload/oss/workbench-image-path-applicationIcon\n\n```\n\n- 2.单张图片上传\n","tags":["IE"]},{"title":"git操作--代码迁移仓库和批量删除分支","url":"/2018/10/22/git新操作备注/","content":"\n使用git进行工程的操作,长期下来git pull 、git add 、git push、就可以完成日常所需,但是近期有些其他的操作,记录下来都是亲身测试有效的\n\n<!-- more -->\n\n### 1.开个新分支\n(1)如果新建一个以develop内容为基础的分支\n\n```\ngit checkout develop \ngit checkout -b yx0628develop_new\n\n```\n\n\n不断修改不断的保存也是可以生成log的\n\n\n```\n git add . //这是提交全部修改,慎重 \n git commit -m'全部修改提交'\n```\n\n\n(2)把新分支的代码合并到develop\n 前提:先把新分支yx0628develop_new的代码add完毕,commit完毕\n \n\n\n```\n - git checkout develop \n - git merge yx0628develop_new\n无冲突\n - git push \n有冲突\n - 解决冲突\n - git add 冲突文件\n - git commit -m'随便你想写什么'\n - git push \n```\n\n\n### 2.代码迁移仓库(保留log)\n不保留log等提交的记录的迁移就不说了,soeasy!\n> 直接新建一个仓库,将代码复制粘贴进去就可以\n\n\n目标:把A仓库的代码迁移到B仓库并且保存所有的git log,B仓库已经存在了哦,哪怕是个空仓库。\n\n再说这个之前先说点其他。在使用git的时候我们可能见到这样的命令。不想看解说想直接看步骤的,[直接步骤](#1)\n\n#### 2.1 前提\n\n\n```\ngit pull origin master\n```\n //拉取远程master分支的代码\n\n```\ngit push origin master\n\n```\n //把代码推到远程master分支上去\n\n有没有人好奇为什么是origin,而不是其他名字,比如git pull original master或者git pull origin2 master;\n\n解答上面的问题很简单,请在你的工程中输入\n\n\n``` javascript\ngit remote\n\n// 或者\n//查看详情\ngit remote -v\n\n```\n\n\n发现了什么呢?\n默认就有一个origin,代表远程仓库。origin是有地址,地址就是当前仓库的git地址,是个url哦。所以为什么git push origin master就自动相应的推到的远程仓库的master分支了。\n\n#### 2.2 开始迁移\n\n下面是正确的操作步骤,(看准情况分类很重要,就两种)<\n不管哪种情况请从情况1开始看哈哈:\n\n##### 2.2.1 情况1\n**B仓库是一个空仓库,除了默认的master分支,没有任何分支**。把A的branch1,branch2,branch3...依次迁入B,B也就有branch1,branch2,branch3,\n\n - 进入A工程\n - `git remote ` \n > 原因:看下当前远程仓库有啥名字,然后取个崭新的名字,不重复的名字,名字是过渡,不必纠结。这里取名origin2\n\n - `git remote add origin2 master`\n > 不纠结照抄这就话\n\n - ` git remote set-url origin2 [email protected]:B.git`\n > 原因:后面的B仓库地址url才是关键,origin2只是过渡,百人百种起法\n\n - 进入A工程的branch1分支上\n - `git pull `\n > 原因:拉取一下最新代码\n\n - `git checkout -b branchB1 `\n > 原因:branchB1是基于A工程branch1开的新分支,代码跟A工程branch1一毛一样,**但是这个名字branchB1非常重要,非常重要**,原因只有一个:这个分支会被推到B工程,结果就是B工程下面就有这个分支。所以你懂的,万一你的B工程下面已经有了该分支名字,你这个做了好多工作的branchB1是根本推不上去的,是不是很疯狂\n\n - `git push origin2`\n \n > 结果:这一步能不能成功就看上一步,不多说,就看你取名字瞎不瞎\n\n\n如果上面的结束,说明已经成功迁移出一个分支了。常见问题:\n\n疑问1:问其他的分支怎么做,请重复\n - 进入A工程的branch2分支上\n - git pull \n - git checkout -b branchB2\n - git push originB2\nbranch2 变成branchB2然后被推到B仓库\n\n疑问2:你还问我第三个分支怎么迁移,不解释啦实在不行哎我还能说啥。\n\n疑问3: 我打眼一看B工程中的分支名字都是branchB1,branchB2....怎么破,\n改分支的名字直接登陆github.com,打开工程你会看到branches这个,点进去可以修改.至于改名字,代码都迁移过去了,改个名字还很远吗\n\n##### 2.2.2 情况2\n**B仓库不是一个空仓库,里面各种各样乱七八糟的分支**。\n\n把A的branch1,branch2,branch3...一次迁入B,B也就有branch1,branch2,branch3,\n\n>呃呃呃呃呃,步骤跟上面一毛一样,能不能推成功,就看你起名字的功夫了。什么名字,就是你最后要推到B工程上去的分支名字,注意在这里.\n\n\n### 3.删除本地分支\n\n看到第一个方法,在工程中新建很多本地的分支,时间一长,本地就有很多分支,如何批量删除呢?\n\n\n```\nyaoxindeMacBook-Pro:cloud-os_manager_fe yaoxin$ git branch\n daily-manager\n develop\n online\n* release\n yx20181204release_style2\n yx20181206release_newfunc\n yx20181207online_self\n yx20181207release_yyref\n yx20181217release_ref\n yx20181221release_role\n yxrelease20181209_openservice\n yxrelease20181213_canzhap\n \n```\n\n要删除本地,首先要考虑以下三点\n- \n- 列出所有本地分支\n- 搜索目标分支如:所有含有‘yx201812’的分支\n- 将搜索出的结果传给删除函数\n- 所以我们可以得到:\n\n\n```\ngit branch |grep 'yx201812' |xargs git branch -d\n```\n\n\n### 4 git查看镜像源\n\n有时候`npm install`的下载sudu太慢会使用国内淘宝镜像源,有时候还需要切回npm的官方镜像源,那么如何查看呢。\n\n```javascript\n //查看镜像使用状态:\n npm get registry\n\n //全局切换镜像: \n npm config set registry http://registry.npm.taobao.org/\n\n //全局切换回官方源 \n npm config set registry http://www.npmjs.org\n\n```\n \n","tags":["git"]},{"title":"高阶组件入门(1)","url":"/2018/10/01/react_hoc_1/","content":"\n\n高阶组件最大的特点就是重用组件逻辑。它并不是由React API定义出来的功能,而是由React的组合特性衍生出来的一种设计模式。\n之前看过几篇文章介绍高阶组件,这篇文章就是动手操作一番,有时候还不得不吐槽,说好能获得到的属性跟方法为什么没有获取到?\n\n<!-- more -->\n\n一、高阶组件定义\n\n> a higher-order component is a function that takes a component and returns a new component.\n> 翻译:高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。\n\n还有一种写法就是:\n\n> hocFactory:: W: React.Component => E: React.Component\n> 其中 W (WrappedComponent) 是指被包裹的 React.Component,\n> E (EnhancedComponent) 指返回类型为 React.Component 的新的 HOC。\n\n二、常见的HOC 实现方式(两种)\n\n> (1)Props Proxy(属性代理): HOC 控制传给WrappedComponent W 的 props\n> \n> 下面是用途:\n\n - 更改 props \n - 抽象 state \n - 通过 refs 获取组件实例 \n - 把 WrappedComponent 与其它 elements包装在一起\n\n> (2)Inheritance Inversion(反向继承): HOC 继承WrappedComponent W 的生命周期、state、各种function\n> \n> 下面是用途:\n\n - 渲染劫持(Render Highjacking)\n - 操作 state\n\n\n看到上面的这些官方介绍也许会有点懵,但是总言而之:高阶组件就是牵扯到两个组件的事情,那么就有下面的两个问题:\n1.高阶组件EnhanceComponent能否将props,state,function传递给wrappedComponent?(看完本篇就会得到答案:能,必须能啊。全部当成props传给WrappedComponent了)\n2.组件wrappedComponent能否将props,state,function传递给EnhancedComponent?\n\n下面介绍开始...\n三、Props Proxy (属性代理)— **操作props**\n(3.1)更改props\n主要是高阶组件HOC控制传入wrappedComponent的props,来修改wrappedComponent内的props。\n如下图所示:\n\n图3.1.1\nHOC将{...this.props},{...newProps}传递给WrappedComponent\n\n![这里写图片描述](http://img.blog.csdn.net/20171102212149019?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2FuZGVyX3Bvb2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)\n\n图3.1.2\nWrappedComponent中的参数,这里不仅仅有从HOC传来的参数,还有WrappedComponent(即Base)中自带的参数。\n\n![这里写图片描述](http://img.blog.csdn.net/20171102212305710?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2FuZGVyX3Bvb2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)\n\n**注意,若是props中有重名的字段,那么HOC的props将会覆盖WrappedComponent中的props.**\n**总言而之:你可以『读取,添加,修改,删除』将要传递给 WrappedComponent 的 props。**\n\n(3.2)抽象 state \n这里不是通过ref获取state, 而是通过 { props, 回调函数 } 传递给wrappedComponent组件,通过回调函数获取state。这里用的比较多的就是react处理表单的时候。通常react在处理表单的时候,一般使用的是受控组件([官方文档](https://reactjs.org/docs/forms.html#controlled-components)),即把input都做成受控的,改变value的时候,用onChange事件同步到state中。*当然这种操作通过Container组件也可以做到,具体的区别放到后面去比较(这点我也没有弄明白呢)*。看一下代码就知道怎么回事了:\n\n```javascript\n// 普通组件Login,这里充当WrappedComponent\nimport React, { Component } from 'react';\nimport formCreate from './form-create';\n\n@formCreate //这个用法是es7语法,下面有文章链接\nexport default class Login extends Component {\n render() {\n return (\n <div>\n <div>\n <label id=\"username\">\n 账户\n </label>\n <input name=\"username\" {...this.props.getField('username')}/>\n </div>\n <div>\n <label id=\"password\">\n 密码\n </label>\n <input name=\"password\" {...this.props.getField('password')}/>\n </div>\n <div onClick={this.props.handleSubmit}>提交</div>\n <div>other content</div>\n </div>\n )\n }\n}\n\n//HOC,高阶组件从这里开始\nimport React, { Component } from 'react';\n\nconst formCreate = WrappedComponent => class extends Component {\n\n constructor() {\n super();\n this.state = {\n fields: {},\n }\n }\n //是下面方法 getField()用到的\n onChange = key => e => {\n const { fields } = this.state;\n fields[key] = e.target.value;\n this.setState({\n fields,\n })\n }\n \n handleSubmit = () => {\n console.log(this.state.fields);\n }\n \n getField = fieldName => {\n return {\n onChange: this.onChange(fieldName),\n }\n }\n \n render() {\n const props = {\n ...this.props,\n handleSubmit: this.handleSubmit,\n getField: this.getField,\n }\n return (<WrappedComponent\n {...props}\n />);\n }\n};\nexport default formCreate;\n```\n**关键:这里我们把state,onChange等方法都放到HOC里**,其实是遵从的react组件的一种规范,子组件简单,傻瓜,负责展示,逻辑与操作放到Container。比如说我们在HOC获取到用户名密码之后,再去做其他操作,就方便多了,而state,处理函数放到Form组件里,只会让Form更加笨重,承担了本不属于它的工作,这样我们可能其他地方也需要用到这个组件,但是处理方式稍微不同,就很麻烦了。\n\n(3.3)通过 refs 获取组件实例\n当我们包装WrappedComponent的时候,想获取到它的实例怎么办,可以通过引用(ref),在WrappedComponent组件挂载的时候,会执行ref的回调函数,在HOC中取到组件的实例。通过打印,可以看到它的props, state,都是可以取到的。\n\n```javascript\nimport React, { Component } from 'react';\n\nconst refHoc = WrappedComponent => class extends Component {\n\n componentDidMount() {\n console.log(this.instanceComponent, 'instanceComponent');\n }\n\n render() {\n return (<WrappedComponent\n {...this.props}\n ref={instanceComponent => this.instanceComponent = instanceComponent}\n />);\n }\n};\n\nexport default refHoc;\n```\n(3.4)把 WrappedComponent 与其它 elements包装在一起\n这一点就是很简单且容易理解:出于操作样式、布局或其它目的,你可以将 WrappedComponent 与其它组件包装在一起。*一些基本的用法也可以使用正常的父组件来实现*。\n\n> 此处看一下父组件能做和不能做的事情(与高阶组件对比):\n>1.操作 props\n>2.抽象 state。但是有缺点,不能再父组件外获取到它的 state,除非明确地实现了钩子。\n>3.与新的 React Element 包装。这似乎是唯一一点,使用父组件要比高阶组件强,但高阶组件也同样可以实现。\n>4.Children 的操控。如果 children 不是单一 root,则需要多添加一层来包括所有 children,可能会使你的 markup 变得有点笨重。使用高阶组件可以保证单一 root。\n>5.父组件可以在元素树立随意使用,它们不像高阶组件一样限制于一个组件。\n\n通常来讲,能使用父组件达到的效果,尽量不要用高阶组件,因为高阶组件是一种更 hack 的方法,但同时也有更高的灵活性。\n下一篇会继续介绍一下II\n\n[高阶组件的es7语法@装饰器](https://github.com/iuap-design/blog/issues/128)\n","tags":["react","react-hoc"]},{"title":"高阶组件入门(2)","url":"/2018/10/01/react_hoc_2/","content":"\n(一)前篇回顾\n-------\n\n上篇文章[React高阶组件操作入门(一)](https://github.com/iuap-design/blog/issues/241)介绍了高阶组件的实现方式之一:PP(属性代理)。\n\n其实在第一篇提到,高阶组件的使用就是涉及到两个问题:\n\n1.高阶组件EnhanceComponent能否将props,state,function传递给wrappedComponent?\n2.组件wrappedComponent能否将props,state,function传递给EnhancedComponent?\n\n<!-- more -->\n在第一篇文章就是解答了第一个问题,答案是能!!HOC将HOCd的props,state,function,当作props传给WrappedComponent组件,WrappedComponent通过this.props取到这些内容,如下图:\n\n 图1-1 EnhacedComponent组件传值\n\n![这里写图片描述](http://img.blog.csdn.net/20171107205438033?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2FuZGVyX3Bvb2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)\n\n 图1-2 WrappedComponent组件接收\n![这里写图片描述](http://img.blog.csdn.net/20171107205535216?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2FuZGVyX3Bvb2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)\n\n(二)Inheritance Inversion(反向继承)\n------------------------------\n\n**反向继承可以解决上述的第二个疑问:**\n组件wrappedComponent能否将props,state,function传递给EnhancedComponent?\n\n这里是指EnhacedComponent继承了WrappedComponent组件的生命周期、state和function。按照上篇文章提到的两点用途出发来深入理解II。\n\n - 渲染劫持(Render Highjacking) \n - 操作 state\n\n\n (2.1)渲染劫持\n\n\n> 本质就是EnhacedComponent继承了WrappedComponent组件的生命周期函数,尤其是render()函数\n\n\n```javascript\n//Wrappedcomponent\nimport React, { Component } from 'react';\nclass Usual extends Component {\n constructor(props) {\n super(props);\n this.state = {\n usual: 'usual',\n }\n }\n\n componentDidMount() {\n console.log('didMount')\n }\n \n render() {\n console.log(this.props);\n\n return (\n <div>\n Usual\n </div>\n )\n }\n}\n\nexport default Usual;\n\n```\n\n\n```javascript\n//下面是HOC\nimport React, { Component } from 'react';\nconst iiHoc = WrappedComponent => class extends WrappedComponent {\n\t constructor(props){\n\t \tsuper(props);\n\t \tthis.state = {\n\t \t\t...this.state,\n\t \t}\n\t }\n\t\n\t alertFunc = () =>{\n\t \talert(\"HOC\");\n\t }\n\n render() {\n \n return <div>{super.render()}</div>\n \n }\n}\nexport default iiHoc;\n```\n这样会正常的渲染出来 Usual\n\n(2.2)操作 state\n\n\n> 本质就是EnhacedComponent继承了WrappedComponent组件的state\n\n\n\n 如图2-2-1通过this.state获取到WrappedComponent的state\n![这里写图片描述](http://img.blog.csdn.net/20171107211149475?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2FuZGVyX3Bvb2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)\n\n\n\n> 但是,关键是在EnhacedComponent的constructor中没有定义自己的state!!\n\n若是定义就会出现的问题如下图2-2-2所示\n\n 图2-2-2 EnhancedComponent的constructor中定义state,那么this.state的取值\n![这里写图片描述](http://img.blog.csdn.net/20171107211424840?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2FuZGVyX3Bvb2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)\n\nEnhacedComponent的state覆盖了Wrapped Component的state。这时候的怎么让两者并存呢?如下图2-2-3所示\n\n 图2-2-3 EnhacedComponent的state与WrappedComponent的state并存\n![这里写图片描述](http://img.blog.csdn.net/20171107211543953?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2FuZGVyX3Bvb2w=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)\n\n(2.3)function的继承\n这里就不多说,通过this.functionName()就可以调用到WrappedComponent中的方法。**但是有一个注意的:若是EnhancedComponent中的方法与WrappedComponent的方法重名,那么EnhacedComponent函数覆盖Wrapped Component函数。**\n\n(三)此处看一下父组件与高阶组件对比\n------------------\n\n有些同学可能会觉得高阶组件有些类似父组件的使用。例如,我们完全可以把高阶组件中的逻辑放到一个父组件中去执行,执行完成的结果再传递给子组件。从逻辑的执行流程上来看,高阶组件确实和父组件比较相像,但是高阶组件强调的是逻辑的抽象。高阶组件是一个函数,函数关注的是逻辑;父组件是一个组件,组件主要关注的是UI/DOM。如果逻辑是与DOM直接相关的,那么这部分逻辑适合放到父组件中实现;如果逻辑是与DOM不直接相关的,那么这部分逻辑适合使用高阶组件抽象,如数据校验、请求发送等。\n\n(四)使用高阶组件遇到的问题\n--------------\n(4.1)静态方法丢失\n无论PP还是II的方式,WrappedComponent的静态方法都不会复制,如果要用需要我们单独复制。因为高阶组件返回的新组件,是不包含被包装组件的静态方法。\n具体的解决方案如下\n\n> [静态方法解决方式](https://segmentfault.com/a/1190000010845410)\n\n(4.2)refs不会传递。 \n意思就是HOC里指定的ref,并不会传递到子组件,如果你要使用最好写回调函数通过props传下去。\n\n(4.3)不要在render方法内部使用高阶组件。\n简单来说react的差分算法会去比较 NowElement === OldElement, 来决定要不要替换这个elementTree。。因为高阶组件每次都会返回一个新的组件,在render中使用会导致每次渲染出来的组件都不相等(===),于是每次render,组件都会卸载(unmount),然后重新挂载(mount),既影响了效率,又丢失了组件及其子组件的状态。高阶组件最适合使用的地方是在组件定义的外部,这样就不会受到组件生命周期的影响了。\n\n(4.4)最重要的原则就是,注意高阶组件不会修改子组件,也不拷贝子组件的行为。\n高阶组件只是通过组合的方式将子组件包装在容器组件中,是一个无副作用的纯函数\n\n(4.5)要给hoc添加class名,便于debugger。\n当我们在chrome里应用React-Developer-Tools的时候,组件结构可以一目了然,所以DisplayName最好还是加上。\n\n```javascript\nconst getDisplayName = component => component.displayName || component.name\nfunction hoc(WrappedComponent){\n return class HOC extends Component {\n static displayName = `HOC(${getDisplayName(WrappedComponent)})`\n constructor(props) {\n }\n \n componentWillMount() { \n\n render() {\n return ()\n }\n }\n}\nexport default hoc;\n```\n\n> [refs不会传递解决方法](https://segmentfault.com/a/1190000010845410)\n\n\n(五)依然存留的疑问\n----------\n\n1.上述两个问题中的第二个问题:.组件wrappedComponent能否将props,state,function传递给EnhancedComponent?中没有解答是wrappedComponent能否将props传递给EnhancedComponent,这个没有找到资料,也许这是没有必要的操作。根据后续的深入开发以及学习中,也许会解答这个问题。\n2.属性的校验\n我们知道PropsTypes有着属性校验,那么EnhacedComponent与WrappedComponent之间的传值可以走属性校验吗?\n\n\n\n","tags":["react","react-hoc"]},{"title":"JS作用域系列—原型3","url":"/2018/09/28/JS作用域系列-原型3/","content":"\n前两篇的总结\n\n<!--more-->\n\n## 1 函数对象\n理解:本文的构造器指的是函数对象\n### 1.1内置的函数对象\n所有**函数对象**的_proto_都指向Function.prototype,它是一个空函数(Empty function)\n\n这句话有条件限制,先去找构造函数的原型对象\n```\nNumber.__proto__ === Function.prototype // true\nNumber.constructor == Function //true\n\nBoolean.__proto__ === Function.prototype // true\nBoolean.constructor == Function //true\n\nString.__proto__ === Function.prototype // true\nString.constructor == Function //true\n\n// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身\nObject.__proto__ === Function.prototype // true\nObject.constructor == Function // true\n\n// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身\nFunction.__proto__ === Function.prototype // true\nFunction.constructor == Function //true\n\nArray.__proto__ === Function.prototype // true\nArray.constructor == Function //true\n\nRegExp.__proto__ === Function.prototype // true\nRegExp.constructor == Function //true\n\nError.__proto__ === Function.prototype // true\nError.constructor == Function //true\n\nDate.__proto__ === Function.prototype // true\nDate.constructor == Function //true\n\n```\nJavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。\n\n剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建。\n\nMath,JSON是以对象形式存在的,无需new。它们的proto是Object.prototype。如下\n\n\n```\nMath.__proto__ === Object.prototype // true\nMath.construrctor == Object // true\n\nJSON.__proto__ === Object.prototype // true\nJSON.construrctor == Object //true\n\n```\n\n### 1.2 自定义的函数对象\n\n\n```\n// 函数声明\nfunction Person() {}\n// 函数表达式\nvar Perosn = function() {}\nconsole.log(Person.__proto__ === Function.prototype) // true\nconsole.log(Man.__proto__ === Function.prototype) // true\n\n```\n**所有的构造器(函数对象)都来自于 Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了==Function.prototype==的属性及方法。如length、call、apply、bind**\n\n### 1.3 原型对象类型\n\nFunction.prototype也是唯一一个typeof XXX.prototype为 function的prototype。\n\n\n```\nconsole.log(typeof Function.prototype) // function\nconsole.log(typeof Object.prototype) // object\nconsole.log(typeof Number.prototype) // object\nconsole.log(typeof Boolean.prototype) // object\nconsole.log(typeof String.prototype) // object\nconsole.log(typeof Array.prototype) // object\nconsole.log(typeof RegExp.prototype) // object\nconsole.log(typeof Error.prototype) // object\nconsole.log(typeof Date.prototype) // object\nconsole.log(typeof Object.prototype) // object\n\n```\n\n知道了所有构造器(含内置及自定义)的__proto__都是Function.prototype,那Function.prototype的__proto__是谁呢?\n\n相信都听说过JavaScript中函数也是一等公民,那从哪能体现呢?如下\n\n```\nconsole.log(Function.prototype.__proto__ === Object.prototype) // true\n```\n\n这说明所有的构造器的原型对象也都是一个普通对象,可以给构造器添加/删除属性等。同时它也继承了Object.prototype上的所有方法:toString、valueOf、hasOwnProperty等。\n\n最后Object.prototype的proto是谁?\n\n```\nObject.prototype.__proto__ === null // true\n```\n\n\n## 2 prototype和_proto_\n\n### 2.1 Object.prototype\n> 在 ECMAScript 核心所定义的全部属性中,最耐人寻味的就要数 prototype 属性了。对于 ECMAScript 中的引用类型而言,prototype 是保存着它们**所有实例方法的真正所**在。换句话所说,诸如 toString()和 valuseOf() 等方法实际上都保存在 prototype 名下,只不过是通过各自对象的实例访问罢了。\n> \n\n我们知道 JS 内置了一些方法供我们使用,比如:\n> - 对象可以用 constructor/toString()/valueOf() 等方法;\n> - 数组可以用 map()/filter()/reducer() 等方法;\n> - 数字可用用 parseInt()/parseFloat()等方法;\n\nWhy ???\n\n当我们创建一个函数时:\n\n```\nvar Person = new Object()\n```\n\nPerson 是 Object 的实例,所以 Person 继承了Object 的原型对象Object.prototype上所有的方法:\n\n```\nObject.prototype\n{\nconstructor: ƒ Object()\nhasOwnProperty: ƒ hasOwnProperty()\nisPrototypeOf: ƒ isPrototypeOf()\npropertyIsEnumerable: ƒ propertyIsEnumerable()\ntoLocaleString: ƒ toLocaleString()\ntoString: ƒ toString()\nvalueOf: ƒ valueOf()\n__defineGetter__: ƒ __defineGetter__()\n__defineSetter__: ƒ __defineSetter__()\n__lookupGetter__: ƒ __lookupGetter__()\n__lookupSetter__: ƒ __lookupSetter__()\nget __proto__: ƒ __proto__()\nset __proto__: ƒ __proto__()\n}\n\n```\n**Object 的每个实例都具有以上的属性和方法。**\n所以我可以用\n```\nPerson.constructor\n```\n 也可以用 \n```\nPerson.hasOwnProperty\n```\n\n### 2.2 Array.prototype\n\n当我们创建一个数组时:\n\n```\nvar num = new Array()\n```\n\nnum 是 Array 的实例,所以 num 继承了Array 的原型对象Array.prototype上所有的方法:\n\n\n```\n[\n concat: ƒ concat()\n constructor: ƒ Array()\n copyWithin: ƒ copyWithin()\n entries: ƒ entries()\n every: ƒ every()\n fill: ƒ fill()\n filter: ƒ filter()\n find: ƒ find()\n findIndex: ƒ findIndex()\n flat: ƒ flat()\n flatMap: ƒ flatMap()\n forEach: ƒ forEach()\n includes: ƒ includes()\n indexOf: ƒ indexOf()\n join: ƒ join()\n keys: ƒ keys()\n lastIndexOf: ƒ lastIndexOf()\n length: 0\n map: ƒ map()\n pop: ƒ pop()\n push: ƒ push()\n reduce: ƒ reduce()\n reduceRight: ƒ reduceRight()\n reverse: ƒ reverse()\n shift: ƒ shift()\n slice: ƒ slice()\n some: ƒ some()\n sort: ƒ sort()\n splice: ƒ splice()\n toLocaleString: ƒ toLocaleString()\n toString: ƒ toString()\n unshift: ƒ unshift()\n values: ƒ values()\n Symbol(Symbol.iterator): ƒ values()\n Symbol(Symbol.unscopables): {copyWithin: true, entries: true, fill: true, find: true, findIndex: true, …}\n __proto__: Object\n]\n```\n\n```\nnum.hasOwnProperty // ƒ hasOwnProperty() { [native code] }\n```\n当你用num.hasOwnPrototype()时,JS 会先查一下它的构造函数 (Array) 的原型对象 Array.prototype 有没有有hasOwnPrototype()方法,没查到的话继续查一下 Array.prototype 的原型对象 Array.prototype.__proto__有没有这个方法。\n\n\n### 2.3 原型对象赋值方式\n\n\n```\n所有对象的 __proto__ 都指向其构造器的 prototype\n\n所有函数对象的 __proto__ 都指向 Function.prototype,它是一个空函数(Empty function)\n\n```\n先看看 JS 内置构造器:\n```\nvar obj = {name: 'jack'}\nvar arr = [1,2,3]\nvar reg = /hello/g\nvar date = new Date\nvar err = new Error('exception')\n \nconsole.log(obj.__proto__ === Object.prototype) // true\nconsole.log(arr.__proto__ === Array.prototype) // true\nconsole.log(reg.__proto__ === RegExp.prototype) // true\nconsole.log(date.__proto__ === Date.prototype) // true\nconsole.log(err.__proto__ === Error.prototype) // true\n\n```\n再看看自定义的构造器,这里定义了一个 Person:\n\n\n```\nfunction Person(name) {\n this.name = name;\n}\nvar p = new Person('jack')\nconsole.log(p.__proto__ === Person.prototype) // true\n```\n每个对象都有一个 constructor 属性,可以获取它的构造器,因此以下打印结果也是恒等的:\n\n\n```\nfunction Person(name) {\n this.name = name\n}\nvar p = new Person('jack')\nconsole.log(p.__proto__ === p.constructor.prototype) // true\n\n```\n上面的Person没有给其原型添加属性或方法,这里给其原型添加一个getName方法:\n\n\n```\nfunction Person(name) {\n this.name = name\n}\n// 修改原型\nPerson.prototype.getName = function() {}\nvar p = new Person('jack')\nconsole.log(p.__proto__ === Person.prototype) // true\nconsole.log(p.__proto__ === p.constructor.prototype) // true\n\nconsole.log(p.constructor) // ƒ Person(name) { this.name = name}\n```\n\n可以看到p.__proto__与Person.prototype,p.constructor.prototype都是恒等的,即都指向同一个对象。\n\n如果换一种方式设置原型,结果就有些不同了:\n\n\n```\nfunction Person(name) {\n this.name = name\n}\n// 重写原型\nPerson.prototype = {\n getName: function() {}\n}\nvar p = new Person('jack')\nconsole.log(p.__proto__ === Person.prototype) // true\nconsole.log(p.__proto__ === p.constructor.prototype) // false\n\nconsole.log(p.constructor) //ƒ Object() { [native code] }\n```\n\n这里直接重写了 Person.prototype(注意:上一个示例是修改原型)。输出结果可以看出p.__proto__仍然指向的是Person.prototype,而不是p.constructor.prototype。\n\n这也很好理解,给Person.prototype赋值的是一个对象直接量{getName: function(){}},使用对象直接量方式定义的对象其构造器(constructor)指向的是根构造器Object,Object.prototype是一个空对象{},{}自然与{getName: function(){}}不等。如下:\n\n上面的的最后一句暴露了\n\n```\nPerson.prototype !== Object.prototype\n```\n\n\n```\nvar p = {}\nconsole.log(Object.prototype) // 为一个空的对象{}\nconsole.log(p.constructor === Object) // 对象直接量方式定义的对象其constructor为Object\nconsole.log(p.constructor.prototype === Object.prototype) // 为true,不解释(๑ˇ3ˇ๑)\n```\n\n## 4 继承\n\n- 原型和原型链是JS实现继承的一种模型。\n- 原型链的形成是真正是靠__proto__ 而非prototype\n\n要深入理解这句话,我们再举个例子,看看前面你真的理解了吗?\n\n```\nvar animal = function(){};\n var dog = function(){};\n\n animal.price = 2000;\n dog.prototype = animal;\n var tidy = new dog();\n console.log(dog.price) //undefined\n console.log(tidy.price) // 2000\n```\n\n这里解释一下:\n\n \n```\nvar dog = function(){};\n dog.prototype.price = 2000;\n var tidy = new dog();\n console.log(tidy.price); // 2000\n console.log(dog.price); //undefined\n var dog = function(){};\n var tidy = new dog();\n tidy.price = 2000;\n console.log(dog.price); //undefined\n```\n\n这个明白吧?想一想我们上面说过这句话:\n\n> 实例(tidy)和 原型对象(dog.prototype)存在一个连接。不过,要明确的真正重要的一点就是,这个连接存在于实例(tidy)与构造函数的原型对象(dog.prototype)之间,而不是存在于实例(tidy)与构造函数(dog)之间。\n> \n\n## 5 解惑\n主要是Function和Object\n疑点解惑:\n\n- \n```\nObject.__proto__ === Function.prototype // true\n```\nObject 是函数对象,是通过new Function()创建的,所以Object.__proto__指向Function.prototype。「所有函数对象的__proto__都指向Function.prototype」)\n\n\n- \n```\nFunction.__proto__ === Function.prototype // true\n```\n\nFunction 也是对象函数,也是通过new Function()创建,所以Function.__proto__指向Function.prototype。\n\n自己是由自己创建的。\n\n\n- \n```\nFunction.prototype.__proto__ === Object.prototype //true\n```\nJS一直强调万物皆对象,函数对象也是对象,给他认个祖宗,指向Object.prototype。Object.prototype.__proto__ === null,保证原型链能够正常结束。\n\n","tags":["javascript"],"categories":["总结"]},{"title":"JS作用域系列—原型2","url":"/2018/09/28/JS作用域系列-原型2/","content":"\n\n\n<!--more-->\n## 0 摘要\n\n前面原型1主要讲述了几个概念,分为函数对象和普通对象,从函数对象引申出来的实例对象和原型对象。函数对象和普通对象就是所有的对象分类,实例对象和原型对象的是函数对象还是普通对象? 答案是:既可以是普通对象,但是也可能是函数对象。具体看前面,不再细讲\n\n## 1 _proto_\n前面主要讲了proptype属性和constructor属性,那么什么是_proto_\n\n### 1.1 _proto_指向原型对象\n\nJS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__ 的内置属性,**用于指向创建它的构造函数的原型对象**。\n\n对象 person1 有一个__proto__属性,创建它的构造函数是 Person,构造函数的原型对象是 Person.prototype ,所以:\n\n```javascript\nperson1.__proto__ == Person.prototype\n```\n请看下图\n\n![流程图](https://raw.githubusercontent.com/XYooo/image/master/yuanxing2.jpg)\n\n根据上面这个连接图,我们能得到:\n\n\n```javascript\nPerson.prototype.constructor == Person;\nperson1.__proto__ == Person.prototype;\nperson1.constructor == Person;\n```\n\n不过,要明确的真正重要的一点就是,这个连接存在于实例(person1)与构造函数(Person)的原型对象(Person.prototype)之间,而不是存在于实例(person1)与构造函数(Person)之间。\n\n> 注意:因为绝大部分浏览器都支持__proto__属性,所以它才被加入了 ES6 里(ES5 部分浏览器也支持,但还不是标准)\n\n\n### 1.2 总结-总结2\n\n1._proto_属性存在每个对象中\n2._proto_属性指向---------->该构造函数的原型对象\n3.prototype属性指向------->自身的原型对象\n4.都可以有_proto_属性,但是prototype属性只有函数对象才有\n\n\n## 2 原型链\n\n上述的总结1,总结2,说了种种关系,这些关系构成了原型链\n\n总结1如下:\n1.对象就分为普通对象,函数对象。原型对象属于普通对象(有特例)\n2.new一下就出现了构造函数跟实例,实例是普通对象(有特例6)\n3.实例都有一个contrutor属性,指向构造函数(有特例6)\n4.函数对象都有prototype属性,prototype属性指向原型对象(因此函数对象都有原型对象),原型对象是普通对象,原型对象中都有constructor属性,指向函数对象\n5.由3,4可得到因为都有constructor属性,因此原型对象也是实例\n6.但 Function.prototype 除外,它是函数对象,但它很特殊,他没有prototype属性(前面说道函数对象都有prototype属性)Function.prototype = new Function()//请看第一对象部分\n\n\n![流程图](https://raw.githubusercontent.com/XYooo/image/master/yuanxing1.jpg)\n\n\n- (Functions)函数对象一列都有_proto_属性(只要是对象就有),还是有prototype属性(因为是函数对象)。\n - _proto_指向构造函数的原型对象\n - prototype指向自身的原型对象 \n- (XX.prototype)原型对象那一列都有constructor属性,只有_proto_属性 \n - _proto_指向构造函数的原型对象\n- 实例那一列那一列只有_proto_属性 \n - _proto_指向构造函数的原型对象\n \n\n## 3 实践\n\n```javascript\nperson1.__proto__ 是什么?\nPerson.__proto__ 是什么?\nPerson.prototype.__proto__ 是什么?\nObject.__proto__ 是什么?\nObject.prototype__proto__ 是什么?\n\n```\n\n答案:\n第一题:\n- 因为 person1.__proto__ === person1 的构造函数.prototype\n- 因为 person1的构造函数 === Person\n- 所以 person1.__proto__ === Person.prototype\n\n第二题:\n- 因为 Person.__proto__ === Person的构造函数.prototype\n- 因为 Person的构造函数 === Function\n- 所以 Person.__proto__ === Function.prototype\n\n第三题:\n- Person.prototype 是一个普通对象,我们无需关注它有哪些属性,只要记住它是一个普通对象。\n- 因为一个普通对象的构造函数 === Object\n- 所以 Person.prototype.__proto__ === Object.prototype\n\n第四题,参照第二题,因为 Person 和 Object 一样都是构造函数\n\n第五题:\n- Object.prototype 对象也有proto属性,但它比较特殊,为 null 。\n- 因为 null 处于原型链的顶端,这个只能记住。\nObject.prototype.__proto__ === null\n\n","tags":["javascript"],"categories":["总结"]},{"title":"JS作用域系列—原型1","url":"/2018/09/28/JS作用域系列-原型1/","content":"\n\n<!--more-->\n\n综述:前面讲了作用域vs作用域链,可以知道作用域链是作用域的部分内容。下面讲原型和原型链~~。涉及几个概念,普通对象vs函数对象;构造函数vs实例,prototype属性vs原型对象\n\n注意:原型链总有例外,这里讲的都是100%可以推出来的,不存在特例\n\n## 1 对象\n\n### 1.1 对象就分两种\n\n在此之前,先普及一下js中的对象分类:**JavaScript中,万物皆对象!**\n\n但对象也是有区别的,**分为普通对象和函数对象**。Object 、Function 是 JS 自带的函数对象。\n\n### 1.2 对象创建方式\n\n我们可以简单的将创建对象的方式分为三种:\n1. 函数创建对象、\n2. 字面量创建、\n3. Object创建。\n\n### 1.3 普通对象和函数对象\n\n\n```javascript\n\ntypeOf(普通对象) === object\ntypeOf(函数对象)====Function\n\n```\n\n```javascript\n//demo1.js\nvar o1 = {}; \nvar o2 =new Object();\nvar o3 = new f1();\n\nfunction f1(){}; \nvar f2 = function(){};\nvar f3 = new Function('str','console.log(str)');\n\nconsole.log(typeof Object); //function \nconsole.log(typeof Function); //function \n\nconsole.log(typeof f1); //function \nconsole.log(typeof f2); //function \nconsole.log(typeof f3); //function \n\nconsole.log(typeof o1); //object \nconsole.log(typeof o2); //object \nconsole.log(typeof o3); //object\n\n```\n\n在上面的例子中 o1 o2 o3 为普通对象,f1 f2 f3 为函数对象。\n>怎么区分,其实很简单,凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。\n\n**f1,f2,归根结底都是通过 new Function()的方式进行创建的。Function Object 也都是通过 New Function()创建的。**\n\n\n### 1.4 new Function是个特例\n\n建议看完全篇之后再看这里\n\n\n```javascript\n\nvar f3 = new Function('str','console.log(str)');\nconsole.log(typeof f3); //function\nf3.constructor === Function; //true\n\n\n```\n\n1. f3是实例,是函数对象而不是普通对象\n\n```javascript\n\nconsole.log(typeof Function.prototype) ;//function\n\nFunction.prototype.construtor == Function; //true\n\nFunction.prototype.prototype; //undefine\n\n```\n2. 原型对象,是函数对象不是普通对象,且这个函数对象没有protype属性\n\n以上全都不符合总结部分的3.4.5条,请注意\n\n\n\n\n## 2 构造函数:函数对象变身构造函数\n\n一定要分清楚普通对象和函数对象,下面我们会常常用到它。\n\n### 2.1 new操作 vs 实例对象\n\n我们先复习一下构造函数的知识:\n\nnew一下就出现构造函数和实例了。\n\n```javascript\nfunction Person(name, age, job) {\n this.name = name;\n this.age = age;\n this.job = job;\n this.sayName = function() { alert(this.name) } \n}\nvar person1 = new Person('Zaxlct', 28, 'Software Engineer');\nvar person2 = new Person('Mick', 23, 'Doctor');\n\n```\n\n上面的例子中 person1 和 person2 都是 Person 的**实例**。\n这**两个实例都有一个 constructor** (构造函数)属性,该属性(是一个指针)指向 Person。 即:\n\n```javascript\n\nconsole.log(person1.constructor == Person); //true\nconsole.log(person2.constructor == Person); //true\n\n```\n\n我们要记住两个概念(构造函数,实例):\n\n- person1 和 person2都是 **构造函数 Person** 的实例。\n- 一个公式:实例的构造函数属性(constructor)指向构造函数。\n\n逆向理解,如果你用函数A去创造了A的实例a,那么A就化身成为‘构造函数’,实例a有个constructor属性(这个属性称之为构造函数属性)指向A\n\n### 2.2 实例对象是普通对象还是函数对象?\n\n## 3.原型对象:函数对象指定原型对象\n\n### 3.1 原型对象\n\n前面讲了普通对象、函数对象,下面是原型对象\n**原型对象,本质它就是一个 普通对象,从现在开始你要牢牢记住原型对象就是 A.prototype 。**\n\n```javascript\n//demo\nfunction Person() {}\n Person.prototype.name = 'Zaxlct';\n Person.prototype.age = 28;\n Person.prototype.job = 'Software Engineer';\n Person.prototype.sayName = function() {\n alert(this.name);\n}\n \nvar person1 = new Person();\nperson1.sayName(); // 'Zaxlct'\n\nvar person2 = new Person();\nperson2.sayName(); // 'Zaxlct'\n\nconsole.log(person1.sayName == person2.sayName); //true\n\n```\n\n那什么是原型对象呢?\n\n我们把上面的例子改一改你就会明白了:\n\n```javascript\nPerson.prototype = {\n name: 'Zaxlct',\n age: 28,\n job: 'Software Engineer',\n sayName: function() {\n alert(this.name);\n }\n}\n```\n上述代码非常简单,Person原型对象定义了公共的say方法。\n\n\n### 3.2 原型对象 vs prototype属性\n\n在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性 。\n**其中每个对象都有_propto_属性,但是只有函数对象才有prototype 属性,这个属性指向函数的原型对象。**\n1. 只有函数对象才有prototype属性\n2. 只有函数对象才有原型对象\n3. 或者说有prototype属性就有原型对象\n\n> 规则1:每个对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性,该属性指向原型对象,换句话说是有原型对象\n\n\n### 3.3 原型对象 vs constructor属性\n\n**在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person)**\n\n> 规则2:上面这句话有点拗口,我们「翻译」一下:有一个默认的 constructor 属性,这个属性是一个指针,指向 Person。即:\n\n> Person.prototype.constructor == Person\n\n\n\n### 3.4 原型对象是实例对象\n这是由3.3 、3.4推论而来\n在上面第二小节《构造函数》里,我们知道实例的构造函数属性(constructor)指向构造函数 :\n\n```javascript\nperson1.constructor == Person\n\n```\n\n这两个「公式」好像有点联系:\n\n```javascript\nperson1.constructor == Person\nPerson.prototype.constructor == Person\n\n```\nperson1 为什么有 constructor 属性?那是因为 person1 是 Person 的实例。\n那 Person.prototype 为什么有 constructor 属性??同理, Person.prototype (你把它想象成 A) 也是Person 的实例。\n**也就是在 Person 创建的时候,创建了一个它的实例对象并赋值给它的 prototype,基本过程如下:**\n\n**超级关键:**\n```javascript\nfunction Person(){};\n//隐形执行:\nPerson.prototype.constructor= Person;\n```\n可以理解为过程自然而然形成\n\n> 结论:原型对象(Person.prototype)是 构造函数(Person)的一个实例。\n> 再次重复一遍。原型对象还有一个必不可少的contructor属性。\n\n### 3.5 原型对象是普通对象还是函数对象?\n\n注意这一点无法100%类推,因为有个 new Fuction()\n\n#### 3.5.1 new Function()\n\n上面1.3就提到了,凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。Function Object 也都是通过 New Function()创建的。\n那么 Function创建的实例对象也是函数对象,\nFunction的原型对象是Function的实例对象,**因此这里的原型对象是函数对象,特别的是没有prototype属性**\n\n#### 3.5.2 其他\n\n我们知道什么对象是有constructor,那就是实例对象,所以原型对象Person.prototype是函数对象(Person)的一个实例。\n在函数对象创建的时候被创建的,因此也是一个普通对象,但是Function函数有点意外\n\n但 Function.prototype 除外,它不是普通对象它是函数对象。但它很特殊,他没有prototype属性(前面说道函数对象都有prototype属性,因此都有原型对象)\n\n可以按照4.2的图对一下\n\n### 3.5 用途\n**原型对象的用途是为每个实例对象存储共享的方法和属性,它仅仅是一个普通对象而已。**\n**并且所有的实例是共享同一个原型对象,因此有别于实例方法或属性,原型对象仅有一份。**(原型继承从此而来)所有就会有如下等式成立:\n\n```javascript\nperson.say == new Person().say\n```\n## 4 总结\n\n### 4.1 总结1\n\n1.对象就分为普通对象,函数对象。原型对象属于普通对象(有特例)\n\n2.new一下就出现了构造函数跟实例,实例是普通对象(有特例6)\n\n3.实例都有一个contrutor属性,指向构造函数(有特例6)\n\n4.函数对象都有prototype属性,prototype属性指向原型对象(因此函数对象都有原型对象),原型对象是普通对象,原型对象中都有constructor属性,指向函数对象\n\n5.由3,4可得到因为都有constructor属性,因此原型对象也是实例\n\n6.但 Function.prototype 除外,它是函数对象,但它很特殊,他没有prototype属性(前面说道函数对象都有prototype属性)Function.prototype = new Function()//请看第一对象部分\n\n\n### 4.2 总结图\n\n![流程图](https://raw.githubusercontent.com/XYooo/image/master/yuanxing1.jpg)\n\n## 5 参考\n\n[最详尽的 JS 原型与原型链终极详解,没有「可能是」。(一)](https://www.jianshu.com/p/dee9f8b14771)\n\n[一个例子让你彻底明白原型对象和原型链](https://www.jianshu.com/p/aa1ebfdad661)","tags":["javascript"],"categories":["总结"]},{"title":"JS作用域系列—闭包","url":"/2018/09/28/JS作用域系列-闭包/","content":"\n综述:作用域,受javascript链式作用域结构的影响,父级变量中无法访问到子级变量的值,为了解决这个问题,才使用的闭包。\n\n<!-- more -->\n## 1. 前言\n\n作用域,受javascript链式作用域结构的影响,父级变量中无法访问到子级变量的值,为了解决这个问题,才使用的闭包。\n\n闭包就是能够读取其他函数内部变量的函数。\n> (在JS中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解为”定义在一个函数内部的函数”。无论是在事件中,for循环中,还是在函数自调用中,只要return出来,便有闭包的应用)。\n\n\n\n## 2. 举例\n```\nfunction a()\n{\n\n var i=0;\n //函数b\n function b()\n {\n alert(++i);\n }\n return b;\n}\n //函数c\n var c = a();\n c();\n```\n\n### 2.1 代码特点:\n1. 函数b嵌套在函数a内部;\n2. 函数a返回函数b。\n\n代码中函数a的内部函数b,被函数a外面的一个变量c引用的时候,这就叫创建了一个闭包。有时候函数b也可以用一个匿名函数代替来返回,即return function(){};\n\n### 2.2 优点:\n\n1. 保护函数内的变量安全,加强了封装性\n\n\n2. 在内存中维持一个变量(用的太多就变成了缺点,占内存)\n\n> 闭包之所以会占用资源是当函数a执行结束后, 变量i不会因为函数a的结束而销毁, 因为b的执行需要依赖a中的变量。\n\n3. 逻辑连续,当闭包作为另一个函数调用的参数时,避免你脱离当前逻辑而单独编写额外逻辑。\n\n\n4. 方便调用上下文的局部变量。\n\n### 2.3 实践\n\n闭包的典型框架应该就是jquery了。\n\n闭包是javascript语言的一大特点,**主要应用闭包场合主要是为了:设计私有的方法和变量。**\n\n这在做框架的时候体现更明显,有些方法和属性只是运算逻辑过程中的使用的,不想让外部修改这些属性,因此就可以设计一个闭包来只提供方法获取。\n\n不适合场景:返回闭包的函数是个非常大的函数\n\n闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。\n\n\n## 3. 场景\n\n闭包的常用场景有\n1. 是函数作为返回值,\n2. 是函数作为参数来传递。\n3. 闭包会把函数中变量的值保存下来,供其他函数使用,这些变量会一直保存在内存当中,这样占用大量的内存,使用不当很可能造成内存泄漏,故要及时清除,清楚方法有两种,一是标记清除,二便是引用计数清除。\n\n\n不适用于\n1. 返回闭包的函数是个特别大的函数,很多高级应用都要依靠闭包实现.\n\n\n> **使用闭包的好处是不会污染全局环境,方便进行模块化开发,减少形参个数,延长了形参的生命周期,坏处就是不恰当使用会造成内存泄漏**","tags":["javascript"],"categories":["总结"]},{"title":"JS作用域系列—作用域链","url":"/2018/09/28/JS作用域系列-作用域链/","content":"\n综述:\n在《JS作用域系列—作用域》中有个EC对象,包含VO,this,和Scope。Scope就是作用域链,也是在执行上下文创建流程图中第二步就开始创建作用域链了\n \n<!-- more -->\n\n## 1. 执行上下文(执行环境)\n略,在《JS作用域系列—作用域》\n\n\n## 2. 作用域链\n\n### 2.1 作用域链用处\n\n在红宝书中对作用域链的描述有这么一段话:\n\n1. 当代码在一个环境中执行时,会创建变量对象的一个作用域链(作用域链指向变量对象)。所以作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端始终是当前执行的代码所在环境的变量对象。\n\n2. 如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象。作用域链的下一个变量对象来自包含环境,而在下一个变量对象则来自下一个包含环境。这样一直延续到全局执行环境;\n\n3. 全局执行环境的变量对象始终都是作用域链中的最后一个对象。 全局环境的变量对象始终存在,而局部环境的变量对象,则只在函数执行的过程中存在。\n\n> 活动对象 === 变量对象?\n> \n> 当函数被调用的时候,一个特殊的对象——活动对象将会被创建。 换句话说,活动对象除了变量和函数声明之外,它还存储了形参和arguments对象。\n> 这不就是vo(变量对象吗)吗?\n\n\n### 2.2 作用域链知识总结\n\n - 当代码在一个环境中执行时,都会创建一个作用域链\n。 **作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。整个作用域链的本质是一个指向变量对象的指针列表。作用域链的最前端,始终是当前正在执行的代码所在环境的变量对象,作用域链中的最后一个对象,始终是全局执行环境的变量对象。**\n \n- 如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,就是函数内部的arguments对象。作用域链中的下一个变量对象来自该函数的包含环境,而再下一个变量对象来自再下一个包含环境。这样,一直延续到全局执行环境,全局执行环境的变量对象始终是作用域链中的最后一个对象。\n\n### 2.3 作用域链形成\n 在创建XX函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链会被保存在内部的[[Scope]]属性中。当调用XX函数时,会为函数创建一个执行环境,然后通过赋值函数的[[Scope]]属性中的对象构建起执行环境的作用域链。\n\n### 2.4 demo\n\n无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。\n\n一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)**。但是闭包的情况又有所不同。**\n\n![此处输入图片的描述][4]\n\n在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。因此,在createComparisonFunction()函数内部定义的匿名函数作用域链中,实际上将会包含外部函数createComparisonFunction()的活动对象。\n\n\n```javascript\nvar compare = createCOmparisionFunction('name');\nvar result = compare({name:'jack'},{name:'lisa'});\n//解除对匿名函数的引用\ncompareName = null ;\n\n```\n\n\n当上述代码执行时,下图展示了包含函数与内部匿名函数的作用域链 ![此处输入图片的描述][5]\n\n在匿名函数从createComparisonFunction()中被返回后,它的作用域链被初始化为包含createComparisonFunction()函数的活动对象和全局变量对象。(从下面看)\n\n这样,匿名函数就可以访问在createComparisonFunction()中定义的所有变量。更为重要的是, createComparisonFunction()函数在执行完毕后,**其活动对象也不会被销毁**,因为匿名函数的作用域链仍然在引用这个活动对象。**即当createComparisonFunction()函数返回后,其执行环境的作用域链会被销毁,但它的活动对象任然会留在内存中;**直到匿名函数被销毁后,createComparisonFunction()的活动对象才会被销毁。\n\n\n [1]: https://raw.githubusercontent.com/XYooo/image/master/this1.png\n [2]: https://raw.githubusercontent.com/XYooo/image/master/this2.png\n [3]: https://raw.githubusercontent.com/XYooo/image/master/this3.png\n [4]: https://raw.githubusercontent.com/XYooo/image/master/this4.png\n [5]: https://raw.githubusercontent.com/XYooo/image/master/this5.png","tags":["javascript"],"categories":["总结"]},{"title":"JS作用域系列—作用域","url":"/2018/09/28/JS作用域系列-作用域/","content":"\n综述:\n在《JS作用域系列—this》中所讲的整个流程就是一个执行环境的创建。\n执行环境又称为作用域又称为执行上下文。\n\n<!-- more -->\n## 1. 执行上下文(执行环境)\n\n> 涉及到上下文,函数的作用域或者说是执行环境\n\n### 1.1 this的指向\n\n略,在《JS作用域系列1——this》\n\n\n### 1.2 执行环境的创建\n为什么this的指向不定的原因?\n一个函数被执行时,会创建一个执行环境(ExecutionContext),函数的所有的行为均发生在此执行环境中。**构建该执行环境时:**\n > \n 1. JavaScript 首先会创建 arguments变量,其中包含调用函数时传入的参数。\n 2. 接下来创建作用域链。\n 3. 然后初始化变量,\n - 3.1 首先初始化函数的形参表,值为arguments变量中对应的值,如果arguments变量中没有对应值,则该形参初始化为 undefined;\n - 3.2 如果该函数中含有内部函数,则初始化这些内部函数;\n - 3.3 如果没有,继续初始化该函数内定义的局部变量,需要注意的是此时这些变量初始化为undefined,其赋值操作在执行环境(ExecutionContext)创建成功后,函数执行时才会执行;这点对于我们理解JavaScript中的变量作用域非常重要,鉴于篇幅,我们先不在这里讨论这个话题。\n 4. 最后为 this变量赋值,如前所述,**会根据函数调用方式的不同**,赋给 this全局对象,当前对象等。\n\n至此函数的执行环境(ExecutionContext)创建成功,函数开始逐行执行,所需变量均从之前构建好的执行环境(ExecutionContext)中读取。\n \n\n> 上面的文字非常的好,非常大好\n![执行上下文创建流程][2]\n\n++上图还有一个大难点就是创建作用域链,下篇文章专讲++\n\n而这个执行环境怎么表示呢?js为每一个执行环境关联了一个变量对象VO(Variable Object )。这个变量对象具体包含了argument、函数的形参,局部变量(内部变量)\n\n>详情见《js作用域系列-this》\n \n### 1.3 Execution Context\n**执行环境、执行上下文**\n\n**每个函数都有自己的执行环境**。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行后,栈将其环境弹出,把控制权返回给之前的执行环境。 \n\n\n#### 1.3.1 执行环境的建立\n分为两个阶段:\n\n> 进入执行上下文(创建阶段)和执行阶段(激活/执行阶段)\n\n(1)进入上下文阶段:就是执行环境的创建,**发生在函数调用时,但在执行具体代码之前**(上面图的最后一步)。具体完成创建,作用域链;创建变量、函数和参数以及求this的值 \n(2)执行代码阶段:主要完成变量赋值、函数引用和解释/执行其他代码\n \n **总的来说可以将执行上下文看作是一个对象**\n \n \n```JavaScript\nEC = {\n Scope:{/*VO以及所有父执行上下文中的VO,或者称之为作用域链*/}\n VO:{/*函数中的arguments对象、参数、内部变量以及函数声明*/},\n this:{},\n \n }\n```\n\n \n **EC的组成正好将执行环境创建的过程给拆分了**\n\n#### 1.3.2 函数表达式\n 这里需要说明一下:函数表达式不包含在变量对象之中\n \n```\nfunction bar() {} // function declaration, FD \n (function baz() {}); // function expression, FE \n\n console.log( \n this.foo == foo, // true \n window.bar == bar // true \n ); \n\n console.log(baz); // ReferenceError, \"baz\" is not defined\n```\n\n## 2 实战\n\n如果上面的过程你真的会了,下面几道道题。\n\n### 2.1.只有形参\n创建:初始化形参a\n执行:逐行执行,a是形参\n```javascript\nt(null);function t(a){console.log(a);}\n// null\n\n```\n### 2.2.形参+function名\n创建:初始化形参和function,同名function覆盖形参,a指想function a\n执行:逐行执行,a是function\n```javascript\nt(null);\nfunction t(a){\n console.log(a);\n function a(){alert(12)};\n console.log(a)\n}\n// ƒ a(){alert(12)}\n// ƒ a(){alert(12)}\n\n```\n\n### 2.3-0.形参+局部变量\n解析:\n创建:初始化形参a;局部变量a重名不覆盖(因为不赋值是undefined)\n执行:逐行执行,a是形参,输出a,作用域找a给a赋值2,输出a\n```javascript\nt(null);function t(a){console.log(a);var a = 2;console.log(a);}\n//null\n// 2\n```\n\n解析:\n创建:初始化形参a,局部变量a重名不覆盖\n执行:逐行执行,a是形参,形参a等于null,找a给a赋值2,输出a\n```javascript\nt(null);function t(a){var a = 2;console.log(a);console.log(a);}\n// 2\n// 2\n```\n解析:\n创建:初始化形参a,局部变量a重名不覆盖\n执行:逐行执行,a是形参,形参a等于null,输出a;找a,a存在且是形参,输出a\n```javascript\nt(null);function t(a){console.log(a);var a ;console.log(a);}\n// null\n// null\n```\n解析:\n创建:初始化形参a,同名局部变量a是undefined不覆盖\n执行:逐行执行,形参a等于null,输出a\n```javascript\nt(null);function t(a){var a ;console.log(a);console.log(a);}\n// null\n// null\n```\n> 创建时:同名局部变量不会覆盖同行形参也不会覆盖同名函数名\n> 执行时:只有赋值操作才能改变上述同名\n\n### 2.3.形参+局部变量+function名\n创建:初始化形参a,a被同名函数名覆盖,同名局部变量a是undefined不进行覆盖\n执行:逐行执行,a是同名函数,找到a,赋值a等于2,输出a\n```javascript\nt(null);\nfunction t(a){\n var a = 2;\n console.log(a);\n function a(){alert(12)};\n console.log(a)\n}\n// 2\n// 2\n\n```\n#### 2.3-1.形参+局部变量(位置改变)+function名\n创建:初始化形参a,a被同名函数名覆盖,同名局部变量a是undefined不进行覆盖\n执行:逐行执行,a是函数,输出a,输出a\n```javascript\nt(null);\nfunction t(a){\n console.log(a);\n function a(){alert(12)};\n console.log(a);\n var a = 2;\n}\n// ƒ a(){alert(12)}\n//ƒ a(){alert(12)}\n```\n\n#### 2.3-2.形参+局部变量(位置改变)+function名\n创建:初始化形参a,a被同名函数名覆盖,同名局部变量a是undefined不进行覆盖\n执行:逐行执行,a是函数,输出a,找到a赋值a等于2,输出a\n\n```javascript\nt(null);\nfunction t(a){\n console.log(a);\n var a = 2;\n function a(){alert(12)};\n console.log(a);\n\n}\n// ƒ a(){alert(12)}\n// 2\n```\n#### 2.3-3.形参+局部变量(位置改变)+function名\n创建:初始化形参a,a被同名函数名覆盖,同名局部变量a是undefined不进行覆盖\n执行:逐行执行,a是函数,输出a,找到a赋值a等于2,输出a\n\n```javascript\nt(null);\nfunction t(a){\n console.log(a);\n function a(){alert(12)};\n var a = 2;\n console.log(a);\n}\n//ƒ a(){alert(12)}\n//1 2\n\n```\n### 2.4.同理window作用域下\n\n创建:初始化函数a,同名局部变量a是undefined不进行覆盖\n执行:逐行执行,a是函数,输出a,找到a赋值a等于2,输出a\n\n```javascript\n\nconsole.log(a);var a = 3;function a(){alert('a')}console.log(a)\n// ƒ a(){alert('a')}\n//3\n```\n\n [1]: https://raw.githubusercontent.com/XYooo/image/master/this1.png\n [2]: https://raw.githubusercontent.com/XYooo/image/master/this2.png\n [3]: https://raw.githubusercontent.com/XYooo/image/master/this3.png\n [4]: https://raw.githubusercontent.com/XYooo/image/master/this4.png\n [5]: https://raw.githubusercontent.com/XYooo/image/master/this5.png","tags":["javascript"],"categories":["总结"]},{"title":"JS作用域系列—this","url":"/2018/09/28/JS作用域系列-this/","content":"综述:关于this最想知道的内容估计就是this的指向了,但是再js中,this的指向的确定不是在定义时候,而是在执行的时候,因为执行方式(调用方式)有多种。\n<!-- more -->\n\n### 1.1 this的指向\n\n首先必须要说的是,this的指向在**函数定义的时候**是确定不了的,**只有函数执行的时候才能确定** this到底指向谁\n下面是决策树,至于文字看面试2\n![此处输入图片的描述][1]\n\n### 1.2为什么this的指向不定呢\n \n总结的一段话:**(IBM developerworks文档库来源)JavaScript 中的函数既可以被当作普通函数执行,也可以作为对象的方法执行,这是导致 this 含义如此丰富的主要原因。**\n\n一个函数被执行时,会创建一个执行环境(ExecutionContext),函数的所有的行为均发生在此执行环境中。\n\n**构建该执行环境时:**\n\n 1. JavaScript 首先会创建 arguments变量,其中包含调用函数时传入的参数。\n 2. 接下来创建作用域链。(很关键的内容,但是不是本篇blog的关键,请读者们忽略)\n 3. 然后初始化变量,\n - 3.1 首先初始化函数的形参表,值为arguments变量中对应的值,如果arguments变量中没有对应值,则该形参初始化为 undefined。\n - 3.2 如果该函数中含有内部函数,则初始化这些内部函数。\n - 3.3 如果没有,继续初始化该函数内定义的局部变量,需要注意的是此时这些变量初始化为undefined,其赋值操作在执行环境(ExecutionContext)创建成功后,函数执行时才会执行\n 这点对于我们理解JavaScript中的变量作用域非常重要,鉴于篇幅,我们先不在这里讨论这个话题。\n 4. 最后为 this变量赋值,如前所述,会根据函数调用方式的不同,赋给 this全局对象,当前对象等。\n\n\n至此函数的执行环境(ExecutionContext)创建成功,函数开始逐行执行,所需变量均从之前构建好的执行环境(ExecutionContext)中读取。\n \n> 上面的文字非常的好,对于理解JS作用域系列也非常关键。将上述文字转化成流程图如下:\n>\n> ![执行上下文创建流程][2]\n> \n> **红色文字,就是JS作用域系列几篇内容需要讲解的**\n\n\n> 而这个执行环境怎么表示呢?总的来说可以将执行上下文看作是一个对象\n\n```javascirpt\n EC = {\n Scope:{/*VO以及所有父执行上下文中的VO*/}\n VO:{/*函数中的arguments对象、参数、内部变量以及函数声明*/}\n this:{},\n }\n```\n\n### 1.3 this的指向规则\n 1.隐式绑定(即使用.)》\n 2.显式绑定 (call,apply,bind)\n 3.new 绑定\n 4.window 绑定\n\n### 1.4 判断this的指向\n\n> 强弱程度 new 绑定> 显式绑定 (call,apply,bind) > 隐式绑定(即使用.)>window 绑定\n\n因此,将所有规则付诸实践,每当我在函数内部看到 this 关键字时,这些就是我为了判断它的引用而采取的步骤。\n1. 查看函数在哪被调用。\n2. 该函数是不是用 “new” 调用的?如果是,“this” 指向的就是 JavaScript 解释器新创建的对象。如果不是,继续第 3 步。\n3.该函数是不是用 “call”、“apply” 或者 “bind” 调用的?如果是,它会显式地指明 “this” 的引用。如果不是,继续第 4 步。\n4. 点左侧有没有对象?如果有,它就是 “this” 的引用。如果没有,继续第 5步。\n5. 是否在“严格模式”下?如果是,“this” 就是 undefined,如果不是,继续第 6 步。\n6. JavaScript 很奇怪,“this” 会指向 “window” 对象。\n(参考你不知道的js上册)\n> 除了3,5,其他与上面的图是对应的\n \n [1]: https://raw.githubusercontent.com/XYooo/image/master/this1.png\n [2]: https://raw.githubusercontent.com/XYooo/image/master/this2.png\n [3]: https://raw.githubusercontent.com/XYooo/image/master/this3.png\n [4]: https://raw.githubusercontent.com/XYooo/image/master/this4.png\n [5]: https://raw.githubusercontent.com/XYooo/image/master/this5.png","tags":["javascript"],"categories":["总结"]},{"title":"屏幕尺寸,分辨率,像素,PPI之间到底什么关系?","url":"/2018/08/31/ 屏幕尺寸,分辨率,像素,PPI之间到底什么关系/","content":"px到底是什么,大小是什么?\n<!-- more -->\n## 屏幕尺寸,分辨率,像素,PPI之间到底什么关系?\n本文通过图解以及公式来讲解分析\n\n> 项目中有个按钮内容是homepage,但是在一同事的chrome下展示的是homepag,在我的chrome展示为homepage,e只有一半,按钮宽度固定90px。\n> 于是我就想是不是分辨率的问题,因为我们的电脑不一样。后来我调整了mac的分辨率,发现还是展示为homepage,e只有一半,难道px跟分辨率没有关系?\n\n在解答上述问题,我们先认识一下这些概念,如尺寸,像素px,分辨率,像素密度等等\n![https://raw.githubusercontent.com/XYooo/image/master/px.png][1]\n\n上面这是一张手机信息图片,下面我们了解下常说的几个参数代表的含义;\n\n### 1.屏幕(主屏)尺寸是什么?\n\n5.2英寸并不是指的手机面积,实际上是指对角线长度。\ninch是**长度单位,1英寸=2.54厘米** , 可见是个物理长度\n\n### 2.分辨率,像素是分辨率的单位?\n这里写的分辨率是1920PX*1080PX。\n可能大家注意了,1920和1080后面都跟了PX的字样,这个PX就是我们很熟悉的像素了,这个像素咱们后面讲,现在你只要明白,像素是分辨率的单位就好了。\n\n> 在我们手机上呈现的一条线、一个面、一张图像都是由最小的单位像素来表示的,你也可以简单理解为是由一个个小方块组成的。看看下面这张图你就明白了。\n\n![https://raw.githubusercontent.com/XYooo/image/master/px2.jpg][2]\n\n> 记住:分辨率1920px*1080px的意思就是,在这个华为荣耀7的5.2英寸屏幕上,在竖向的高度上有1920个像素块,在横向的宽度上有1080个像素块。\n\n\n### 3.什么是分辨率比?\n稍微计算一下高是1920px,宽是1080px的比例就会发现这个高和宽的比例是16:9的。可以比例很常见,过多不解释。\n\n### 4.什么是屏幕像素密度?\n屏幕像素密度,即**每英寸屏幕**所拥有的像素数,英文简称PPI。注意,这里是指每英寸,还是个长度单位。这个英寸跟之前手机屏幕的尺寸一样,也是对角线的长度。所以,我们可以这么理解屏幕像素密度,即在一个对角线长度为1英寸的正方形内所拥有的像素数。\n有个公式方便理解\n\n![https://raw.githubusercontent.com/XYooo/image/master/px3.jpg][3]\n\n\n### 5.像素的大小是固定的吗?答:居然是不固定的,尤其不同设备之间!\n\n**像素是没有实际的物理尺寸的。**\n下面举个例子\n\n![https://raw.githubusercontent.com/XYooo/image/master/px4.png][4]\n![https://raw.githubusercontent.com/XYooo/image/master/px.png][5]\n\n\n上面的这两张图,第一张是苹果6手机的屏幕参数,第二张还是出镜率最高的华为荣耀7的屏幕参数。\n\n虽然这两个手机的分辨率都是1920px*1080px。\n但是,苹果手机的屏幕尺寸比华为荣耀7小了0.2英寸。\n**但是,苹果手机的屏幕像素密度(PPI)却比华为荣耀7高了45个PPI,也就是每英寸像素个数多了45个。** \n\n这说明了同样的一英寸的长度,苹果手机用469个ppi来显示,华为只用了424个ppi来显示。你能告诉我哪个手机的显示效果更加精细吗?同样是一英寸,同样都是像素,为啥苹果手机一英寸有469个像素,华为只有424个呢?\n\n> 手动计算下,按上面的平方公式,华为是423.653846 苹果440.6,但是实际上苹果确实469\n\n我们知道,英寸是长度单位,它有固定的物理尺寸。那么问题就只能出在了像素身上。\n\n**这说明,像素这个东西,在苹果手机上变小了。所以,像素这个东西,就像金箍,能变大能变小。**\n\n\n### 6.电脑屏幕可以调分辨率,难道是通过调整它的像素大小实现的?答:当然不是!(非常重要解答了问题)\n\n**在同一个设备上,它的像素个数是固定的,像素大小也是固定的,这是厂商在出厂时就设置好了的。只有不同的设备之间,才有像素大小的区别。**\n\n既然在同一个设备上,像素点数早就设定好了,那电脑上可以调整分辨率是怎么回事?我再怎么调,像素点数还是那么多啊。\n系统推荐的是1366px*768px的分辨率,意味着微软在这块屏幕上横向设置了768个像素,竖向设置了1366个像素。再怎么拉扯,这个数字是不会变了。那么,为什么还能调整分辨率呢?如800px*600px,按照定义,横向就是600个像素,竖向就是800个像素了啊。实际上把分辨率调成800*600,系统就会分配给你800*600个**有效像素个数,也就是真实的色彩块**。其他的个数就由系统自作主张,通过一系列运算给一个模拟色彩块,填充后达到1366*768个色彩块。那些拿来充数的像素块,和真实的像素块放到一起。就好比一个正规军,里面掺了很多杂牌军一样,只能是队伍不好带了。\n\n### 7.PPI大的手机显示效果就越精细吗?答:的确是这样子的!\n屏幕的清晰程度其实是分辨率和尺寸大小共同决定,用ppi指数衡量屏幕清晰程度更加准确。\n\n> 搜索一下blog,发现也有一篇blog涉及到像素,分辨率,下面放个链接\n[通俗易懂!超全面的移动端尺寸基础知识科普指南](https://github.com/iuap-design/blog/issues/75)\n\n\n [1]: https://raw.githubusercontent.com/XYooo/image/master/px.png\n [2]: https://raw.githubusercontent.com/XYooo/image/master/px2.jpg\n [3]: https://raw.githubusercontent.com/XYooo/image/master/px3.jpg\n [4]: https://raw.githubusercontent.com/XYooo/image/master/px4.png\n [5]: https://raw.githubusercontent.com/XYooo/image/master/px.png","tags":["css"]},{"title":"webpack搭建初试水(一)","url":"/2018/08/27/webpack初试水/","content":"虽然之前研究过一些webpack配置,但是也没有着手去做过。再者平时的demo也都是基于react的,所以不得不使用webpack,这里就随便写个单页和多页的webpack。\n\n前提,不考虑任何优化,什么代码分割,什么图片压缩,什么公共代码提取都一边去,这是最最简单的可以使用的单页和多页的应用.希望你还是要懂得一些基本的webpack配置,比如entry,output等等,如果这些也不是很清楚请直接ctrl+c ctrl+v\n<!-- more -->\n# 在此处输入标题\n\n### 1.单页应用\n\n对应的就是一个页面,所有的功能请在一个页面实现,那么只需要两个文件:index.js和index.html。\n\n#### 1.1 index.js\n随便你想怎么拆分都行,随便你想引入什么文件都可以,请最后一定要有一句\n\n```javascript\n ReactDOM.render(\n <h1>haha</h1>,\n document.querySelector('#main')\n );\n```\n注意:\n 1.import ReactDOM from 'react-dom';\n 2.index.html一定要有`<div id=\"main\"></div>`\n\n#### 1.2 index.html\n```html\n <html>\n <body>\n <div id=\"main\"></div>\n <script type=\"text/javascript\" src=\"./bundle.js\"></script>\n </body>\n </html>\n```\n\n 注意:\n 1.bundle.js命名以及路径与webpack.config.js有关系\n \n #### 1.3 webpack.config.js\n```javascript\n var path = require('path');\n module.exports = {\n devtool: '#source-map',\n entry: './main.js',\n output: {\n filename: 'bundle.js',\n // path: path.resolve(__dirname, 'dist'),\n },\n module: {\n rules: [\n {\n test: /\\.js?$/,\n exclude: /node_modules/,\n use: {\n loader: 'babel-loader',\n options: {\n presets: ['es2015', 'react']\n }\n }\n },\n {\n \n test: /\\.css$/,\n use: [\n {\n loader: 'style-loader'\n },\n {\n loader: 'css-loader',\n \n }\n ]\n }\n ]\n },\n };\n```\n\n#### 1.4 packgae.json\n\n```json\n \"scripts\": {\n \"start\": \"webpack-dev-server --inline --open\",\n \"build\": \"webpack --config webpack.config.js -p\"\n },\n```\n \n### 2.多页应用\n\n多页应用其实要做好需要做很多,关键有公共代码的提取,毕竟每个页面差不多都要引入react 、react-dom等等一些公共文件,如果不提出来那么基础每夜会造成文件有点大,但是我们这里全都不考虑。\n\n多页的应用关键就在于一个插件的使用:\nhtmlwebpackplugin,为了生成多个html\n\n#### 2.1.页面请都放在pages/下面\n\n建立一个单独的文件夹,login/, 下面需有index.js 和 index.html\n\n#### 2.2 index.js\n```javascript\n import ReactDOM from 'react-dom';\n import React, { Component } from 'react';\n import './index.css';\n \n ReactDOM.render(<h1>这里是page1</h1>, document.getElementById('main'));\n```\n#### 2.3 index.html\n```html\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n <title>锚点</title>\n </head>\n <body>\n <div id=\"main\"></div>\n </body>\n </html>\n```\n#### 2.4 webpack.config.js\n```javascript\n var path = require('path');\n var glob = require('glob');\n // const webpack = require('webpack');\n const ExtractTextPlugin = require('extract-text-webpack-plugin');\n const HtmlWebpackPlugin = require('html-webpack-plugin');\n const CleanWebpackPlugin = require('clean-webpack-plugin');\n var entries = {}, chunks = [],plugins=[];\n \n glob.sync('./pages/*/index.js').forEach(path => {\n const chunk = path.split('./pages/')[1].split('/index.js')[0];\n entries[chunk] = path;\n chunks.push(chunk);\n });\n glob.sync('./pages/*/index.html').forEach(path => {\n const chunk = path.split('./pages/')[1].split('/index.html')[0];\n const filename = chunk + '.html';\n const htmlConf = {\n filename: filename,\n template: path,\n inject: true,\n favicon: '',\n hash: true,\n // 每个html引用的js模块,也可以在这里加上vendor等公用模块\n chunks: [chunk]\n }\n plugins.push(new HtmlWebpackPlugin(htmlConf));\n });\n \n module.exports = {\n devtool:'#source-map',\n entry: entries,\n output: {\n filename: 'bundle_[name].js',\n path:path.join(__dirname,'dist')\n },\n module: {\n rules: [\n {\n test: /\\.js?$/,\n exclude: /node_modules/,\n use: {\n loader: 'babel-loader',\n options: {\n presets: ['es2015', 'react']\n }\n }\n },\n {\n \n test: /\\.css$/,\n use:ExtractTextPlugin.extract({\n use: [\n 'css-loader'\n ],\n fallback: 'style-loader'\n })\n }\n ]\n },\n plugins:[\n new ExtractTextPlugin({\n filename: '[name].css', \n }),\n new CleanWebpackPlugin([\"dist\"]),\n ...plugins,\n ],\n devServer:{\n inline: true, // hot load\n // contentBase: './pages',\n https:false,\n open:true,\n }\n };\n```\n\n#### 2.5 package.json\n```json\n \"scripts\": {\n \"dev\": \"webpack-dev-server --hot\",\n \"build\": \"webpack \"\n },\n```\n\n","tags":["webpack"],"categories":["实践"]},{"title":"前端模块化-3AMD","url":"/2018/08/27/前端模块化-3AMD/","content":" Commonjs,AMD,CMD,ES6的模块化总结\n<!-- more -->\n\n<!-- more -->\n\n## 1.摘要\n### 1.1 总结\n \n前端模块规范有三种:CommonJs,AMD和CMD。\n\n- CommonJs用在服务器端,AMD和CMD用在浏览器环境\n- AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。**AMD提前执行(异步加载:依赖先执行)+延迟执行)**\n- CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。 **CMD:延迟执行(运行到需加载,根据顺序执行)**\n\n\n### 1.2 前端的四种模块化方案\n\n(webpack/require.js/seajs/browserify)\n\nJS不具备原生的模块化技能,因此需要采用第三方开发的模块依赖处理库来实现模块化:CommonJS、AMD、CMD、ES6\n\n这四种方案的实现对比:\n\n> CommonJS: exports(或者module.exports)+ require \n>\n> AMD: define + require \n>\n> CMD:define+require+exports/exports \n>\n> ES6: export + import\n\n\n**++Ps. CommonJS 和AMD上下的require也不是一样的,cmd与amd的文字一样内容差了好多参数)++**\n\n由于ES6本身是原生语言支持实现的模块化,但是现代浏览器大多都还未支持,因此必须使用相应的transpiler工具转换成ES5的AMD,CMD模块,再借助于systemjs/requirejs等模块加载工具才能使用。其中ES6的transpiler(代码转换器)一般是用babel,他的作用是将ES6\n\n## 2 AMD\n\n### 2.1 简介\n\n`AMD(Asynchronous Module Definition)`见名知意,就是异步模块定义,当然是为了解决上述同步加载问题。\n`AMD`对应的就是很有名的`RequireJS`。\n\n### 2.2 格式\n\n#### 2.2.1 定义\n\n如何写个符合AMD的js文件\n\n```javascript\n\n// 定义模块 myModule.js\ndefine(['dependency'], function(){\n var name = 'Byron';\n function printName(){\n console.log(name);\n }\n\n return {\n printName: printName\n };\n});\n```\n\n#### 2.2.2 使用\n\n```javascript\n// 加载模块\nrequire(['myModule'], function (my){\n my.printName();\n});\n\n```\n\n### 2.3 语法解析\n\n`requireJS`定义了一个函数 `define`,它是全局变量,用来定义模块\n\n```javascript\n\ndefine(id?, dependencies?, factory);\n\n```\n\nid:可选参数,用来定义模块的标识,如果没有提供该参数,脚本文件名(去掉拓展名)\n\ndependencies:是一个当前模块依赖的模块名称数组\n\nfactory:工厂方法,模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值\n\n在页面上使用require函数加载模块\n\n\n### 2.4 如何使用\n\n```javascript\nrequire([dependencies], function(){});\n\n```\nrequire()函数接受两个参数*\n\n第一个参数是一个数组,表示所依赖的模块\n\n第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块\n\nrequire()函数在加载依赖的函数的时候是异步加载的,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。\n","tags":["模块化"]},{"title":"前端模块化-4CMD","url":"/2018/08/27/前端模块化-4CMD/","content":" Commonjs,AMD,CMD,ES6的模块化总结\n<!-- more -->\n\n\n## 1.摘要\n### 1.1 总结\n \n前端模块规范有三种:CommonJs,AMD和CMD。\n\n- CommonJs用在服务器端,AMD和CMD用在浏览器环境\n- AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。**AMD提前执行(异步加载:依赖先执行)+延迟执行)**\n- CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。 **CMD:延迟执行(运行到需加载,根据顺序执行)**\n\n\n### 1.2 前端的四种模块化方案\n\n(webpack/require.js/seajs/browserify)\n\nJS不具备原生的模块化技能,因此需要采用第三方开发的模块依赖处理库来实现模块化:CommonJS、AMD、CMD、ES6\n\n这四种方案的实现对比:\n\n- CommonJS: exports(或者module.exports)+ require \n- AMD: define + require \n- CMD:define+require+exports/exports \n- ES6: export + import\n\n\n**Ps. CommonJS 和AMD上下的require也不是一样的,cmd与amd的文字一样内容差了好多参数)**\n\n由于ES6本身是原生语言支持实现的模块化,但是现代浏览器大多都还未支持,因此必须使用相应的transpiler工具转换成ES5的AMD,CMD模块,再借助于systemjs/requirejs等模块加载工具才能使用。其中ES6的transpiler(代码转换器)一般是用babel,他的作用是将ES6\n\n\n## 2 CMD\n\n### 2.1 简介\n\nCMD 即`Common Module Definition`通用模块定义,`CMD`规范是国内发展出来的,就像`AMD有个requireJS`,CMD有个浏览器的实现`SeaJS`,`SeaJS`要解决的问题和`requireJS`一样,只不过在模块定义方式和模块加载(可以说运行、解析)时机上有所不同。\n\n\n\n### 2.2 格式\n\n#### 2.2.1 定义\n\n如何写个符合CMD的js文件\n\n在 CMD 规范中,一个模块就是一个文件。代码的书写格式如下:\n```javascript\n\n// a.js文件\ndefine(id?, deps?, factory)\n或者\ndefine(function(require, exports, module) {\n\n // 模块代码\n\n});\n\n```\n\n一般多用第二种写法 ,原因如下\n\n1、因为CMD推崇一个文件一个模块,所以经常就用文件名作为模块id\n2、CMD推崇依赖就近,所以一般不在define的参数中写依赖,在factory中写\n3、factory有三个参数\n\n#### 2.2.1 使用\n\n配合seajs的使用\n```javascript\n/ 加载模块\nseajs.use(['a.js'], function(A){\n var star= A.data;\n console.log(star); //1\n});\n```\n\n\n### 2.3 语法解析\n\n\nrequire是可以把其他模块导入进来的一个参数;require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口\n\nexports是可以把模块内的一些属性和方法导出的;\n\nmodule 是一个对象,上面存储了与当前模块相关联的一些属性和方法\n\n\n## 3 CMD vs AMD\n\n### 3.1 写法\n\nAMD是依赖关系前置,在定义模块的时候就要声明其依赖的模块;\n\nCMD是按需加载依赖就近,只有在用到某个模块的时候再去require:\n\n具体的区别可以看下面的代码\n\n```javascript\n// CMD\ndefine(function(require, exports, module) {\n var a = require('./a')\n a.doSomething()\n // 此处略去 100 行\n var b = require('./b') // 依赖可以就近书写\n b.doSomething()\n // ... \n})\n\n```\n\n```javascript\n// AMD 默认推荐的是\ndefine(['./a', './b'], function(a, b) { // 依赖必须一开始就写好\n a.doSomething()\n // 此处略去 100 行\n b.doSomething()\n ...\n}) \n\n```\n\n\n\n### 3.2 机制\n\nAMD在加载完成定义(define)好的模块就会立即执行,所有执行完成后,遇到require才会执行主逻辑。(提前加载)\n\nCMD在加载完成定义(define)好的模块,仅仅是下载不执行,在遇到require才会执行对应的模块。(按需加载)\n\nAMD用户体验好,因为没有延迟,CMD性能好,因为只有用户需要的时候才执行。\n\n\n## 4 seajs的使用\n\nseajs使用例子\n\n\n```javascript\n\n// 定义模块 myModule.js\ndefine(function(require, exports, module) {\n var $ = require('jquery.js')\n $('div').addClass('active');\n exports.data = 1;\n});\n\n// 加载模块\nseajs.use(['myModule.js'], function(my){\n var star= my.data;\n console.log(star); //1\n});\n\n```","tags":["javascript"]},{"title":"前端模块化-2CommonJS","url":"/2018/08/27/前端模块化-2CommonJS/","content":"\n上一篇讲了前端模块化中的旧方法,下面就讲我们常见但是易混的三个前端模块化。这里先讲commonjs\n\n\n<!-- more -->\n\n## 1.摘要\n### 1.1 总结\n \n前端模块规范有三种:CommonJs,AMD和CMD。\n\n- CommonJs用在服务器端,AMD和CMD用在浏览器环境\n- AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。**AMD提前执行(异步加载:依赖先执行)+延迟执行)**\n- CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。 **CMD:延迟执行(运行到需加载,根据顺序执行)**\n\n\n### 1.2 前端的四种模块化方案\n\n(webpack/require.js/seajs/browserify)\n\nJS不具备原生的模块化技能,因此需要采用第三方开发的模块依赖处理库来实现模块化:CommonJS、AMD、CMD、ES6\n\n这四种方案的实现对比:\n\n> CommonJS: exports(或者module.exports)+ require \n>\n> AMD: define + require \n>\n> CMD:define+require+exports/exports \n>\n> ES6: export + import\n\n\n**++Ps. CommonJS 和AMD上下的require也不是一样的,cmd与amd的文字一样内容差了好多参数)++**\n\n由于ES6本身是原生语言支持实现的模块化,但是现代浏览器大多都还未支持,因此必须使用相应的transpiler工具转换成ES5的AMD,CMD模块,再借助于systemjs/requirejs等模块加载工具才能使用。其中ES6的transpiler(代码转换器)一般是用babel,他的作用是将ES6\n\n\n## 2. CommonJS\n\n### 2.1 简介 \n\nNode 应用由模块组成,采用 CommonJS 模块规范。\n\n每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。\n\n所以**虽然JavaScript在web端发展这么多年,第一个流行的模块化规范却由服务器端的JavaScript应用带来,** CommonJS规范是由NodeJS发扬光大,这标志着JavaScript模块化编程正式登上舞台。\n\n\n### 2.2 格式\n\n```javascript\n\n// example.js\nvar x = 5;\nvar addX = function (value) {\n return value + x;\n};\n\n```\n\n上面代码中,变量x和函数addX,是当前文件example.js私有的,其他文件不可见。\n\n如果想在多个文件分享变量,必须定义为global对象的属性。\n\n\n```javascript \n\nglobal.warning = true;\n\n```\n上面代码的`warning`变量,可以被所有文件读取。当然,这样写法是不推荐的。\n\n`CommonJS`规范规定,每个模块内部,`module`变量代表当前模块。这个变量是一个对象,它的`exports`属性(即`module.exports`)是对外的接口。加载某个模块,其实是加载该模块的`module.exports`属性。\n\n\n看个例子\n\n```javascript\n//模块定义 myModel.js\n\nvar name = 'Byron';\n\nfunction printName(){\n console.log(name);\n}\n\nfunction printFullName(firstName){\n console.log(firstName + name);\n}\n\nmodule.exports = {\n printName: printName,\n printFullName: printFullName\n}\n//或者\n// module.exports.printName = printName;\n// module.exports.printFullName = printFullName;\n\n\n```\n\n```javascript\n//加载模块\n\nvar nameModule = require('./myModel.js');\n\nnameModule.printName();\n\n```\n\n### 2.3 模块解析\n\n根据CommonJS规范,\n\n1. 一个单独的文件就是一个模块。\n**每一个模块都是一个单独的作用域**,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global对象的属性\n\n2. 模块输出:\n**模块只有一个出口,module.exports对象**,我们需要把模块希望输出的内容放入该对象\n\n3. 加载模块:\n加载模块使用require方法,该方法读取一个文件并执行,返回文件内部的module.exports对象\n\n### 2.4 commonjs总结\n>总结如下:\n>\n>1.所有代码都运行在模块作用域,不会污染全局作用域。\n>\n>2.模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。\n>\n>3.模块加载的顺序,按照其在代码中出现的顺序。\n\n### 2.5 commonjs缺点\n\n仔细看上面的代码,**++会发现require是同步的++**。模块系统需要同步读取模块文件内容,并编译执行以得到模块接口。\n\n这在服务器端实现很简单也很自然,然而, 想在浏览器端实现问题却很多。\n\n因为浏览器端,加载JavaScript最佳、最容易的方式是在document中插入script 标签。**++但脚本标签script天生异步,传统CommonJS模块在浏览器环境中无法正常加载。++**\n\n解决思路之一是,开发一个服务器端组件,对模块代码作静态分析,将模块与它的依赖列表一起返回给浏览器端。 这很好使,但需要服务器安装额外的组件,并因此要调整一系列底层架构。\n\n另一种解决思路是,用一套标准模板来封装模块定义,但是对于模块应该怎么定义和怎么加载,又产生的分歧:\n\n\n> 总结CommonJs适合服务器端,比如node,不适合浏览器\n\n\n## 3 module对象\n\n这部分是不是属于node的知识点了。\n\nNode内部提供一个`Module`构建函数。所有模块都是`Module`的实例。\n\n```javascript\nfunction Module(id, parent) {\n this.id = id;\n this.exports = {};\n this.parent = parent;\n // ...\n\n```\n\n每个模块内部,都有一个`module`对象,代表当前模块。它有以下属性。\n\n> module.id 模块的识别符,通常是带有绝对路径的模块文件名。\n> \n> module.filename 模块的文件名,带有绝对路径。\n> \n> module.loaded 返回一个布尔值,表示模块是否已经完成加载。\n> \n> module.parent 返回一个对象,表示调用该模块的模块。\n> \n> module.children 返回一个数组,表示该模块要用到的其他模块。\n> \n> module.exports 表示模块对外输出的值。\n\n下面是一个示例文件,最后一行输出module变量。\n\n```javascript\n// example.js\nvar jquery = require('jquery');\nexports.$ = jquery;\nconsole.log(module);\n\n```\n\n执行这个文件,命令行会输出如下信息。\n\n```javascript\n{ id: '.',\n exports: { '$': [Function] },\n parent: null,\n filename: '/path/to/example.js',\n loaded: false,\n children:\n [ { id: '/path/to/node_modules/jquery/dist/jquery.js',\n exports: [Function],\n parent: [Circular],\n filename: '/path/to/node_modules/jquery/dist/jquery.js',\n loaded: true,\n children: [],\n paths: [Object] } ],\n paths:\n [ '/home/user/deleted/node_modules',\n '/home/user/node_modules',\n '/home/node_modules',\n '/node_modules' ]\n}\n\n```\n\n如果在命令行下调用某个模块,比如`node something.js`,那么`module.parent`就是`null`。如果是在脚本之中调用,比如`require('./something.js')`,那么`module.parent`就是调用它的模块。利用这一点,可以判断当前模块是否为入口脚本。\n\n```javascript\n\nif (!module.parent) {\n // ran with `node something.js`\n app.listen(8088, function() {\n console.log('app listening on port 8088');\n })\n} else {\n // used with `require('/.something.js')`\n module.exports = app;\n}\n\n```\n\n### 3.1 module.exports属性\n\n`module.exports`属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取`module.exports`变量。\n\n### 3.2 exports变量\n\n为了方便,`Node`为每个模块提供一个`exports`变量,指向`module.exports`。这等同在每个模块头部,有一行这样的命令。\n\n```javascript\nvar exports = module.exports;\n\n```\n\n造成的结果是,在对外输出模块接口时,可以向exports对象添加方法。\n\n\n```javascript\n\nexports.area = function (r) {\n return Math.PI * r * r;\n};\n\nexports.circumference = function (r) {\n return 2 * Math.PI * r;\n};\n\n```\n\n注意,不能直接将`exports`变量指向一个值,**因为这样等于切断了`exports`与`module.exports`的联系。**\n\n```javascript\n\nexports = function(x) {console.log(x)};\n\n\n或者\n\nexports.hello = function() {\n return 'hello';\n};\n\nmodule.exports = 'Hello world';\n\n```\n\n第一句 :上面这样的写法是无效的,因为`exports`不再指向`module.exports`了。\n第二句 hello函数是无法对外输出的,因为`module.exports`被重新赋值了。\n\n**这意味着,如果一个模块的对外接口,就是一个单一的值,不能使用`exports`输出,只能使用`module.exports`输出。**\n\n```javascript\nmodule.exports = function (x){ console.log(x);};\n```\n\n如果你觉得,`exports`与`module.exports`之间的区别很难分清,一个简单的处理方法,就是放弃使用`exports`,只使用`module.exports`。\n\n\n## 4 require命令\n\n\n### 4.1 基本用法\n\n\n`Node`使用`CommonJS`模块规范,内置的`require`命令用于加载模块文件。\n\n`require`命令的基本功能是,读入并执行一个`JavaScript`文件,然后返回该模块的`exports`对象。如果没有发现指定模块,会报错。\n\n```javascript\n\n// example.js\nvar invisible = function () {\n console.log(\"invisible\");\n}\n\nexports.message = \"hi\";\n\nexports.say = function () {\n console.log(message);\n}\n\n\n```\n运行下面的命令,可以输出exports对象。\n\n```javascript\nvar example = require('./example.js');\nexample\n// {\n// message: \"hi\",\n// say: [Function]\n// }\n\n```\n\n如果模块输出的是一个函数,那就不能定义在`exports`对象上面,而要定义在`module.exports`变量上面。\n\n```javascript\n\nmodule.exports = function () {\n console.log(\"hello world\")\n}\n\nrequire('./example2.js')()\n\n\n```\n\n上面代码中,`require命令`调用自身,等于是执行`module.exports`,因此会输出 `hello world`。\n\n\n### 4.2 加载规则\n\n`require`命令用于加载文件,后缀名默认为`.js`。\n\n```javascript\nvar foo = require('foo');\n// 等同于\nvar foo = require('foo.js');\n\n```\n\n\n根据参数的不同格式,require命令去不同路径寻找模块文件。\n\n-(1)如果参数字符串以“/”开头,则表示加载的是一个位于绝对路径的模块文件。比如,require('/home/marco/foo.js')将加载/home/marco/foo.js。\n\n-(2)如果参数字符串以“./”开头,则表示加载的是一个位于相对路径(跟当前执行脚本的位置相比)的模块文件。比如,require('./circle')将加载当前脚本同一目录的circle.js。\n\n-(3)如果参数字符串不以“./“或”/“开头,则表示加载的是一个默认提供的核心模块(位于Node的系统安装目录中),或者一个位于各级node_modules目录的已安装模块(全局安装或局部安装)。\n**反正去node_module里面去找**\n举例来说,脚本/home/user/projects/foo.js执行了require('bar.js')命令,Node会依次搜索以下文件。\n>/usr/local/lib/node/bar.js\n>\n>/home/user/projects/node_modules/bar.js\n>\n>/home/user/node_modules/bar.js\n>\n>/home/node_modules/bar.js\n>\n>/node_modules/bar.js\n\n\n这样设计的目的是,使得不同的模块可以将所依赖的模块本地化。\n\n(4)如果参数字符串不以“./“或”/“开头,而且是一个路径,比如require('example-module/path/to/file'),则将先找到example-module的位置,然后再以它为参数,找到后续路径。\n\n(5)如果指定的模块文件没有发现,Node会尝试为文件名添加.js、.json、.node后,再去搜索。.js件会以文本格式的JavaScript脚本文件解析,.json文件会以JSON格式的文本文件解析,.node文件会以编译后的二进制文件解析。\n\n(6)如果想得到require命令加载的确切文件名,使用require.resolve()方法。\n\n### 4.3 目录的加载规则\n\n通常,我们会把相关的文件会放在一个目录里面,便于组织。这时,最好为该目录设置一个入口文件,让`require`方法可以通过这个入口文件,加载整个目录。\n\n在目录中放置一个`package.json`文件,并且将入口文件写入`main`字段。\n\n发包时候已经使用到了\n\n`require`发现 参数 字符串指向一个目录以后,会自动查看该目录的`package.json`文件,然后加载`main字`段指定的入口文件。如果`package.json`文件没有`main`字段,或者根本就没有`package.json`文件,则会加载该目录下的`index.js文件`或`index.node文件`。\n\n\n\n## 参考文献\n\n[阮一峰的nodejs-commjs规范](http://javascript.ruanyifeng.com/nodejs/module.html)","tags":["javascript"]},{"title":"前端模块化-1旧方法","url":"/2018/08/27/前端模块化-1旧方法/","content":"\n记得刚入门前端的时候,看到前端模块化,总是能看到cmd,amd,RequireJS,commonJS等等,这些究竟是什么,这里就为你揭开他们的面纱。\n\n<!-- more -->\n\n## 1.摘要\n \n在互联网应用越来越复杂,不可避免的需要工具来管理我们的前端代码。以前是一个巨大的脚本文件,如今希望可以将文件写入不同的文件模块,并且希望代码可以重用,可以简单地引用和添加各种各样的依赖到我们的项目。\n \n## 2.前提\n\n 如果按照以往的以页面为单位的开发,会导致很多问题,类似依赖管理、命名冲突等等问题。\n\n**命名冲突是最常见的问题,描述如下**:\n\n //在util.js中\n\n```javascript\n \n function log(message){\n }\n \n```\n\n //在logger.js中\n\n```javascript\n\n function log(message){\n }\n\n```\n\n因此**如果在页面的script标签同时依赖这两个文件**的时候就会产生冲突,导致后面的函数会被覆盖。从而可能产生一些预料之外的结果。\n\n\n**传统的解决方案是使用命名空间:**\n\n //在util.js中\n\n```javascript\n function util.log(message){\n }\n```\n\n //在logger.js中\n\n```javascript\n function logger.log(message){\n }\n```\n\n这样带来的显而易见的问题是:**所有的代码会变得冗余并且编写困难**\n\n\n## 3. 解决(从旧到新)\n\n> 思路:\n>\n>在Java中有一个重要的概念——package,逻辑上相关的代码组织到同一个包内,包内是一个相对独立的王国,不用担心命名冲突什么的。\n>\n>**那么外部如果使用呢?直接import对应的package即可。**\n>\n>import java.util.ArrayList;\n\n遗憾的是JavaScript在设计时定位原因,没有提供类似的功能,**javascript,甚至没有类的概念,更不用说模块(module)了**。\n\n**前端开发人员需要模拟出类似的功能,来隔离、组织复杂的JavaScript代码,我们称为模块化。**\n\n> 一个模块就是实现特定功能的文件,**有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块**。模块开发需要遵循一定的规范,各行其是就都乱套了\n\n\n### 3.1 函数封装\n函数一个功能就是实现特定逻辑的一组语句打包,在一个文件里面编写几个相关函数就是最开始的模块了\n\n> 缺点:就如1所讲,污染了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间没什么关系。\n\n\n### 3.2 对象\n为了解决上面问题,对象的写法应运而生,可以把所有的模块成员封装在一个对象中\n```javascirpt\nvar myModule = {\n var1: 1,\n\n var2: 2,\n\n fn1: function(){\n\n },\n\n fn2: function(){\n\n }\n}\n```\n\n这样我们在希望调用模块的时候引用对应文件,然后\n\n\n```\nmyModule.fn2();\n```\n\n这样避免了变量污染,只要保证模块名唯一即可,同时同一模块内的成员也有了关系\n\n> 缺点:看似不错的解决方案,但是也有缺陷,外部可以随意修改内部成员\n\n```javascript\nmyModel.var1 = 100;\n```\n\n这样就会产生意外的安全问题\n\n### 3.3 立即执行函数\n\n可以通过立即执行函数,来达到隐藏细节的目的\n\n\n```\nvar myModule = (function(){\n var var1 = 1;\n var var2 = 2;\n\n function fn1(){\n\n }\n\n function fn2(){\n\n }\n\n return {\n fn1: fn1,\n fn2: fn2\n };\n})();\n```\n\n**这样在模块外部无法修改我们没有暴露出来的变量、函数。**\n\n上述做法就是我们模块化的基础,目前,通行的JavaScript模块规范主要有两种:CommonJS和AMD和CMD\n\n首先最先登场的是CommonJs,CommonJS规范是由NodeJS发扬光大,适用于服务器端。\n","tags":["javascript"]},{"title":"redux","url":"/2018/08/24/redux-吐槽翻译/","content":"\n有一篇redux的吐槽写的甚好,翻译一下。\n通过这篇吐槽,反而更能理解redux。\n<!-- more -->\n\n## 1.前言\n\n### 1.1 mirror\n\nA simple and powerful React framework with minimal API and zero boilerplate. (Inspired by dva and jumpstate)\n\nmirror是一款简洁、高效、易上手的 React 框架,有最少的api和0模板。(受dva 和 jumpstate 启发)\n\n### 1.2 redux\n\n> Painless React and Redux.\n> Why?\n\nWe love React and Redux.\nA typical React/Redux app looks like the following:\n\n一个**典型的** React/Redux 应用看起来像下面这样:\n\n- An actions/ directory to manually create all action types (or action creators)\n> 一个 actions/ 目录用来手动创建所有的 action type(或者 action creator)\n\n- A reducers/ directory and tons of switch clause to capture all action types\n> 一个 reducers/ 目录和超级多的 switch语句 来捕获所有的 action type\n\n- Apply middlewares to handle async actions\n> 必须要依赖 middleware 才能处理 异步 action;\n\n- Explicitly invoke dispatch method to dispatch all actions\n> 显示调用 dispatch 方法来 dispatch 所有的 action;\n\n- Manually create history to router and/or sync with store\n> 手动创建 history 对象关联路由组件,可能还需要与 store 同步;\n\n- Invoke methods in history or dispatch actions to programmatically changing routes\n> 调用 history 上的方法或者 dispatch action 来手动更新路由;\n\n**The problem? [Too much boilerplates](https://github.com/reduxjs/redux/blob/master/docs/recipes/ReducingBoilerplate.md) and a little bit tedious.**\n**存在的问题?太多的 [样板文件](https://github.com/reduxjs/redux/blob/master/docs/recipes/ReducingBoilerplate.md)以及繁琐甚至重复的劳动**\n\n\nIn fact, most part of the above steps could be simplified. Like, create actions and reducers in a single method, or dispatch both sync and async actions by simply invoking a function without extra middleware, or define routes without caring about history, etc.\n\n实际上,上述大部分操作都是可以简化的。比如,在单个 API 中创建所有的 action 和 reducer;比如,简单地调用一个函数来 dispatch 所有的同步和异步 action,且不需要额外引入 middleware;再比如,使用路由的时候只需要关心定义具体的路由,不用去关心 history 对象,等等。\n\nThat's exactly what Mirror does, encapsulates the tedious or repetitive work in very few APIs to offer a high level abstraction with efficiency and simplicity, and without breaking the pattern.\n\n这正是 Mirror 的使命,用极少数的 API 封装所有繁琐甚至重复的工作,提供一种简洁高效的更高级抽象,同时保持原有的开发模式。\n\n## 2 redux\n\n**超级重点,样板文件**\n2-1 Actions\n2-2 Action Creators\n2-3 Generating Action Creators\n2-4 Async Action Creators\n2-5 Reducers\n2-6 Generating Reducers\n\n### 2.1 Actions\n#### 2.1.1 Reducing Boilerplate\n\nRedux is in part inspired by Flux, and the most common complaint about Flux is how it makes you write a lot of boilerplate. In this recipe, we will consider how Redux lets us choose how verbose we'd like our code to be, depending on personal style, team preferences, longer term maintainability, and so on.\n\nRedux的部分是受Flux启发的,关于Flux最常见的抱怨就是它让你写出很多样板文件的。在下面的介绍中,我们将意识到当基于个人风格、团队偏好、长期可维护性等等因素时,Redux是如何让我们选择使用这种超级冗长的代码。\n\n\n#### 2.1.2 Actions 意义\nActions are plain objects describing what happened in the app, and serve as the sole way to describe an intention to mutate the data. It's important that actions being objects you have to dispatch is not boilerplate, but one of the fundamental design choices of Redux.\n\n Actions 是一个描述app中发生了什么的普通对象(变量),也是唯一一种方式来描述数据意图变化。要意识到是你必须dispatch的action ,并不是无用的模板,而是redux基本设计规则 。\n\nThere are frameworks claiming to be similar to Flux, but without a concept of action objects. In terms of being predictable, this is a step backwards from Flux or Redux. If there are no serializable plain object actions, it is impossible to record and replay user sessions, or to implement hot reloading with time travel. If you'd rather modify data directly, you don't need Redux.\n\n有些框架声称类似Flux,但是没有action概念。就可预测而言,这些框架相比Flux和react是一种倒退。如果没有action对象, it is impossible to record and replay user sessions, or to implement hot reloading with time travel. 。如果你宁愿选择直接修改数据,则不需要使用Redux。\n\n#### 2.1.3 Actions 格式\n\n<span id=\"action\">Actions look like this:</span>\n```javascript\n{ type: 'ADD_TODO', text: 'Use Redux' }\n{ type: 'REMOVE_TODO', id: 42 }\n{ type: 'LOAD_ARTICLE', response: { ...} }\n\n```\n\nIt is a common convention that actions have a constant type that helps reducers (or Stores in Flux) identify them. We recommend that you use strings and not Symbols for action types, because strings are serializable, and by using Symbols you make recording and replaying harder than it needs to be.\n\n约定习俗,actions有一个type(是个常量),目的为了帮助reducers(或者Stores in Flux)识别他们。我们建议将type定义成string类型或者不是symbols类型,因为strings are serializable,并且使用symbols类型会让你记录或者重演的行为变得更困难\n\nIn Flux, it is traditionally thought that you would define every action type as a string constant:\n在Flux,一般都会把type定义成一个字符串常量\n\n```javascript\nconst ADD_TODO = 'ADD_TODO'\nconst REMOVE_TODO = 'REMOVE_TODO'\nconst LOAD_ARTICLE = 'LOAD_ARTICLE'\n{ type: 'ADD_TODO', text: 'Use Redux' }\n{ type: 'REMOVE_TODO', id: 42 }\n{ type: 'LOAD_ARTICLE', response: { ...} }\n\n```\nWhy is this beneficial? It is often claimed that constants are unnecessary, and for small projects, this might be correct. For larger projects, there are some benefits to defining action types as constants:\n\n为什么好呢?在小项目中,常量声明不是必须的,但是在大项目中,type定义成常量的好处如下:\n\n - It helps keep the naming consistent because all action types are gathered in a single place.\n> 它有助于保持命名的一致性,因为所有操作类型都集中在一个地方。\n\n - Sometimes you want to see all existing actions before working on a new feature. It may be that the action you need was already added by somebody on the team, but you didn't know.\n> 有时再添加新特性之前需要查看一下所有已存在的actions。也有可能你需要新增的action,你的队友已经添加但是你不知道\n\n- The list of action types that were added, removed, and changed in a Pull Request helps everyone on the team keep track of scope and implementation of new features.\n> action的type列表,删除添加修改等操作很容易跟踪到\n\n- If you make a typo when importing an action constant, you will get undefined. Redux will immediately throw when dispatching such an action, and you'll find the mistake sooner.\n> 当你引入一个action常量的时候出现书写错误,那么你会得到undefined。当dispatch这个action的时候,redux立即报出问题,你就会很快地发现问题。\n\nIt is up to you to choose the conventions for your project. You may start by using inline strings, and later transition to constants, and maybe later group them into a single file. Redux does not have any opinion here, so use your best judgment.\n\n这取决于你,在项目中使用约定习俗 。你可以使用inline strings, and later transition to constants 或者把他们都放入一个单独文件中。redux在这里没做任何要求。\n\n#### 2.1.4 Actions in Diwork\n(个人公司实际项目使用,这部分不是英文文档)\n**(把它们放入一个单独文件中使我们项目中使用方式,就是diwork工作台)**\n\n在diwork中一个store中会有四个文件\n```javascript\nactions.js \napi.js\nindex.js\ntypes.js\n```\n```javascript\n// util.js中的createTypes\nexport const createTypes = (...types) => {\n return types.reduce((obj, type) => {\n obj[type] = type;\n return obj;\n }, {});\n};\n```\n\n```javascript\n// types.js\nimport { createTypes } from '@u';\nexport default createTypes(\n 'GET_APPLICATION_LIST',\n 'MODIFY_APPLICATION_STATE',\n 'GET_APPLICATION_SERVICE',\n 'SET_APPLICATION_SERVICE',\n);\n// actions.js文件\nimport { createActions } from '@u';\nimport types from './types';\nimport {\n getApplicationList,\n modifyApplicationState,\n getApplicationService,\n setApplicationService,\n} from './api';\n\nconst {\n GET_APPLICATION_LIST,\n MODIFY_APPLICATION_STATE,\n GET_APPLICATION_SERVICE,\n SET_APPLICATION_SERVICE,\n} = types;\n\nconst actions = createActions(\n {\n namespace: 'application',\n },\n {\n [GET_ALL_USER_LIST]: getAllUserList,\n [MODIFY_APPLICATION_STATE]: modifyApplicationState,\n [GET_APPLICATION_SERVICE]: getApplicationService,\n [SET_APPLICATION_SERVICE]: setApplicationService,\n \n },\n);\n\nexport default actions;\n```\n\n\n### 2.3 Action Creators\n\nIt is another common convention that, instead of creating action objects inline in the places where you dispatch the actions, you would create functions generating them.\n\n另外一个约定习俗是:不要在你要dispatch action的地方内联式地创建action,而是创建些生成他们的函数更好\n\nFor example, instead of calling dispatch with an object literal:\n\n比如不要dipatch一个字面量对象\n\nsomewhere in an event handler\n\n dispatch({\n type: 'ADD_TODO',\n text: 'Use Redux'\n })\n\nYou might write an action creator in a separate file, and import it into your component:\n你最好在一个独立文件中写个action creator,然后import进来\n\n actionCreators.js\n \n export function addTodo(text) {\n return {\n type: 'ADD_TODO',\n text\n }\n }\n\n AddTodo.js\n \n import { addTodo } from './actionCreators'\n \n // somewhere in an event handler\n dispatch(addTodo('Use Redux'))\n \n<span id=\"actionCreators\">\n\n其实action creators就是return 一个action(一个action表现形式是type+其他内容)。就是把action封装下,在action的周围可以加上其他逻辑</span>,如下:\n\n\nAction creators have often been criticized as boilerplate. Well, you don't have to write them! You can use object literals if you feel this better suits your project. There are, however, some benefits for writing action creators you should know about.\n\nAction creator经常被批评为是模板文件。好吧,实际上你可以不写他们。如果你觉得合适,你可以使用字面量对象。但是关于action creator的好处你需要知道一下\n\nLet's say a designer comes back to us after reviewing our prototype, and tells us that we need to allow three todos maximum. We can enforce this by rewriting our action creator to a callback form with redux-thunk middleware and adding an early exit:\n\n假设一个设计师在检查完原型之后回来告诉我们,我们需要允许最多三个任务。我们可以通过使用redux-thunk中间件将我们的action creator重写回调,并添加一个提前退出来实现这一点:\n\n \n\n function addTodoWithoutCheck(text) {\n return {\n type: 'ADD_TODO',\n text\n }\n }\n \n \n export function addTodo(text) {\n // This form is allowed by Redux Thunk middleware\n // described below in “Async Action Creators” section.\n return function (dispatch, getState) {\n if (getState().todos.length === 3) {\n // Exit early\n return\n }\n dispatch(addTodoWithoutCheck(text))\n }\n }\n\nWe just modified how the addTodo action creator behaves, completely invisible to the calling code. We don't have to worry about looking at each place where todos are being added, to make sure they have this check. Action creators let you decouple additional logic around dispatching an action, from the actual components emitting those actions. It's very handy when the application is under heavy development, and the requirements change often.\n\n我们只需要修改addTodo这个action creator,完全可以忽略调用这个函数的任何地方(因此不用找到所有调用这个函数的地方添加任何额外操作)。action creator把dispatch an action周围的附加逻辑与发出这些动作的实际组件分离开来。\n在繁重的开发阶段以及需求经常变动的情况下,这样开发变得非常敏捷\n\n\n### 2.3 Generating Action Creators\n\n\nSome frameworks like Flummox generate action type constants automatically from the action creator function definitions. The idea is that you don't need to both define ADD_TODO constant and addTodo() action creator. Under the hood, such solutions still generate action type constants, but they're created implicitly so it's a level of indirection and can cause confusion. We recommend creating your action type constants explicitly.\n\n一些框架像Flummox可以通过action creator函数来生成action的type常量。这种想法意味着你不需要同时定义ADD_TODO 常量和 addTodo()这个 action creator。但是实质上还是需要生成action type常量,并且这种隐式创建会导致混淆。我们建议显式地创建action type\n\nWriting simple action creators can be tiresome and often ends up generating redundant boilerplate code:\n编写简单的action creator是非常累人的,并且常常会生成冗余的样板代码:\n\n export function addTodo(text) {\n return {\n type: 'ADD_TODO',\n text\n }\n }\n \n export function editTodo(id, text) {\n return {\n type: 'EDIT_TODO',\n id,\n text\n }\n }\n \n export function removeTodo(id) {\n return {\n type: 'REMOVE_TODO',\n id\n }\n }\n\nYou can always write a function that generates an action creator:\n\n function makeActionCreator(type, ...argNames) {\n return function (...args) {\n const action = { type }\n argNames.forEach((arg, index) => {\n action[argNames[index]] = args[index]\n })\n return action\n }\n }\n\n const ADD_TODO = 'ADD_TODO'\n const EDIT_TODO = 'EDIT_TODO'\n const REMOVE_TODO = 'REMOVE_TODO'\n \n export const addTodo = makeActionCreator(ADD_TODO, 'text')\n export const editTodo = makeActionCreator(EDIT_TODO, 'id', 'text')\n export const removeTodo = makeActionCreator(REMOVE_TODO, 'id')\n //上面的这个函数会返回来\n addTodo('hahah')\n {type: \"ADD_TODO\", text: \"hahah\"}\n\n> 上面代码真的很不错,可以学习一下,闭包知识也有用到,type,argNames被保存了。注意返回的是一个函数\n\nThere are also utility libraries to aid in generating action creators, such as redux-act and redux-actions. These can help reduce boilerplate code and enforce adherence to standards such as Flux Standard Action (FSA).\n\n有一些公共库帮助生成action creators,比如redux-act 和redux-actions . 这样可以减少模板代码和强制遵循FSA规范\n\n\n### 2.4 Async Action Creators\n\n异步的action creators\n\nMiddleware lets you inject custom logic that interprets every action object before it is dispatched. Async actions are the most common use case for middleware.\n\nMiddleware 允许您注入自定义逻辑在每个action对象中 before it is dispatched。异步操作是middleware最常见的用例。\n\n#### 2.4.1 middleware\nWithout any middleware, dispatch only accepts a plain object, so we have to perform AJAX calls inside our components:\n\n没有middleware,**dispatch只接受普通对象,因此我们不得不在组件中执行AJAX调用**:\n\n actionCreators.js //其实action creators就是return 一个action(一个action表现形式是type+其他内容)。就是把action封装下,在action的周围可以加上其他逻辑\n \n export function loadPostsSuccess(userId, response) {\n return {\n type: 'LOAD_POSTS_SUCCESS',\n userId,\n response\n }\n }\n \n export function loadPostsFailure(userId, error) {\n return {\n type: 'LOAD_POSTS_FAILURE',\n userId,\n error\n }\n }\n\n export function loadPostsRequest(userId) {\n return {\n type: 'LOAD_POSTS_REQUEST',\n userId\n }\n }\n\nUserInfo.js\n\n import { Component } from 'react'\n import { connect } from 'react-redux'\n import {\n loadPostsRequest,\n loadPostsSuccess,\n loadPostsFailure\n } from './actionCreators'\n \n class Posts extends Component {\n loadData(userId) {\n // Injected into props by React Redux `connect()` call:\n // 通过调用React Redux `connect()`注入了props\n const { dispatch, posts } = this.props\n \n if (posts[userId]) {\n // There is cached data! Don't do anything.\n // 是否有缓存数据\n return\n }\n \n // Reducer can react to this action by setting\n // `isFetching` and thus letting us show a spinner.\n // 显示加载\n dispatch(loadPostsRequest(userId))\n \n // Reducer can react to these actions by filling the\n // `users`.\n \n fetch(`http://myapi.com/users/${userId}/posts`).then(\n response => dispatch(loadPostsSuccess(userId, response)),\n error => dispatch(loadPostsFailure(userId, error))\n );//问题出现在这里,正确错误这部分代码若存在上千个组件,那么重写这块上千次\n }\n \n componentDidMount() {\n this.loadData(this.props.userId)\n }\n \n componentWillReceiveProps(nextProps) {\n if (nextProps.userId !== this.props.userId) {\n this.loadData(nextProps.userId)\n }\n }\n \n render() {\n if (this.props.isFetching) {\n return <p>Loading...</p>\n }\n \n const posts = this.props.posts.map(post =>\n <Post post={post} key={post.id} />\n )\n \n return <div>{posts}</div>\n }\n }\n \n export default connect(state => ({\n posts: state.posts,\n isFetching: state.isFetching\n }))(Posts)\n\n\nHowever, this quickly gets repetitive because different components request data from the same API endpoints. Moreover, we want to reuse some of this logic (e.g., early exit when there is cached data available) from many components.\n\n然而这很快就会重复因为不同的组件使用相同api来请求数据,而且,我们希望重用许多组件中的一些逻辑(比如缓存数据存在的时候提前退出)\n(如果有多个组件,需要重写很多次fetch请求呢)\n\nMiddleware lets us write more expressive, potentially async action creators. It lets us dispatch something other than plain objects, and interprets the values. For example, middleware can “catch” dispatched Promises and turn them into a pair of request and success/failure actions.\n\nMiddleware使我们能够编写更有表现力的、潜在的异步action creator。它允许我们dispatch something 而不是普通对象,并interprets the values。例如,middleware可以“捕获”已发出的promise,并将它们转换为一对请求+成功/失败操作。\n\nThe simplest example of middleware is redux-thunk. “Thunk” middleware lets you write action creators as “thunks”, that is, functions returning functions. This inverts the control: you will get dispatch as an argument, so you can write an action creator that dispatches many times.\n\nmiddleware最简单的例子就是redux-thunk。**thunk是允许你将action creators作为thunks,就是函数返回函数。这种反转控件,你可以将dispatch作为参数,因为写一个可以dispatch很多次的action creator**\n\n> Note :\n> \n> Thunk middleware is just one example of middleware. Middleware is\n> not about “letting you dispatch functions”. It's about letting you\n> dispatch anything that the particular middleware you use knows how to\n> handle. Thunk middleware adds a specific behavior when you dispatch\n> functions, but it really depends on the middleware you use. Thunk\n>\n> middleware只是一个简单例子,Middleware不是让你‘dispatch functions’。他是让你dispatch\n> 任何Middleware知道如何处理的事情。Thunk Middleware可以在你dispatch\n> functions之前添加一些特殊操作,不过这一切取决你你使用的Middleware\n\nConsider the code above rewritten with redux-thunk:\n\nactionCreators.js\n\n export function loadPosts(userId) {\n // Interpreted by the thunk middleware:\n // 交给thunk Middleware 解释\n return function (dispatch, getState) {\n const { posts } = getState()\n if (posts[userId]) {\n // There is cached data! Don't do anything.\n return\n }\n \n dispatch({\n type: 'LOAD_POSTS_REQUEST',\n userId\n })\n \n // Dispatch vanilla actions asynchronously\n fetch(`http://myapi.com/users/${userId}/posts`).then(\n response =>\n dispatch({\n type: 'LOAD_POSTS_SUCCESS',\n userId,\n response\n }),\n error =>\n dispatch({\n type: 'LOAD_POSTS_FAILURE',\n userId,\n error\n })\n )\n }\n }\n\nUserInfo.js\n\n import { Component } from 'react'\n import { connect } from 'react-redux'\n import { loadPosts } from './actionCreators'\n \n class Posts extends Component {\n componentDidMount() {\n this.props.dispatch(loadPosts(this.props.userId));//这块就变得超级简洁了,error与response都在actioncreator.js中处理了\n }\n \n componentWillReceiveProps(nextProps) {\n if (nextProps.userId !== this.props.userId) {\n this.props.dispatch(loadPosts(nextProps.userId))\n }\n }\n \n render() {\n if (this.props.isFetching) {\n return <p>Loading...</p>\n }\n \n const posts = this.props.posts.map(post =>\n <Post post={post} key={post.id} />\n )\n \n return <div>{posts}</div>\n }\n }\n \n export default connect(state => ({\n posts: state.posts,\n isFetching: state.isFetching\n }))(Posts)\n\nThis is much less typing! If you'd like, you can still have “vanilla” action creators like loadPostsSuccess which you'd use from a container loadPosts action creator.\n\n更少的书写。如果你喜欢,你可以有vanilla action creators,如loadPostsSuccess\n(其实就是把第一个UserInfo.js中loadData提出来加工一下改为loadPosts)\n\nFinally, you can write your own middleware. Let's say you want to generalize the pattern above and describe your async action creators like this instead:\n\n最后你可以编写自己的Middleware。如果你想要概括并推广上面的模式,可以这样的描述你的异步action creators\n\n export function loadPosts(userId) {\n return {\n \n // Types of actions to emit before and after定义Types\n types: ['LOAD_POSTS_REQUEST', 'LOAD_POSTS_SUCCESS', 'LOAD_POSTS_FAILURE'],\n \n // Check the cache (optional):检查缓存\n shouldCallAPI: state => !state.posts[userId],\n \n // Perform the fetching:执行fetch\n callAPI: () => fetch(`http://myapi.com/users/${userId}/posts`),\n // Arguments to inject in begin/end actions 参数注入在开始或者结束的actions\n payload: { userId }\n }\n }\n\n\nThe middleware that interprets such actions could look like this:\n\n定义这个actions的middleware需要写成如下:\n\n function callAPIMiddleware({ dispatch, getState }) {\n return next => action => {\n const {\n types,\n callAPI,\n shouldCallAPI = () => true,\n payload = {}\n } = action\n \n if (!types) {\n // Normal action: pass it on\n return next(action)\n }\n \n if (\n !Array.isArray(types) ||\n types.length !== 3 ||\n !types.every(type => typeof type === 'string')\n ) {\n throw new Error('Expected an array of three string types.')\n }\n \n if (typeof callAPI !== 'function') {\n throw new Error('Expected callAPI to be a function.')\n }\n \n if (!shouldCallAPI(getState())) {\n return\n }\n \n const [requestType, successType, failureType] = types\n \n dispatch(\n Object.assign({}, payload, {\n type: requestType\n })\n )\n \n return callAPI().then(\n response =>\n dispatch(\n Object.assign({}, payload, {\n response,\n type: successType\n })\n ),\n error =>\n dispatch(\n Object.assign({}, payload, {\n error,\n type: failureType\n })\n )\n )\n }\n }\n\nAfter passing it once to applyMiddleware(...middlewares), you can write all your API-calling action creators the same way:\n\n只要使用一次applyMiddleware(...middlewares), 您可以用同样的方式编写所有api调用的action creators\n\n\n export function loadPosts(userId) {\n return {\n types: ['LOAD_POSTS_REQUEST', 'LOAD_POSTS_SUCCESS', 'LOAD_POSTS_FAILURE'],\n shouldCallAPI: state => !state.posts[userId],\n callAPI: () => fetch(`http://myapi.com/users/${userId}/posts`),\n payload: { userId }\n }\n }\n \n export function loadComments(postId) {\n return {\n types: [\n 'LOAD_COMMENTS_REQUEST',\n 'LOAD_COMMENTS_SUCCESS',\n 'LOAD_COMMENTS_FAILURE'\n ],\n shouldCallAPI: state => !state.comments[postId],\n callAPI: () => fetch(`http://myapi.com/posts/${postId}/comments`),\n payload: { postId }\n }\n }\n \n export function addComment(postId, message) {\n return {\n types: [\n 'ADD_COMMENT_REQUEST',\n 'ADD_COMMENT_SUCCESS',\n 'ADD_COMMENT_FAILURE'\n ],\n callAPI: () =>\n fetch(`http://myapi.com/posts/${postId}/comments`, {\n method: 'post',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ message })\n }),\n payload: { postId, message }\n }\n }\n\n\n### 2.5 Reducers\n\n\nRedux reduces the boilerplate of Flux stores considerably by describing the update logic as a function. A function is simpler than an object, and much simpler than a class.\n\nRedux通过将更新逻辑描述为函数,大大减少了Flux样板。函数比对象简单,比类简单得多。\n\nConsider this Flux store:\n\n const _todos = []\n \n const TodoStore = Object.assign({}, EventEmitter.prototype, {\n getAll() {\n return _todos\n }\n })\n \n AppDispatcher.register(function (action) {\n switch (action.type) {\n case ActionTypes.ADD_TODO:\n const text = action.text.trim()\n _todos.push(text)\n TodoStore.emitChange()\n }\n })\n\n export default TodoStore\n\n\nWith Redux, the same update logic can be described as a reducing function:\n\n<span id=\"reducer\">使用redux,同样的更新操作可以写成这样</span>\n\n export function todos(state = [], action) {\n switch (action.type) {\n case ActionTypes.ADD_TODO:\n const text = action.text.trim()\n return [...state, text]\n default:\n return state\n }\n }\n \n> 去掉了TodoStore 和 TodoStore.emitCHange(),同时用state和return state来代替\n\n\nThe switch statement is not the real boilerplate. The real boilerplate of Flux is conceptual: the need to emit an update, the need to register the Store with a Dispatcher, the need for the Store to be an object (and the complications that arise when you want a universal app)。\n\n这是对Flux的解析:\nSwitch语句不是真正冗余,Flux真正冗余的是概念:\n需要emit an update\n需要用一个dispatcher来注册store(就是store需要被注册在一个dispatcher上)\n需要store是个对象(当你想要一个通用app的时候这就会非常的复杂)\n\nIt's unfortunate that many still choose Flux framework based on whether it uses switch statements in the documentation. If you don't like switch, you can solve this with a single function, as we show below.\n\n愚蠢的是,许多人仍然根据文档中是否使用switch语句来选择Flux框架。如果您不喜欢switch,可以使用一个函数来解决这个问题,如下所示。\n\n### 2.6 Generating Reducers\n\n\nLet's write a function that lets us express reducers as an object mapping from action types to handlers. For example, if we want our todos reducers to be defined like this:\n\n让我们编写一个函数,让我们将reducers表述为一个对象映射,映射关系是action type 映射到 handlers。例如,如果我们希望我们的todos是这样定义的:\n\n export const todos = createReducer([], {\n [ActionTypes.ADD_TODO]: (state, action) => {\n const text = action.text.trim()\n return [...state, text]\n }\n })\n\n越来越像我们diwork项目中用到了这种映射关系。去掉了switch,We can write the following helper to accomplish this:\n可以使用下面的帮助来完成\n\n function createReducer(initialState, handlers) {\n return function reducer(state = initialState, action) {\n if (handlers.hasOwnProperty(action.type)) {\n return handlers[action.type](state, action)\n } else {\n return state\n }\n }\n }\n\n\nThis wasn't difficult, was it? Redux doesn't provide such a helper function by default because there are many ways to write it. Maybe you want it to automatically convert plain JS objects to Immutable objects to hydrate the server state. Maybe you want to merge the returned state with the current state. There may be different approaches to a “catch all” handler. All of this depends on the conventions you choose for your team on a specific project.\nThe Redux reducer API is (state, action) => newState, but how you create those reducers is up to you.\n\n这并不难,不是吗?\n\nRedux默认情况下不提供这样的帮助helper,因为有很多方法可以编写它。也许您希望它自动将普通JS对象转换为不可变对象,以适应服务器状态。(玩事就是讲js对象转化成immutable对象)。也许您希望将返回的state与当前state合并。也有很多不同的方式来使用‘catch all’handler。所有这些都取决于您根据特定项目中选择的约定。\n\nRedux reducer API是(state, action) => newState,但是如何创建这些reducer取决于您。\n\n##3 总结\n看完全篇之后有几个概念是我们经常遇见的,\n>\n> action 、 action creators 、 reducer以及之间 的区别\n>\n> action = type + new value [点击跳转](#action)\n>\n> action creators = logic + action [点击跳转](#actionCreators)\n>\n> reducer = state + action creators\n[点击跳转](#reducer)\n\n## 4 diwork项目中的redux\n\n![单页中完整redux分成四个文件](https://raw.githubusercontent.com/XYooo/image/master/[email protected])\n![actions目录](https://raw.githubusercontent.com/XYooo/image/master/[email protected])\n![reducer目录](https://raw.githubusercontent.com/XYooo/image/master/[email protected])\n\n","tags":["redux"],"categories":["翻译"]}]