2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > JavaScript高级语法-面向对象编程模式的特性及实现

JavaScript高级语法-面向对象编程模式的特性及实现

时间:2019-09-28 12:05:40

相关推荐

JavaScript高级语法-面向对象编程模式的特性及实现

@写在前面:对于初学者友好,变量声明没有采取ES6+标准,且学且珍惜🌹

来着是客,客随主便,如果帮到你麻烦动动您的金手指点个赞支持本蜗牛,我将倍儿有面子

以下知识如果帮助到喜欢学习的你我将倍感荣幸,如果有哪里缺漏或错误的地方望指正

本文将介绍有关面向对象的特性及实现,想要快速上手,这篇文章很适合你,但是对于对象,我只是没有大哥而已。🤣除此以外的知识这里一笔带过,我将会通过学习不断补全一笔带过的知识点,希望和你一起共同成长共同进步😘Mu~a~**

关于面向对象编程

面向对象编程是大家耳熟能详的编程模式,对于初学者而言,对象容易理解,girlFriends嘛,简简单单。但是对于一个项目的开发模式,除了历史课出现过的面向过程编程,随着技术的不断进步,这种模式显然已经不能满足那些秃头大佬的潮流进步,所以面向对象编程就大踏步的走来了。

将现实抽象,把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统更贴近事物的自然运行模式。 当然,这些除了我的理解也只有度娘更详细了

面向对象的特性(优势)

抽象性:将现实业务抽象为对象,以对象之间的关系分析业务封装性:将属性方法封装到对象里,方便维护、统一进行管理,便于二次开发,安全性也有保障继承性:将对象的属性方法进行传递,继承公用的方法属性可以大大提高开发效率,减少不必要的内存消耗多态性:一个类可以产生多种类型的对象。JavaScript原生实现的多态就只有函数重写,JavaScript无法直接实现,其他的形式可以通过某些技巧来模拟,但不推荐。

抽象性是一个概念,理解就行,实际操作就是将现实抽象出他的某些属性和行为

封装性

就是将对象的手用麻袋套起来🙅

属性分为两大类

公有属性:任何人在任意位置都可以访问并修改的属性私有属性:有一定的限制条件,达到条件才可以访问或更改,这样的属性安全性较高

下面将通过案例代码实现共有属性与私有属性

//创建一个构造函数及其实例function User(name,phone){this.name=name;this.phone=phone;}//实例化一个对象var u1=new User("张三","13284956871");//张三的phone可以直接访问console.log(u1.phone);//13284956871//而且可以直接修改u1.phone="12347594740";console.log(u1);//User {name: '张三', phone: 12347594740}

显然,这样的公有属性是不安全的,所以我们就可以利用面向对象的封装性,进行属性的私有封装,这就类似于闭包的方式了,在构造函数内部创建两个方法(getset),我们可以通过get方法获取对应数据,利用set方法修改对应数据,数据无法直接在外部进行访问,这样就提高了数据的安全性

//在User构造函数内部封装一个局部变量,只能通过其方法获得function User(name,phone){this.name=name;var phone=phone;//创建方法时,我们可以判断其传入的用户(模拟当前用户)//get方法this.getPhone=function(user){//判断user.name是不是当前对象的this.nameif(user.name==this.name){//如果用户是当前对象,那么就返回他的phonereturn {uname:this.name,uphone:phone};}else{//如果不是,那就说明权限不够return "这是"+this.name+"的数据"+user.name+"权限不够无法查看";}}//set方法this.setPhone=function(user,uphone){//当然还是要判断传入的user,然后传入修改的phoneif(user.name==this.name){//如果是当前用户,修改phone时还需要验证其输入的修改格式是否正确var reg=/\d{11}/;if(reg.test(uphone)){//如果符合规范,才可以修改phone=uphone;return "修改成功";}else{//如果不符合return "格式错误";}}else{return "这是"+this.name+"的数据"+user.name+"权限不够无法修改";}}}//实例化一个对象var u1=new User("张三","17480285760");//先输出看一下console.log(u1);//User {name: '张三', getPhone: ƒ, setPhone: ƒ}这里只能拿到它的共有属性和方法,私有属性只能通过其内部方法获得//实例一个李四var u2=new User("李四","17482047563");//接着让李四查看或修改张三的手机号console.log(u1.getPhone(u2));//权限不够console.log(u1.setPhone(u2));//权限不够//接着让张三自己获取修改自己的电话console.log(u1.getPhone(u1));//{uname: '张三', uphone: '17480285760'}//接着,张三随便输入修改console.log(u1.setPhone(u1,"12345678"));//格式错误//再次查看console.log(u1.getPhone(u1));//没有被修改//格式正确后console.log(u1.setPhone(u1,"16352786789"));////再次查看console.log(u1.getPhone(u1));//修改成功--{uname: '张三', uphone: '16352786789'}

以上代码只是实现了一个简单的逻辑,说明私有属性安全性较高,这只是一个思想,具体逻辑还需要看实际业务需求

继承性

就是JavaScript中的基因遗传,儿子除了有自己的属性还继承了父亲的属性,接着儿子的儿子也继承了父亲的属性。但在JavaScript中,继承可比遗传学简单多了。

这里使用原生JavaScript来实现继承,这其实也可以算一道面试题了,为了理解原理,我们将层层递进,而不是一次就学会最终效果的完美继承

例:

//实现原生继承//举例:动物园管理系统-有多个类型的动物(2只老虎,3只狮子,3只熊猫)//不利用js的继承性思想,实现该需求/*创建老虎的构造函数以及2只老虎实例对象*/function Tiger(name,age){this.name=name;this.age=age;}//老虎的行为都是共有的,所以可以放到原型中以节省内存消耗Tiger.prototype={move:function(){console.log(this.name+"正在走");},eat:function(){console.log(this.name+"正在吃饭");},play:function(){console.log(this.name+"正在打滚");}};//创建两只老虎var t1=new Tiger("大皮",3);var t2=new Tiger("小皮",4);/*创建狮子的构造函数以及3只狮子实例对象*/function Lion(name,age){this.name=name;this.age=age;}//共有行为Lion.prototype={move:function(){console.log(this.name+"正在走");},eat:function(){console.log(this.name+"正在吃饭");},mating:function(){console.log(this.name+"正在造小狮子");}};//3只狮子var l1=new Lion("阿俊",3);var l2=new Lion("大头",2);var l3=new Lion("采花",2);/*创建熊猫的构造函数以及3只熊猫*/function Panda(name,age){this.name=name;this.age=age;}//共有行为Lion.prototype={move:function(){console.log(this.name+"正在走");},eat:function(){console.log(this.name+"正在吃饭");},climbing:function(){console.log(this.name+"正在爬树");}};//3只熊猫var p1=new Panda("大花",3);var p2=new Panda("小花",2);var p3=new Panda("翠花",2);~这一顿操作下来,费时又费力,艾玛累坏我了~

通过以上案例我们可以看出,不同的构造函数具有相同的属性和行为(方法),而且这样创建对象效率极低,代码量多,内存消耗也大

所以,我们是否可以将重复的属性及方法单独放到一块,放到一个构造函数中,让其他构造函数可以复用,这也就是继承的思想和概念

//我们可以创建一个构造函数,把共有的属性和方法都给该构造函数function Animal(name,age){this.name=name;this.age=age;}Animal.prototype={move:function(){console.log(this.name+"正在走");},eat:function(){console.log(this.name+"正在吃饭");}};

那么如何实现其他构造函数继承Animal中的属性和方法,这里就需要简单解释一下原型的一些概念

一个对象操作时,属性或方法的默认查找方式为:先从该对象的构造函数内部查找有没有某个属性,如果有直接获取使用,如果没有再从其构造函数的原型[[prototype]](也就是该对象原型__proto__指向的prototype)中查找,如果还没有找到,那就去构造函数原型对象的构造函数中去查找,直到找到返回或未找到提示。

依据上面的思想,我们也就可以实现一种继承方式——原型继承

原型继承

//创建一个猫的构造函数function Cat(name,age){//这里可以不必写,因为是继承过来的,但name,age参数还是要有,需要进行传值}//属性或方法可以找原型要,那么我们就可以将Animal构造函数属性都挂载到Cat原型上//也就是说,可以让Cat.prototype(原型对象) 等于Animal对象Cat.prototype=new Animal("继承",3);//将动物对象赋值给Cat原型对象,所以Animal属性也就一起给了Cat.prototype//创建一个对象,测试一下var c1=new Cat("加菲",4);console.log(c1);

可以看到,Cat对象中没有属性,但在他的原型上继承了Animal的属性

通过这种方式继承的属性被原型定义好的属性取代,无法直接使用,优点是实现了原型方法的继承,eatmove都被Cat继承了过来,但是缺点就是所有的属性值,只和创建animal对象时保持一致

虽然以上的原型继承方式不能直接使用,但是依据对象中属性或方法的查找方式,我们依旧可以利用这样的特性另辟蹊径

依据以上思想, 我们就能知道,可以将附带共同属性的构造函数挂载到继承它的构造函数的原型上,但是继承之后属性值无法通过对象实例改变,所以能否将这些属性进行重置,这样就可以在对象的新实例中修改属性的值了

我们知道,构造函数中是可以改变this的指向,也就是说创建对象实例时,构造函数的属性值就是这个对象实例的属性值,所以想到,在构造函数中,将Animal的属性都挂载过来

依据如上想法,我们也就有了第二种继承方式——冒充继承

冒充继承

/*在使用new创建对象实例的过程中,会在其构造函数内部创建一个空对象,然后让this指向该空对象,接着把所有的属性都挂载给空对象并赋值,最后返回出来赋值到创建的实例上*///所以我们就可以想到,在构造函数中,将Animal的属性都挂载过来function Dog(name,age){//根据new的原理,可以在Dog函数内部调用Animal(arg1,arg2...),这样从Dog传进来的值会传入Animal,最后再Animal中,this就指向了这些属性Animal(name,age);}var dog=new Dog("小黑",2);console.log(dog);//打印出来后发现dog并没有属性值,说明Animal中的this并没有指向new创建的对象//如果Animal中的this等同于Dog中的this,那么就意味着Animal中的this指向new创建的对象//这里我们可以使用call方法强行改变this指向function Dog2(name,age){Animal.call(this,name,age);//意思是将Dog中的this与参数对应的传给Animal}var dog2=new Dog2("小黄",3);console.log(dog2);

改变了this指向后,让Dog2中的this等于了Animal中的this,实例对象的this也就等于了Animal的this,换句话说,Animal中的this指向new创建的对象

这样的继承方式叫冒充继承:通过改变this的指向性实现的继承方法。缺点:这样的方式只继承了属性,没有继承原型,也只是调用了一次属性而已

通过观察以上两种继承方式可以发现,原型继承不能重置属性但可以继承原型,冒充继承不能继承原型但可以重置属性,所以我们可以结合这两种方法和思路,实现一个完美的继承方式 ——组合继承

组合继承

//创建一个猴子构造函数,实现继承于Animalfunction Monkey(name,age){//第二步,利用冒充继承改变Monkey内部的this指向,实现属性继承Animal.call(this,name,age);}//第一步:利用原型继承继承Animal的原型中的内容Monkey.prototype=new Animal()//不传入参数//除了继承过来的方法,我们也可以有自己独特的行为Monkey.prototype.laugh=function(){console.log(this.name+"正在嘲笑");}//实例化一个猴子var m1=new Monkey("屁孩",3);//当然,m1也可以调用继承过来的movem1.move();//屁孩正在走m1.laugh()//屁孩正在嘲笑

这样的组合继承法,既继承了属性还继承了原型,这样的最终继承方案算是比较完美了。

注意: 如果要新加入方法或属性,一定要加在继承以后,以防加完之后再继承,会被继承覆盖。

@写在最后:ES6还没有出来之前,以上js原生组合继承的方式算是非常ok的,如果对你有任何启发,那就给本蜗牛来个点赞+关注+收藏,以后的日子一起成长🌹

还有一件事:对于上述this指向问题、原型原型链,还有ES6+的优化,以后的文章中会一一出现,敬请期待

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。