Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
JavaScript 的 Window 對象是瀏覽器對象模型(BOM)的核心,它為開發者提供了操作瀏覽器窗口和控制用戶體驗的強大工具。本篇文章將帶你深入了解 Window 對象的各個方面:BOM、JavaScript 執行機制、Location 對象、Navigator 對象、History 對象以及本地存儲。
BOM 全稱為瀏覽器對象模型(Browser Object Model),它提供了與瀏覽器互動的各種接口和方法。通過 BOM,開發者可以訪問和操作瀏覽器窗口、文檔、歷史記錄等。BOM 的核心對象是 Window 對象。
// 設置窗口的高度和寬度
window.innerHeight=800;
window.innerWidth=1200;
// 打開一個新窗口
let newWindow=window.open('https://www.example.com', '_blank');
JavaScript 代碼在瀏覽器中的執行機制主要包括兩個方面:事件循環和回調隊列。
事件循環(Event Loop)是 JavaScript 的執行模型,它確保非阻塞操作能夠正常執行。
console.log('任務開始');
setTimeout(()=> {
console.log('異步任務');
}, 2000);
console.log('任務結束');
執行順序為:
Location 對象包含有關當前 URL 的信息,并提供了一些重定向和導航的方法。
// 獲取當前 URL
console.log(location.href);
// 重定向到新的 URL
location.href='https://www.new-url.com';
// 重新加載當前頁面
location.reload();
Navigator 對象包含有關瀏覽器的信息,如瀏覽器名稱、版本、以及用戶代理信息。
// 獲取用戶代理信息
console.log(navigator.userAgent);
// 獲取用戶地理位置
navigator.geolocation.getCurrentPosition((position)=> {
console.log(`Latitude: ${position.coords.latitude}, Longitude: ${position.coords.longitude}`);
});
History 對象允許操作瀏覽器的歷史記錄(會話歷史)。
// 返回上一個頁面
history.back();
// 前進到下一個頁面
history.forward();
// 前往歷史記錄中的第一個頁面
history.go(-1);
本地存儲是一種在客戶端存儲數據的方式,數據存儲在瀏覽器中,不會隨頁面刷新而丟失。
// 存儲數據
localStorage.setItem('username', 'john_doe');
// 獲取數據
let username=localStorage.getItem('username');
console.log(username); // 輸出: john_doe
// 刪除數據
localStorage.removeItem('username');
// 清空所有數據
localStorage.clear();
通過對這些對象和機制的深入了解,你可以更好地利用 JavaScript 提供的各種功能來優化用戶體驗,增強應用程序的互動性。無論是控制瀏覽器窗口、獲取用戶信息,還是操作歷史記錄和本地存儲,掌握這些知識都會讓你的開發工作事半功倍。
任何一家Saas企業都需要有自己的低代碼平臺.在可視化低代碼的前端研發過程中, 發現了很多有意思的技術需求, 在解決這些需求的過程中, 往往也會給自己帶來很多收獲, 今天就來分享一下在研發Dooring過程中遇到的前端技術問題——javascript函數存儲.
?
我們都知道要想搭建一個前端頁面基本需要如下3個要素:
在 「數據驅動視圖」 的時代, 這三個要素的關系往往如下圖所示:
可視化搭建平臺的設計思路往往也是基于上面的過程展開的, 我們需要提供編輯器環境給用戶來創建「視圖」和「交互」, 最終用戶保存的產物可能是這樣的:
{
"name": "Dooring表單",
"bgColor": "#666",
"share_url": "http://xxx.cn",
"mount_event": [
{
"id": "123",
"func": ()=> {
// 初始化邏輯
GamepadHapticActuator();
},
"sourcedata": []
}
],
"body": [
{
"name": "header",
"event": [
{
"id": "123",
"type": "click",
"func": ()=> {
// 組件自定義交互邏輯
showModal();
}
}
]
}
]
}
那么問題來了, json 字符串我們好保存(可以通過JSON.stringify序列化的方式), 但是如何將「函數」也一起保存呢? 保存好了函數如何在頁面渲染的時候能正常讓 js 運行這個函數呢?
我們都知道將 js 對象轉化為json 可以用 JSON.stringify 來實現, 但是它也會有局限性, 比如:
我們可以看到第4條, 如果我們序列化的對象中有函數, 它將會被忽略! 所以常理上我們使用JSON.stringify 是無法保存函數的, 那還有其他辦法嗎?
也許大家會想到先將函數轉換成字符串, 再用 JSON.stringify 序列化后保存到后端, 最后在組件使用的時候再用 eval 或者 Function 將字符串轉換成函數. 大致流程如下:
不錯, 理想很美好, 但是現實很_______.
接下來我們就一起分析一下關鍵環節 func2string 和 string2func 如何實現的.
熟悉 JSON API 的朋友可能會知道 JSON.stringify 支持3個參數, 第二個參數 replacer 可以是一個函數或者一個數組。作為函數,它有兩個參數,鍵(key)和值(value),它們都會被序列化。 函數需要返回 JSON 字符串中的 value, 如下所示:
所以我們可以在第二個函數參數里對 value類型為函數的數據進行轉換。如下:
const stringify=(obj)=> {
return JSON.stringify(obj, (k, v)=> {
if(typeof v==='function') {
return `${v}`
}
return v
})
}
這樣我們看似就能把函數保存到后端了. 接下來我們看看如何反序列化帶函數字符串的 json.
因為我們將函數轉換為字符串了, 我們在反解析時就需要知道哪些字符串是需要轉換成函數的, 如果不對函數做任何處理我們可能需要「人肉識別」.
? 「人肉識別」的缺點在于我們需要用正則把具有函數特征的字符串提取出來, 但是函數寫法有很多, 我們要考慮很多情況, 也不能保證具有函數特征的字符串一定是函數.
?
所以我換了一種簡單的方式, 可以不用寫復雜正則就能將函數提取出來, 方法就是在函數序列化的時候注入標識符, 這樣我們就能知道那些字符串是需要解析為函數了, 如下:
stringify: function(obj: any, space: number | string, error: (err: Error | unknown)=> {}) {
try {
return JSON.stringify(obj, (k, v)=> {
if(typeof v==='function') {
return `${this.FUNC_PREFIX}${v}`
}
return v
}, space)
} catch(err) {
error && error(err)
}
}
this.FUNC_PREFIX 就是我們定義的標識符, 這樣我們在用 JSON.parse 的時候就能快速解析函數了. JSON.parse 也支持第二個參數, 他的用法和 JSON.stringify 的第二個參數類似, 我們可以對它進行轉換, 如下:
parse: function(jsonStr: string, error: (err: Error | unknown)=> {}) {
try {
return JSON.parse(jsonStr, (key, value)=> {
if(value && typeof value==='string') {
return value.indexOf(this.FUNC_PREFIX) > -1 ? new Function(`return ${value.replace(this.FUNC_PREFIX, '')}`)() : value
}
return value
})
} catch(err) {
error && error(err)
}
}
new Function 可以把字符串轉換成 js 函數, 它只接受字符串參數,其可選參數為方法的入參,必填參數為方法體內容, 一個形象的例子:
我們上述的代碼中函數體的內容:
new Function(`return ${value.replace(this.FUNC_PREFIX, '')}`)()
之所以要 return 是為了把原函數原封不動的還原, 大家也可以用 eval , 但是出于輿論還是謹慎使用.
以上方案已經能實現前端存儲函數的功能了, 但是為了更工程化和健壯性還需要做很多額外的處理和優化, 這樣才能讓更多人開箱即用的使用你的庫.
為了讓更多人能直接使用這個功能, 我將完整版 json 序列化方案封裝成了類庫, 支持功能如下:
安裝方式如下:
# or npm install xijs
yarn add xijs
使用:
import { parser } from 'xijs';
const a={
x: 12,
b: function() {
alert(1)
}
}
const json=parser.stringify(a);
const obj=parser.parse(json);
// 調用方法
obj.b();
. 各種存儲方案的簡單對比
UserData僅IE支持, Google Gears SQLite需要插件,Flash已經伴隨著HTML5的出現漸漸退出了歷史舞臺,因此今天我們的主角只有他們三個:Cookie,LocalStorge,SesstionStorge;
2. Cookie
作為一個前端和Cookie打交道的次數肯定不會少了,Cookie算是比較古老的技術了
1993 年,網景公司雇員 Lou Montulli 為了讓用戶在訪問某網站時,進一步提高訪問速度,同時也為了進一步實現個人化網絡,發明了今天廣泛使用的 Cookie。
2.1 Cookie的特點
我們先來看下Cookie的特點:
cookie的內容主要包括:名字,值,過期時間,路徑和域。路徑與域一起構成cookie的作用范圍。若不設置過期時間,則表示這個cookie的生命期為瀏覽器會話期間,關閉瀏覽器窗口,cookie就消失。這種生命期為瀏覽器會話期的cookie被稱為會話cookie。會話cookie一般不存儲在硬盤上而是保存在內存里,當然這種行為并不是規范規定的。若設置了過期時間,瀏覽器就會把cookie保存到硬盤上,關閉后再次打開瀏覽器,這些cookie仍然有效直到超過設定的過期時間。存儲在硬盤上的cookie可以在不同的瀏覽器進程間共享,比如兩個IE窗口。而對于保存在內存里的cookie,不同的瀏覽器有不同的處理方式。
2.2 Session
說到Cookie就不能不說Session。
Session機制。session機制是一種服務器端的機制,服務器使用一種類似于散列表的結構(也可能就是使用散列表)來保存信息。當程序需要為某個客戶端的請求創建一個session時,服務器首先檢查這個客戶端的請求里是否已包含了一個session標識(稱為session id),如果已包含則說明以前已經為此客戶端創建過session,服務器就按照session id把這個session檢索出來使用(檢索不到,會新建一個),如果客戶端請求不包含session id,則為此客戶端創建一個session并且生成一個與此session相關聯的session id,session id的值應該是一個既不會重復,又不容易被找到規律以仿造的字符串,這個session id將被在本次響應中返回給客戶端保存。保存這個session id的方式可以采用cookie,這樣在交互過程中瀏覽器可以自動的按照規則把這個標識發送給服務器。一般這個cookie的名字都是類似于SEEESIONID。但cookie可以被人為的禁止,則必須有其他機制以便在cookie被禁止時仍然能夠把session id傳遞回服務器。經常被使用的一種技術叫做URL重寫,就是把session id直接附加在URL路徑的后面。比如:http://damonare.cn?sessionid=123456還有一種技術叫做表單隱藏字段。就是服務器會自動修改表單,添加一個隱藏字段,以便在表單提交時能夠把session id傳遞回服務器。比如:
<form name="testform" action="/xxx"> <input type="hidden" name="sessionid" value="123456"> <input type="text"> </form>
實際上這種技術可以簡單的用對action應用URL重寫來代替。
2.3 Cookie和Session簡單對比
Cookie和Session 的區別:
2.4 document.cookie的屬性
expires屬性
指定了coolie的生存期,默認情況下coolie是暫時存在的,他們存儲的值只在瀏覽器會話期間存在,當用戶推出瀏覽器后這些值也會丟失,如果想讓cookie存在一段時間,就要為expires屬性設置為未來的一個過期日期。現在已經被max-age屬性所取代,max-age用秒來設置cookie的生存期。
path屬性
它指定與cookie關聯在一起的網頁。在默認的情況下cookie會與創建它的網頁,該網頁處于同一目錄下的網頁以及與這個網頁所在目錄下的子目錄下的網頁關聯。
domain屬性
domain屬性可以使多個web服務器共享cookie。domain屬性的默認值是創建cookie的網頁所在服務器的主機名。不能將一個cookie的域設置成服務器所在的域之外的域。例如讓位于http://order.damonare.cn的服務器能夠讀取http://catalog.damonare.cn設置的cookie值。如果http://catalog.damonare.cn的頁面創建的cookie把自己的path屬性設置為“/”,把domain屬性設置成“.damonare.cn”,那么所有位于http://catalog.damonare.cn的網頁和所有位于http://orlders.damonare.cn的網頁,以及位于http://damonare.cn域的其他服務器上的網頁都可以訪問這個cookie。
secure屬性
它是一個布爾值,指定在網絡上如何傳輸cookie,默認是不安全的,通過一個普通的http連接傳輸
2.5 cookie實戰
這里我們使用javascript來寫一段cookie,借用w3cschool的demo:
function getCookie(c_name){ if (document.cookie.length>0){ c_start=document.cookie.indexOf(c_name + "=") if (c_start!=-1){ c_start=c_start + c_name.length+1 c_end=document.cookie.indexOf(";",c_start) if (c_end==-1) c_end=document.cookie.length return unescape(document.cookie.substring(c_start,c_end)) } } return ""; } function setCookie(c_name,value,expiredays){ var exdate=new Date() exdate.setDate(exdate.getDate()+expiredays) document.cookie=c_name+ "=" +escape(value)+ ((expiredays==null) ? "" : "; expires="+exdate.toUTCString()) } function checkCookie(){ username=getCookie('username') if(username!=null && username!=""){alert('Welcome again '+username+'!')} else{ username=prompt('Please enter your name:',"") if (username!=null && username!=""){ setCookie('username',username,355) } } }
注意這里對Cookie的生存期進行了定義,也就是355天
3. localStorage
這是一種持久化的存儲方式,也就是說如果不手動清除,數據就永遠不會過期。
它也是采用Key - Value的方式存儲數據,底層數據接口是sqlite,按域名將數據分別保存到對應數據庫文件里。它能保存更大的數據(IE8上是10MB,Chrome是5MB),同時保存的數據不會再發送給服務器,避免帶寬浪費。
3.1 localStorage的屬性方法
下表是localStorge的一些屬性和方法
屬性方法 說明 localStorage.length 獲得storage中的個數 localStorage.key(n) 獲得storage中第n個元素對的鍵值(第一個元素是0) localStorage.getItem(key) 獲取鍵值key對應的值 localStorage.key 獲取鍵值key對應的值 localStorage.setItem(key, value) 添加數據,鍵值為key,值為value localStorage.removeItem(key) 移除鍵值為key的數據 localStorage.clear() 清除所有數據
3.2 localStorage的缺點
4. sessionStorage
和服務器端使用的session類似,是一種會話級別的緩存,關閉瀏覽器會數據會被清除。不過有點特別的是它的作用域是窗口級別的,也就是說不同窗口間的sessionStorage數據不能共享的。使用方法(和localStorage完全相同):
屬性方法 說明 sessionStorage.length 獲得storage中的個數 sessionStorage.key(n) 獲得storage中第n個元素對的鍵值(第一個元素是0) sessionStorage.getItem(key) 獲取鍵值key對應的值 sessionStorage.key 獲取鍵值key對應的值 sessionStorage.setItem(key, value) 添加數據,鍵值為key,值為value sessionStorage.removeItem(key) 移除鍵值為key的數據 sessionStorage.clear() 清除所有數據
5. sessionStorage和localStorage的區別
5.1 測試
sessionStorage:
if (sessionStorage.pagecount){ sessionStorage.pagecount=Number(sessionStorage.pagecount) +1; }else{ sessionStorage.pagecount=1; } console.log("Visits "+ sessionStorage.pagecount + " time(s).");
測試過程:我們在控制臺輸入上述代碼查看打印結果
控制臺首次輸入代碼:
關閉窗口,控制臺再次輸入代碼:
所謂的關閉窗口即銷毀,就是這樣,關閉窗口重新打開輸入代碼輸出結果還是上面圖片的樣子,也就是說關閉窗口后sessionStorage.pagecount即被銷毀,除非重心創建。或者從歷史記錄進入才會相關數據才會存在。好的,我們再來看下localStorge表現:
if (localStorage.pagecount){ localStorage.pagecount=Number(localStorage.pagecount) +1; }else{ localStorage.pagecount=1; } console.log("Visits "+ localStorage.pagecount + " time(s).");
控制臺首次輸入代碼:
關閉窗口,控制臺再次輸入代碼:
6. web Storage和cookie的區別
Web Storage(localStorage和sessionStorage)的概念和cookie相似,區別是它是為了更大容量存儲設計的。Cookie的大小是受限的,并且每次你請求一個新的頁面的時候Cookie都會被發送過去,這樣無形中浪費了帶寬,另外cookie還需要指定作用域,不可以跨域調用。
除此之外,Web Storage擁有setItem,getItem,removeItem,clear等方法,不像cookie需要前端開發者自己封裝setCookie,getCookie。
但是Cookie也是不可以或缺的:Cookie的作用是與服務器進行交互,作為HTTP規范的一部分而存在 ,而Web Storage僅僅是為了在本地“存儲”數據而生
*請認真填寫需求信息,我們會在24小時內與您取得聯系。