軟件開發中,尤其是JavaScript中,觀察者模式是一種行為設計模式,它定義了一種一對多的關系。它允許多個觀察者對象監聽一個主題對象,并在主題狀態發生變化時自動得到通知。這種模式常用于事件系統、數據綁定等場景。
在JavaScript中,我們可以利用Proxy對象來實現觀察者模式。Proxy對象允許我們攔截和自定義對目標對象的操作,如屬性訪問、賦值、枚舉和函數調用。
本文將逐步講解如何使用Proxy在JavaScript中實現觀察者模式。我們將創建一個觀察者類,定義一個處理程序對象,并創建一個可觀察對象。此外,我將展示一個常見的前端場景,應用我們的Proxy基礎觀察者實現來解決問題。最后,我將對文章內容進行總結。
觀察者模式是一種設計模式,其中一個對象(主題)維護一組依賴于它的對象(觀察者),并在其狀態發生變化時通知這些觀察者,通常是通過調用它們的方法。此模式常用于實現分布式事件處理系統。
第一步:創建觀察者類
首先,我們需要創建一個觀察者類,該類將包含添加、刪除和通知觀察者的方法。
class Observer {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notifyObservers(message) {
this.observers.forEach(observer => observer.update(message));
}
}
class ConcreteObserver {
update(message) {
console.log('Received message:', message);
}
}
在這個例子中,Observer類維護一個觀察者列表,并提供添加、刪除和通知觀察者的方法。ConcreteObserver類是一個具體的觀察者,實現了處理接收通知的update方法。
第二步:定義處理程序對象
接下來,我們定義一個處理程序對象,以攔截和處理對可觀察對象的操作。
const handler = {
set(target, property, value, receiver) {
target[property] = value;
target.notifyObservers({ property, value });
return true;
}
};
在這個例子中,處理程序對象包含一個set方法,用于攔截對可觀察對象的屬性賦值操作。每當可觀察對象的屬性發生變化時,處理程序將通知所有觀察者。
第三步:創建可觀察對象
然后,我們創建一個可觀察對象并用Proxy包裝它。
class Observable extends Observer {
constructor(target) {
super();
return new Proxy(target, handler);
}
}
const observableObject = new Observable({ name: 'John', age: 30 });
在這個例子中,Observable類繼承了Observer類,并用Proxy包裝目標對象,以攔截和處理屬性操作。
接下來,我們將展示如何在一個經典的前端數據綁定場景中使用Proxy實現觀察者模式。假設我們有一個簡單的HTML表單,需要實現雙向數據綁定。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Observer Pattern with Proxy</title>
</head>
<body>
<input type="text" id="nameInput" placeholder="Enter your name">
<p id="nameDisplay"></p>
<script>
// 觀察者類和具體觀察者類
class Observer {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notifyObservers(message) {
this.observers.forEach(observer => observer.update(message));
}
}
class ConcreteObserver {
constructor(element) {
this.element = element;
}
update(message) {
this.element.textContent = message.value;
}
}
// 處理程序對象
const handler = {
set(target, property, value, receiver) {
target[property] = value;
target.notifyObservers({ property, value });
return true;
}
};
// 可觀察類
class Observable extends Observer {
constructor(target) {
super();
return new Proxy(target, handler);
}
}
// 創建可觀察對象
const data = new Observable({ name: '' });
// 創建具體觀察者
const nameDisplayObserver = new ConcreteObserver(document.getElementById('nameDisplay'));
data.addObserver(nameDisplayObserver);
// 處理輸入更改事件
document.getElementById('nameInput').addEventListener('input', (event) => {
data.name = event.target.value;
});
</script>
</body>
</html>
在這個例子中,我們創建了一個可觀察對象data,并將其name屬性綁定到一個輸入字段和一個顯示段落。當用戶在輸入字段中鍵入時,data.name的值會發生變化。處理程序會攔截此更改并通知所有觀察者。觀察者nameDisplayObserver隨后更新顯示段落的內容,實現了雙向數據綁定。
通過使用Proxy實現觀察者模式,我們可以有效地攔截和處理對象上的屬性操作,實現雙向數據綁定和響應式更新。本文介紹了觀察者模式的基本概念,并詳細講解了如何在JavaScript中使用Proxy實現該模式。希望本文能幫助你更好地理解和應用這種強大的設計模式在你的項目中。
天給大家講解的是include標簽,在打代碼的時候總會出現一些重復的樣式,這個時候就可以用include標簽來減少打代碼的次數。
文件名index.html,代碼:
{% from 'macros/forms.html' import input %}<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8">
<title>宏</title>
<style>
*{ list-style: none; text-decoration: none; }
.header{ height: 60px; background: #3a3a3a; color: #fff; margin-bottom: 20px; }
.header .nav-group{ margin-left: 10px; }
.header .nav-group li{ float: left; line-height: 60px; margin: 0px 20px; }
.nav-group li a{ color: #fff; }
.footer{ height: 60px; background: #3a3a3a; }
.footer p{ color: #fff; margin-left: 30px; padding-top: 20px; }
</style></head><body>
<div class="header">
<ul class="nav-group">
<li><a href="#">新聞</a></li>
<li><a href="#">音樂</a></li>
<li><a href="#">貼吧</a></li>
<li><a href="#">視頻</a></li>
</ul>
</div>
<table>
<tbody>
<tr>
<td>賬號</td>
<td>{{ input(placeholder="請輸入賬號") }}</td>
</tr>
<tr>
<td>密碼</td>
<td>{{ input(type="password", placeholder="請輸入密碼") }}</td>
</tr>
<tr>
<td></td>
<td>{{ input(type="submit", value="提交") }}</td>
</tr>
</tbody>
</table>
<div class="footer">
<p>頁面底部</p>
</div></body></html>
現在考慮這樣一個問題,如果頁面頭部和底部是很多頁面要用的樣式,那么如果在每一個新的文件中都要復制相同的代碼肯定不是我們希望的,這時候就可以用到include標簽了:
{% include '引用文件路徑' %}
用include前提是把相同的代碼先提取出來,所以我們將對應的代碼先提取成文件:
headers.html
<style>
*{ list-style: none; text-decoration: none; }
.header{ height: 60px; background: #3a3a3a; color: #fff; margin-bottom: 20px; }
.header .nav-group{ margin-left: 10px; }
.header .nav-group li{ float: left; line-height: 60px; margin: 0px 20px; }
.nav-group li a{ color: #fff; }</style><div class="header">
<ul class="nav-group">
<li><a href="#">新聞</a></li>
<li><a href="#">音樂</a></li>
<li><a href="#">貼吧</a></li>
<li><a href="#">視頻</a></li>
</ul></div>
footers.html
<style>
.footer{ height: 60px; background: #3a3a3a; }
.footer p{ color: #fff; margin-left: 30px; padding-top: 20px; }</style><div class="footer">
<p>頁面底部</p></div>
將公共部分提取出以后在調用的地方只需要用include標簽調用即可:
index.html
{% from 'macros/forms.html' import input %}<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8">
<title>宏</title></head><body>
{% include 'index/headers.html' %} <table>
<tbody>
<tr>
<td>賬號</td>
<td>{{ input(placeholder="請輸入賬號") }}</td>
</tr>
<tr>
<td>密碼</td>
<td>{{ input(type="password", placeholder="請輸入密碼") }}</td>
</tr>
<tr>
<td></td>
<td>{{ input(type="submit", value="提交") }}</td>
</tr>
</tbody>
</table>
{% include 'index/footers.html' %}</body></html>
如果還有一個詳情頁,那么只需要:
detail.html
<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8">
<title>Detail</title></head><body>
{% include 'index/headers.html' %} <p>這是詳情頁</p>
{% include 'index/footers.html' %}</body></html>
如果對接口、性能、自動化測試、面試經驗交流等感興趣的,可以關注我的頭條號,我會不定期的發放免費的資料,這些資料都是從各個技術網站搜集、整理出來的,如果你有好的學習資料可以私聊發我,我會注明出處之后分享給大家。歡迎分享,歡迎評論,歡迎轉發。需要資料的同學可以關注小編+轉發文章+私信【測試資料】
文為大家介紹如何使用 CSS 創建一個帶搜索的導航欄。
以下實例均是響應式的。
可以先看下效果圖:
<div class="topnav">
<a class="active" href="#home">主頁</a>
<a href="#about">關于</a>
<a href="#contact">聯系我們</a>
<input type="text" placeholder="搜索..">
</div>
/* 在頂部導航欄中添加黑色背景顏色 */
.topnav {
overflow: hidden;
background-color: #e9e9e9;
}
*請認真填寫需求信息,我們會在24小時內與您取得聯系。