闭包我的理解 函数内部还有一个函数,其做用就是能够访问上一层函数中的变量 下面的案例中函数内部有函数,this的指向就变为window了 结果闭包中this指向的两种方法 1.call对象冒充能够改变this的指向 obj.say().call(obj) 这里把this的指向换成了obj 闭包中的this指向的是window对象,this.name=window.name 2.在方法内部改变this指向 既然对象中的say方法中this是指向obj的,那么咱们就使用that代替this,在闭包函数中写成that.name 那么结果就是obj
自定义函数参数传递为 字符串格式 ,传递方式 1:用this传递 2:引号缺省 3:转义字符(html中 " 表明"双引号,'表明单引号,javascript中直接\\" 和Java通用转义字符集) <html> <head> <script language="LiveScript"> function print(arg){ alert("你好!"+arg); } </script> </head> <body> <form> <input type="button" name="Button1" value="first" onclick="print(this.str)" str="你好one"> <br><br> <input type="button" name="button2" value="second" onclick=print("你好two")> <br><br> <input type="button" name="button3" value="Third" onclick="print ("你好three")"> </form> </body> </html> 总结 以上是脚本之家为你收集整理的javascript自定义函数参数传递为字符串格式所有内容,但愿文章可以帮你解决javascript自定义函数参数传递为字符串格式所遇到的程序开发问题。
https://blog.csdn.net/Web_J/article/details/83900073javascript
这是一个很是常见的面试题,出题方式多样,但考察点相同,下面咱们来看看这几种方法: 方法一: var itemli = document.getElementsByTagName("li"); for(var i = 0; i<itemli.length; i++){ itemli\[i\].index = i; //给每一个li定义一个属性索引值 itemli\[i\].onclick = function(){ alert(this.index+this.innerHTML); } } 方法二: var itemli = document.getElementsByTagName("li"); for(var i = 0; i<itemli.length; i++){ (function(n){ itemli\[i\].onclick = function(){ alert(n+itemli\[n\].innerHTML); } })(i) } 方法三: var itemli = document.getElementsByTagName("li"); for(var i = 0; i<itemli.length; i++){ itemli\[i\].onclick = function(n){ return function(){ alert(n+itemli\[n\].innerHTML); } }(i) } 方法四: $("ul li").click(function(){ var item = $(this).index(); //获取索引下标 也从0开始 var textword = $(this).text(); //文本内容 alert(item+textword); }) 上面这四种方法均可以实现循环绑定,可是咱们知道,频繁的操做DOM是很是消耗性能的,若是有1000个li,怎么办呢?咱们还有另外一种思路,事件代理,又称事件委托。简单的来说就是利用JS中事件的冒泡属性,把本来须要绑定的事件委托给父元素,让父元素担当事件监听的职务。下面咱们来看看。 方法五(JS事件代理): var ul = document.querySelector("ul"); ulItem.onclick = function (e) { e = e || window.event; //这一行及下一行是为兼容IE8及如下版本 var target = e.target || e.srcElement; if (target.tagName.toLowerCase() === "li") { var li = this.querySelectorAll("li"); index = Array.prototype.indexOf.call(li, target); alert(target.innerHTML + index); } } 上述代码中,为何须要 “index = Array.prototype.indexOf.call(li,target);” 这样使用数组的indexOf方法呢,这是由于querySelectorAll方法获取到的元素列表不是数组,和函数的arguments同样是一种类数组类型,不能够直接使用数组的方法。 方法六(jQuery事件代理): $(document).ready(function () { $("ul").on("click", function (event) { var target = $(event.target); alert(target.html() + target.index()) }); });
https://www.jianshu.com/p/83a...html
foreach底层利用迭代器Iterator来实现的vue
map底层是利用hashMap来实现的java
forEach比map快node
定义 foreEach()方法: 针对每个元素执行提供的函数。 map()方法: 建立一个新的数组,其中每个元素由调用数组中的每个元素执行提供的函数得来。 区别 forEach()方法不会返回执行结果,而是undefined。也就是说,forEach()会修改原来的数组。而map()方法会获得一个新的数组并返回。 例子 制做一个数组的平方 有以下一个数组 let arr =\[1,2,3,4,5,6\] 下面分别用forEach()和Map() forEach() 注意,forEach是不会返回有意义的值的。 咱们在回调函数中直接修改arr的值。 arr.forEach((value, key) => { return arr\[key\] = value \* value; }); 执行结果以下: 执行结果 Map() let list = arr.map(value => { return value \* value; }); 执行结果以下: 执行结果 执行速度对比 forEach()的执行速度 < map()的执行速度 速度比试 image.png 如何使用 forEach适合于你并不打算改变数据的时候,而只是想用数据作一些事情 – 好比存入数据库或则打印出来。 let arr = \['a', 'b', 'c', 'd'\]; arr.forEach((letter) => { console.log(letter); }); // a // b // c // d map()适用于你要改变数据值的时候。不只仅在于它更快,并且返回一个新的数组。这样的优势在于你可使用复合(composition)(map(), filter(), reduce()等组合使用)来玩出更多的花样。 let arr = \[1, 2, 3, 4, 5\]; let arr2 = arr.map(value => value \* value).filter(value => value > 10); // arr2 = \[16, 25\] 咱们首先使用map将每个元素乘以它们自身,而后紧接着筛选出那些大于10的元素。最终结果赋值给arr2。 总结 forEach()能够作到的东西,map()也一样能够。反过来也是如此。 map()会分配内存空间存储新数组并返回,forEach()不会返回数据。 forEach()容许callback更改原始数组的元素。map()返回新的数组。
https://blog.csdn.net/Beijiya...ios
思路 map 迭代方法接收两个参数: 对每一项执行的函数 该函数接收三个参数: 数组项的值 数组项的下标 数组对象自己 指定 this 的做用域对象 map 方法返回每次函数调用结果组成的数组。 代码表示: arr.map(function(item, index, arr) {}, this); 1 实现 由此,实现 fakeMap 方法以下 代码: Array.prototype.fakeMap = function fakeMap(fn, context) { if (typeof fn !== "function") { throw new TypeError(\`${fn} is not a function\`); } let arr = this; let temp = \[\]; for (let i = 0; i < arr.length; i++) { // 迭代执行 let result = fn.call(context, arr\[i\], i, arr); temp.push(result); } return temp; }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 检测: let arr = \["x", "y", "z"\]; arr.fakeMap((item, index, arr) => console.log(item, index, arr)); 1 2 3 输出: x 0 \[ ‘x’, ‘y’, ‘z’ \] y 1 \[ ‘x’, ‘y’, ‘z’ \] z 2 \[ ‘x’, ‘y’, ‘z’ \] this 指向: let arr = \["x", "y", "z"\]; let obj = { a: 1 }; arr.fakeMap(function() { console.log(this.a); }, obj); 1 2 3 4 5 6 7 输出 1 1 1 更新 Array.prototype.myMap = function (fn, context) { const array = this context = Object(context) || global // 严格模式下 const resultArr = \[\] for (let i = 0; i < array.length; i++) { let item = array\[i\] result = fn.call(context, item, i, array) resultArr.push(result) } return resultArr }; 1 2 3 4 5 6 7 8 9 10 11 12 注: 严格模式下,context 为 null 或 undefined 时 Object(context) 返回空对象,不会被赋值为global
\- 父组件向子组件传值 --Props传递数据 在父组件中使用儿子组件 <template> <div> 父组件:{{money}} <Son1 :money="money"><Son1> </div> </template> <script> import Son1 from ''./Son1"; export default{ components:{ Son1 }, data(){ return { money: 100}; } }; </script> 子组件接受数据 props:{ value:{ type:Number, default:1 } } 若是是数组 props:{ value:{ type:Array, default: ()=>\[\] } } \- 子组件通讯父组件 $emit使用 <template> <div> 父组件:{{money}} <Son1 :money="money" @input="change"><Son1> </div> </template> <script> import Son1 from ''./Son1"; export default{ methods:{ change(data){ this.money = data } }, components:{ Son1 }, data(){ return { money: 100}; } }; </script> 子组件触发绑定本身身上的方法 <template> <div> 子组件1:{{money}} <button @click="$emit('input',200)">修改父组件的值<Son1> </div> </template> <script> export default{ props:{ money:{ type:Number } } }; </script> \- $parent、$children(多层级传递) <Grandson1 :value="value"></Grandson1> <template> <div> 孙子1:{{value}} <---调用父组件的input事件--> <button @click="$parent.$emit('input',200)">更改<Son1> </div> </template> <script> export default{ props:{ value:{ type:Number } } }; </script> \- $attrs、 $listeners: $attrs批量向下传入属性: <Son2 name="小明" age="18"></Son2> <--能够在son2组件中使用$attrs,能够将属性继续向下传递--> <div> 儿子2:{{ $attrs.name }} <Grandson2 v-bind="$attrs"></Grandson2> </div> <tempalte> <div>孙子:{{$attrs}}</div> </template> $listeners批量向下传入方法: <Son2 name="小明" age="18" @click=“()=>{this.money =500}”></Son2> <--能够在son2组件中使用$attrs,能够将属性继续向下传递--> <Grandson2 v-bind="$attrs" v-on="$listeners"></Grandson2> <button @click="$listeners.click()">更改<Son1> \- Provide&Inject Provide 在父级中注入数据 provide(){ return {parentMsg:'父亲'}; } Inject 在任意子组件中能够注入父级数据 inject:\['parentMsg'\]//会将数据挂载在当前实例上 \- ref使用 <Grandson2 name="花花" ref="grand2"></Grandson2> mounted(){ console.log(this.$refs.grand2.name); } \- EventBus:用于跨组件通知 Vue.prototype.$bus = new Vue(); Son2组件和Grandson1互相通讯 mounted() { //父亲组件注册 this.$bus.$on('my',data=>{ console.log(data) }) } mounted(){ //侄子组件调用 this.$nextTick(()=>{ this.$bus.$emit('my',"我是小红”); }) }
http://dongfanker.coding.me/2...git
为何须要Bus 通常来讲,都是利用父组件给子组件使用query或者params传递参数来实现子组件的数据显示 不过,当出现子组件须要向父组件传递数据的时候,就须要用到bus,bus能够本身建立,也能够经过装包来实现 直接建立Bus 在此处直接将Bus注入Vue对象中,成为全局的组件。 在子组件中经过this.$root.Bus.$on(key,method),this.$root.Bus.$emit(key,data)来调用 将$on放在mounted,created这些钩子函数中,相应的函数使用(e)=>{function}比较便捷 1 2 3 4 5 6 7 8 9 10 import Vue from 'vue' const Bus = new Vue() var app= new Vue({ el:'#app', data:{ Bus } }) 使用vue-bus 使用yarn或者npm安装vue-bus以后,在main.js中引用它 1 2 import VueBus from 'vue-bus'; Vue.use(VueBus); 因而调用直接能够写为 this.$bus.on(key, this.method),this.$bus.emit(key, { text: …… } 其中第一个字符串参数表明key,每一个key都可以实现本身的独立传输,this.method为事先定义好的method,用于对传入的数据进行处理 为何使用vuex 当咱们的应用遇到多个组件共享状态时,会须要: 多个组件依赖于同一状态。 来自不一样组件的行为须要变动同一状态。 通过个人观察,vuex在其中的做用就是组件与状态的捆绑剥离开来,使得组件状态的改变依赖于某个行为,这使得代码变得易于调试。 Vuex采用集中式存储管理应用的全部组件的状态,这里的关键在于集中式存储管理。这意味着原本须要共享状态的更新是须要组件之间通信的,而如今有了vuex,就组件就都和store通信了。 使用vuex 使用yarn或者npm安装vuex以后,在main.js中引用它 1 2 3 4 5 6 7 import Vuex from 'vuex' import store from './vuex/store' Vue.use(Vuex) new Vue({ el: '#app', store }) 随后建立vuex目录,将store.js放在目录下,定义state和mutation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ state: { author: 'Wise Wrong' }, mutations:{ newAuthor(state,msg){ state.author = msg } } }) export default store 在使用的时候,不直接修改this.$store.state.author,而是使用this.$store.commit()来提交,可让咱们更好的跟踪每个状态的变化,在大型项目中更为适用
https://www.cnblogs.com/18252...es6
1.为何须要路由拦截器 为了防止用户在知道组件名称的状况下,没有登陆而直接进入相应的页面下,因此要为路由设置一个拦截器,来判断用户是否登陆过。 2.怎样设置路由拦截器: 分析:当咱们第一次登陆的时候,向服务器发送请求,服务器会给咱们一个token标记符(这个token时先后台约定好的一个值),客户端拿到这个token后将它保存到本地localstorage或vueX中,当咱们再次访问时,将这个token在携带给服务器。服务器会经过算法校验这个token的合法性(这个token会有一个有效期),若是合法,则不干涉,不合法则强制跳转到登陆界面。 import axios from 'axios' const baseURL = 'http://localhost:8888/api/private/v1/' axios.defaults.baseURL = baseURL // 添加请求拦截器 axios.interceptors.request.use(function (config) { // 将token给到一个先后台约定好的key中,做为请求发送 // mytoken是咱们第一次登陆成功后,服务器会返回给我一个token值,咱们将它保存在localstorage中 let token = localStorage.getItem('mytoken') // 获取本地存储的token值 if (token) { // 判断token值是否存在 // 我的认为在此期间能够再次判断 token是否还在有效期内,若是在,就将token放在请求头中;若是不在,就将token= '',并返回错误信息 config.headers\['Authorization'\] = token // 若是token值存在,就将token值放在请求头中,发送给服务器 } return config }, function (error) { // 若是不存在,就返回一个错误信息 // Do something with request error return Promise.reject(error) }) 3.当我咱们设置了拦截器后,咱们能够注册一个全局守卫(在main.js入口文件中注册),防止未登陆的用户跳转到其余页面 // 注册一个全局守卫。做用是在路由跳转前对路由判断,防止未登陆的用户跳转到其余页面 router.beforeEach((to, from, next) => { let token = localStorage.getItem('mytoken') // 若是已经登陆,那我不干涉你,让你随便访问 if (token) { next() } else { if (to.path !== '/login') { // 若是没有登陆,但你访问其余须要登陆的页面,那我就让你跳到登陆页面去 next({path: '/login'}) } else { // 若是没有登陆,但你访问的login,那就不干涉你,让你访问 next() } } })
进入你要操做的目录,跟Linux环境同样 git status ./ 查看这个文件夹下的文件状态,会列出有哪些没有加入追踪,哪些没有commit git add ./\* 把这个文件下的全部应该加入追踪的,加入到暂存区 git commit -m "日志描述" ./ 把这个文件夹下能够commit的,都commit到本地库 git push push到远程库
https://blog.csdn.net/xinzhif...面试
Git回滚代码到某个commit 回退命令: git reset --hard HEAD^ 回退到上个版本 git reset --hard HEAD~3 回退到前3次提交以前,以此类推,回退到n次提交以前 git reset --hard commit\_id 退到/进到,指定commit的哈希码(此次提交以前或以后的提交都会回滚) 回滚后提交可能会失败,必须强制提交 强推到远程:(可能须要解决对应分支的保护状态) git push origin HEAD --force
https://www.cnblogs.com/ztfjs...算法
写在前面 一个好的架构须要通过血与火的历练,一个好的工程师须要通过无数项目的摧残。 昨天博主分析了一下在vue中,最为基础核心的api,parse函数,它的做用是将vue的模板字符串转换成ast,从而构建vnode,构建指令,实现virtual dom,而后在这基础之上实现双向绑定等。【vuejs深刻二】vue源码解析之一,基础源码结构和htmlParse解析器 今天博主就来详细的实现一个拥有核心功能的htmlParse函数,看看它内部的实现逻辑,了解它是怎么样去解析一个vue模板的。 小目标 咱们最终的目标是将html转换成ast对象,那么首先咱们定一个小目标: <div id="div1"></div> 我但愿将上面的html解析成ast格式,相似于下面: 复制代码 { "tag":"div", "attrs":\[ { "id":"div1" } \], "children":\[\], "type":1 } 复制代码 最终想要达成的第一个小目标是能够将div标签字符串输出成这样一个object格式,tag表示标签名称,attrs表示属性,children表示这个div全部的子节点,type的话表示节点的类型,咱们今天只三个类型: 1.元素类型,也就是标签类型,全部用<tag attr=""></tag>这样的标签。2.变量text,如今咱们实现一个{{text}}的变量转换,它实际上是一个节点。3.普通文本,普通文本包括普通文字和空格、换行。 基本结构 基本结构的设计决定的代码能扩展多远,若是一开始结构设计错误,最后在新加入的功能没法嵌入的时候,那就只有重构一条路能够走了。 首先理清楚咱们的思路。 匹配单个字符》匹配标签》匹配属性》匹配文本》匹配结束标签 而后,你想啊,html标签都是有开始,有结束的。那么这里问题就来了,能够想到的方式,解析一个标签的开始与结束吧,例如咱们使用正则匹配开始标签<div id='div1'> 而后找到结束标签</div>,这样是否是就能够解析div里面的内容了? 难。 开始标签比较好找,结束标签就恶心了,例如 <div><div></div></div> ,,完了,怎么区分嵌套关系?第一个<div>到底匹配哪个结束标签? 这个思路是错的,很难。 那么咱们换个思路,若是咱们单个字符匹配呢, 例如咱们匹配一个 <div><div></div></div>, ok 脑补步骤 1。匹配到 < 匹配到这个字符我就能够认为,后面的要么是开始标签,要么是结束标签。 2。用正则匹配从<到后面的字符,若是是开始标签,如今记录一下,啊,我遇到了一个开始标签<div> 顺便用正则记录attrs 3. 如今咱们匹配走走走。。。走到<div></div></div> 4.又匹配到一个 < 老步骤啊。 5.发现是开始标签,再次记录,啊,我又遇到一个开始标签 <div> 顺便用正则记录attrs 6. 如今咱们匹配走走走。。。走到</div></div> 7. 又匹配到一个 < 老步骤啊。 8.发现是一个结束标签</div> ,嗯?结束标签!它是谁的结束标签?想想。。。。。。应该是最后一个遇到的开始标签吧。 第一个遇到的结束标签不就是最后一个开始标签的结束么? 9.啊,结束了一个。 10.再匹配,再完成。 恩。。。思路清晰了有没有,来实现走一个: 复制代码 //转化HTML至AST对象 function parse(template){ var currentParent; //当前父节点 var root; //最终生成的AST对象 var stack = \[\]; //插入栈 var startStack = \[\]; //开始标签栈 var endStack = \[\]; //结束标签栈 //console.log(template); parseHTML(template,{ start:function start(targetName,attrs,unary,start,end,type,text){//标签名 ,attrs,是否结束标签,文本开始位置,文本结束位置,type,文本, var element = { //咱们想要的对象 tag:targetName, attrsList:attrs, parent:currentParent, //须要记录父对象吧 type:type, children:\[\] } if(!root){ //根节点哈 root = element; } if(currentParent && !unary){ //有父节点而且不是结束标签? currentParent.children.push(element); //插入到父节点去 element.parent = currentParent; //记录父节点 } if (!unary) { //不是结束标签? if(type == 1){ currentParent = element;//不是结束标签,当前父节点就要切换到如今匹配到的这个开始标签哈,后面再匹配到 startStack.push(element); //推入开始标签栈 } stack.push(element); //推入总栈 }else{ endStack.push(element); //推入结束标签栈 currentParent = startStack\[endStack.length-1\].parent; //结束啦吧当前父节点切到上一个开始标签,这能理解吧,当前这个已经结束啦 } //console.log(stack,"currentstack") }, end:function end(){ }, chars:function chars(){ } }); console.log(root,"root"); return root; }; /\*\* \* Not type-checking this file because it's mostly vendor code. \*/ /\*! \* HTML Parser By John Resig (ejohn.org) \* Modified by Juriy "kangax" Zaytsev \* Original code by Erik Arvidsson, Mozilla Public License \* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js \*/ // Regular Expressions for parsing tags and attributes var singleAttrIdentifier = /(\[^\\s"'<>/=\]+)/; var singleAttrAssign = /(?:=)/; var singleAttrValues = \[ // attr value double quotes /"(\[^"\]\*)"+/.source, // attr value, single quotes /'(\[^'\]\*)'+/.source, // attr value, no quotes /(\[^\\s"'=<>\`\]+)/.source \]; var attribute = new RegExp( '^\\\\s\*' + singleAttrIdentifier.source + '(?:\\\\s\*(' + singleAttrAssign.source + ')' + '\\\\s\*(?:' + singleAttrValues.join('|') + '))?' ); // could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName // but for Vue templates we can enforce a simple charset var ncname = '\[a-zA-Z\_\]\[\\\\w\\\\-\\\\.\]\*'; var qnameCapture = '((?:' + ncname + '\\\\:)?' + ncname + ')'; var startTagOpen = new RegExp('^<' + qnameCapture); var startTagClose = /^\\s\*(\\/?)>/; var endTag = new RegExp('^<\\\\/' + qnameCapture + '\[^>\]\*>'); var doctype = /^<!DOCTYPE \[^>\]+>/i; var comment = /^<!--/; var conditionalComment = /^<!\\\[/; //偷懒哈 上面的正则是我在vue上拿下来的,这个后期能够研究,下面的话简单的写两个用用,和vue原版的是有一些差异的 //{{变量}} var varText = new RegExp('{{' + ncname + '}}'); //空格与换行符 var space = /^\\s/; var checline = /^\[\\r\\n\]/; /\*\* type 1普通标签 type 2代码 type 3普通文本 \*/ function parseHTML(html,options){ var stack = \[\]; //内部也要有一个栈 var index = 0; //记录的是html当前找到那个索引啦 var last; //用来比对,当这些条件都走完后,若是last==html 说明匹配不到啦,结束while循环 var isUnaryTag = false; while(html){ last = html; var textEnd = html.indexOf('<'); if(textEnd === 0){ //这一步若是第一个字符是<那么就只有两种状况,1开始标签 2结束标签 //结束标签 var endTagMatch = html.match(endTag); //匹配 if(endTagMatch){ console.log(endTagMatch,"endTagMatch"); isUnaryTag = true; var start = index; advance(endTagMatch\[0\].length); //匹配完要删除匹配到的,而且更新index,给下一次匹配作工做 options.start(null,null,isUnaryTag,start,index,1); continue; } //初始标签 var startMatch = parseStartTag(); if(startMatch){ parseStartHandler(startMatch);//封装处理下 console.log(stack,"startMatch"); continue; } } if(html === last){ console.log(html,"html"); break; } } function advance (n) { index += n; html = html.substring(n); } //处理起始标签 主要的做用是生成一个match 包含初始的attr标签 function parseStartTag(){ var start = html.match(startTagOpen); if(start){ var match = { tagName: start\[1\], // 标签名(div) attrs: \[\], // 属性 start: index // 游标索引(初始为0) }; advance(start\[0\].length); var end, attr; while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {//在endClose以前寻找attribute advance(attr\[0\].length); match.attrs.push(attr); } if (end) { advance(end\[0\].length); // 标记结束位置 match.end = index; //这里的index 是在 parseHTML就定义 在advance里面相加 return match // 返回匹配对象 起始位置 结束位置 tagName attrs } } } //对match进行二次处理,生成对象推入栈 function parseStartHandler(match){ var \_attrs = new Array(match.attrs.length); for(var i=0,len=\_attrs.length;i<len;i++){ //这儿就是找attrs的代码哈 var args = match.attrs\[i\]; var value = args\[3\] || args\[4\] || args\[5\] || ''; \_attrs\[i\] = { name:args\[1\], value:value } } stack.push({tag: match.tagName,type:1, lowerCasedTag: match.tagName.toLowerCase(), attrs: \_attrs}); //推栈 options.start(match.tagName, \_attrs,false, match.start, match.end,1); //匹配开始标签结束啦。 } } 复制代码 咱们执行 parse("<div id='test1'><div></div></div>"); 大功告成哈哈哈哈哈 呃。
\[1,2,3,4,5\].concat(\[1,2,3,4,5\].reduce((prev,cur)=>\[...prev, cur\*cur\], \[\]))
function groupBy(objectArray, property) { return objectArray.reduce(function (acc, obj) { var key = obj\[property\]; if (!acc\[key\]) { acc\[key\] = \[\]; } acc\[key\].push(obj); return acc; }, {}); }
https://www.cnblogs.com/cheny...
首先咱们要明白一个前提,CommonJS模块规范和ES6模块规范彻底是两种不一样的概念。 require: node 和 es6 都支持的引入 export / import : 只有es6 支持的导出引入 module.exports / exports: 只有 node 支持的导出 CommonJS模块规范 Node应用由模块组成,采用CommonJS模块规范。 根据这个规范,每一个文件就是一个模块,有本身的做用域。在一个文件里面定义的变量、函数、类,都是私有的,对其余文件不可见。 CommonJS规范规定,每一个模块内部,module变量表明当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,实际上是加载该模块的module.exports属性。 CommonJS规范,http://javascript.ruanyifeng.com/nodejs/module.html ES6模块规范 不一样于CommonJS,ES6使用 export 和 import 来导出、导入模块。 ES6 Module 的语法,http://es6.ruanyifeng.com/#docs/module node模块 Node里面的模块系统遵循的是CommonJS规范。 那问题又来了,什么是CommonJS规范呢? 因为js之前比较混乱,各写各的代码,没有一个模块的概念,而这个规范出来其实就是对模块的一个定义。 CommonJS定义的模块分为: 模块标识(module)、模块定义(exports) 、模块引用(require) 先解释 exports 和 module.exports 在一个node执行一个文件时,会给这个文件内生成一个 exports和module对象, 而module又有一个exports属性。他们之间的关系以下图,都指向一块{}内存区域。 1 exports = module.exports = {}; 那下面咱们来看看代码的吧。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //utils.js let a = 100; console.log(module.exports); //能打印出结果为:{} console.log(exports); //能打印出结果为:{} exports.a = 200; //这里辛苦劳做帮 module.exports 的内容给改为 {a : 200} exports = '指向其余内存区'; //这里把exports的指向指走 //test.js var a = require('/utils'); console.log(a) // 打印为 {a : 200} 从上面能够看出,其实require导出的内容是module.exports的指向的内存块内容,并非exports的。 简而言之,区分他们之间的区别就是 exports 只是 module.exports的引用,辅助后者添加内容用的。 用白话讲就是,exports只辅助module.exports操做内存中的数据,辛辛苦苦各类操做数据完,累得要死,结果到最后真正被require出去的内容仍是module.exports的,真是好苦逼啊。 其实你们用内存块的概念去理解,就会很清楚了。 而后呢,为了不糊涂,尽可能都用 module.exports 导出,而后用require导入。 ES中的模块导出导入 说实话,在es中的模块,就很是清晰了。不过也有一些细节的东西须要搞清楚。 好比 export 和 export default,还有 导入的时候,import a from ..,import {a} from ..,总之也有点乱,那么下面咱们就开始把它们捋清楚吧。 export 和 export default 首先咱们讲这两个导出,下面咱们讲讲它们的区别 一、export与export default都可用于导出常量、函数、文件、模块等 二、在一个文件或模块中,export、import能够有多个,export default仅有一个 三、经过export方式导出,在导入时要加{ },export default则不须要 四、export能直接导出变量表达式,export default不行。 下面我们看看代码去验证一下 testEs6Export.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 'use strict' //导出变量 export const a = '100'; //导出方法 export const dogSay = function(){ console.log('wang wang'); } //导出方法第二种 function catSay(){ console.log('miao miao'); } export { catSay }; //export default导出 const m = 100; export default m; //export defult const m = 100;// 这里不能写这种格式。 index.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 //index.js 'use strict' var express = require('express'); var router = express.Router(); import { dogSay, catSay } from './testEs6Export'; //导出了 export 方法 import m from './testEs6Export'; //导出了 export default import \* as testModule from './testEs6Export'; //as 集合成对象导出 /\* GET home page. \*/ router.get('/', function(req, res, next) { dogSay(); catSay(); console.log(m); testModule.dogSay(); console.log(testModule.m); // undefined , 由于 as 导出是 把 零散的 export 汇集在一块儿做为一个对象,而export default 是导出为 default属性。 console.log(testModule.default); // 100 res.send('恭喜你,成功验证'); }); module.exports = router; 从上面能够看出,确实感受 ES6的模块系统很是灵活的。
https://www.cnblogs.com/yangd...
class People{ constructor(){ this.name='张三' } sayHi(){ console.log('hi') } } function People(){ this.name='张三' } People.prototype.sayHi=function(){ console.log('hi') }