JavaScript之BOM(一):window两大事件、四大对象与执行机制

BOM 浏览器对象模型与window常见事件对象
白龙网看来,浏览器对象模型,他是与浏览器窗口进行交互的对象,由相关的对象组成,并且每个对象提供了很多方法、属性。JS标准是由ECMA提供,DOM标准由W3C提供,BOM标签是由netscap提供,缺乏标准。
BOM比DOM大,它的最高对象是window,包含:document、location、navigation、screen、history.
window对象是浏览器的顶级对象,它具有双重角色:
A.它是JS访问浏览器的一个窗口;
B.它是一个全局对象,定义在全局作用域上的变量、函数都会变成window对象的属性和方法。
另外,widnow下的一个特殊属性:window.name

一、window常见事件

1、加载页面事件
传统的注册事件的方式,只能放在元素的后面才能生效;而使用window.load、DOMContentLoaded则可以放在任何地方让JS成功执行。
<body>
  <script>
    //load页面加载:等页面全部内容加载完毕,再执行JS
    window.addEventListener('load',function() {
      var btn = document.querySelector('button');
      btn.addEventListener('click',function(){
      alert('方法1:传统方式注册事件-只能放在元素后面');
     })
    })
    // 或者
    window.onload = function() {
      var btn = document.querySelector('button');
      btn.addEventListener('click',function(){
      alert('方法2:传统方式注册事件-只能放在元素后面');
     })
    }
    //2.DOMContentLoaded,等DOM加载完毕就执行JS,不用等CSS/FLASH等等加载完,执行速度快些
    window.addEventListener('DOMContentLoaded',function() {
      var btn = document.querySelector('button');
      btn.addEventListener('click',function() {
        alert('DOM加载完毕,执行JS');
      })
    })
  </script>
<button>按钮</button>
</body>
2、窗口大小事件
<body>
  <style>
    div {
      width: 200px;
      height: 200px;
      background-color: red;
    }
  </style>
  <script>
    //1.方式1:传统方式onresize改变窗口大小,就化触发事件
  //  window.onresize = function() {
  //   console.log('变化了');
  //  }
   //2.方式2:触发窗口大小事件resize
  window.addEventListener('load',function() {//在任意位置加载页面
    var div = document.querySelector('div');
  window.addEventListener('resize',function() {
    console.log(window.innerWidth);
    if (window.innerWidth <= 800) {//获取窗口宽度
      console.log('大小');
      div.style.display = 'none';
    } else {
      div.style.display = 'block';
    }
  })
  })
  </script>
<div></div>
</body>

二、window常用对象

1、定时器对象
1)setTimeout()
  ①基本语法
  <script>
    //1.setTimeout是window的一个对象,因此可以活力掉window
   setTimeout(function() {
    console.log('爆炸了');
   }, 3000);
   //2.函数可以写在setTimeout对象之内,也可以写在setTimeout对象之外(用函数名调用即可)
   function callback() {
    console.log('5秒之后就显示出来了');
   }
   //3.setTimeout的第二个参数是倒计时,单位是毫秒,默认省略时,为0,即没有倒计时
   setTimeout(callback, 5000);
   //4.用字符串的形式调用:'函数名()',不提倡这种方式
   setTimeout('callback()', 6000);
   //5.给不同的定时器起不同的标识符(名子)
   var time1 = setTimeout('callback()', 6000);
  </script>
②回调函数--广告在5秒之后消失(隐藏)
setTimeout(fn,time)与之前学过的element.onclick = function() {}、element.addEventListen('click',fn() {})中,提到的fn,都是回调函数,即:等某些事干完之后,再执行这个函数。
<body>
  <img src="./close.png" alt="">
  <script>
   var img = document.querySelector('img');
   setTimeout(function() {
    img.style.display = 'none';
   }, 5000);
  </script>
</body>
③停止定时器
关闭定时器需要两步,一是给定时器标记一个ID,二是使用clearTimeout(定时器ID )。
<body>
  <button>关闭定时器</button>
  <img src="./close.png" alt="">
  <script>
  var btn = document.querySelector('button');
  //1.给定时器标记一个ID
   var img = document.querySelector('img');
   var timer = setTimeout(function() {
    img.style.display = 'none';
   }, 5000);
  btn.addEventListener('click',function() {
    //2.通过一个点击事件,关闭定时器
    clearTimeout(timer);
  });
  </script>
</body>
2)setInterval() 
①重复调用回调函数
  <script>
    //1.每间隔多长时间调用一次回调函数,即:重复调用回调函数;setTimeout只调用一次就结束
  setInterval(function() {
    console.log('123');
  }, 2000);
  </script>
②案例1:倒计时
<body>
  <style>
    span {
      display: inline-block;
      width: 20px;
      height: 20px;
      background-color: #999;
      text-align: center;
      line-height: 20px;
    }
  </style>
  <span class="hour"></span>
  <span class="minite"></span>
  <span class="second"></span>
  <button class="begin">开始</button>
  <button class="stop">停止</button>
  <script>
    //1.获取元素
      var hour = document.querySelector('.hour');
      var minite = document.querySelector('.minite');
      var second = document.querySelector('.second');
      var inputTime = +new Date('2022-7-23 17:38:21');//计算停止时间
    //2.计算时、分、秒,把计算得到的时、分、秒分别赋值给三个盒子
    function getCountDown() {
            var nowTime = +new Date();//计算当前时间
            
            var times = (inputTime - nowTime) / 1000;//转换为秒
            var h = parseInt(times / 60 / 60 % 24);//时
            h = h < 10 ? '0' + h : h;
            hour.innerHTML = h;
            var m = parseInt(times / 60 % 60);//分
            m = m < 10 ? '0' + m : m;
            minite.innerHTML = m;
            var s = parseInt(times % 60);//秒
            s = s < 10 ? '0' + s : s;
            second.innerHTML = s;
        }
    //3.每间隔1秒调用一次回调函数,实现倒计时功能
      getCountDown();//调用时分秒函数的目的是在刷新时不显示空白
      //4-1获取开始、停止按钮
      var begin = document.querySelector('.begin');
      var stop = document.querySelector('.stop');
      //4-2定义一个定时器的全局变量,用来关闭定时器
      var timer = null;
      begin.addEventListener('click',function() {
      //4-3点击开始,定时器开始工作
        timer = setInterval(getCountDown,1000);
      })
      stop.addEventListener('click',function() {
      //4-4点击结束,定时器停止工作:clearInnerval('定时器标识符')
        clearInterval(timer);
      })
    //4.
  </script>
</body>
③案例2:发送短信
<body>
  <input type="number" name="" id="">
  <button>发送</button>
  <script>
    //1.获取元素
    var btn = document.querySelector('button');
    var time = 5;
    var timer = null;
    //2.添加点击事件
    btn.addEventListener('click',function() {
      btn.disabled = true;
      timer = setInterval(function() {
        if (time == 0){
          //3.如果定时器为0时,要停止定时器、恢复按钮状态/文字显示,同时要恢复倒计时的时间
          clearInterval(timer);
          btn.disabled = false;
          btn.innerHTML = '发送';
          time = 5;
        } else {
          //4.如果定时器不为0,就把按钮文字修改成倒计时内容,同时让变量执行--操作
          btn.innerHTML = '剩下等待时间' + time + '秒';
          time--;
        }
      }, 1000);
    })
  </script>
</body>
2、location对象
(1)获取设置窗口URL,并解析URL。location.href
<body>
  <button>按钮</button>
  <script>
    var btn = document.querySelector('button');
    btn.addEventListener('click',function() {
      //1.获取当前窗口的参数,直接写location.href
      console.log(location.href);
      //2.给location.href赋值,就可以跳转至新的页面
      location.href = 'https://www.bailong.org.cn';
    })
  </script>
</body>
案例1:5秒之后自动跳转至新的页面
<body>
  <div></div>
  <script>
    var div = document.querySelector('div');
    var time = 5;
    setInterval(function() {
      if (time == 0) {
        location.href = 'https://www.bailong.org.cn';
      } else {
        div.innerHTML = '你将在' + time + '秒之内进入新的网站';
        time--;
      }
    }, 1000);
  </script>
</body>
(2)获取URL参数,在不同页面传递参数 location.search
<body>
  //1.action用来放置跳转的目标页面
  <form action="plus.html">
    //2.name必须写名称,用来接收传递的参数
    <input type="text" name="uname">
    <input type="submit" value="提交">
  </form>
</body>
 
plus.html页面的布局与JS:
<body>
  <div></div>
  <script>
      //1.获取参数
      console.log(location.search);
      //2.截取目标参数
      var params = location.search.substr(1);
      console.log(params);
      //3.把字符串转换为数组
      var arr = params.split('=');
      console.log(arr);
      //4.从数据中提取目标参数,并赋值给div标签
      var div = document.querySelector('div');
      div.innerHTML = arr[1] + '欢迎你';
  </script>
</body>
 
(3)location其它属性
  <script>
    console.log(location.href);
    console.log(location.protocol);
    console.log(location.hostname);
    console.log(location.port);
    console.log(location.pathname);
    console.log(location.hash);
    console.log(location.search);
  </script>
(4)location常用方法
<script>
  setTimeout(function() {
    //1.重定向,可以回退,类似于.href
    location.assign('http://www.bailong.org.cn');
    //2.替换页,实现跳转,不能回退
    location.replace('http://www.bailong.org.cn');
    //3.加载页面,没有参数类似F5刷新,参数为true,类似强制刷新
    location.reload(true);
  }, 1000);
</script>
3、navigator对象
<script>
  //打印浏览器相关信息
  console.log(navigator.userAgent);
</script>
4、history对象
与浏览器历史记录交互的对象,它包含用户访问过的URL。
<body>
<a href="https://www.bailong.org.cn">白龙网</a>
<button>前进</button>
<script>
  var btn = document.querySelector('button');
  btn.addEventListener('click',function() {
    //1.前进一步
    history.forward();
    //2.后退一步
    history.back();
    //3.前进与后退一步
    history.go(1);
    history.go(-1)
  })
</script>
</body>

三、JS运行机制

(1)this指代问题
this,一般情况下,指向的是调用它的那个对象。例如,在全局变量、普通函数、定时器、对象中的方法中this指代的window;而在事件中this指代的是调用他的元素;在构造函数中this指代的是调用它的构造函数。
<body>
  <button>按钮</button>
  <script>
    //1.全局变量下this指代widow
   console.log(this);
   //2.普通函数下this,函数要调用才能生效指代widow
   function fn() {
    console.log(this);
   }
   fn();
   //3.定时器函数中的this指代widow
   setTimeout(function()  {
    console.log(this);
   }, 1000);
   //4.对象中方法的this指代widow
   var bailong = {
    sayHai: function() {
      console.log(this);
    }
   }
   bailong.sayHai();
   //5.事件中上的方法this指代btn
   var btn = document.querySelector('button');
   btn.onclick = function() {
    console.log(this);
   }
   btn.addEventListener('click',function() {
    console.log(this);
   })
   //6.构造函数中的this指代构造函数Fn,函数与构造函数都是先声明,再调用才能生效
   function Fn() {
    console.log(this);
   }
   var bailong = new Fn();
  </script>
</body>
(2)JS执行机制
①JS是单线程
即同一时间就做一件事。这意味着,只有前面一件事做完,才能做后面的事。如果前面执行时间过长,就会导致渲染页面加载时受到阻塞。
为了解决这个问题,可以使用同步与异步的做法。同步,就是前一个事儿做完之后,再做另外一件事。异常,在前一件事结束前,同时做另外的事,只不过另外的事与前一件事同时结束。
②常规执行过程
同步任务都在主线程上执行,形成一个执行栈。JS异步放在任务队列之中,是通过回调函数实现的,主要包括3种:普通事件(click/resize)、资源加载(load/error)、定时器(setTimeout/setInnerval)。
首先,执行执行栈中的同步任务;
其次,把异步任务放到任务队列中;
最后,同步任务执行完毕,到任务队列中找没有执行的异常任务,把它放入执行栈中执行。
③有异步任务的执行过程
如果有事件、定时器、页面加载等异步任务,那么执行过程如下:
首先执行同步栈中的任务;
然后发现异步任务,提交给异步处理进程,如果触发了事件,就把事件放入异步栈;如果定时器时间到了,就把定时器放放任务队列……
最后,同步任务执行结束后,到任务队列中检查,有任务就把任务放到执行栈去执行,执行栈与任务队列循环的过程,叫事件循环。