Js高级程序设计第三版学习(八章,九章)
第八章 BOM
如果要在web中使用js,那么bom(浏览器对象)就是核心,BOM提供了很多对象用与访问浏览器功能
1.window对象:
- 全局作用域
window对象再es标准中也代表着global对象,所以在全局作用域中声明的变量,方法,都会成为window对象的变量方法,在全局作用域定义的变量不能delete删除,而window对象的属性可以,这是因为用var 声明的变量 他的描述符中[[Configurable]]的默认值为false
var variables = 'TT';
console.log(window.variables);//TT
delete variables; //证明此行代码无效
delete window.variables;//证明此行代码无效
console.log(variables) //TT
//{value: "TT", writable: true, enumerable: true, configurable: false}
console.log(Object.getOwnPropertyDescriptor(window,'variables'));
window.ppap = 'TT TT'
//{value: "TT TT", writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(window,'ppap'));
delete window.ppap;
console.log(window.ppap) //undefined
- 窗口及框架关系
如果页面包含框架,那么每个框架都拥有自己的window对象,并且保存在frame集合中, 我们可以通过索引或者框架名来访问,每个window对象都有一个name,包含框架名. PS(由于h5已经废除 frame frameset noframes 所以本节知识点 仅仅了解大概 并未详细了解)
<iframe src="https://www.baidu.com" frameborder="0" name="frames1"> </iframe>
<iframe src="https://www.baidu.com" frameborder="0" name="frames2"></iframe>
<script>
console.log(window.frames[0].name) //frames1
console.log(window.frames[1].name) //frames2
</script>
top对象指向的是最外层的框架,既浏览器对象,他可以确保一个框架正确使用另一个框架,parent对象指向的是当前框架的直接上层对象,在没有框架的情况下parent = top
<iframe src="https://www.baidu.com" frameborder="0" name="frames1">
</iframe>
<iframe src="https://www.baidu.com" frameborder="0" name="frames2"></iframe>
<script>
console.log(top === window); //true
console.log(top.frames[0].name)//frames1
console.log(top.frames[1].name)//frames2
console.log(parent === window);//true
console.log(top.frames[0].parent === window) //true
</script>
self也指向window, 有时候可以与window互换, 在存在多个框架时, 每个框架都有各自的构造函数, 所以 top.obj !== top.frames[0].obj
console.log(self === window); // true
- 窗口位置
主流浏览器都提供 4个属性 用来表示窗口距离屏幕的位置 screenTop,screenLeft,screenX,screenY, (电脑只有 火狐,谷歌和ie11) 但是这4个属性在不同浏览器返回的值并不相同(火狐64以上已支持screenTop), 其中谷歌的两对属性返回值相同,但他是相对于窗口的坐标 在全屏下返回 0 0 , 而火狐两队也相同, 但是全屏返回-8,而ie screenTop 返回的是导航栏 下方距离屏幕的距离, left 等于0 而screenX 和 Y 全屏下都是-8 , 综上所以由于浏览器不同对于这两队属性的返回值也不相同, 相对统一的就是screenX和ScreenY这两个属性
- 窗口大小
可视窗口大小 innerWidth innerHeight 浏览器窗口大小 outerHeight Width, 书中讲到了对于不同浏览器可能存在的不同差异,但在2019年1月 拿火狐 谷歌 ie11 测试的时候 除了浏览器自身的些许差异 inner 就是代表可视窗口,而outer代表浏览器窗口大小,这三个浏览器都相同
对于视口 我们也可以用 clientWidth 和 clientHeigh 来获取, 我们可以通过 document.body 和document.documentElement来调用, 其中document.body代表着 body节点 document.documentElement代表着html节点, 这两个节点调用这两个属性是有区别的, body节点调用会获取 body的宽和body的实例的高(内容高度)(不包含滚动条宽度), 而用documentElement 会获得可视区域的宽高 (包含滚动条宽度),(都不包含工具栏,地址栏)
- 导航和打开窗口
window.open(), 用来打开一个地址, 类似与A标签的click效果, 第一个参数: url 第二个参数 可以是框架或者字符串, 字符串可以用特殊字如 '_self' 代表原窗口打开, 第三个参数, 如果在新窗口打开 那么可以调整打开窗口的一些显示属性 如 宽高等, 另外在目前我测试的浏览器中除了ie 在页面加载或者onload 的时候 调用window.open() 都会被默认拦截, 但是写在某个dom元素点击事件不会拦截(与浏览器设置有关) 而 ie默认会直接打开
//直接使用window.open() 会被浏览器拦截
window.onload = function() {
window.open('file:///D:/books/study/one/study2.html');
};
//把window.open()放在点击事件中 就可以调用了
// 第一个参数 打开的地址 第二个参数 打开的框架 也可以本身打开 '_self' 第三个参数设置打开窗口的大小等属性
var iframe1 = top.frames['test'];
var study2 = null;
document.getElementById('asd').onclick = function() {
study2 = window.open('file:///D:/books/study/one/study2.html','','width=400;height=500;');
};
window.open(); 会返回被打开的窗口对象(ie会返回null), 这个对象的opener属性 代表着 调用window.open()的这个窗口对象(parent) , 如果这个属性 设置为null 那么就切断了 两个窗口的通信. 如果在发生通信时 会报错
document.getElementById('dsa').onclick = function() {
console.log(study2)
if (study2 !== null) {
console.log(study2.opener === top); // true
/*
Blocked a frame with origin "null" from accessing a cross-origin frame.
at HTMLLIElement.document.getElementById.onclick
*/
study2.opener = null // 如果设置为null 那么就会切断框架间的通信
study2.close(); //会关闭弹出窗口
}
};
-
间歇调用和超时调用
超时调用 setTimeout() 间歇调用 setInterval() ,两者参数相同 第一个参数: 方法(匿名,箭头)或字符串(不推荐性能损耗严重). 第二个参数 : 时间,毫秒为单位, 在传入方法时, 如果使用函数名的形式, 那么不能传入参数, 不过可以使用立即执行函数来迂回,setTimeout或setInterval 都会返回一个数值id, 我们可以使用clearTimeout 或 clearInterval 来清除, 就不会执行了, 减少了性能损耗,也可以在某一时间点结束调用.
var tt = setTimeout(() => {
console.log('timeout');
}, 1000); //timeout
var ti = setInterval(() => {
console.log('inteval');
}, 1000); //inteval
setTimeout(tttttt, 5000);
setTimeout(function() {
tttttt('asd');
}, 5000);
function tttttt(obj) {
console.log(obj);
}
setTimeout(function() {
clearInterval(ti);
}, 5000);
js是单线程模式,也就是说在当前代码未执行完之前不会执行其他的代码, 而setT 或setI 都是延迟执行的,它们只会在当前执行队列为空时,才会执行,那怕第二个参数传入了0ms 其实也会有4ms延迟, 所以这两个代码总是会在当前执行顺序的最后才会执,行,这也就造成了可能setT或许并不会在规定的延迟时间后执行(执行队列可能不为空), 也有可能造成在setInterval间歇执行时,每次执行的间隙并不一定相等,所以书中也不推荐使用间歇调用, 而是用超时调用来模拟间歇调用,(之前在写转盘抽奖时,也使用了setinterval,出现了转盘抽奖指针位置不对的情况, 当时使用了 promise和 return 来解决误差)
var timeout = setTimeout(function(){
console.log('timeout1');
},0)
for (let index = 0; index < 15000; index++) {
console.log('delay1')
}
var timeout = setTimeout(function(){
console.log('timeout2');
},1000)
for (let index = 0; index < 15000; index++) {
console.log('delay2')
}
for (let index = 0; index < 15000; index++) {
console.log('delay3')
}
var timeout = setTimeout(function(){
console.log('timeout3');
},3000)
for (let index = 0; index < 15000; index++) {
console.log('delay4')
}
- 系统对话框
alert() confrim(), 都接收字符串, 在对话框执行时 页面停止(之后的代码不会执行), confrim()点击确定会返回true 点击cancel会返回false , window.print() 会打印页面, 弹出浏览器打印页
if (confirm('nihao')) {
console.log('ok');
} else {
console.log('cancel');
}
window.print();
2.location对象:
location对象提供了窗口中加载的文档有关信息,还提供了导航功能, window.location 与 document.location 引用的是同一个对象, location对象提供了 路径 host 端口号 ...等等信息,(需要什么自己查)
其中loaction.href location = location.assign()方法都会是页面跳转, 其实每次修改location的对象属性都会造成页面重新加载(hash属性除外), 并且会产生新的历史纪录(用户可以使用回退按钮, 返回上一次的页面), 如果不想有新的历史纪录 可以使用 replace方法,他会导致浏览器位置改变但并不会产生新的历史纪录
/* 三者实际效果相同
window.location.href = 'https://www.baidu.com';
window.location ='https://www.baidu.com';
*/
window.location.replace('https://www.baidu.com');
//不产生新的历史纪录
window.location.replace('https://www.baidu.com')
location.reload(), 页面重新加载, 如果不传参数, 会采用最有效率的加载,页面未曾改变过,会从内存直接加载, 如果参数为true,则会重新向服务器请求页面
3.navigator对象:
navigator用来识别客户端浏览器标准,不同浏览器的属性都会不同, 像appName 基本所有浏览器都返回Netscape , userAgent属性对于各个浏览器返回的信息比较全,
console.log(window.chrome);
console.log(navigator.appName);
console.log(navigator.appVersion);
console.log(navigator.userAgent)
4.history对象:
history对象保存着用户浏览的纪录,虽然我们无法获取纪录url 但是可以用根据history提供的方法 来实现页面的后退或前进,go() -1代表后退 -2 后退两次 1代表前进1次 字符串: 历史纪录中包含这个字符串 就跳转 不包含就不动 back() 后退 forward() 前进, history记录历史纪录的个数
if (history.length) {
history.go(-1)
}
console.log(history.length)
第九章 客户端检测
由于不同浏览器,提供的公共方法不同,所以我们需要设计最通用的方案,在使用特定浏览器技术增强该方案
1.能力检测:
能力检测不是为了确定浏览器,而是识别浏览器的能力,例如有时候我需要确定在当前浏览器中是否可以使用该方法,然后再执行
if (document.getElementById) {
var pp = document.getElementById('asd');
console.log(pp);
}
但有时我们并不单单需要确认是否存在改属性或方法 而是需要判断 是不是符合我们的预期,如 下面的代码,我重写了toString 如果toString 不是fuction类型就不执行
var ppap = {
toString:'change'
}
if (typeof ppap.toString === 'function') {
console.log(ppap.toString())
}
var hehe = {
pp:'a'
}
if (typeof hehe.toString === 'function') {
console.log(hehe.toString());//[object Object]
}
2.用户代理检测:
客户端代理检测,是万不得已的用法,应该最后使用,这是由于浏览器厂商根据版本不同,都会留有一些错误的或者欺骗性的信息,来达到欺骗服务器的目的, 例如下方代码中 ie11 chrome firefox 都是一样的字段
console.log(navigator.appCodeName); //Mozilla
console.log(navigator.appName); //Netscape
大多数情况下我们需要识别浏览器引擎,或者识别浏览器, 再navigator.userAgent 字段中来自己判断是什么
由此可见每个浏览器都有不同之处, 所以建议我们优先使用能力检测或怪异检测