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小时内与您取得联系。