ECMA-262把面向對象定義爲:「無序屬性的集合,其屬性能夠包含基本值、對象、或者函數」java
熟悉java等其餘面向對象的語言的同窗確定對面向對象的概念很熟悉,經過面向對象,可我能夠把很複雜的邏輯抽象化,提升代碼的複用性,提升內聚性,下降耦合度。 而和其餘正統的面向對象不一樣的是,js採用的是原型對象。程序員
既然是面向原型的面向對象,咱們首先就須要搞懂原型是什麼。 直接上圖。 es6
建立一個新函數,哈數內自動回建立一個prototype屬性。這屬性是一個指針,指向了原型對象
面試在這個原型對象中有一個默認constructor屬性,這個屬性也是一個指針,默認指向它的構造函數,也就是Person方法。(實現繼承的關鍵屬性)
設計模式在原型對象中定義咱們須要的相關屬性,如圖上的name,age等。
bash經過new方法新建對象。
函數
這樣一個js對象就建立完畢了。從這個步驟中咱們也能夠看出來,prototype是js建立對象的核心,理解prototype也是理解js面向對象的第一步。
ui
固然,prototype的內容不止是以上內容,這裏再也不贅述,咱們能夠自行了解。this
如今最流行的面向對象設計模式叫組合使用構造函數和原型模式,因此咱們須要先理解構造函數模式。
直接上代碼:es5
// 構造函數模式
function Person (name,age){
this.name = name;
this.age = age;
this.sayAge = function (){
alert(this.age);
}
}
let person = new Person('tjh',11);
person.sayAge(); //11
複製代碼
學過java的同窗必定知道構造函數是什麼東西。構造函數模式就和構造函數同樣,能夠傳入參數來給對象賦初始值。
優勢:能夠傳入值來給對象付初始值。
缺點:每一個方法也須要給每一個對象建立一次,佔用了多餘的空間,代碼冗餘
好比上述的sayAge方法,咱們每次new一個對象,每一個對象內都存在一個sayAge方法,其實它只須要建立一遍便可。
原型模式也是經常使用的構建模式,和構造函數是互補的模式,二者各有所長。
直接上代碼。
function Person() {
}
Person.prototype.name = "tjh";
Person.prototype.age = 11;
Person.prototype.sayAge = function(){
alert(this.age)
}
let person = new Person();
person.sayAge(); //11
複製代碼
經過prototype中的constructor指針,咱們把prototype對象從新指向了Person。
優勢:全部對象共享屬性和方法,十分節省內存
缺點:
一、沒法傳遞參數,全部實例默認相同
二、當屬性值存在引用類型時,會發生全部實例共用一個引用對象的現象。
緣由是js中,值類型都是按值傳遞的,而引用類型傳遞的是指針!
理解了上面兩中類型以後,咱們能夠發現,他們的優缺點是互補的。那咱們爲何不把他們結合起來呢?
先看代碼:
//組合使用原型模式和構造函數模式
function Person(name, age, job) { //每一個成員的私有變量放在這裏
this.name = name;
this.age = age;
this.job = job;
this.friends = ['a','b'];
}
Person.prototype = { //每一個成員的共有方法放在這裏
constructor: Person,
sayName: function (){
alert(this.name);
}
}
let person1 = new Person('tjh',18,'student');
let person2 = new Person('zxy',13,'wife');
person1.friends.push('c');
alert(person1.friends); //a,b,c
alert(person2.friends); //a,b
person1.sayName(); //tjh
person2.sayName(); //zxy
複製代碼
咱們把二者的優勢都結合起來,就能夠幾乎完美的模擬類了。簡單來講,只須要把私有的變量或方法放在構造函數中,把共有的成員變量或方法放在prototype中,既保證了代碼重用,又保證了構造函數的不一樣。
ES6的class本質上其實就是es5的面向對象的語法糖, 爲了簡化面向對象的代碼和簡化程序員理解js面向對象的難度。
因此只要咱們理解了es5的面向對象,es6的class只是單單變化了一些語法,是它更靠近正統的面向對象了而已。 class的基本語法很簡單,咱們直接看代碼
// ES6class
class Person {
constructor(name,age){
this.name = name;
this.age = age;
}
sayAge(){
alert(this.age);
}
}
let person = new Person('tjh',11);
person.sayAge(); //11
複製代碼
是否是發現了class和組合使用方式用法基本一致!只是簡化了部分代碼。每一個class都有一個constructor函數,若沒有,則會默認返回this。原理基本與組合方式一致。
可是有一點須要注意:class不存在變量提高!
1.prototype(原型)是什麼,它是怎麼使用的?
答:每一個函數都有一個prototype屬性, 它是一個引用變量, 默認指向一個空Object對象,也就是prototype對象 。
當調用一個對象的函數或者屬性的時候,若是在當前對象裏面找不到,那麼就到原型裏面去找,重複以上過程。
2.構造函數模式和原型模式的優缺點各自是什麼?
以在上文解答。
3.寫出下面題目的顯示
function Foo(){
getName = function(){console.log(1);};
return this;
}
Foo.getName = function(){console.log(2);};
Foo.prototype.getName = function(){console.log(3);};
var getName = function(){console.log(4);};
function getName(){console.log(5);}
Foo.getName();//2
getName();//4
Foo().getName();//1
getName();//1
new Foo.getName();//2
new Foo().getName();//3
new new Foo().getName();//3
複製代碼
4.實現停車場
參考自 www.jianshu.com/p/02f7f2779…
某停車場,分3層,每層100個車位
每一個車位都能監控到車輛的駛入和離開
車輛進入前,顯示每層的空餘車位數量
車輛進入時,攝像頭可識別車牌號和時間
車輛出來時,出口顯示器顯示車牌號和停車時長
// 停車場
class Park {
constructor() {
this.parkNumList = []; // 每層空餘車位
}
getPrakNumList() {
for (let i of this.parkNumList) {
console.log(`第${i.floor}層停車場,剩餘停車位${i.parkNum}`);
}
}
registerFloor(floor) {
this.parkNumList.push(floor);
}
}
// 層數
class Floor {
constructor(floor, num) {
this.floor = floor; // 層數
this.parkNum = num; // 該層剩餘車空位
}
carEnter() {
this.parkNum = this.parkNum - 1;
}
carLeave() {
this.parkNum = this.parkNum + 1;
}
}
// 停車位
class ParkSpace {
constructor(num, belongFloor) {
this.parkSpaceNum = num; //停車位編號
this.belongFloor = belongFloor; //停車位屬於哪一層
this.isAir = false; //停車位是否空閒
}
monitorCarEnter() {
this.isAir = false;
this.belongFloor.carEnter();
console.log('-------停車位已監控到車輛進入------');
}
monitorCarLeave() {
this.isAir = true;
this.belongFloor.carLeave();
console.log('-----停車位已監控到車輛離開-----');
}
}
// 車
class Car {
constructor(carNum) {
this.carNum = carNum; // 車牌號
this.carEnterTime = ''; // 車進入停車位的時間
this.carLeaveTime = ''; // 車離開停車位的時間
}
beforeEnter(park) {
park.getPrakNumList();
}
enter(parkSpace, camera, enterTime) {
this.carEnterTime = enterTime;
parkSpace.monitorCarEnter();
camera.shot(this);
}
leave(parkSpace, monitor, leaveTime) {
this.carLeaveTime = leaveTime;
parkSpace.monitorCarLeave();
monitor.show(this);
}
}
// 攝像頭
class Camera {
shot(car) {
console.log(`攝像頭已識別車牌號:${car.carNum}`);
console.log(`攝像頭已識別車輛進入時間:${car.carEnterTime}`);
}
}
// 出口顯示器
class Monitor {
show(car) {
console.log(`出口顯示器顯示車牌號:${car.carNum}`);
console.log(`出口顯示器顯示車輛停車時長:${car.carEnterTime} - ${car.carLeaveTime}`);
}
}
// 舒店長要開停車場營業啦~
let park = new Park(); //初始化停車場
let firstFloor = new Floor(1, 100); //初始化一樓停車場
park.registerFloor(firstFloor);
let secondFloor = new Floor(2, 100); //初始化二樓停車場
park.registerFloor(secondFloor);
let thirdFloor = new Floor(3, 100); //初始化三樓停車場
park.registerFloor(thirdFloor);
let camera = new Camera(); //初始化攝像頭
let monitor = new Monitor(); //初始化出口顯示屏
// 歡迎第一輛車停車~
let car1 = new Car('桂G8888');
car1.beforeEnter(park);
let parkSpace1 = new ParkSpace(88, secondFloor); //這個車想停在二樓停車場的88號位置
car1.enter(parkSpace1, camera, '12:45:30');
// 而後又來了第二輛車~
let car2 = new Car('贛B8888');
car2.beforeEnter(park);
let parkSpace2 = new ParkSpace(88, thirdFloor); //這個車想停在三樓停車場的88號位置
car2.enter(parkSpace2, camera, '23:01:30');
// 而後又來了第三輛車~
let car3 = new Car('粵A6666');
car3.beforeEnter(park);
let parkSpace3 = new ParkSpace(88, thirdFloor); //這個車想停在一樓停車場的88號位置
car3.enter(parkSpace3, camera, '19:01:30');
// 而後又來了第四輛車~可是沒有停車~唉
let car4 = new Car('粵A6666');
car4.beforeEnter(park);
複製代碼
結語:搞懂js面向對象只是js的基本入門,你我還需繼續努力。