【設計模式】學習JS設計模式?先掌握面向對象!

設計模式

今天開始初學設計模式,在此記錄以便往後複習。javascript

什麼是設計模式

一個模式就是一個可重用的方案,可應用於在軟件設計中的常見問題,另外一種解釋就是一個咱們如何解決問題的模板 - 那些能夠在許多不一樣的狀況裏使用的模板。 --w3cschool前端

我爲何要學習設計模式

  • 高級工程師面試必考知識點
  • 寫出更好的代碼,設計模式是必經之路
  • 掌握設計模式更容易閱讀流行框架源碼
  • 想要成爲項目負責人,從零架構工程,設計模式是基石
  • 沒事裝個逼
  • ......

我學習設計模式的四個階段

  • 鞏固面向對象相關知識,es6 class語法,封裝,繼承,多態等
  • 掌握設計原則
  • 掌握設計模式
  • 最後作一個練習案例

面向對象

學習設計模式,面向對象是基礎,那就先來複習一下java

面向對象和函數式編程的區別

數據存放方式

  • 對於面向對象,數據存放在對象的屬性裏面,以及全局變量。
  • 對於函數式,數據存放在各級做用域裏面,做用域包括全局做用域。

數據訪問方式

數據存放方式決定了訪問的方式。git

  • 對於面向對象來講,訪問數據(全局變量除外)須要先獲取對象的引用,而後再進行操做(直接訪問——公共屬性,或者調用成員函數/方法訪問——私有屬性)
  • 對於函數式,訪問數據是直接訪問(經過函數入參或者做用域鏈查找)

基於類的面向對象和基於原型的面向對象

基於類的面向對象

類定義了全部用於具備某一特徵對象的屬性。類是抽象的事物,而不是其所描述的所有對象中的任何特定的個體。另外一方面,一個實例是一個類的實例化,是其中的一個成員es6

基於原型的面向對象

構造函數(constructor)實例(instance)原型(prototype)自己都是對象。基於原型的語言具備所謂的原型對象的概念,新對象能夠從中得到原始的屬性。github

JavaScript中有一個頗有意思的__proto__屬性用於訪問其原型對象,構造函數,實例,原型自己都有__proto__指向原型對象。最後順着原型鏈都會指向Object這個構造函數,然而Object的原型對象的原型是null面試

經過原型添加方法

Student.prototype.study = function() {
  console.log('我在學習' + this.subject);
}
複製代碼

爲何要將方法掛載到原型上?

對於每個實例對象,其方法的函數體是如出一轍的,方法的執行結果只根據其實例對象決定,然而生成的每一個實例都須要生成一個方法去佔用一分內存。編程

ES6以前的面向對象

ES6以前建立對象有兩種方式,這兩種方式在特定的使用場景分別有其優勢和缺點, 下面咱們來分別介紹這兩種建立對象的方式。後端

  • 第一種是使用對象字面量的方式
  • 第二種是使用構造函數的方式

使用對象字面量的方式

咱們經過對象字面量的方式建立兩個student對象,分別是student1student2設計模式

var student1 = {
  name: '小呆',
  age: 22,
  subject: '前端開發'
};

var student2 = {
  name: '小傻',
  age: 22,
  subject: '後端開發'
};
複製代碼

對象字面量的方式在建立單一簡單對象的時候是很是方便的。可是,它也有其缺點:

  • 在生成多個實例對象時, 咱們須要每次重複寫不少屬性,寫起來特別的麻煩。
  • 雖然都是學生的對象, 可是看不出兩個對象之間有什麼聯繫。

使用構造函數的方式

構造函數就其實就是一個普通的函數,當對構造函數使用new進行實例化時,會將其內部this的指向綁定到實例對象上.

咱們來建立一個Student構造函數(構造函數一般使用大寫字母開頭,和普通函數作區分)。

function Student (name, age, subject) {
  this.name = name;
  this.age = age; 
  this.subject = subject;
  console.log(this);
}
複製代碼

咱們在構造函數中打印出this的指向。由於構造函數其實就是一個普通的函數, 那麼咱們即可以使用普通函數的調用方式去調用Student

Student('小呆', 22, '前端開發'); //window{}
複製代碼

能夠看到,採用普通方式調用Student時, this的指向是window。下面使用new來實例化該構造函數, 生成一個實例對象student1

let student1 = new Student('小呆', 22, '前端開發'); 
//Student {name: "阿輝", age: 22, subject: "前端開發"}
複製代碼

能夠看到,當咱們採用new生成實例化對象student1時, this再也不指向window, 而是指向的實例對象自己。

上面的就是採用構造函數的方式生成實例對象的方式, 而且當咱們生成其餘實例對象時,因爲都是採用Student這個構造函數實例化而來的, 咱們可以清楚的知道各實例對象之間的聯繫。

new都作了什麼?

  • 建立一個新對象
  • 分配內存,將構造函數的做用域賦值給新對象,即this指向這個新對象
  • 執行構造函數中的代碼
  • 返回新對象

ES6中的面向對象

ES6中提供了基於類class的語法。但class本質上是ES6提供的一顆語法糖,但實際上JavaScript是一門基於原型的面嚮對象語言。

ES6中使用class來建立對象

//定義類
class Student {
  //構造方法
  constructor(name, age, subject) {
    this.name = name;
    this.age = age;
    this.subject = subject;
  }

  //類中的方法
  study(){
    console.log('我在學習' + this.subject);
  }
}
//實例化類
let student3 = new Student('小癡', 24, '前端開發');
student3.study(); //我在學習前端開發
複製代碼

上面的代碼定義了一個Student類, 能夠看到裏面有一個constructor方法, 這就是構造方法,而this關鍵字則表明實例對象。也就是說,ES5中的構造函數Student, 對應的是Es6Student類中的constructor方法。

Student類除了構造函數方法,還定義了一個study方法。方法之間不要用逗號分隔,加了會報錯。並且,類中的方法所有是定義在原型上的,咱們能夠用下面的代碼進行驗證。

console.log(student3.__proto__.study === Student.prototype.study); //true
console.log(student3.hasOwnProperty('study')); // false
複製代碼

上面的第一行的代碼中, student3.__proto__是指向的原型對象,其中Student.prototype也是指向的原型的對象,結果爲true就能很好的說明類中的方法所有是定義在原型上的。

第二行代碼是驗證student3實例中是否有study方法,結果爲false, 代表實例中沒有study方法,這也更好的說明了上面的結論。

而將study方法掛載到prototype原型對象,全部的實例都能繼承該方法,多個實例的方法指向了同一個內存地址。

實例與類

javascript中實例方法與類方法的區別

  • 類屬性(靜態屬性):經過類直接訪問,不須要聲明類的實例來訪問
  • 類方法(靜態方法):經過類直接訪問,不須要聲明類的實例來訪問
  • 實例屬性(動態屬性):不能經過類直接訪問,須要經過該類聲明的實例才能訪問
  • 實例方法(動態方法):不能經過類直接訪問,須要經過該類聲明的實例才能訪問
Person = function(){
}
Persion.sex = "woman";  //類屬性
Person.eat= function(){  //類方法
  alert("eat");
}
Person.prototype.age = 10; //實例屬性
Person.prototype.dance = function(){ //實例方法
  alert("dance");
}
const person = new Person();
person.age;//實例屬性
person.dance();//實例方法
Person.sex;//靜態屬性
Person.eat();//靜態方法
複製代碼

ES6中的實例與類

靜態屬性和實例屬性

靜態屬性須要使用static關鍵字

class Foo {
  static num = 1;
    age = 2
}
const foo = new Foo();
console.log(Foo.num);// 1
console.log(foo.num);// undefined
console.log(Foo.age);// undefined
console.log(foo.age);// 2
複製代碼

若是在實例上調用靜態方法,會拋出一個錯誤,表示不存在該方法。

靜態方法須要使用static關鍵字

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

const foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

複製代碼

靜態方法能夠與非靜態方法重名

若是靜態方法包含this關鍵字,這個this指的是類,而不是實例。

class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}
const foo = new Foo();
Foo.bar() // hello
foo.bar() // world
複製代碼

父類的靜態方法,能夠被子類繼承。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'
複製代碼

靜態方法也是能夠從super對象上調用的

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"
複製代碼

封裝,繼承,多態

面向對象三大特性:封裝繼承多態

封裝

封裝就是將數據操做的細節隱藏起來,只暴露對外的接口,外界調用端不須要也不可能知道細節,只能經過經過對外暴露的接口來訪問該對象

class School {
  // 構造器:建立對象完成初始化操做
  constructor(id, name) {
    this.id = id
    this.name = name
  }
  // 實例方法
  schoolName() {
    console.log(this.name)
  }
  // 類方法
  static schoolOnly() {
    console.log('我是類方法只可經過函數自己調用')
  }
}
複製代碼

繼承

繼承表示子類繼承父類,子類除了擁有父類全部的特徵之外,還有一些更具體的特性;

class Student extends School {
  constructor(sId, sName, id, name) {
    super(id, name)
    this.sId = sId
    this.sName = sName
  }
  studentName() {
    console.log(this.sName)
  }
  say() {
    console.log('I am a student')
  }
}
複製代碼

多態

由繼承產生的多個不一樣的類,對同一個方法能夠有不一樣的響應,好比學生和老師均可以繼承自學校,可是它們分別實現了本身的say方法;此時針對某一個實例,咱們無需瞭解他是學生仍是老師,咱們能夠直接調用say方法,程序會自動判斷應該如何執行這個方法;

class Teacher extends School {
  constructor(tId, tName, id, name) {
    super(id, name)
    this.tId = tId
    this.tName = tName
  }
  TeacherName() {
    console.log(this.tName)
  }
  say() {
    console.log('I am a teacher')
  }
}
測試
let school = new School(1, '第一小學')
let student = new Student(10, 'Daming', 1, '第一小學')
let teacher = new Teacher(100, 'MrLi', 1, '第一小學')
console.log(student) //Object Student {id: 1, name: "第一小學", sId: 10, sName: "Daming"}
console.log(teacher) //Object Teacher {id: 1, name: "第一小學", tId: 100, tName: "MrLi"}
student.studentName() //Daming
student.schoolName()  //第一小學
student.say() //I am a student
teacher.say() //I am a teacher
School.schoolOnly() //我是類方法只可經過函數自己調用
複製代碼

面向對象的基本知識差很少都掌握了,接下來就開始學習設計原則!

打個廣告

16.gif 這是我一個開源的收藏網址的項目

項目地址👉👉點擊進入,能夠直接設置爲瀏覽器主頁或者桌面快捷方式進行使用,本人在用,長期維護。

徹底開源,你們能夠隨意研究,二次開發。固然仍是十分歡迎你們點個Star⭐⭐⭐
👉👉源碼連接(gitee)       👉👉源碼連接(github)

連接整合

🔊項目預覽地址(GitHub Pages):👉👉alanhzw.github.io

🔊項目預覽備用地址(本身的服務器):👉👉warbler.duwanyu.com

🔊源碼地址(gitee):👉👉gitee.com/hzw_0174/wa…

🔊源碼地址(github):👉👉github.com/alanhzw/War…

🔊個人博客:👉👉www.duwanyu.com

相關文章
相關標籤/搜索