JavaScript對象入門指南
前言
不少開發對JavaScript實現物件導向編程存在一知半解,並且不少的在項目實踐中寫的都是面向過程編程的代碼,因此,希望能從零入手介紹物件導向的一些概念到實現簡單的物件導向的例子讓大家包括我自己加深對物件導向的認知。硬文一篇,希望能對大家有協助 ^v^
對象基礎 概念
對象是一個包含相關資料和方法的集合,是通過變數和函數組成,通常被我們說成屬性和方法,常用對象字面量的形式表示。
建立方法
1.初始化對象
var person={}
2.添加屬性(變數)和方法(函數)
var person={
name:'aaron',
say:function(){
alert('hello')
}
}
3.擷取屬性和執行方法
person.nameperson.say()
備忘:擷取屬性和執行方法有兩種方法,就是說我上面列舉的其一:點標記法,還有一種就是括弧標記法。如下:
person['name']
person['say']()
因此,有時對象也被叫做關聯陣列,即對象做了字串到值的映射,而數組做的是數字到值的映射。
4.運行
5.設定對象成員
備忘:有一點需要瞭解到的是,括弧標記法能做到通過定義變數名的方式去設定對象成員,而這一點是點標記法沒法實現的。
6.“this”的含義
this的指向其實是一直都讓開發人員頭大的問題了,尤其是後端寫JS時。其實說白了this就是指向了當前代碼運行時的對象。
例如:
由於對象字面量執行的是當前對象,所以this指向了person。而像建立的建構函式等this的指向就是建構函式執行個體對象了
優點
一目瞭然,對象字面量建立的對象的好處可以有效把對象關聯的屬性和方法統一了起來,也減少了全域變數的汙染,得到一定程度的安全(減少了定義全變數覆蓋對象屬性的危險)。
物件導向--建構函式 瞭解OOP思想
例如從現實世界的某個執行個體出發,對於一個人(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 具有這個方法,於是該方法被調用。
prototype屬性
通過對valueOf方法的調用過程,我們也就瞭解到了那些能被繼承的屬性和方法(對象中存在不能被繼承的屬性方法,例is()/keys())是定義在prototype屬性上的。因此,在建構函式是需要被子類繼承的屬性方法需要定義在prototype上。
constructor屬性
每個執行個體對象都有constructor屬性,它是指向建立該執行個體的建構函式。
而我們也可以通過在constructor後添加()形式實現建立新執行個體。
修改原型
通過我們可以瞭解到了,雖然已經建立了執行個體對象person1,當時之後再像構造器Person()prototype中添加方法,person1還是能調用,這就說明了函數調用會通過上溯原型鏈,從上遊對象中調用方法。
,若在prototype上定義屬性的話,則this的當前執行環境為全域,返回的為undefined。並且,在對象繼承上看,一般的做法是在構造器中定義屬性,在prototype屬性中定義方法。
小demo--執行個體理解JavaScript中的繼承 1.建立構造器Person並在構造器上定義方法
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 + '.');
};
2.建立Teacher類Teacher構造器,並繼承Person的所有成員,同時具備自有屬性和方法:subject,greeting()
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests);
this.subject = subject;
}
3.設定Teacher()的原型和構造器引用
,建立的新的Teacher()構造器只有一個空的原型屬性,則需從Person()的原型prototype中繼承方法:
Teacher.prototype = Object.create(Person.prototype);
,我們又遇到了個問題,由於我們建立Teacher的prototype的方式,Teacher()構造器的prototype屬性執行了Person(),因此,我們需要設定指向Teacher:
Teacher.prototype.constructor = Teacher;
通過這樣,我們就能實現了需要繼承的方法都定義在了構造器的prototype屬性內,這樣才不會打亂了類繼承結構。
4.向Teacher()添加新的greeting方法
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 + '.');
};
5.最後運行建立teacher執行個體,我們就能得到了從Person()構造器繼承的屬性和方法,並具有只有Teacher()的構造器才有的屬性方法。 6.對象成員
通過學習,我們可以知道一般對象所具有的對象成員包括三類:
1.那些定義在構造器函數中的、用於給予對象執行個體的。一般為對象屬性。
2.那些直接在建構函式上定義、僅在建構函式上可用的。這些通常僅在內建的瀏覽器對象中可用,並通過被直接連結到建構函式而不是執行個體來識別。 例如Object.keys()。
3.那些在建構函式原型上定義、由所有執行個體和對象類繼承的。一般為對象方法。
深入--設計模式原則
當然,通過上述的方法我們可以實現了基本的物件導向的編程,但是,要實現更進階的類庫封裝和架構實現,則需要能對設計模式有很好的認知。
在這,我就列舉下設計模式原則,希望大家包括我自己有學習的方向。一般的設計原則為:
1.單一職責原則
2.裡氏替換原則
3.依賴倒置原則
4.介面隔離原則
5.迪米特原則
6.開閉原則
當然,還有關於物件導向的設計模式(23種),則需要深入瞭解了,其實這已經是有深入到我們自己的代碼中了,只是我們對它的認知並不深。這個就後續瞭解了~~~
實戰:構建對象--彈跳球(ES6實現) 背景
通過對物件導向基本概念,物件導向的編程思想和構造器,原型實現方法實現一個簡單的彈跳球小遊戲。
介紹
主要通過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實現物件導向有個基本的瞭解和概念。如有描述不當的地方望能指出,謝謝。
本文永久更新連結地址:https://www.bkjia.com/Linux/2018-03/151528.htm