# BOM

# 定时器

# setTimeout()

🏃‍♀️

用来指定某个函数或字符串在指定的毫秒数之后执行。它返回一个整数,表示定时器的编号,这个值可以传递给 clearTimeout()用于取消这个函数的执行

// 控制台先输出0,大概过1000ms即1s后,输出定时器setTimeout()方法的返回值1
var Timer = setTimeout(function() {
  console.log(Timer);
}, 1000);
console.log(0);
// 0
// 1

🤹

也可以写成字符串参数的形式,由于这种形式会造成 javascript 引擎两次解析,降低性能,故不建议使用

  • setTime默认最低延迟
//  chrome为例 最低延迟为1ms ,如果设置延迟时间为2的31次方-1 ,等同于设置为1ms
//设置最大值
setTimeout(() => {
    console.log("a");
}, 2 ** 31);

//设置最小值
setTimeout(() => {
    console.log("b");
}, 1);

setTimeout(() => {
    console.log("c");
}, 0.5);

//设置0 
setTimeout(() => {
    console.log("d");
}, 0);

// 输出 a b c d
  • setTIme嵌套五层,最小延迟为4ms
var Timer = setTimeout("console.log(Timer);", 1000);
console.log(0);

如果省略 setTimeout 的第二个参数,则该参数默认为 0

var Timer = setTimeout(function() {
  console.log(1);
});
console.log(0); // 0 1

TIP

  • 实际上,除了前两个参数,setTimeout()方法还允许添加更多的参数,它们将被传入定时器中的函数中
  • 而 IE9-浏览器只允许 setTimeout 有两个参数,不支持更多的参数
  • 以下代码中,控制台大概过 1000ms 即 1s 后,输出 2,IE9-会在控制台输出 NaN
setTimeout(
  function(a, b) {
    console.log(a + b);
  },
  1000,
  1,
  1
);

兼容 IE9-浏览器

🐠

  • 1、可以使用 IIFE 传参
  • 2、将函数写在定时器外面,然后函数在定时器中的匿名函数中带参数调用
// 1
setTimeout(
  (function(a, b) {
    return function() {
      console.log(a + b);
    };
  })(1, 1),
  1000
);

// 2
function sum(a, b) {
  console.log(a + b);
}
setTimeout(function() {
  sum(1, 1);
}, 1000);

🥗

[注意]IE8-浏览器不允许向定时器中传递事件对象 event,如果要使用事件对象中的某些属性,可以将其保存在变量中传递进去

div.onclick = function(e) {
  e = e || event;
  var type = e.type;
  setTimeout(function() {
    console.log(type); //click
    console.log(e.type); //报错
  });
};

# 定时器中 this 指向

定时器中的 this 存在隐式丢失的情况

var a = 0;
function foo() {
  console.log(this.a);
}
var obj = {
  a: 2,
  foo: foo,
};
obj.foo(); // 2
setTimeout(obj.foo, 100); // 0

// 等价于以下代码
var a = 0;
setTimeout(function foo() {
  console.log(this.a);
}, 100); //0

若想获得 obj 对象中的 a 属性值

  • 可以将 obj.foo 函数放置在定时器中的匿名函数中进行隐式绑定
  • 使用 bind 方法将 foo()方法的 this 绑定到 obj 上
setTimeout(function() {
  obj.foo();
}, 100); //2

setTimeout(obj.foo.bind(obj), 100); //2

# clearTimeout()

setTimeout 函数返回一个表示计数器编号的整数值,将该整数传入 clearTimeout 函数,取消对应的定时器

//过100ms后,控制台输出setTimeout()方法的返回值1
var Timer = setTimeout(function() {
  console.log(Timer);
}, 100);

clearTimeout(Timer);
// 或者直接使用返回值作为参数
clearTimeout(1);

🍱

一般来说,setTimeout 返回的整数值是连续的,也就是说,第二个 setTimeout 方法返回的整数值比第一个的整数值大 1

# setInterval()

setInterval 的用法与 setTimeout 完全一致,区别仅仅在于 setInterval 指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行

🍲

[注意]HTML5 标准规定,setTimeout 的最短时间间隔是 4 毫秒;setInterval 的最短间隔时间是 10 毫秒,也就是说,小于 10 毫秒的时间间隔会被调整到 10 毫秒

实际上,把 setTimeout 的第二个参数设置为 0s,并不是立即执行函数的意思,只是把函数放入异步队列。浏览器先执行完同步队列里的任务,才会去执行异步队列中的任务

🍽️

  • 使用 setInterval()的问题在于,定时器代码可能在代码再次被添加到队列之前还没有完成执行,结果导致定时器代码连续运行好几次,而之间没有任何停顿。而 javascript 引擎对这个问题的解决是:当使用 setInterval()时,仅当没有该定时器的任何其他代码实例时,才将定时器代码添加到队列中。这确保了定时器代码加入到队列中的最小时间间隔为指定间隔

  • 但是,这样会导致两个问题:1、某些间隔被跳过;2、多个定时器的代码执行之间的间隔可能比预期的小

  • 假设,某个 onclick 事件处理程序使用 setInterval()设置了 200ms 间隔的定时器。如果事件处理程序花了 300ms 多一点时间完成,同时定时器代码也花了差不多的时间,就会同时出现跳过某间隔的情况

# 迭代 setTimeout

为了避免 setInterval()定时器的问题,可以使用链式 setTimeout()调用

setTimeout(function fn() {
  setTimeout(fn, interval);
}, interval);

// 这个模式链式调用了setTimeout(),每次函数执行的时候都会创建一个新的定时器。第二个setTimeout()调用当前执行的函数,并为其设置另外一个定时器。这样做的好处是,在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔。而且,它可以保证在下一次定时器代码执行之前,至少要等待指定的间隔,避免了连续的运行

# requestAnimationFrame()

# 定时器应用(时钟、倒计时、秒表和闹钟)

# window 属性

# 对话框

# alert()

alert()方法接受一个字符串,并将其显示给用户并等待用户关闭对话框

🏦

[注意]该方法包含默认的 String()隐式类型转换,非字符串类型会被转换为字符串

alert([1, 2, 3]); //'1,2,3'

\n 换行

alert("hello \n world");

# confirm()

confirm()方法同样接收一个字符串,并将其显示给用户。返回的布尔值若是 true 表示单击 OK,false 表示单击 Cancel 或者右上角的关闭按钮

let isSun = confirm("确定不带雨伞嘛?");
if (isSun) {
  console.log("不用带伞");
} else {
  console.log("注意带上雨伞哦");
}

# prompt()

🚀

prompt()方法接收两个参数,要显示给用户的文本提示和文本输入域的默认值(可以是一个空字符串)。如果用户单击了 OK 按钮,则返回文本输入域的值;如果用户单击了 Cancel 或者右上角的关闭按钮,则该方法返回 null

🎃

[注意]prompt()方法的第二个参数是可选的,如果不提供的话,IE 浏览器会在输入框中显示 undefined。因此,最好总是提供第二个参数,作为输入框的默认值

var result = prompt("能告诉你叫什么吗?", "小H");
if (result != null) {
  alert("欢迎你," + result);
} else {
  alert("");
}

# print()

window.print()方法可以用来显示打印对话框

window.print();

# 窗口操作

# location 对象

TIP

location 提供了与当前窗口中加载的文档有关的信息,还提供了一些导航功能。location 是一个很特别的对象,因为它既是 window 对象的属性,也是 document 对象的属性,而且还可以单独使用

document.location === window.location; // true

# 属性

属性 例子 说明
hash #location 返回 URL 中的 hash(#号后跟 0 或者多个字符串),如果 URL 中不包含散列,则返回空字符串
host 127.0.0.1:8080 返回服务器名称和端口号(如果有端口号)
hostname 127.0.0.1 服务器名称
href https://127.0.0.1/CSS/ 返回当前页面的完整 URL
pathname /CSS/ 返回 URL 中的目录和(或)文件名
port 8080 返回指定的端口号(无则返回空字符串)
protocol http 返回使用的协议,http:或 https:
search ?q=javascript 返回 URL 的查询字符串。以问号开头
function urlToObj(href) {
  let name, value;
  let str = decodeURIComponent(href);
  let num = str.indexOf("?");
  str = str.substr(num + 1); //取得所有参数
  let arr = str.split("&"); //各个参数放到数组里
  let json = {};
  for (let i = 0; i < arr.length; i++) {
    num = arr[i].indexOf("=");
    if (num > 0) {
      name = arr[i].substring(0, num);
      value = arr[i].substr(num + 1);
      json[name] = value;
    }
  }
  return json;
}

# 方法

使用 location 对象可以通过很多方式来改变浏览器的位置

  • 1、assign()

使用 assign()方法并为其传递一个 URL,可以立即打开新 URL 并在浏览器的历史记录中生成一条记录。如果是将 location.href 或 window.location 设置为一个 URL 值,相当于调用 assign()方法

location.assign("http://baidu.com");
window.location = "http://baidu.com";
document.location = "http://baidu.com";
location.href = "http://baidu.com"; //最常用

👊

每当修改 location 的属性(hash 除外),页面都会以新 URL 重新加载

//修改hash值
location.hash = "#test";
//修改查询字符串
location.search = "?q=es6";
//修改主机名
location.hostname = "www.baidu.com";
//修改路径
location.pathname = "mydir";
//修改端口号
location.port = "8080";
  • 2、replace() 替换记录栈里面的栈顶 url,此时栈顶的容量不变
location.replace("http://baidu.com");
  • 3、reload() reload()方法用于重新加载当前显示的页面。如果调用 reload()方法时不传递任何参数,页面会以最有效的方式重新加载。也就是说,如果页面自上次请求以来并没有改变过,页面就会从浏览器缓存中重新加载。如果要强制从服务器重新加载,则需要传递参数 true。位于 reload()调用之后的代码可能会也可能不会执行,这要取决于网络延迟或系统资源等因素,为此,最好将 reload()放在代码的最后一行
//有可能从缓存中加载
location.reload();
//从服务器重新加载
location.reload(true);

DANGER

[注意]千万不要在页面中直接使用 location.reload()方法,此方法会造成页面的无限刷新

# 事件

HTML5 新增了 hashchange 事件,以便在 URL 的参数列表(及 URL 中“#”号后面的所有字符串)发生变化时通知开发人员。

[注意]IE7-浏览器不支持 haschange 事件

window.onhashchange = function(e) {
  e = e || event;
  console.log(e.oldURL, e.newURL);
};

# history 对象

History 接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。

# 方法

  • 1、go() 使用 go()方法可以在用户的历史记录中任意跳转。这个方法接收一个参数,表示向后或向前跳转的页面数的一个整数值。负数表示向后跳转(类似于后退按钮),正数表示向前跳转(类似于前进按钮)
//后退一页
history.go(-1);
//前进一页
history.go(1);
//前进两页
history.go(2);
// 刷新当前页
history.go();
  • 2、back() back()方法用于模仿浏览器的后退按钮,相当于 history.go(-1)
history.back(); // 后退一页
  • 3、forward() forward()方法用于模仿浏览器的前进按钮,相当于 history.go(1)
history.forward(); // 前进一页

TIP

如果移动的位置超出了访问历史的边界,以上三个方法并不报错,而是静默失败

[注意]使用历史记录时,页面通常从浏览器缓存之中加载,而不是重新要求服务器发送新的网页.

  • 4、pushState() H5 新增 history.pushState()方法向浏览器历史添加了一个状态。pushState()方法带有三个参数:一个状态对象、一个标题(现在被忽略了)以及一个可选的 URL 地址
// [注意]IE9-浏览器不支持
history.pushState(state, title, url);
// state object —— 状态对象是一个由pushState()方法创建的、与历史纪录相关的javascript对象。当用户定向到一个新的状态时,会触发popstate事件。事件的state属性包含了历史纪录的state对象。如果不需要这个对象,此处可以填null
// title —— 新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null
// URL —— 这个参数提供了新历史纪录的地址。新URL必须和当前URL在同一个域,否则,pushState()将丢出异常。这个参数可选,如果它没有被特别标注,会被设置为文档的当前URL

总结

  • pushState 方法不会触发页面刷新,只是导致 history 对象发生变化,地址栏的显示地址发生变化

  • 如果 pushState 的 url 参数,设置了一个新的锚点值(即 hash),并不会触发 hashchange 事件,即使新的 URL 和旧的只在 hash 上有区别

  • 如果设置了一个跨域网址,则会报错。这样设计的目的是,防止恶意代码让用户以为他们是在另一个网站上

  • 5、replaceState() H5 新增 history.replaceState 方法的参数与 pushState 方法一模一样,不同之处在于 replaceState()方法会修改当前历史记录条目而并非创建新的条目

// 当前 example.com/example.html
history.pushState({ page: 1 }, "title 1", "?page=1");
history.pushState({ page: 2 }, "title 2", "?page=2");
history.replaceState({ page: 3 }, "title 3", "?page=3");

history.back();
// url显示为http://example.com/example.html?page=1

history.back();
// url显示为http://example.com/example.html

history.go(2);
// url显示为http://example.com/example.html?page=3

state history.state 属性返回当前页面的 state 对象

history.pushState({ page: 1 }, "title 1", "?page=1");
history.state; // { page: 1 }

# screen 对象

# 能力检测

Last Updated: 10/29/2022, 11:39:49 PM