今天是圣诞节,转眼就要迎来2019年的到来了。有时候想想,不禁感叹一下。啊!时间可过得可真快啊!
在这里就说说2018年我都做了些啥,2019我还要做些啥吧。自认为,自己在2018年还是成长了不少的(难免有些自恋哈…),其实吧,今年呢,写了三个项目。一个P2P的pc端和H5以及后台的管理、一个金融项目的H5和后台管理最后一个是公司内部运营的小程序和后台管理。全是用vue写的,感觉自己都成了资深的vue专家了,又是嵌App又是小程序又是后台管理又是前台pc(兼容到了ie9)。其实成长最多的还是P2P的这个项目,算是比较复杂的一个项目,里面的风控,以及各种权限的自定义配置。感觉学到了很多,PC客户端和H5是我一个人写的。各位如果有兴趣可以看看。点击这里!点击这里!这个P2P项目连带着后台管理做了半年了。这个项目的客户端,我定义了很多可复用的组件,所以维护起来特别快捷。后台的管理系统这里就不贴出来了,后台的这一套也是用vue,但是有点非主流,因为我还没来的时候,这家公司的老大以前是写php的会点前端,用的一个特别古老的sea.js进行的开发。没用用webpack,然后后台只能写es5特别蛋疼。当时改兼容算是噩梦了,当然pc客户端同样是噩梦,上传功能由于改flash所以就发着,其他的一些ajax的一些300问题,css的flex不兼容问题,还有最坑的一个table的html渲染问题这个我印象深刻!h5倒还好,没有改多少。P2P项目的客户端,让我学会灵活的使用vue给我提供的各种api,然后感受到vue-router、vuex给我们提供的各种方便。这里就不细说了。(其实当时pc客户端打算用nuxt的,但是老大说没有这个必要就没有用。)
这个项目之后到7月份闲了一段时候,自己看了看小程序。照着之前那个h5撸了一遍大概一个月不到吧,感觉小程序没有vue写的爽,template用的不爽。后来就想,如果能用vue和h5写小程序就爽了,一查还真有美团的mpvue,之后就大概了解了一下。后续的有一个内部运营的项目,有一个小程序的,当时就用了mpvue写起来那叫一个舒服,听说还可打包成h5。因为没用到就没研究!
通过这一系列的项目,以及我业余时间的学习,算是对js原生和vue这一系列知识有了一个系统的了解。愿明年、我在web前端这个岗位放光发热!
基于mpVue写了一个企业运营管理项目
前言
接近两个月没有更新博客了,上一个月一直在写一个小程序的项目,因为本人一直都是写vue也没怎么写过小程序,所以在网上看了看发现了一个mpvue这个框架发现可以用h5和vue写小程序果断看了一天,然后运用在项目中了!废话不多说,在这里说一说在实际开发中的一些坑。
基本配置
这里就不多说基本配置了官方都有点击这里!
组件中的生命周期
我们都知道vue的生命周期函数,小程序也有他的生命周期函数!在mpvue里面我们都可以用!没错是都可以用,这个位置就是一个坑!我是在这样的业务场景发现有这样一个坑。。。
本人做的这个项目有七个管理栏目 每个栏目中有相同样式的列表也和详情页,少部分也存在编辑新增!我将七个栏目列表共用了一个页面。 七个详情分别写了七个组件,我是根据页面路径传递不同的参数通过if判断实现的。在这里组件里面就存在一个问题,vue的钩子函数只会执行第一次(created和mounted),后续无论多少次进入是没办法执行的。然而小程序中的(onload和onshow)则是第一次不会执行后续再进入就会执行!在这里我是通过同时调用两边的钩子函数来避免这个问题的!
编辑新增刷新数据
在栏目里面有些数据是会有编辑和新增的,这里有一个功能就是编辑/新增完成之后需要刷新数据。由于新增/编辑是另外的一个页面算是返回之后刷新在这里为了良好的用户体验、以及完成新增/编辑完成之后才刷新这个业务,我用了小程序里面的一个方法getCurrentPages()
这个函数可以获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。
1
2
3
4
5
6
7
8
9
10
11
12
13mounted() {
let _this = this;
let pages = getCurrentPages();
_this._2Page = pages[pages.length - 2]
_this._3Page = pages[pages.length - 3]
_this.isShow = true
},
onShow() {
let _this = this;
let pages = getCurrentPages();
_this._2Page = pages[pages.length - 2]
_this._3Page = pages[pages.length - 3]
}
这里大家肯定会有疑问为啥要调用两次还是分别在vue的钩子函数中和小程序钩子函数中;答案就是我上面提到的那个问题!
这里刷新数据就是通过他获取上级以及上上级页面的参数在编辑成功后执行上级或者上上级的钩子函数完成刷新页面!
一些ui框架的组件初始化的问题
这个项目中我用了很多ui组件毕竟项目紧就一个月…这里就有一些组件初始化的问题,比如通知栏这个组件 。由于,通知的消息是后台获取的,所以这里有一个循环滚动这个属性没有生效 。这里,我是用的if给他先隐藏然后再他获取消息成功之后在显示,这样就没问题啦~
最后
因为本项目已经上线涉及保密不能给大家源码!这些如果能帮助大家是最好的,记得star我哟!
Javascript基于对象三大特性(封装性、继承性、多态性)[转载]
- 这篇文章主要介绍了Javascript基于对象三大特性,包括封装性、继承性、多态性,感兴趣的小伙伴们可以参考一下
前言
Javascript基于对象的三大特征和C++,Java面向对象的三大特征一样,都是封装(encapsulation)、继承(inheritance )和多态(polymorphism )。只不过实现的方式不同,其基本概念是差不多的。其实除三大特征之外,还有一个常见的特征叫做抽象(abstract),这也就是我们在一些书上有时候会看到面向对象四大特征的原因了。
封装性
封装就是把抽象出来的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
案例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<html>
<head>
<script type="text/javascript">
function Person(name, agei, sal){
// 公开
this.name = name;
// 私有
var age = agei;
var salary = sal;
}
var p1 = new Person('zs', 20, 10000);
window.alert(p1.name + p1.age);
</script>
</head>
<body>
</body>
</html>
PS:JS封装只有两种状态,一种是公开的,一种是私有的。
通过构造函数添加成员方法和通过原型法添加成员方法的区别
1、通过原型法分配的函数是所有对象共享的.
2、通过原型法分配的属性是独立.(如果你不修改属性,他们是共享)
3、建议,如果我们希望所有的对象使用同一一个函数,最好使用原型法添加函数,这样比较节省内存.
案例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18function Person(){
this.name="zs";
var age=20;
this.abc=function(){
window.alert("abc");
}
function abc2(){
window.alert("abc");
}
}
Person.prototype.fun1=function(){
window.alert(this.name);//ok
//window.alert(age);//no ok
//abc2();//no ok
this.abc();//ok
}
var p1=new Person();
p1.fun1();
继承性
继承可以解决代码复用,让编程更加靠近人类思维。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过继承父类中的属性和方法。
JS中实现继承的方式
1、对象冒充
案例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37<html>
<head>
<script type="text/javascript">
function Stu(name, age){
this.name = name;
this.age = age;
this.show = function(){
window.alert(this.name + " " + this.age);
}
}
function MidStu(name, age) {
this.stu = Stu;
// 通过对象冒充来实现继承的
// 对象冒充的意思就是获取那个类的所有成员,因为js是谁调用那个成员就是谁的,这样MidStu就有了Stu的成员了
this.stu(name, age);
this.payFee = function(){
window.alert("缴费" + money * 0.8);
}
}
function Pupil(name, age) {
this.stu = Stu;
// 通过对象冒充来实现继承的
this.stu(name, age);
this.payFee = function(){
window.alert("缴费" + money * 0.5);
}
}
var midStu = new MidStu("zs", 13);
midStu.show();
var pupil = new Pupil("ls", 10);
pupil.show();
</script>
</head>
<body>
</body>
</html>
2、通过call或者apply实现
案例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40<html>
<head>
<script type="text/javascript">
//1. 把子类中共有的属性和方法抽取出,定义一个父类Stu
function Stu(name,age){
// window.alert("确实被调用.");
this.name=name;
this.age=age;
this.show=function(){
window.alert(this.name+"年龄是="+this.age);
}
}
//2.通过对象冒充来继承父类的属性的方法
function MidStu(name,age){
//这里这样理解: 通过call修改了Stu构造函数的this指向,
//让它指向了调用者本身.
Stu.call(this,name,age);
//如果用apply实现,则可以
//Stu.apply(this,[name,age]); //说明传入的参数是 数组方式
//可以写MidStu自己的方法.
this.pay=function(fee){
window.alert("你的学费是"+fee*0.8);
}
}
function Pupil(name,age){
Stu.call(this,name,age);//当我们创建Pupil对象实例,Stu的构造函数会被执行,当执行后,我们Pupil对象就获取从 Stu封装的属性和方法
//可以写Pupil自己的方法.
this.pay=function(fee){
window.alert("你的学费是"+fee*0.5);
}
}
//测试
var midstu=new MidStu("zs",15);
var pupil=new Pupil("ls",12);
midstu.show();
midstu.pay(100);
pupil.show();
pupil.pay(100);
</script>
</html>
小结:
1、JS对象可以通过对象冒充,实现多重继承
2、Object类是所有Js类的基类
多态性
JS的函数重载
这个是多态的基础,在之前的Javascript入门已经说过了,JS函数不支持多态,但是事实上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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45<html>
<head>
<script type="text/javascript">
//*****************说明js不支持重载*****
/*function Person(){
this.test1=function (a,b){
window.alert('function (a,b)');
}
this.test1=function (a){
window.alert('function (a)');
}
}
var p1=new Person();
//js中不支持重载.
//但是这不会报错,js会默认是最后同名一个函数,可以看做是后面的把前面的覆盖了。
p1.test1("a","b");
p1.test1("a");*/
//js怎么实现重载.通过判断参数的个数来实现重载
function Person(){
this.test1=function (){
if(arguments.length==1){
this.show1(arguments[0]);
}else if(arguments.length==2){
this.show2(arguments[0],arguments[1]);
}else if(arguments.length==3){
this.show3(arguments[0],arguments[1],arguments[2]);
}
}
this.show1=function(a){
window.alert("show1()被调用"+a);
}
this.show2=function(a,b){
window.alert("show2()被调用"+"--"+a+"--"+b);
}
function show3(a,b,c){
window.alert("show3()被调用");
}
}
var p1=new Person();
//js中不支持重载.
p1.test1("a","b");
p1.test1("a");
</script>
</html>
1、多态基本概念
多态是指一个引用(类型)在不同情况下的多种状态。也可以理解成:多态是指通过指向父类的引用,来调用在不同子类中实现的方法。
案例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61<script type="text/javascript">
// Master类
function Master(name){
this.nam=name;
//方法[给动物喂食物]
}
//原型法添加成员函数
Master.prototype.feed=function (animal,food){
window.alert("给"+animal.name+" 喂"+ food.name);
}
function Food(name){
this.name=name;
}
//鱼类
function Fish(name){
this.food=Food;
this.food(name);
}
//骨头
function Bone(name){
this.food=Food;
this.food(name);
}
function Peach(name){
this.food=Food;
this.food(name);
}
//动物类
function Animal(name){
this.name=name;
}
//猫猫
function Cat(name){
this.animal=Animal;
this.animal(name);
}
//狗狗
function Dog(name){
this.animal=Animal;
this.animal(name);
}
//猴子
function Monkey(name){
this.animal=Animal;
this.animal(name);
}
var cat=new Cat("猫");
var fish=new Fish("鱼");
var dog=new Dog("狗");
var bone=new Bone("骨头");
var monkey=new Monkey("猴");
var peach=new Peach("桃");
//创建一个主人
var master=new Master("zs");
master.feed(dog,bone);
master.feed(cat,fish);
master.feed(monkey,peach);
</script>
多态利于代码的维护和扩展,当我们需要使用同一类树上的对象时,只需要传入不同的参数就行了,而不需要再new 一个对象。
以上就是Javascript基于对象三大特性,希望对大家的学习有所帮助。
一名前端Web架构师的成长之路(转载)
本人也是coding很多年,虽然很失败,但也总算有点失败的心得,不过我在中国,大多数程序员都是像我一样,在一直走着弯路。如果想成为一个架构师,就必须走正确的路,否则离目标越来越远,正在辛苦工作的程序员们,你们有没有下面几种感觉?
一、我的工作就是按时完成领导交给我的任务,至于代码写的怎样,知道有改进空间,但没时间去改进,关键是领导也不给时间啊。
二、我发现我的水平总是跟不上技术的进步,有太多想学的东西要学,jQuery用的人最近比较多啊,听说最近MVC比较火,还有LINQ,听说微软又有Silverlight了……
三、我发现虽然我工作几年了,除了不停的coding,Ctrl+C和Ctrl+V更熟练了,但编码水平并没有提高,还是一个普通程序员,但有人已经做到架构师了。
四、工作好几年了,想跳槽换个工作,结果面试的考官都问了一些什么数据结构,什么垃圾回收,什么设计模式之类的东西,虽然看过,但是平时用不着,看了也忘记了,回答不上来,结果考官说我基础太差。。。
有没有,如果没有,接下来就不用看了,你一定是大拿了,或者已经明白其中之道了,呵呵。
如果有,恭喜你,你进入学习误区了,如果想在技术上前进的话,就不能一直的coding,为了完成需求而工作,必须在coding的同时,让我们的思维,水平也在不停的提高。
写代码要经历下面几个阶段。
一 、你必须学习面向对象的基础知识,如果连这个都忘了,那你的编程之路注定是在做原始初级的重复!
很多程序员都知道类、方法、抽象类、接口等概念,但是为什么要面向对象,好处在哪里,要解决什么问题?只是明白概念,就是表达不清楚,然后在实 际工作中也用不上,过了一段时间,面向对象的东西又模糊了,结果是大多数程序员用着面向对象的语言做着面向过程的工作,因此要学习面向对象,首先应该明白 面向对象的目的是什么?
面向对象的目的是什么?
开发语言在不断发展,从机器语言,到汇编,到高级语言,再到第四代语言;软件开发方法在不断发展,从面向过程,面向对象,到面向方面等。虽然这些都在不断发展,但其所追求的目标却一直没变,这些目标就是:
1. 降低软件开发的复杂度
2. 提高软件开发的效率
3. 提高软件质量:可维护性,可扩展性,可重用性等。
其中语言的发展,开发方法的发展在1,2两条上面取得了极大的进步,但对于第3条,我们不能光指望开发方法本身来解决。
提高软件质量:可维护性,可扩展性,可重用性等,再具体点,就是高内聚、低耦合,面向对象就是为了解决第3条的问题。因此要成为一个好的程序员,最绕不开的就是面向对象了。可以500%提高开发效率的前端UI框架!
二、 要想学好面向对象,就必须学习设计模式。
假定我们了解了面向对象的目的,概念了,但是我们coding过程中却发现,我们的面向对象的知识似乎一直派不上用场,其实道理很简单,是因为 我们不知道怎么去用,就像游泳一样,我们已经明白了游泳的好处,以及游泳的几种姿势,狗刨、仰泳、蛙泳、自由泳,但是我们依然不会游泳。。。。
因此有了这些基本原则是不行的,我们必须有一些更细的原则去指导我们的设计,这就有了更基础的面向对象的五大原则,而把这几种原则更详细的应用 到实际中来,解决实际的问题,这就是设计模式。因此要学好OO,必须要学习设计模式,学习设计模式,按大师的话说,就是在人类努力解决的许多领域的成功方 案都来源于各种模式,教育的一个重要目标就是把知识的模式一代一代传下去。
因此学习设计模式,就像我们在看世界顶级的游泳比赛,我们为之疯狂,为之着迷。
三、学习设计模式
正像我们并不想只是看别人表演,我们要自己学会游泳,这才是我们的目的所在。
当我们看完几篇设计模式后,我们为之精神振奋,在新的coding的时候,我们总是想努力的用上学到的设计模式,但是经常在误用模式,折腾半天发现是在脱裤子抓痒。。。
当学完设计模式之后,我们又很困惑,感觉这些模式简直太像了,很多时候我们分不清这些模式之间到底有什么区别,而且明白了设计过程中的一个致命 的东西——过度设计,因为设计模式要求我们高扩展性,高重用性,但是在需求提出之初,我们都不是神,除了依靠过去的经验来判断外,我们不知道哪些地方要扩 展,哪些地方要重用,而且过去的经验就一定是正确的吗?所以我们甚至不敢再轻易用设计模式,而是还一直在用面向过程的方法在实现需求。
四、学习重构
精彩的代码是怎么想出来的,比看到精彩的代码更加令人期待。于是我们开始思考,这些大师们莫非不用工作,需求来了没有领导规定完成时间,只以设 计精彩的代码为标准来开展工作?这样的工作太爽了,也不可能,老板不愿意啊。就算这些理想的条件他都有,他就一开始就设计出完美的代码来了?也不可能啊, 除非他是神,一开始就预料到未来的所有需求,那既然这些条件都没有,他们如何写出的精彩代码?
Joshua Kerievsky在那篇著名的《模式与XP》〔收录于《极限编程研究》一书)中明白地指出:在设计前期使用模式常常导致过度工程(over- engineering)。这是一个残酷的现实,单凭对完美的追求无法写出实用的代码,而「实用」是软件压倒一切的要素。
在《重构——改善既有的代码的设计》一书中提到,通过重构(refactoring),你可以找出改变的平衡点。你会发现所谓设计不再是一切动 作的前提,而是在整个开发过程中逐渐浮现出来。在系统构筑过程中,你可以学习如何强化设计;其间带来的互动可以让一个程序在开发过程中持续保有良好的设 计。
总结起来就是说,我们在设计前期就使用设计模式,往往导致设计过度,因此应该在整个开发过程,整个需求变更过程中不断的重构现在的代码,才能让 程序一直保持良好的设计。由此可见,开发过程中需要一直重构,否则无论当初设计多么的好,随着需求的改变,都会变成一堆烂代码,难以维护,难以扩展。所谓 重构是这样一个过程:「在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构」。重构的目标,就是设计模式,更本质的讲就是使程序的架构 更趋合理,从而提高软件的可维护性,可扩展性,可重用性。
《重构——改善既有的代码的设计》一书也是Martin Fowler等大师的作品,软件工程领域的超级经典巨著,与另一巨著《设计模式》并称”软工双雄”,不可不读啊。
五、开始通往优秀软件设计师的路上。可以500%提高开发效率的前端UI框架!
通过设计模式和重构,我们的所学和我们工作的coding终于结合上了,我们可以在工作中用面向对象的思维去考虑问题,并开始学习重构了。这就 像游泳一样,我们看完了各种顶级的游泳比赛,明白各种规则,名人使用的方法和技巧,现在是时候回家去村旁边的小河里练练了。练习也是需要有教练的,推荐另 一本经典书叫《重构与模式》,引用他开篇的介绍,本书开创性地深入揭示了重构与模式这两种软件开发关键技术之间的联系,说明了通过重构实现模式改善既有的 设计,往往优于在新的设计早期使用模式。本书不仅展示了一种应用模式和重构的创新方法,而且有助于读者结合实战深入理解重构和模式。
这本书正是我们需要的教练,值得一读。
六、没有终点,只有坚持不懈的专研和努力。
经过了几年的坚持,终于学会了灵活的运用各种模式,我们不需要去刻意的想用什么模式,怎么重构。程序的目标,就是可维护性,可扩展性,可重用 性,都已经成了一种编程习惯,一种思维习惯,就像我们练习了几年游泳之后,我们不用再刻意的去考虑,如何让自己能在水上漂起来,仰泳和蛙泳的区 别….. 而是跳进水里,就自然的游了起来,朝对岸游去。但是要和大师比起来,嘿嘿,我们还有很长的路要走,最终也可能成不了大师,但无论能不能成为大师,我们已经 走在了成为大师的正确的路上,我们和别的程序员已经开始不一样,因为他们无论再过多少年,他们的水平不会变,只是在重复造轮子,唯一比你快的,就是 Ctrl+C和Ctrl+V。
正确的路上,只要坚持,就离目标越来越近,未来就一定会是一个优秀的架构师,和优秀架构师的区别,可能只是时间问题。
ES6的迁移之路---[5]
前言
对ES6中的Proxy
、Class
、Promise
、iterator
、genertaor
、修饰器
、import
进行学习笔记
Proxy
Proxy(代理) 是 ES6 中新增的一个特性。
Proxy 让我们能够以简洁易懂的方式控制外部对对象的访问。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50{
let obj={
time:'2017-03-11',
name:'net',
_r:123
};
let monitor=new Proxy(obj,{
// 拦截对象属性的读取
get(target,key){
return target[key].replace('2017','2018')
},
// 拦截对象设置属性
set(target,key,value){
if(key==='name'){
return target[key]=value;
}else{
return target[key];
}
},
// 拦截key in object操作
has(target,key){
if(key==='name'){
return target[key]
}else{
return false;
}
},
// 拦截delete
deleteProperty(target,key){
if(key.indexOf('_')>-1){
delete target[key];
return true;
}else{
return target[key]
}
},
// 拦截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames
ownKeys(target){
return Object.keys(target).filter(item=>item!='time')
}
})
console.log('get',monitor.time);//get 2018-03-11
monitor.time='2018';
monitor.name='mukewang';
console.log('set',monitor.time);set 2018-03-11
console.log('has','name' in monitor,'time' in monitor);//has true false
console.log('ownKeys',Object.keys(monitor));//ownKeys ["name",'_r']
}
Class
Class这个概念,作为对象的模板,通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96{
// 基本定义和生成实例
class Parent{
constructor(name='mukewang'){
this.name=name;
}
}
let v_parent=new Parent('v');
console.log('构造函数和实例',v_parent);//构造函数和实例 Parent { name: 'v' }
}
{
// 继承
class Parent{
constructor(name='mukewang'){
this.name=name;
}
}
class Child extends Parent{
}
console.log('继承',new Child());继承 Child { name: 'mukewang' }
}
{
// 继承传递参数
class Parent{
constructor(name='mukewang'){
this.name=name;
}
}
class Child extends Parent{
constructor(name='child'){
super(name);
this.type='child';
}
}
console.log('继承传递参数',new Child('hello'));//继承传递参数 Child { name: 'hello', type: 'child' }
}
{
// getter,setter
class Parent{
constructor(name='mukewang'){
this.name=name;
}
get longName(){
return 'mk'+this.name
}
set longName(value){
this.name=value;
}
}
let v=new Parent();
console.log('getter',v.longName);//getter mkmukewang
v.longName='hello';
console.log('setter',v.longName);//setter mkhello
}
{
// 静态方法
class Parent{
constructor(name='mukewang'){
this.name=name;
}
static tell(){
console.log('tell');
}
}
Parent.tell();//test
}
{
// 静态属性
class Parent{
constructor(name='mukewang'){
this.name=name;
}
static tell(){
console.log('tell');
}
}
Parent.type='test';
console.log('静态属性',Parent.type);//静态属性 test
}
Promise
Promise 是异步编程的一种解决方案,比传统的解决方案–回调函数和事件--更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了语法,原生提供了Promise
所谓Promise ,简单说就是一个容器,里面保存着某个未来才回结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。
Promise 对象的状态不受外界影响1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78{
// 基本定义
let ajax=function(callback){
console.log('执行');
setTimeout(function () {
callback&&callback.call()
}, 1000);
};
ajax(function(){
console.log('timeout1');
}) // 执行 timeout1
}
{
let ajax=function(){
console.log('执行2');
return new Promise(function(resolve,reject){
setTimeout(function () {
resolve()
}, 1000);
})
};
ajax().then(function(){
console.log('promise','timeout2');
})//执行2 promise timeout2
}
{
let ajax=function(){
console.log('执行3');
return new Promise(function(resolve,reject){
setTimeout(function () {
resolve()
}, 1000);
})
};
ajax()
.then(function(){
return new Promise(function(resolve,reject){
setTimeout(function () {
resolve()
}, 2000);
});
})
.then(function(){
console.log('timeout3');//执行3 timeout3
})
}
{
let ajax=function(num){
console.log('执行4');
return new Promise(function(resolve,reject){
if(num>5){
resolve()
}else{
throw new Error('出错了')
}
})
}
ajax(6).then(function(){
console.log('log',6);
}).catch(function(err){
console.log('catch',err);
});//执行4 log6
ajax(3).then(function(){
console.log('log',3);
}).catch(function(err){
console.log('catch',err);
});//执行4 error
}
下面是一个用Promise对象实现的 Ajax 操作的例子。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
深入理解Promise.all()
我们在来看一下这段代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18Promise.all([asyncTask(1),asyncTask(2),asyncTask(3)])
.then(resultList => {
console.log('results:',resultList);
});
传入3个Promise实例:
Promise.all([
new Promise(function(resolve, reject) {
resolve(1)
}),
new Promise(function(resolve, reject) {
resolve(2)
}),
new Promise(function(resolve, reject) {
resolve(3)
})
]).then(arr => {
console.log(arr) // [1, 2, 3]
})
Promise.race()
语法和all()一样,但是返回值有所不同,race根据传入的多个Promise实例,只要有一个实例resolve或者reject,就只返回该结果,其他实例不再执行。1
2
3
4
5
6
7
8
9
10
11
12
13Promise.race([
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000)
}),
new Promise(function(resolve, reject) {
setTimeout(() => resolve(2), 100)
}),
new Promise(function(resolve, reject) {
setTimeout(() => resolve(3), 10)
})
]).then(value => {
console.log(value) // 3
})
iterator
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
作用:
为各种数据结构,提供一个统一的、简便的访问接口
使得数据结构的成员能够按某种次序排列
ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of消费
Iterator的遍历过程:
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
- 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
- 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
- 不断调用指针对象的next方法,直到它指向数据结构的结束位置。
1 | { |
自定义for of 循环1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32{
let obj={
start:[1,3,2],
end:[7,9,8],
[Symbol.iterator](){
let self=this;
let index=0;
let arr=self.start.concat(self.end);
let len=arr.length;
return {
next(){
if(index<len){
return {
value:arr[index++],
done:false
}
}else{
return {
value:arr[index++],
done:true
}
}
}
}
}
}
for(let key of obj){
console.log(key);
}
}
// 1 3 2 7 9 8
##Generator
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。
Generator函数有多种理解角度。从语法上,首先可以把它理解成,Generator函数一个状态机,封装了多个内部状态。
执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。
形式上,Generator函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号,二是,函数体内部使用yield语句,定义不同的内部状态。(yield语句在英语里的意思就是“产出”)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62{
// genertaor基本定义
let tell=function* (){
yield 'a';
yield 'b';
return 'c'
};
let k=tell();
console.log(k.next());
console.log(k.next());
console.log(k.next());
console.log(k.next());
}
/*
{ value: 'a', done: false }
{ value: 'b', done: false }
{ value: 'c', done: true }
{ value: undefined, done: true }
*/
{
let obj={};
obj[Symbol.iterator]=function* (){
yield 1;
yield 2;
yield 3;
}
for(let value of obj){
console.log('value',value);
}
/*
value 1
value 2
value 3
*/
}
{
let state=function* (){
while(1){
yield 'A';
yield 'B';
yield 'C';
}
}
let status=state();
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
/*
{ value: 'A', done: false }
{ value: 'B', done: false }
{ value: 'C', done: false }
{ value: 'A', done: false }
{ value: 'B', done: false }
*/
}
修饰器(descriptor)
decorator是ES7引入的功能,它是一个函数,用来修改类甚至于是方法的行为。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34{
let readonly=function(target,name,descriptor){
descriptor.writable=false;
return descriptor
};
class Test{
@readonly
time(){
return '2017-03-11'
}
}
let test=new Test();
// test.time=function(){
// console.log('reset time');
// };
console.log(test.time());
}
{
let typename=function(target,name,descriptor){
target.myname='hello';
}
@typename
class Test{
}
console.log('类修饰符',Test.myname);//'hello'
// 第三方库修饰器的js库:core-decorators; npm install core-decorators
}
import
import语句用于导入由另一个模块导出的绑定。
创建a.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15let A=123;
let test=function(){
console.log('test');
}
class Hello{
test(){
console.log('class');
}
}
export default {
A,
test,
Hello
}
b.js1
2
3
4import a from "./a.ja"
console.log(a.A) // 123
a.test()// 'test'
new a.Hello().test() // 'class'
ES6的迁移之路---[4]
前言
对ES6中的 Symbol
、Reflect
、set
、WeakSet
、map
、WeakMap
Symbol
Symbol是ES6新定义的一直数据类型.他表示独一无二。
声明1
2
3
4{
let a = Symbol(),b = Symbol()
console.log(a === b) //false
}
Symbol.for机制有点类似于单例模式,首先在全局中搜索有没有以该参数作为名称的Symbol值,如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。和直接的Symbol就点不同了1
2
3
4
5{
let a=Symbol.for('a');
let b=Symbol.for('a');
console.log(a===b); //true
}
symbol类型的数据使用一般的方法是查不出其属性的。需要使用Object.getOwnPropertySymbols()或者Reflect.ownKeys()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24{
let a1=Symbol.for('abc');
let obj={
[a1]:'123',
'abc':345,
'c':456
};
console.log('obj',obj);} //obj { abc: 345, c: 456, [Symbol(abc)]: '123' }
for(let [key,value] of Object.entries(obj)){
console.log('let of',key,value);//let of abc 345
// let of c 456
}
Object.getOwnPropertySymbols(obj).forEach(function(item){
console.log(obj[item]); // 123
})
Reflect.ownKeys(obj).forEach(function(item){
console.log('ownkeys',item,obj[item]);
})
/*
ownkeys abc 345
ownkeys c 456
ownkeys Symbol(abc) 123
*/
Reflect对象
在上面提到的一个Reflect这个对象,多数Reflect方法原生的Object已经重新实现了.
为什么要使用Reflect
1 | 复制代码 |
在ES5中这样判断:name in obj ; 或者删除一个属性 :delete obj[name], 虽然这些很好用, 很简短, 很明确, 但是要使用的时候也要封装成一个类;
有了Reflect, 它帮你封装好了, Reflect.has(obj, name), Reflect.deleteProperty(obj, name);
Reflect.ownKeys()方法的使用:
Reflect.ownKeys, Object可没有ownKeys方法, Reflect.ownKeysz他的作用是返回对象的keys;1
2
3
4
5
6
7
8复制代码
console.log(Reflect.ownKeys({"a":0,"b":1,"c":2,"d":3})); //输出 :["a", "b", "c", "d"]
console.log(Reflect.ownKeys([])); // ["length"]
var sym = Symbol.for("comet");
var sym2 = Symbol.for("meteor");
var obj = {[sym]: 0, "str": 0, "773": 0, "0": 0,
[sym2]: 0, "-1": 0, "8": 0, "second str": 0};
Reflect.ownKeys(obj); //输出:/ [ "0", "8", "773", "str", "-1", "second str", Symbol(comet), Symbol(meteor) ]
Set
ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74{
let list = new Set();
list.add(5);
list.add(7);
console.log('size',list.size);//2
}
{
let arr = [1,2,3,4,5];
let list = new Set(arr);
console.log('size',list.size);//5
}
{
let list = new Set();
list.add(1);
list.add(2);
list.add(1);
console.log('list',list);//list Set { 1, 2 }
let arr=[1,2,3,1,'2'];//unique Set { 1, 2, 3, '2' }
let list2=new Set(arr);
console.log('unique',list2);
}
{
let arr=['add','delete','clear','has'];
let list=new Set(arr);
console.log('has',list.has('add'));//has true
console.log('delete',list.delete('add'),list);//delete true Set { 'delete', 'clear', 'has' }
list.clear();
console.log('list',list);//list Set {}
}
{
let arr=['add','delete','clear','has'];
let list=new Set(arr);
for(let key of list.keys()){
console.log('keys',key);
}
/*
keys add
keys delete
keys clear
keys has
*/
for(let value of list.values()){
console.log('value',value);
}
/*
value add
value delete
value clear
value has
*/
for(let [key,value] of list.entries()){
console.log('entries',key,value);
}
/*
entries add add
entries delete delete
entries clear clear
entries has has
*/
list.forEach(function(item){console.log(item);})
/*
add
delete
clear
has
*/
}
Set实现并集,交集,差集
1 | let set1 = new Set([1,2,3,4,5,6]); |
Map
1 | { |
WeakMap、WeakSet
他们对值的引用都是不计入垃圾回收机制的,所以名字里才会有一个weak,表示这个弱引用
下面以 WeakMap 为例,看看它是怎么解决内存泄漏的1
2
3
4const wm = new WeakMap();
const element = document.getElementById('example');
vm.set(element,'something');
vm.get(element);
先新建一个 Weakmap 实例。然后,将一个 DOM 节点作为键名存入该实例,并将一些附加信息作为键值,一起存放在 WeakMap 里面。这时,WeakMap 里面对element的引用就是弱引用,不会被计入垃圾回收机制。
也就是说,DOM 节点对象的引用计数是1,而不是2。这时,一旦消除对该节点的引用,它占用的内存就会被垃圾回收机制释放。Weakmap 保存的这个键值对,也会自动消失。
基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap
ES6的迁移之路---[3]
前言
接着数据类型这块Array
、Object
、function
Array
Array.of()方法基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。1
2
3
4
5
6
7{
let arr = Array.of(3,4,7,9,11);
console.log('arr=',arr);
let empty=Array.of();
console.log('empty',empty);
}
Array.from()方法就是将一个类数组对象或者可遍历对象转换成一个真正的数组。1
2
3
4
5
6
7
8
9
10
11
12
13{
let arrayLike = {
0: 'tom',
1: '65',
2: '男',
3: ['jane','john','Mary'],
'length': 4
}
let arr = Array.from(arrayLike)
console.log(arr) // ['tom','65','男',['jane','john','Mary']]
console.log(Array.from([1,3,5],function(item){return item*2}));//[ 2, 6, 10 ]
}
Array.fill()有三个参数分别是覆盖的值,以及覆盖范围1
2
3
4{
console.log('fill-7',[1,'a',undefined].fill(7));//fill-7 [ 7, 7, 7 ]
console.log('fill,pos',['a','b','c'].fill(7,1,3));//fill,pos [ 'a', 7, 7 ]
}
获取Array的相关数据1
2
3
4
5
6
7
8
9
10
11{
for(let index of ['1','c','ks'].keys()){
console.log(index);
}//0;1;2
for(let value of ['1','c','ks'].values()){
console.log(value);//1;c;ks
}
for(let [index,value] of ['1','c','ks'].entries()){
console.log(index,value);0,1;1,c;2,ks
}
}
查找Array数据的api1
2
3
4
5
6
7
8
9
10
11
12
13
14//Array.prototype.copyWithin(target, start = 0, end = this.length)
{
console.log([1,2,3,4,5].copyWithin(0,3,4));//[4,2,3,4,5]
}
{
console.log([1,2,3,4,5,6].find(function(item){return item>3}));//4
console.log([1,2,3,4,5,6].findIndex(function(item){return item>3}));//3
}
{
console.log('number',[1,2,NaN].includes(1));//true
console.log('number',[1,2,NaN].includes(NaN));//true
}
Object
在说Object新增了哪些api之前我先说说,es6的一些简易写法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26{
// 简洁表示法
let o=1;
let k=2;
let es5={
o:o,
k:k
};
let es6={
o,
k
};
console.log(es5,es6);
let es5_method={
hello:function(){
console.log('hello');
}
};
let es6_method={
hello(){
console.log('hello');
}
};
console.log(es5_method.hello(),es6_method.hello());
}
接下来是属性表达式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15{
// 属性表达式
let a='b';
let es5_obj={
a:'c',
b:'c'
};
let es6_obj={
[a]:'c'
}
console.log(es5_obj,es6_obj);//{ a: 'c', b: 'c' } { b: 'c' }
}
新增的api1
2
3
4
5
6
7
8
9
10
11
12{
// 新增API
console.log('字符串',Object.is('abc','abc'),'abc'==='abc');//字符串 true true
console.log('数组',Object.is([],[]),[]===[]);//数组 false false
console.log('拷贝',Object.assign({a:'a'},{b:'b'}));//拷贝 { a: 'a', b: 'b' }
let test={k:123,o:456};
for(let [key,value] of Object.entries(test)){
console.log([key,value]);//[ 'k', 123 ][ 'o', 456 ]
}
}
扩展运算符reset写法1
2
3
4{
let {a,b,...c}={a:'test',b:'kill',c:'ddd',d:'ccc'};
console.log(a,b,c);//test ,kill, { c: 'ddd', d: 'ccc' }
}
function
函数默认值1
2
3
4
5
6
7
8
9
10
11
12
13
14{
function test(x, y = 'world'){
console.log('默认值',x,y);//默认值 hello world
}
test('hello');
test('hello','kill');//默认值 hello kill
}
{
let x='test';
function test2(x,y=x){
console.log('作用域',x,y);
}
test2('kill');//作用域 kill kill
}
函数reset参数1
2
3
4
5
6
7
8
9
10
11
12
13{
function test3(...arg){
for(let v of arg){
console.log('rest',v);
}
}
test3(1,2,3,4,'a');//rest 1 rest 2 rest 3 rest 4 rest a
}
{
console.log(...[1,2,4]);//124
console.log('a',...[1,2,4]);//a123
}
箭头函数1
2
3
4
5
6{
let arrow = v => v*2;
let arrow2 = () => 5;
console.log('arrow',arrow(3));//arrow 6
console.log(arrow2());// 5
}
ES6的迁移之路---[2]
前言
接着写没写完的!今天主要针对的是数据类型的迁移、这一篇主要针对Number
、String
Number
二进制和八进制的新的表示方法
二进制以’0b’或者’0B’开头
八进制以’0o’或者’0O’开头
1 | 0b111110111 === 503 // true |
Number对象新增的方法
Number.isFinite()检测一个数值是不是有限的,只有数字且是有限的才会返回true,否则一律是false
Number.isNaN(),检测一个数值是不是NaN,只有数字且是NaN的时候才会返回true,否则一律false
1 | { |
Number.parseInt(),Number.parseFloat()将全局的parseInt和parseFloat移植到Number对象上,行为不变
Number.isInteger(),判断数值是不是整数1
2
3
4
5
6// 3.0000000000000002达到了小数点后16个10进制位数,转换为2进制就超出了2^53,所以最后一个2被忽略了,仍然被认为是整数
Number.isInteger(3.0000000000000002) // true
console.log('25',Number.isInteger(25));// true
console.log('25.0',Number.isInteger(25.0));//true
console.log('25.1',Number.isInteger(25.1));//false
console.log('25.1',Number.isInteger('25'));//false
Number.EPSILON,是一个常量,表示1和大于1的最小浮点数的差
1 | Number.EPSILON === Math.pow(2,-52); // true |
Number.isSafeInteger(),判断一个整数是不是在常量Number.MIN_SAFE_INTEGER和Number.MAX_SAFE_INTEGER之间,包括2端1
2
3
4
5{
console.log(Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER);
console.log('10',Number.isSafeInteger(10));//true
console.log('a',Number.isSafeInteger('a'));//false
}
Math对象的拓展
Math.trunc()方法用于去除一个数的小数部分,返回整数部分。1
2
3
4{
console.log(4.1,Math.trunc(4.1));//4
console.log(4.9,Math.trunc(4.9));//4
}
注意1
console.log(Math.trunc(NaN),Math.trunc('foo'),Math.trunc())//NaN,NaN,NaN
Math.sign()方法用来判断一个数到底是正数、负数还是0
(1)参数为正数返回+1
(2)参数为负数返回-1
(3)参数为0返回0
(4)参数为-0返回-0
(5)其他值返回NaN
1 | console.log( |
Math.cbrt()方法用于计算一个数的立方根
1 | { |
Math.hypot()方法返回所有参数的平方和的平方根。1
2
3
4
5
6
7Math.hypot(3,4)//5
Math.hypot(3,4,5)//7.07106
Math.hypot()//0
Math.hypot(NaN)//NaN
Math.hypot(3,4,'foo')//NaN
Math.hypot(3,4,'5')//7.07106
Math.hypot(-3)//3
注意
ES6新增了6个三角函数方法.
(1)Math.sinh(x) 返回x的双曲正弦
(2)Math.cosh(x) 返回x的双曲余弦
(3)Math.tanh(x) 返回x的双曲正切
(4)Math.asinh(x) 返回x的反双曲正弦
(5)Math.acosh(x) 返回x的反双曲余切
(6)Math.atanh(x) 返回x的反双曲正切ES7增加了一个指数运算符**,可以与等号结合
2**3//8
let a=2;
a**=2;//等价于a=a**a;
String类型
includes(), startsWith(), endsWith()
从字面就可以明白,一个是看是否包含子串,一个是是否以子串开头,一个是是否以子串结尾。1
2
3
4
5let s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
includes()比indexOf()的优势在于,indexOf()的返回结果不是布尔值,须与-1对比,不够直观。
这三个方法都支持第二个参数,表示从哪里开始检索。
其中,includes()和startsWith()的默认值是0,表示检索范围是参数值检索到末尾,很简单不多说。
endsWith()很特殊,它第二个参数的默认值是字符串总长度值,表示检索范围是从开头检索到这个值。1
2
3
4
5let s = 'Hello world!';
s.includes('Hello', 6) // false 从左起第6位到末尾的范围内检索,结果是没找到
s.startsWith('world', 6) // true 从左起第6位到末尾,看看是不是以world开头,结果是
s.endsWith('Hello', 8) // false 从开头找到第8位,看看是不是以Hello结束,结果不是
repeat()
表示重复多少次。
如果参数是正小数,会取整。
如果参数是负数,会报错。
如果参数是0,会得到空串。
如果参数是字符串、布尔型或其他类型,会先换算成整数。1
2
3
4{
let str="abc";
console.log(str.repeat(2));//abcabc
}
padStart(),padEnd()
这两个是ES8(也就是2017)新增方法
用于在开头补全字符和在末尾补全字符,padStart和padEnd一共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串。1
2
3
4{
console.log('1'.padStart(2,'0'));//01
console.log('1'.padEnd(2,'0'));//10
}
1 | { |
我们可以看出,现在这种写法更加的友好了。
比如有一段HTML是:1
2
3
4
5
6
7<div class="page">
<div class="box">
<a target="_blank" class="news-link" href="/news/92199/nodemon-1-14-8">Nodemon v1.14.8 发布</a>
<span class="box-fr news-date">01/07</span>
</div>
... 重复若干个div.box
</div>
我就可以设一个返回最终HTML的函数,这个函数使用了ES6的简写方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17const createTmpl = boxes => `
<div class="page">
${boxes.map(box => `
<div class="box">
<a target="_blank" class="news-link" href="${box.href}">${box.title}</a>
<span class="box-fr news-date">${box.date}</span>
</div>
`).join('')}
</div>
`;
let boxdata = [
{href: "http://...1", title: "biaoti1", date: "01/08"},
{href: "http://...2", title: "biaoti2", date: "02/08"},
];
createTmpl(boxdata);
然后传入数据,调用函数,得到:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15"
<div class="page">
<div class="box">
<a target="_blank" class="news-link" href="http://...1">biaoti1</a>
<span class="box-fr news-date">01/08</span>
</div>
<div class="box">
<a target="_blank" class="news-link" href="http://...2">biaoti2</a>
<span class="box-fr news-date">02/08</span>
</div>
</div>
"
今天就写到这里吧,下次给大家带来Array、Object、Function的变化
JavaScript学习之浅谈函数柯里化(Currying)
前言
最近是想着坚实一下自己JS的基础,所以想着看看原生的知识。
准备
在这里先说说什么是函数柯里化。
- 什么是函数柯里化
在一个函数中首先填充几个参数(然后再返回一个新函数)的技术称为柯里化(Currying)。(来自《JavaScript忍者秘籍》一书中)
开始
这里有两个例子可以帮助理解!
最简单的加法函数1
2
3
4
5
6//函数定义
function add(x,y){
return x + y;
}
//函数调用
add(3,4);//5
如果采用柯里化是怎样将接受两个参数的函数变成接受单一参数的函数呢,其实很简单如下:1
2
3
4
5
6
7
8//函数表达式定义
var add = function(x){
return function(y){
return x + y;
}
};
//函数调用
add(3)(4);
其实实质利用的就是闭包的概念。
作用
本质上讲柯里化(Currying)只是一个理论模型,柯里化所要表达是:如果你固定某些参数,你将得到接受余下参数的一个函数,所以对于有两个变量的函数y^x
,如果固定了y=2
,则得到有一个变量的函数2^x
。这就是求值策略中的部分求值策略。
柯里化(Currying)具有:延迟计算、参数复用、动态生成函数的作用。
JS中bind()与函数柯里化
其实在实际使用中使用最多的一个柯里化的例子就是Function.prototype.bind()
函数,我们也一并给出一个较为简单的Function.prototype.bind()
函数的实现方式。
1 | //《JS权威指南》原文的例子 |
实战演练一道关于闭包和函数的柯里化方面的编程题目
编程题目的要求如下,完成plus函数,通过全部的测试用例。
1
2
3
4
5'use strict';
function plus(n){
}
module.exports = plus
测试用例如下
1 | 'use strict'; |
根据测试用例我们可以发现,plus函数的要求就是接受单一函数,例如:
1
plus(1)(4)(2)(3).toString()
但是与柯里化不同之处在于,柯里化返回的一个新函数。我们观察其实最后的求值是通过toString函数得到的,那么我们就很容易想到,我们可以给返回的函数增加一个toString属性就可以了。答案如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22'use strict';
function plus(num) {
var adder = function () {
var _args = [];
var _adder = function _adder() {
[].push.apply(_args, [].slice.call(arguments));
return _adder;
};
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
}
return _adder;
}
return adder()(num);
}
module.exports = plus;
运行一下,通过全部的测试用例,需要注意的是由于题目的要求运行在严格模式下,所以我们在_adder函数内部是不能引用arguments.callee,这时我们采用的方法是给函数表达式中函数本身起名_adder,这样就解决的这个问题。
ES6的迁移之路---[1]
前言
现在前端这个大环境,如果不会ES6还真算是落伍了。今天我就将一些常用的一些有关ES6的一些常用的语法糖,和新添加的一些特性一一列出来。
准备
往往使用ES6开发的前端应用都是要依赖babel这个工具包的(为什么使用babel呢!当然因为为了我们的项目更好的兼容)。至于bable是干什么的!大家可以点击这个!话不多说,接下来为大家es6为我们提供的一系列语法糖!
块级作用域、let和const的用法
什么是块级作用域!
{}中就是一个块级作用域!
我们可以用它替代我们的(fun)()
es6以前我们什么一个变量,往往都是使用var。但是ES6中官方为我们推荐了let,const,两者到底有什么区别呢?
1
2
3
4
5
6
7
8
9
10function test(){
for(let i=1;i<3;i++){
console.log(i);
}
console.log(i);
let a = 2 ;
a = 3;
console.log(a)
}
test() //1,2 3
从这个函数输出的情况可以看出,我们只输出了for循环中的变量。这里就可以看出let只能在同一个块级作用域才能有效声明。
同时我们的变量a也随这我们的改变改变了!
1 | function first(){ |
前者可以看出const声明出来的变量不能进行修改,否则就会报错。
后者就是正常的输出了我们想要的一个object。
使用 let 声明一个可以改变的变量,用 const 声明一个不能被重新赋值的变量。
结构赋值
es6特有的语法糖,很好用。
1 | { |
这个三个块级作用域中是不同类型的结构赋值的声明方法,是不是特别神奇!大家直接用吧!
最好我模拟了从后台拿接口的场景。各位可以跟着套用!
RegExp的变化
我从三个方面来剖析ES5-ES6的变化。
构造函数
1 | { |
在ES5的时候regex3这种声明方法是会报错的因为,RegExp构造函数的参数是一个正则表达式的时候,ES5 不允许此时使用第二个参数添加修饰符!
ES6 改变了这种行为。
字符串的正则方法
字符串对象共有 4 个方法,可以使用正则表达式:match()、replace()、search()和split()。
ES6 将这 4 个方法,在语言内部全部调用RegExp的实例方法,从而做到所有与正则相关的方法,全都定义在RegExp对象上。
- String.prototype.match 调用 RegExp.prototype[Symbol.match]
- String.prototype.replace 调用 RegExp.prototype[Symbol.replace]
- String.prototype.search 调用 RegExp.prototype[Symbol.search]
- String.prototype.split 调用 RegExp.prototype[Symbol.split]
1 | { |
修饰符
1 | { |