很多開發對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實現面向對象有個基本的瞭解和概念。若有描述不當的地方望能指出,謝謝。