今天開始初學設計模式,在此記錄以便往後複習。javascript
一個模式就是一個可重用的方案,可應用於在軟件設計中的常見問題,另外一種解釋就是一個咱們如何解決問題的模板 - 那些能夠在許多不一樣的狀況裏使用的模板。 --w3cschool前端
學習設計模式,面向對象是基礎,那就先來複習一下java
數據存放方式決定了訪問的方式。git
類定義了全部用於具備某一特徵對象的屬性。類是抽象的事物,而不是其所描述的所有對象中的任何特定的個體。另外一方面,一個實例是一個類的實例化,是其中的一個成員es6
構造函數(constructor),實例(instance),原型(prototype)自己都是對象。基於原型的語言具備所謂的原型對象的概念,新對象能夠從中得到原始的屬性。github
在JavaScript
中有一個頗有意思的__proto__
屬性用於訪問其原型對象,構造函數,實例,原型自己都有__proto__
指向原型對象。最後順着原型鏈都會指向Object
這個構造函數,然而Object
的原型對象的原型是null
面試
Student.prototype.study = function() {
console.log('我在學習' + this.subject);
}
複製代碼
對於每個實例對象,其方法的函數體是如出一轍的,方法的執行結果只根據其實例對象決定,然而生成的每一個實例都須要生成一個方法去佔用一分內存。編程
在ES6
以前建立對象有兩種方式,這兩種方式在特定的使用場景分別有其優勢和缺點, 下面咱們來分別介紹這兩種建立對象的方式。後端
咱們經過對象字面量的方式建立兩個student
對象,分別是student1
和student2
。設計模式
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
這個構造函數實例化而來的, 咱們可以清楚的知道各實例對象之間的聯繫。
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
, 對應的是Es6
中Student
類中的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
原型對象,全部的實例都能繼承該方法,多個實例的方法指向了同一個內存地址。
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();//靜態方法
複製代碼
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
複製代碼
若是在實例上調用靜態方法,會拋出一個錯誤,表示不存在該方法。
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'
複製代碼
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() //我是類方法只可經過函數自己調用
複製代碼
面向對象的基本知識差很少都掌握了,接下來就開始學習設計原則!
這是我一個開源的收藏網址的項目
項目地址👉👉點擊進入,能夠直接設置爲瀏覽器主頁或者桌面快捷方式進行使用,本人在用,長期維護。
徹底開源,你們能夠隨意研究,二次開發。固然仍是十分歡迎你們點個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