# 10分钟回顾Location对象的几个知识点

Location对象包含有关当前URL的信息.它是一个比较特别的对象,因为它既是Window对象的一部分,可通过window.location属性来访问.也是document对象的属性,通过document.location来使用.

我的页面路径是 http://127.0.0.1:5501/html/index.html?id=123#test,在控制台键入window.location,返回一个Location对象.

Location Object

这其中有些是属性,有些是方法,我们来简单了解一下.

# 对象属性

我们按照上图中展开的Location对象,来说一说其中的一些属性

属性 描述
hash 锚点部分,以#开头
host 主机名 + 端口号
hostname 主机名 如127.0.0.1 localhost
href 包含完整URL
origin 协议 + 主机名 + 端口号(我们常说的跨域,跨的就是它)
pathname 路径部分,以 / 开头
port 端口,常见的80 443
protocol URL对应的协议,注意最后是有一个:的,如http: https:
search 参数部分,以? 开头

下面这个是一个在MDN上的例子,比较清晰直观的展示了各个部分的内容:

Location

其中上面的属性,除了 origin 是个只读属性不可修改外,其余的属性都是读写兼备的. 下面是属性如何获取和设置的用法

console.log(window.location.hash)  // 读取当前页面的hash 结果为 #test
window.location.hash = 'test2' // 设置当前页面的hash 此时可以观察到浏览器的url会变化
console.log(window.location.hash) // 再次读取hash,结果变成了 #test2
window.location.href = 'https://www.baidu.com'  // 页面直接跳转到百度

在这里顺便提一下,现在大部分的浏览器都支持 hashchange 事件.就是当浏览器# 后面的内容变化时,触发此事件.

window.addEventListener('hashchange', () => {
    console.log('Hash has changed')
})

执行上面的代码后,当我们改变url的hash部分时,控制台就会打印出提示.当然,对于那些不兼容hashchange事件的浏览器,也是有办法可以模拟的,比如设置一个定时器,每隔一定时间去查询当前的hash,和之前的hash去做对比从而判断url中的hash是否改变了. 注意: 每次修改location 的属性(hash除外),页面都会以新URL重新加载.

# 补充案例

曾经项目中有一个需求是这样的,点击按钮复制一个带有个人邀请码的注册链接.如这种样子的:https://www.xx.com/register?code=8888.那么这个链接就需要我们自己去拼接了.我原先伪代码如下

let inviteCode = 8888
let url = location.origin + '/register?code' + inviteCode
copy(url)

但是后来测试同学在兼容性测试的时候告诉我这里有问题,在IE11以下的浏览器中复制出来的路径是有问题的.于是,我去排查原因,发现location.origin 这个属性在IE10及其以下是不存在的,会返回undefined,既然发现了问题所在,那我们就好解决了.解决代码如下:

if(!location.origin){
    location.origin = location.protocol + '//' + location.hostname + (location.port ? ':'+location.port : '') 
}
let inviteCode = 8888
let url = location.origin + '/register?code' + inviteCode
copy(url)

提交代码,于是测试同学高兴的告诉我说问题解决了.

# 对象方法

从文章最开头的图中可以看到 Location 对象有3个方法 (toString方法就不说了,就是返回一个包含完整url的字符串)

方法 描述
assign 加载新页面
reload 重加载当前页面
replace 用一个新页面代替当前页面

这里来说明一下这几个方法的用法和区别.首先我们说的是reload方法.此方法的参数是一个可选的布尔类型,不填默认为false,表示可能从缓存中读取当前页面刷新,相当于普通的F5刷新,true表示强制浏览器从服务器去重新获取页面资源,相当于强制刷新(Shift+F5 或者 Cmd+Shift+R),具体用法如下:

window.location.reload()  // 执行后会重新加载当前页面

然后是 assignreplace 方法,这两个方法都需要传入一个参数,表示新页面的地址.它们的区别在于,使用了 assign 后,会在浏览器的历史记录中留下之前老页面的地址,当我们在新页面点击回退键的时候还可以回到之前的页面.而 replace 则是使用新页面的地址替换老页面的地址,此时再点击回退按钮就回不到我们之前的页面去了.大家可以新开一个空白页面,然后分别将下面的代码放到控制台去执行一下,再之后点击浏览器左上角的回退按钮,观察效果.

// 新开两个空白页,各执行其中一句代码
window.location.assign('https://www.baidu.com')
window.location.replace('https://www.baidu.com')

MDN中提到,Location 其实是一个接口,表示链接到的对象的URL.DocumentWindow 接口都有这样一个链接的Location.所以我们可以通过document.locationwindow.location 来访问 Location 对象.甚至于location 都是一个全局变量.我们直接在控制台键入 location 也会返回一个 Location 对象

# meta属性实现刷新和跳转

其实当我们想要实现页面的跳转或者每隔一段时间刷新当前页面,不一定非得使用定时器+location方法.我们还可以使用meta属性来达到相同的效果.假如我这是一个监控页面,想要每隔20秒刷新一次页面.使用定时器的方法代码如下:

setTimeout(() => {
    window.location.reload()
},20000)

而使用meta标签则只需要

<meta http-equiv="refresh" content="20"/>

又假如这是一个简单的介绍页,在10s后差不多就要自动跳到其他页面.我们也可以使用上面的meta属性.只需要在content里面时间的后面加上跳转的地址就可以了,等时间到了它就会自动跳转.

<meta http-equiv="refresh" content="10;https://www.baidu.com"/>

当然使用meta有一个不好的地方就是它的刷新和跳转是不可中途取消的.因此它的使用场景要视具体的业务而定.比如一个需要权限的页面被非法访问,那么就可以控制一定时间后自动跳到其他页面去.

# 获取url中的查询字符串

很多时候,我们有这样的需求,就是获取url查询字符串中某个key对应的value.比如在一个如 http://127.0.0.1:5501/html/index.html?id=123&name=zhangsan 的页面中获取id对应的值,也就是123.那么我们也有不少方法可以来做这个事情.

# 传统方法

function getUrlQuery(){
  let url = window.location.search
  let obj = Object.create(null)
  if(url.includes('?')){
    let str = url.substr(1)
    let strs = str.split('&')
    for(let i = 0;i < strs.length;i++){
        let tmp = strs[i].split('=')
        obj[decodeURIComponent(tmp[0])] = decodeURIComponent(tmp[1])
    }
  }
  return obj
}

注意了,上面的代码中使用了 decodeURIComponent 来解码,因为查询字符串一般都是被 encodeURIComponent 编码过的,

# URLSearchParams接口

const urlParams = new URLSearchParams(window.location.search)
console.log(urlParams.get('id'))  // 123

URLSearchParams 接口定义了一些实用的方法来处理URL中的查询字符串.这里是它的具体介绍,有兴趣的同学可以自己去看看,我们就先不展开了.

# 总结

其实对于 Location 对象,它的知识相对而言并不是很多.在红宝书中也只有短短的3页内容.作为BOM中的一部分,它常常也会和 Navigator, History对象一起提到,这部分内容我们后面有时间再说好了.