很多开发对JavaScript实现面向对象编程存在只知其一;不知其二,而且很多的在项目实践中写的都是面向过程编程的代码,所以,但愿能从零入手介绍面向对象的一些概念到实现简单的面向对象的例子让你们包括我本身加深对面向对象的认知。硬文一篇,但愿能对你们有帮助 ^v^css
对象是一个包含相关数据和方法的集合,是经过变量和函数组成,一般被咱们说成属性和方法,经常使用对象字面量的形式表示。html
1.初始化对象编程
var person={}
2.添加属性(变量)和方法(函数)canvas
var person={ name:'aaron', say:function(){ alert('hello') } }
3.获取属性和执行方法后端
person.name
person.say()
备注:获取属性和执行方法有两种方法,就是说我上面列举的其一:点表示法,还有一种就是括号表示法。以下:设计模式
person['name']
person['say']()
所以,有时对象也被叫作关联数组,即对象作了字符串到值的映射,而数组作的是数字到值的映射。数组
4.运行截图浏览器
5.设置对象成员
备注:有一点须要了解到的是,括号表示法能作到经过定义变量名的方式去设置对象成员,而这一点是点表示法无法实现的。安全
6.“this”的含义框架
this的指向实际上是一直都让开发者头大的问题了,尤为是后端写JS时。其实说白了this就是指向了当前代码运行时的对象。
例如:
因为对象字面量执行的是当前对象,因此this指向了person。而像建立的构造函数等this的指向就是构造函数实例对象了
一目了然,对象字面量建立的对象的好处能够有效的把对象关联的属性和方法统一了起来,也减小了全局变量的污染,获得必定程度的安全(减小了定义全变量覆盖对象属性的危险)。
例如从现实世界的某个实例出发,对于一我的(person)来讲,咱们能在他们身上获取到不少信息(他们的住址,身高,鞋码等等),而后咱们会基于这些信息介绍关于他们,并须要他们作出回应。而在面向对象的语言中,咱们就能够经过类(class)的概念去描述一个对象,而这个类就是定义对象特质的模板。经过建立的class,咱们就能够基于它来建立一些拥有class中属性和方法的对象,即实例对象。而这些实例对象通常是具体的人,例如老师,学生。在OOP中,咱们也能够基于这个class,建立其余的新类,而这些新的子类(例如家长)能够继承它们父类的属性(数据)和方法(功能),来使用父对象共有的功能。
所以,经过对泛指人到具体的某个学生/老师的关系,咱们就能够总结到面向对象的三个基本特性:封装,继承,多态。
经过了解面向对象编程(OOP)的基本概念,什么是对象和对象的属性,方法,并了解实现面向对象编程的基本特性。也了解经常使用的建立对象方法--对象字面量,咱们已经对对象的基本概念有了了解。可是,经过对象字面量来建立的只是单一的实体类,并不能实现通用对象(现实模型)的封装,即真正的实现面向对象。
JavaScript是经过构建函数的方式来定义对象和特征的,而构建的实例对象也有经过原型链的方式来继承某些特性。
1.Person()构造函数,建立实例对象并访问属性和方法:
2.其余建立对象实例的姿式
1.Object()构造函数
var person1=new Object(); person1.name='ace'; person1.age=30; person1.greeting=function(){ alert('Hi! I\'m ' + this.name + '.'') }
2.使用create():这样就能够基于person1建立与person1具备相同属性和方法的对象。
var person2=Object.create(person1);
JavaScript的继承机制是有别于其余经典的面向对象编程语言的,是经过原型来实现从其余对象继承功能特性的。
所以,JavaScript常被描述为基于原型的语言--每一个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性,原型对象也有可能拥有原型,从中继承方法和属性,以此类推。而这种关系被称为原型链。
咱们须要知道的是,这些属性和方法是定义在实例的构造函数上的prototype属性,固然,实例对象也__proto__属性,是从构造函数的prototype属性派生的,即实例对象.__proto===构造函数.prototype。
从截图咱们看到,person1实例除了具备Person()构造器中的属性方法外,还具备其余属性和方法,而这些则是Person()构造器原型对象Object上的成员。
经过调用valueOf,所以,咱们也了解到了调用方法的过程:
1.浏览器首先检查,person1 对象是否具备可用的 valueOf() 方法。
2.若是没有,则浏览器检查 person1 对象的原型对象(即 Person)是否具备可用的 valueof() 方法。
3.若是也没有,则浏览器检查 Person() 构造器的原型对象(即 Object)是否具备可用的 valueOf() 方法。Object 具备这个方法,因而该方法被调用。
经过对valueOf方法的调用过程,咱们也就了解到了那些能被继承的属性和方法(对象中存在不能被继承的属性方法,例is()/keys())是定义在prototype属性上的。所以,在构造函数是须要被子类继承的属性方法须要定义在prototype上。
每一个实例对象都有constructor属性,它是指向建立该实例的构造函数。
而咱们也能够经过在constructor后添加()形式实现建立新实例。
经过截图咱们能够了解到了,虽然已经建立了实例对象person1,当时以后再像构造器Person()prototype中添加方法,person1仍是能调用,这就说明了函数调用会经过上溯原型链,从上游对象中调用方法。
如图,若在prototype上定义属性的话,则this的当前执行环境为全局,返回的为undefined。而且,在对象继承上看,通常的作法是在构造器中定义属性,在prototype属性中定义方法。
function Person(first, last, age, gender, interests) { this.name = { first, last }; this.age = age; this.gender = gender; this.interests = interests; }; Person.prototype.bio = function() { alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.'); }; Person.prototype.greeting = function() { alert('Hi! I\'m ' + this.name.first + '.'); };
function Teacher(first, last, age, gender, interests, subject) { Person.call(this, first, last, age, gender, interests); this.subject = subject; }
如图,建立的新的Teacher()构造器只有一个空的原型属性,则需从Person()的原型prototype中继承方法:
Teacher.prototype = Object.create(Person.prototype);
如图,咱们又遇到了个问题,因为咱们建立Teacher的prototype的方式,Teacher()构造器的prototype属性执行了Person(),所以,咱们须要设置指向Teacher:
Teacher.prototype.constructor = Teacher;
经过这样,咱们就能实现了须要继承的方法都定义在了构造器的prototype属性内,这样才不会打乱了类继承结构。
Teacher.prototype.greeting = function() { var prefix; if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') { prefix = 'Mr.'; } else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') { prefix = 'Mrs.'; } else { prefix = 'Mx.'; } alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.'); };
经过学习,咱们能够知道通常对象所具备的对象成员包括三类:
1.那些定义在构造器函数中的、用于给予对象实例的。通常为对象属性。
2.那些直接在构造函数上定义、仅在构造函数上可用的。这些一般仅在内置的浏览器对象中可用,并经过被直接连接到构造函数而不是实例来识别。 例如Object.keys()。
3.那些在构造函数原型上定义、由全部实例和对象类继承的。通常为对象方法。
固然,经过上述的方法咱们能够实现了基本的面向对象的编程,可是,要实现更高级的类库封装和框架实现,则须要能对设计模式有很好的认知。
在这,我就列举下设计模式原则,但愿你们包括我本身有学习的方向。通常的设计原则为:
1.单一职责原则
2.里氏替换原则
3.依赖倒置原则
4.接口隔离原则
5.迪米特原则
6.开闭原则
固然,还有关于面向对象的设计模式(23种),则须要深刻了解了,其实这已是有深刻到咱们本身的代码中了,只是咱们对它的认知并不深。这个就后续了解了~~~
经过对面向对象基本概念,面向对象的编程思想和构造器,原型实现方法实现一个简单的弹跳球小游戏。
主要经过ES6,class语法糖,经过canvas绘制背景并控制evil的上下左右,吃掉小球。
1.index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Bouncing balls</title> <link rel="stylesheet" href="style.css"> </head> <body> <canvas></canvas> <script src="mainUpgrade.js"></script> </body> </html>
2.mainUpgrade.js
const canvas=document.querySelector('canvas'); const ctx=canvas.getContext('2d'); const width=canvas.width=window.innerWidth; const height=canvas.height=window.innerHeight; //create a random number between min and max. random=(min,max)=>{ let num=Math.floor(Math.random()*(max-min+1))+min; return num; }; //create constructor for ball class Shape{ constructor(x,y,velX,velY,exists){ this.x=x; this.y=y; //坐标 this.velX=velX; this.velY=velY; //水平和竖直速度 this.exists=exists; //是否存在 } } class Ball extends Shape{ constructor(x,y,velX,velY,exists,color,size){ super(x,y,velX,velY,exists); this.color=color; this.size=size; } // draw ball draw (){ ctx.beginPath(); ctx.fillStyle=this.color; ctx.arc(this.x,this.y,this.size,0,2*Math.PI); // arc()绘制圆弧 ctx.fill(); } //update ball location update (){ if((this.x + this.size)>=width){ this.velX=-(this.velX) } if((this.x - this.size)<= 0){ this.velX=-(this.velX) } if((this.y + this.size)>= height){ this.velY=-(this.velY) } if((this.y - this.size)<= 0){ this.velY=-(this.velY) } this.x+=this.velX; this.y+=this.velY; } //spy collision collisionDetect (){ for(let j=0;j<balls.length;j++){ if(!(this===balls[j])){ const dx=this.x - balls[j].x; const dy=this.y - balls[j].y; const distance=Math.sqrt(dx*dx + dy*dy); if(distance<this.size + balls[j].size){ balls[j].color=this.color='rgb('+random(0,255)+','+random(0,255)+','+random(0,255)+')'; } } } } } //create evil circle class EvilCircle extends Shape{ constructor(x,y,exists){ super(x,y,exists); this.color='white'; this.size=10; this.velX=20; this.velY=20; } draw(){ ctx.beginPath(); ctx.strokeStyle=this.color; ctx.lineWidth=3; ctx.arc(this.x,this.y,this.size,0,2*Math.PI); ctx.stroke(); } //check evil location checkBounds(){ if((this.x + this.size)>width){ this.x-=this.size } if((this.y + this.size)>height){ this.y-=this.size } if((this.x - this.size)<0){ this.x+=this.size; } if((this.y - this.size)<0){ this.y+=this.size; } } setControls(){ window.onkeydown=(e)=>{ if(e.keyCode===38){ this.y-=this.velY } else if(e.keyCode===40){ this.y+=this.velY; } else if(e.keyCode===37){ this.x-=this.velX } else if(e.keyCode===39){ this.x+=this.velX } } } collisionDetect(){ for(let i=0;i<balls.length;i++){ if(balls[i].exists){ const dx=this.x-balls[i].x; const dy=this.y-balls[i].y; const distance=Math.sqrt(dx*dx+dy*dy); if(distance<this.size+balls[i].size){ balls[i].exists=false; } } } } } let balls=[]; const evil=new EvilCircle( random(0,width), random(0,height), true ); loop=()=>{ ctx.fillStyle='rgba(0,0,0,0.25)'; ctx.fillRect(0,0,width,height); while (balls.length < 25){ const ball=new Ball( random(0,width), random(0,height), random(-7,7), random(-7,7), true, 'rgb('+ random(0,255)+','+random(0,255)+','+random(0,255)+')', random(10,20) ); balls.push(ball); } for(let i=0;i<balls.length;i++){ if(balls[i].exists){ balls[i].draw(); balls[i].update(); balls[i].collisionDetect(); } } evil.draw(); evil.checkBounds(); evil.setControls(); evil.collisionDetect(); window.requestAnimationFrame(loop) //执行动画并请求浏览器在下一次重绘以前调用指定的函数来更新动画 } loop();
经过该博文,但愿能让你们对了解JavaScript实现面向对象有个基本的了解和概念。若有描述不当的地方望能指出,谢谢。