整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          JavaScript入門簡(jiǎn)介

          JavaScript入門簡(jiǎn)介

          么是 JavaScript

          JavaScript,我們一般簡(jiǎn)稱為 JS,是一種具有函數(shù)優(yōu)先的輕量級(jí),解釋型或即時(shí)編譯型的編程語(yǔ)言。

          JavaScript 現(xiàn)在已經(jīng)被用到了很多非瀏覽器環(huán)境中,JavaScript 基于原型編程、多范式的動(dòng)態(tài)腳本語(yǔ)言,并支持面向?qū)ο蟆⒚钍胶吐暶魇斤L(fēng)格。

          HTML、CSS、JavaScript三者不同的功能:

          • HTML:定義網(wǎng)頁(yè)的內(nèi)容。
          • CSS:描述頁(yè)面的布局。
          • JavaScript:指定網(wǎng)頁(yè)的行為。

          JavaScript 基本特點(diǎn)

          JavaScript 是一種屬于網(wǎng)絡(luò)的腳本語(yǔ)言,已經(jīng)被廣泛用于 Web 應(yīng)用開發(fā),常用來(lái)為網(wǎng)頁(yè)添加各式各樣的動(dòng)態(tài)功能,為用戶提供更流暢美觀的瀏覽效果。通常 JavaScript 腳本是通過(guò)嵌入在 HTML 中來(lái)實(shí)現(xiàn)自身的功能的(就是寫在 HTML 頁(yè)面中的 <script> 標(biāo)簽中)。

          JavaScript 的基本特點(diǎn)如下所示:

          • Javascript 是一種解釋性腳本語(yǔ)言,代碼不進(jìn)行預(yù)編譯。
          • Javascript 主要作用是用來(lái)向 HTML 頁(yè)面添加交互行為。
          • Javascript 代碼可以直接嵌入 HTML 頁(yè)面,但寫成單獨(dú)的 .js 文件有利于結(jié)構(gòu)和行為分離。
          • 腳本語(yǔ)言,Javascript 腳本語(yǔ)言同其他語(yǔ)言一樣,有它自身的基本數(shù)據(jù)類型,表達(dá)式和算術(shù)運(yùn)算符及程序的基礎(chǔ)程序框架。
          • 跨平臺(tái)特性,JavaScript 腳本語(yǔ)言不依賴于操作系統(tǒng),僅需要瀏覽器的支持。在絕大多數(shù)瀏覽器的支持下,可以在 Windows、Android、Linux、Mac、IOS 等平臺(tái)下運(yùn)行。

          JavaScript 可以做一些什么事情

          JavaScript 可以做一些什么事情呢,我們下面簡(jiǎn)單的介紹一下,可能還沒學(xué)過(guò) JavaScript 的同學(xué)看不懂,不要急,后面我們都會(huì)詳細(xì)的介紹。

          • 直接寫入 HTML 輸出流,如下所示,我們可以直接在 JavaScript 中寫入HTML 代碼:
          <script type="text/javascript">
            document.write("<h1>俠課島</h1>");
            document.write("<p>俠課島歡迎你!</p>");
          </script>
          
          • 對(duì)事件作出反應(yīng),可以使用 onclick 在HTML元素上綁定事件:
          <button type="button" onclick="alert('俠課島')"> 點(diǎn)擊彈出 </button>
          
          • 修改 HTML 中的內(nèi)容,例如自定義一個(gè) change() 方法,然后綁定到按鈕上:
          <body>
            <p id="content">原來(lái)的內(nèi)容</p>
            <button type="button" onclick="change()">點(diǎn)擊修改</button>
          </body>
          <script type="text/javascript">
            function change(){
              document.getElementById('content').innerHTML='新的內(nèi)容'
            }
          </script>
          
          • JavaScript 還可以實(shí)現(xiàn)改變 HTML 圖像,例如通過(guò) <img> 標(biāo)簽的 src 屬性來(lái)改變一張 HTML 圖像,如下所示:
          <body>
            <button onclick="changeImage()">修改圖片</button>
            <div>
              <img id="myImage" src="./img/part1-01.jpg">
            </div>
          </body>
          <script type="text/javascript">
            function changeImage(){
              element=document.getElementById('myImage');
              if(element.src.match("02")){
                element.src="./img/part1-01.jpg";
              }else{
                element.src="./img/part1-02.jpg";
              }
            }
          </script>
          
          • JavaScript 可以用來(lái)改變 HTML 樣式,例如:
          <p id="style">JavaScript改變HTML樣式</p>
          
          //點(diǎn)擊之后呈現(xiàn)的效果是:顏色變綠
          <button type="button" onclick="document.getElementById('style').style.color='green'">點(diǎn)擊</button>
          
          • 驗(yàn)證輸入,例如:
          if isNaN(x) {alert("不是數(shù)字")};
          

          本節(jié)我們基本講了一下JavaScript是什么,它能做什么,下一節(jié)我們講JavaScript最基本的語(yǔ)法,學(xué)習(xí)一門語(yǔ)言,我們都要先弄清楚這門語(yǔ)言的基本語(yǔ)言,才好開始編寫代碼。

          .為何學(xué)習(xí) JavaScript?

          ?JavaScript 是 web 開發(fā)者必學(xué)的三種語(yǔ)言之一:
          HTML 定義網(wǎng)頁(yè)的內(nèi)容----[制作網(wǎng)頁(yè)]----簡(jiǎn)單枯燥
          CSS 規(guī)定網(wǎng)頁(yè)的布局和樣式—[美化網(wǎng)頁(yè)]
          HTML+CSS—網(wǎng)頁(yè)—沒有動(dòng)態(tài)的網(wǎng)頁(yè)【1.無(wú)動(dòng)態(tài)效果 2.數(shù)據(jù)沒有動(dòng)態(tài)化】
          JavaScript 對(duì)網(wǎng)頁(yè)行為進(jìn)行編程【動(dòng)態(tài)效果,數(shù)據(jù)動(dòng)態(tài)加載】
          2. 什么是JavaScript?
          ?? 1.javascript和java是完全不同的語(yǔ)言,不論是概念還是設(shè)計(jì)。
          ?? 網(wǎng)景----瀏覽器【Netscape Navigator】----LiveScript-----JavaScript
          ?? 微軟----瀏覽器[IE]----JScript
          …等等
          ?? ECMA—?dú)W洲計(jì)算機(jī)廠家協(xié)會(huì)–ECMA-262標(biāo)準(zhǔn)—ECMAScript
          ?? 2.JavaScript面向?qū)ο蟮哪_本程序設(shè)計(jì)語(yǔ)言
          ?? 面向?qū)ο蟆幊趟枷?br>?? 腳本程序—依據(jù)一定的格式編寫的可執(zhí)行文件【直接運(yùn)行】
          ?? 3.JavaScript使用
          ??1.標(biāo)記的事件中使用

          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="utf-8">
          		<title></title>
          	</head>
          	<body>
          		<h1>1.標(biāo)記的事件中使用</h1>
          		<input type="button" value="測(cè)試Javascript的使用" 
          		onclick="alert('歡迎使用Javascript')" />
          	</body>
          </html>
          
          


          2.script標(biāo)記中的使用
          ?? script–<script>javascript程序</script>
          ?? 位置–<head>標(biāo)記中/<body>標(biāo)記中【最后】

          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="utf-8">
          		<title></title>
          		<script type="text/javascript">
          			alert("歡迎使用JavaScript!");
          		</script>
          	</head>
          	<body>
          		<h1>script標(biāo)記的中使用</h1>
          		<h1>script標(biāo)記出現(xiàn)在head標(biāo)記中</h1>
          		<input type="button"  value="測(cè)試javascript的使用" onclick="test1();" />
          	</body>
          </html>
          
          


          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="utf-8">
          		<title></title>
          	</head>
          	<body>
          		<h1>script標(biāo)記的使用</h1>
          		<h1>script標(biāo)記出現(xiàn)在body標(biāo)記中</h1>
          		<input type="button" value="測(cè)試JavaScript的使用" onclick="test1()" />
          		<script type="text/javascript">
          			function test1(){
          				alert("歡迎使用javascript!");
          			}
          		</script>
          	</body>
          </html>
          
          


          3.外部腳本—將JavaScript程序編寫到獨(dú)立的文件【.js】,通過(guò)<script>標(biāo)記提供的src屬性將獨(dú)立的文件【.js】,導(dǎo)入到html文件中。
          ?? 您可以在<head>或<body>中放置外部腳本引用。
          ?? 格式:<script src=“js 文件路徑”></script>
          獨(dú)立的jsWENJIAN 【myscript1.js】
          function test1(){
          ?alert("歡迎使用javascript!”);
          }
          .js文件

          function test1(){
          	alert("歡迎使用JavaScript!")
          }
          

          html文件

          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="utf-8">
          		<title></title>
          		<script src="javascript.js" type="text/javascript" charset="utf-8"></script>
          	</head>
          	<body>
          		<h1>script外部腳本使用</h1>
          		<input type="button"  value="測(cè)試JavaScript的使用" onclick="test1();" />
          	</body>
          </html>
          
          


          3.JavaScript 輸出
          JavaScript能夠以不同方式“顯示”數(shù)據(jù):
          ?? 1.使用window.alert()寫入警告框
          ?? 2.使用document.write()寫入HTML輸出
          ?? 3.使用innerHTML寫入HTML元素
          ?? 4.使用console.log()寫入瀏覽器控制臺(tái)

          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="utf-8">
          		<title></title>
          	</head>
          	<body>
          		<h1>1.使用window.alert()寫入警告框</h1>
          		<!--
          		<script type="text/javascript">
          			window.alert("測(cè)試window.alert()寫入警告框")
          		</script>
          		-->
          		<h1>2.使用document.write()寫入HTML輸出</h1>
          		<!--
          		<script type="text/javascript">
          			document.write("測(cè)試document.write()寫入HTML輸出")
          		</script>
          		-->
          		<h1>3.使用innerHTML寫入HTML元素</h1>
          		<p id="p1"></p>
          		<!--
          		<script type="text/javascript">
          			document.getElementById("p1").innerHTML="<font size='7'>測(cè)試innerHTML寫入HTML元素</font>"
          		</script>
          		-->
          		<h1>4.使用console.log()寫入瀏覽器控制臺(tái)</h1>
          		<script type="text/javascript">
          			console.log("console.log()寫入瀏覽器控制臺(tái)")
          		</script>
          	</body>
          </html>
          
          


          4.JavaScript 語(yǔ)句
          JavaScript 語(yǔ)句由以下構(gòu)成:
          ?? 值、運(yùn)算符、表達(dá)式、關(guān)鍵詞和注釋。
          ?? document.getElementById(“demo”).innerHTML=“Hello Kitty.”;
          “Hello Kitty.”----值
          ??=— 賦值運(yùn)算符
          ?? 關(guān)鍵詞–常常通過(guò)某個(gè)關(guān)鍵詞來(lái)標(biāo)識(shí)需要執(zhí)行的 JavaScript 動(dòng)作


          注釋–解釋說(shuō)明代碼的含義/調(diào)試程序【注釋的內(nèi)容不會(huì)被執(zhí)行】
          // 單行注釋 【保證一行代碼被注釋掉不會(huì)被執(zhí)行】
          /*
          多行注釋 【保證多行代碼被注釋掉不會(huì)被執(zhí)行】
          // 可以包含單行注釋
          */

            <script>
          			/*
          			//console.log("console.log() 寫入瀏覽器控制臺(tái)");
          			console.log("console.log() 寫入瀏覽器控制臺(tái)");
          			console.log("console.log() 寫入瀏覽器控制臺(tái)");
          console.log("console.log() 寫入瀏覽器控制臺(tái)");
          			*/
          		</script>
          
          

          分號(hào) ; 表示一條javascript語(yǔ)句的結(jié)束。
          ??可以將有分號(hào)的語(yǔ)句寫在同一行,通常我們都不這么寫,出錯(cuò)以后查找不方便,所以一行一句。
          JavaScript代碼塊【{代碼塊}】
          function myFunction(){
          ??document.getElementById(“demo”).innerHTML=“Hello Kitty.”;
          ??document.getElementById(“myDIV”).innerHTML=“How are you?”;
          }
          5.JavaScript 語(yǔ)法
          JavaScript 值
          JavaScript 語(yǔ)句定義兩種類型的值:混合值和變量值。
          混合值被稱為字面量(literal)— 常量值
          寫數(shù)值有無(wú)小數(shù)點(diǎn)均可:12.5 1001
          寫字符串:”zhangsan”
          變量值被稱為變量[程序運(yùn)行的最小單位]保存數(shù)據(jù)值。
          需要通過(guò)var 關(guān)鍵詞 來(lái)定義變量
          用法1 : var a; //定義變量
          ??a=1001; //變量賦值
          用法2: var b=”hello”;
          JavaScript 運(yùn)算符
          JavaScript使用算是運(yùn)算符(+ - * /)來(lái)計(jì)算值。
          JavaScript使用(=)賦值運(yùn)算符。
          JavaScript使用(> <==>=<=!=)比較運(yùn)算符。
          JavaScript使用(|| && !)邏輯運(yùn)算符。

          JavaScript表達(dá)式
          表達(dá)式是值、變量和運(yùn)算符的組合,計(jì)算結(jié)果是值。
          100+123【算數(shù)表達(dá)式】
          var b=“hello”; 【賦值表達(dá)式】
          12>23 【關(guān)系表達(dá)式】
          (12>23)&&(23<12) —【邏輯表達(dá)式】

          JavaScript 標(biāo)識(shí)符
          標(biāo)識(shí)符就是為JavaScript元素【函數(shù),變量,對(duì)象…】起名字的字符串。
          1.可以由字母、下劃線(_)或美元符號(hào)($),數(shù)字組成,數(shù)字不能開頭。
          2.不能用關(guān)鍵詞
          3.區(qū)分大小寫
          駝峰式命名規(guī)則—首字母大寫【FirstName, LastName, MasterCard, InterCity.】
          JavaScript 程序員傾向于使用以小寫字母開頭的駝峰大小寫
          6. JavaScript 變量
          ??JavaScript 變量是存儲(chǔ)數(shù)據(jù)值的容器。
          ??JavaScript 數(shù)據(jù)類型
          ??在編程中,文本值被稱為字符串。
          ??JavaScript 可處理多種數(shù)據(jù)類型,但是現(xiàn)在,我們只關(guān)注數(shù)值和字符串值。
          ??字符串被包圍在雙引號(hào)或單引號(hào)中。數(shù)值不用引號(hào)。
          ??如果把數(shù)值放在引號(hào)中,會(huì)被視作文本字符串。
          ??true/false 布爾型
          ??null null型
          聲明(創(chuàng)建) JavaScript 變量
          在 JavaScript 中創(chuàng)建變量被稱為“聲明”變量。
          您可以通過(guò) var 關(guān)鍵詞來(lái)聲明 JavaScript 變量:
          var carName;
          聲明之后,變量是沒有值的。(技術(shù)上,它的值是 undefined。)我們?cè)谑褂玫淖兞康臅r(shí)候需要賦予初始值,否則被視為未定義。
          通過(guò)”=”給變量賦值。
          var carName;
          carName=”BMW”;

          var carName=”BMW”;
          一條語(yǔ)句,多個(gè)變量
          var person=“Bill Gates”, carName=“porsche”, price=15000;

          7.JavaScript運(yùn)算符

          JavaScript 使用(=)賦值運(yùn)算符。
          JavaScript 使用算數(shù)運(yùn)算符(+ - * / % ++ --)來(lái)計(jì)算值。

          <!DOCTYPE html>
          <html>
          	<head>
          		<meta charset="utf-8">
          		<title>變量-運(yùn)算符-表達(dá)式</title>
          		<script>
          			function  suanshu(){
          				//聲明變量
          				var num1=10;
          				var num2=3;
          				document.getElementById("h1").innerHTML=num1+"+"+num2+"="+(num1+num2);
          				document.getElementById("h2").innerHTML=num1+"-"+num2+"="+(num1-num2);
          				document.getElementById("h3").innerHTML=num1+"*"+num2+"="+(num1*num2);
          				document.getElementById("h4").innerHTML=num1+"/"+num2+"="+(num1/num2);
          				document.getElementById("h5").innerHTML=num1+"%"+num2+"="+(num1%num2);
          				// ++ [自動(dòng)加1]
          				// 變量++  [先用后加]
          				//num1++;  //10
          				//document.getElementById("h6").innerHTML=num1; //11
          				// ++變量  [先加后用]
          				//++num1;  //11
          				//document.getElementById("h6").innerHTML=num1; //11
          				// -- [自動(dòng)減1]
          				// 變量-- [先用后減]
          				//num1-- ; //10
          				//document.getElementById("h6").innerHTML=num1; //9
          				//  -- 變量
          				--num1;  // 9
          				document.getElementById("h6").innerHTML=num1; //9
          			}
          		</script>
          	</head>
          	<body>
          		<input  type="button"  value="測(cè)試算數(shù)運(yùn)算符" onclick="suanshu()"/><br>
          		<h1 id="h1"></h1>
          		<h1 id="h2"></h1>
          		<h1 id="h3"></h1>
          		<h1 id="h4"></h1>
          		<h1 id="h5"></h1>
          		<h1 id="h6"></h1>
          	</body>
          </html>
          
          


          JavaScript 使用(> <==>=<=!=)比較運(yùn)算符運(yùn)算符。

          JavaScript 使用(|| && ! )邏輯運(yùn)算符運(yùn)算符。

          為一名前端愛好者, 我利用空余時(shí)間研究了幾個(gè)國(guó)外網(wǎng)站的源碼,發(fā)現(xiàn)不管是庫(kù),還是業(yè)務(wù)代碼,都會(huì)用到了一些比較有意思的API,雖然平時(shí)在工作中部分接觸過(guò),但是經(jīng)過(guò)這次的研究,覺得很有必要總結(jié)一下,畢竟已經(jīng)2020年了,是時(shí)候更新一下技術(shù)儲(chǔ)備了,本文主要通過(guò)實(shí)際案例來(lái)帶大家快速了解以下幾個(gè)知識(shí)點(diǎn):

          • Observer 原生觀察者
          • script標(biāo)簽事件深入 - 移除script標(biāo)簽后事件仍然能執(zhí)行的原因
          • Proxy/Reflect 自定義事件
          • fileReader API Fullscreen 網(wǎng)頁(yè)全屏
          • URL API的使用
          • Geolocation 地理位置API的使用
          • Notifications 瀏覽器原生消息通知
          • Battery Status 設(shè)備電量情況

          我會(huì)對(duì)部分API做一些比較有意思的案例,那么開始我們的學(xué)習(xí)吧~

          1. Observer API

          Observer是瀏覽器自帶的觀察者,它主要提供了Intersection, Mutation, Resize, Performance這四類觀察者, 這里筆者重點(diǎn)介紹Intersection Observer.

          1.1 Intersection Observer

          IntersectionObserver提供了一種異步觀察目標(biāo)元素與其祖先元素交叉狀態(tài)的方法。當(dāng)一個(gè)IntersectionObserver對(duì)象被創(chuàng)建時(shí),其被配置為監(jiān)聽根中一段給定比例的可見區(qū)域,并且無(wú)法更改其配置,所以一個(gè)給定的觀察者對(duì)象只能用來(lái)監(jiān)聽可見區(qū)域的特定變化值;然而,我們可以在同一個(gè)觀察者對(duì)象中配置監(jiān)聽多個(gè)目標(biāo)元素。

          說(shuō)簡(jiǎn)單點(diǎn)就是該api可以異步監(jiān)聽目標(biāo)元素在根元素里的位置變動(dòng),并觸發(fā)響應(yīng)事件.我們可以利用它來(lái)實(shí)現(xiàn)更為高效的圖片懶加載, 無(wú)限滾動(dòng)以及內(nèi)容埋點(diǎn)上報(bào)等.接下來(lái)我們通過(guò)一個(gè)例子來(lái)說(shuō)明一下它的使用步驟.

          // 1.定義觀察者及觀察回調(diào)
          const intersectionObserver=new IntersectionObserver((entries, observer)=> {
              entries.forEach(entry=> { 
                console.log(entry)
                // ...一些操作
              }); 
             },
             {
              root: document.querySelector('#root'),
              rootMargin: '0px',
              threshold: 0.5
             }
          )
          // 2. 定義要觀察的目標(biāo)對(duì)象
          const target=document.querySelector(“.target”); 
          intersectionObserver.observe(target);
          

          以上代碼就實(shí)現(xiàn)了一個(gè)基本的Intersection Observer,雖然已有代碼中還體現(xiàn)不出什么實(shí)質(zhì)性功能. 接下來(lái)介紹一下代碼中使用到的參數(shù)的含義: * callback IntersectionObserver實(shí)例的第一個(gè)參數(shù), 當(dāng)目標(biāo)元素與根元素通過(guò)閾值 時(shí)就會(huì)觸發(fā)該回調(diào).回調(diào)中第一個(gè)參數(shù)是被觀察對(duì)象列表,一旦被觀察對(duì)象發(fā)生突變就會(huì)被移入該列表, 列表中每一項(xiàng)都保留有觀察者的位置信息;第二個(gè)參數(shù)為observer,觀察者本身.如下圖控制臺(tái)打印:

          其中rootBounds表示根元素的位置信息, boundingClientRect表示目標(biāo)元素的位置信息,intersectionRect表示叉部分的位置信息, intersectionRatio表示目標(biāo)元素的可見比例.

          • 配置屬性 IntersectionObserver實(shí)例的第二個(gè)參數(shù),用來(lái)配置監(jiān)聽屬性,具體有以下三個(gè)屬性:
            • root 所監(jiān)聽對(duì)象的具體祖先元素(element)。如果未傳入值或值為null,則默認(rèn)使用頂級(jí)文檔的視窗。
            • rootMargin 計(jì)算交叉時(shí)添加到根(root)邊界盒bounding box的矩形偏移量, 可以有效的縮小或擴(kuò)大根的判定范圍從而滿足計(jì)算需要
            • thresholds 一個(gè)包含閾值的列表, 按升序排列, 列表中的每個(gè)閾值都是監(jiān)聽對(duì)象的交叉區(qū)域與邊界區(qū)域的比率。當(dāng)監(jiān)聽對(duì)象的任何閾值被越過(guò)時(shí),都會(huì)生成一個(gè)通知(Notification)。如果構(gòu)造器未傳入值, 則默認(rèn)值為0。 以上屬性介紹字面上可能很難理解,筆者花幾個(gè)草圖來(lái)讓大家有個(gè)直觀的認(rèn)知:

          當(dāng)我們?cè)O(shè)置rootMargin為10px時(shí),我們的root會(huì)增大影響范圍,但目標(biāo)元素移動(dòng)到淡紅色區(qū)域式就會(huì)被監(jiān)聽到,當(dāng)然我們還可以設(shè)置rootMargin為負(fù)值來(lái)減少影響區(qū)域.其支持的值為百分比和px,如下:

          rootMargin: '10px'
          rootMargin: '10%'
          rootMargin: '10px 0px 10px 10px'
          

          thresholds可以如下圖理解:

          由上圖所示,當(dāng)我們?cè)O(shè)置閾值為[0.25, 0.5]時(shí), 目標(biāo)元素的25%和50%進(jìn)入根元素的影響范圍時(shí)都會(huì)觸發(fā)回調(diào).利用這個(gè)特性我們往往可以實(shí)現(xiàn)位差動(dòng)畫,或者更根據(jù)目標(biāo)元素的位置變化做不同的交互. 當(dāng)然Intersection還提供了以下幾個(gè)方法來(lái)控制觀察對(duì)象: disconnect() 使IntersectionObserver對(duì)象停止監(jiān)聽工作 takeRecords() 返回所有觀察目標(biāo)的IntersectionObserverEntry對(duì)象數(shù)組 * unobserve() 使IntersectionObserver停止監(jiān)聽特定目標(biāo)元素

          了解了使用方法和api之后,我們來(lái)看看一個(gè)實(shí)際應(yīng)用--實(shí)現(xiàn)圖片懶加載:

          <img src="loading.gif" data-src="absolute.jpg">
          <img src="loading.gif" data-src="relative.jpg">
          <img src="loading.gif" data-src="fixed.jpg">
          
          <script>
          let observerImg=new IntersectionObserver(
          (entries, observer)=> { 
              entries.forEach(entry=> {
                  // 替換為正式的圖片
                  entry.target.src=entry.target.dataset.src;
                  // 停止監(jiān)聽
                  observer.unobserve(entry.target);
                });
              }, 
              {
                root: documennt.getElementById('scrollView'),
                threshold: 0.3
              }
          );
          
          document.querySelectorAll('img').forEach(img=> { observerImg.observe(img) });
          </script>

          以上代碼就實(shí)現(xiàn)了一個(gè)圖片懶加載功能, 當(dāng)圖片的30%進(jìn)入根元素時(shí)才加載真實(shí)的圖片,這又讓我想起了之前在某條做廣告埋點(diǎn)上報(bào)時(shí)使用react-lazyload的畫面.大家還可以利用它實(shí)現(xiàn)無(wú)限滾動(dòng), H5視差動(dòng)畫等有意思的交互場(chǎng)景.

          1.2 Mutation Observer和Resize Observer

          Mutation Observer主要用來(lái)實(shí)現(xiàn)dom變動(dòng)時(shí)的監(jiān)聽,同樣也是異步觸發(fā),對(duì)監(jiān)聽性能非常友好. Resize Observer主要用來(lái)監(jiān)聽元素大小的變化,相比于每次窗口變動(dòng)都觸發(fā)的window.resize事件, Resize Observer有更好的性能和對(duì)dom有更細(xì)粒度的控制,它只會(huì)在繪制前或布局后觸發(fā)調(diào)用. 以上兩個(gè)api的使用和Intersection使用非常類似,官方資料也寫得很全,大家可以好好研究一下.

          2. 移除script標(biāo)簽后事件仍然能執(zhí)行的原因

          這個(gè)問(wèn)題主要是之前有朋友問(wèn)過(guò)我,當(dāng)時(shí)的想法就是簡(jiǎn)單的認(rèn)為script內(nèi)的代碼執(zhí)行完之后以及與dom綁定了,存放在了瀏覽器內(nèi)存中,最近查了很多資料發(fā)現(xiàn)有一個(gè)有點(diǎn)意思的解釋,放出來(lái)大家可以感受一下:

          JavaScript解釋器在執(zhí)行腳本時(shí),是按塊來(lái)執(zhí)行的,也就是說(shuō)瀏覽器在解析HTML文檔流時(shí),如果遇到一個(gè)script標(biāo)簽,javascript解釋器會(huì)等待這個(gè)代碼塊都加載完了,才進(jìn)行預(yù)編譯,然后才執(zhí)行。所以,當(dāng)開始執(zhí)行這個(gè)代碼塊的代碼時(shí),這個(gè)代碼段已經(jīng)被解析完了。這時(shí)再?gòu)腄OM中刪去也就不影響代碼的執(zhí)行了。

          3. Proxy/Reflect

          Proxy/Reflect雖然是es6的api,出現(xiàn)也已經(jīng)有幾年了,但是在項(xiàng)目中用的還是比較少,如果是做底層架構(gòu)方面的工作,還是建議大家多去使用,畢竟vue/react這種框架源碼把這些api玩的如火純青,還是很有必要掌握一下的。

          其實(shí)我們認(rèn)真看mdn的介紹或者阮一峰老師的文章,還是很好理解這些api的用法的,接下來(lái)我們?cè)敿?xì)介紹一下這兩個(gè)api以及應(yīng)用場(chǎng)景.

          3.1 Proxy

          Proxy 可以理解成,在目標(biāo)對(duì)象之前架設(shè)一層“攔截”,外界對(duì)該對(duì)象的訪問(wèn),都必須先通過(guò)這層攔截,因此提供了一種機(jī)制,可以對(duì)外界的訪問(wèn)進(jìn)行過(guò)濾和改寫。Proxy在很多場(chǎng)景中都會(huì)和Reflect一起使用. 用法也很簡(jiǎn)單,我們看看Proxy的基本用法:

          const obj={
             name: '徐小夕',
             age: '120'
           }
           const proxy=new Proxy(obj, {
             get(target, propKey, receiver) {
              console.log('get:' + propKey)
              return Reflect.get(target, propKey, receiver)
             },
             set(target, propKey, value, receiver) {
              console.log('set:' + propKey)
              return Reflect.set(target, propKey, value, receiver)
             }
           })
           console.log(proxy.name) // get:name 徐小夕
           proxy.work='frontend' // set:work frontend
          

          以上代碼攔截了obj對(duì)象,并重新定義了讀寫(get/set)方法,這樣我們就可以在訪問(wèn)對(duì)象時(shí)進(jìn)行額外的操作了.

          Proxy還有apply(攔截 Proxy 實(shí)例作為函數(shù)調(diào)用的操作)和construct(攔截 Proxy 實(shí)例作為構(gòu)造函數(shù)調(diào)用的操作)等屬性可以使用,我們可以在對(duì)象操作的不同階段進(jìn)行攔截,這里我就不一一樣舉例了.接下來(lái)看看Proxy的實(shí)際應(yīng)用場(chǎng)景. * 實(shí)現(xiàn)數(shù)組讀取負(fù)數(shù)的索引

          我們一般操作數(shù)組大多數(shù)都是正向操作的,不能通過(guò)指定負(fù)數(shù)來(lái)逆向查找數(shù)組,如下圖:

          我們不能通過(guò)arr[-1]來(lái)拿到數(shù)組的尾部元素(字符串同理),這個(gè)時(shí)候我們就可以用Proxy來(lái)實(shí)現(xiàn)這一功能,這是我們的結(jié)構(gòu)有點(diǎn)像環(huán)狀:

          這種實(shí)現(xiàn)的好處是如果我們想訪問(wèn)數(shù)組的最后一個(gè)元素時(shí),我們不需要先拿到長(zhǎng)度,再通過(guò)索引訪問(wèn)了:

          // 原始寫法
          arr[arr.length -1]
          // 通過(guò)proxy改造后寫法
          arr[-1]
          

          實(shí)現(xiàn)代碼如下:

          function createArray(...elements) {
            let handler={
              get(target, propKey, receiver) {
                let index=Number(propKey);
                if (index < 0) {
                  propKey=String(target.length + index);
                }
                return Reflect.get(target, propKey, receiver);
              }
            };
          
            let target=[];
            target.push(...elements);
            return new Proxy(target, handler);
          }
          

          我們可以發(fā)現(xiàn)以上代碼使用proxy來(lái)代理數(shù)組的讀取操作,在內(nèi)部封裝了支持負(fù)值查找的功能,當(dāng)然我們也可以不用proxy來(lái)實(shí)現(xiàn)同樣的功能,這里實(shí)現(xiàn)參考阮一峰老師的實(shí)現(xiàn). * 利用proxy實(shí)現(xiàn)更優(yōu)雅的校驗(yàn)器

          一般我們?cè)谧霰韱涡r?yàn)的時(shí)候會(huì)寫一些if else或者switch判斷來(lái)實(shí)現(xiàn)對(duì)不同屬性值的校驗(yàn),同樣我們也可以用proxy來(lái)優(yōu)雅的實(shí)現(xiàn)它,代碼如下:

          const formData={
             name: 'xuxi',
             age: 120,
             label: ['react', 'vue', 'node', 'javascript']
           }
           // 校驗(yàn)器
           const validators={
             name(v) {
               // 檢驗(yàn)name是否為字符串并且長(zhǎng)度是否大于3
               return typeof v==='string' && v.length > 3
             },
             age(v) {
               // 檢驗(yàn)age是否為數(shù)值
               return typeof v==='number'
             },
             label(v) {
               // 檢驗(yàn)label是否為數(shù)組并且長(zhǎng)度是否大于0
               return Array.isArray(v) && v.length > 0
             }
           }
           // 代理校驗(yàn)對(duì)象
           function proxyValidator(target, validator) {
            return new Proxy(target, {
              set(target, propKey, value, receiver) {
                if(target.hasOwnProperty(propKey)) {
                  let valid=validator[propKey]
                  if(!!valid(value)) {
                    return Reflect.set(target, propKey, value, receiver)
                  }else {
                    // 一些其他錯(cuò)誤業(yè)務(wù)...
                    throw Error(`值驗(yàn)證錯(cuò)誤${propKey}:${value}`)
                  }
                }
              }
            })
           }
          

          有了以上實(shí)現(xiàn)模式,我們就可以實(shí)現(xiàn)對(duì)表單中某個(gè)值進(jìn)行設(shè)置時(shí)進(jìn)行校驗(yàn)了,用法如下:

          let formObj=proxyValidator(formData, validators)
          formObj.name=333;   // Uncaught Error: 值驗(yàn)證錯(cuò)誤name:f
          formObj.age='ddd'   // Uncaught Error: 值驗(yàn)證錯(cuò)誤age:f
          

          以上代碼中當(dāng)設(shè)置了不合法的值時(shí),控制臺(tái)將會(huì)剖出錯(cuò)誤,如果在實(shí)際業(yè)務(wù)中,我們可以給用戶做出適當(dāng)?shù)奶嵝? 實(shí)現(xiàn)請(qǐng)求攔截和錯(cuò)誤上報(bào) 實(shí)現(xiàn)數(shù)據(jù)過(guò)濾

          以上幾點(diǎn)筆者在之前的文章中也寫過(guò),所以這里不在詳細(xì)介紹了.大家也可以根據(jù)實(shí)際情況自己實(shí)現(xiàn)更加靈活的攔截操作.當(dāng)然Proxy提供的API遠(yuǎn)遠(yuǎn)不止這幾個(gè),我們可以在MDN或者其他渠道了解更多高級(jí)用法.

          3.2 Reflect

          Reflect對(duì)象與Proxy對(duì)象一樣,也是 ES6 為了操作對(duì)象而提供的新 API,更多的應(yīng)用場(chǎng)景是配合proxy一起使用,在上文中已經(jīng)用到了.可以將Object對(duì)象的一些明顯屬于語(yǔ)言內(nèi)部的方法放到Reflect對(duì)象上,并修改某些Object方法的返回結(jié)果. Reflect對(duì)象的方法與Proxy對(duì)象的方法一一對(duì)應(yīng),只要是Proxy對(duì)象的方法,就能在Reflect對(duì)象上找到對(duì)應(yīng)的方法。

          4. 自定義事件

          CustomEvent API是個(gè)非常有意思的api, 而且非常實(shí)用, 更重要的是學(xué)起來(lái)非常簡(jiǎn)單,而且被大部分現(xiàn)代瀏覽器支持.我們可以讓任意dom元素監(jiān)聽和觸發(fā)自定義事件,只需要如下操作:

          // 添加一個(gè)適當(dāng)?shù)氖录O(jiān)聽器
          dom1.addEventListener("boom", function(e) { something(e.detail.num) })
          
          // 創(chuàng)建并分發(fā)事件
          var event=new CustomEvent("boom", {"detail":{"num":10}})
          dom1.dispatchEvent(event)
          

          我們來(lái)看看CustomEvent的參數(shù)介紹: type 事件的類型名稱,如上面代碼中的'boom' CustomEventInit 提供了事件的配置信息,具體有以下幾個(gè)屬性 * bubbles 一個(gè)布爾值,表明該事件是否會(huì)冒泡 * cancelable 一個(gè)布爾值,表明該事件是否可以被取消 * detail 當(dāng)事件初始化時(shí)傳遞的數(shù)據(jù)

          我們可以通過(guò)dispatchEvent來(lái)觸發(fā)自定義事件.其實(shí)他的用途有很多,比如創(chuàng)建觀察者模式, 實(shí)現(xiàn)數(shù)據(jù)雙向綁定, 亦或者在游戲開發(fā)中實(shí)現(xiàn)打怪掉血,比如下面的例子:


          筆者上面畫了一個(gè)打boss的草圖, 現(xiàn)在的場(chǎng)景是兩個(gè)玩家一起打boss, 我們可以在玩家發(fā)動(dòng)攻擊的時(shí)候觸發(fā)dispatch掉血的自定義事件, boss監(jiān)聽到事件后將血量自動(dòng)扣除, 至于不同角色的傷害值,我們可以存放在detail中,然后通過(guò)策略模式去分發(fā)傷害.筆者曾今在學(xué)校開發(fā)的H5游戲時(shí)就大量采用類似的模式,還是非常有意思的.

          5. fileReader

          File API使得我們?cè)跒g覽器端可以訪問(wèn)文件的數(shù)據(jù),比如預(yù)覽文件,獲取文件信息(比如文件名,文件內(nèi)容,文件大小等), 并且可以在前端實(shí)現(xiàn)文件下載(可以借助canvas和 window.URL.revokeObjectURL的一些能力).當(dāng)然我們還可以實(shí)現(xiàn)拖拽上傳文件這樣高用戶體驗(yàn)的操作.接下來(lái)我們來(lái)看看幾個(gè)實(shí)際例子. * 顯示縮略圖

          function previewFiles(files, previewBox) {
              for (var i=0; i < files.length; i++) {
                var file=files[i];
                var imageType=/^image\//;
          
                if (!imageType.test(file.type)) {
                  continue;
                }
          
                var img=document.createElement("img");
                previewBox.appendChild(img); // 假設(shè)"preview"就是用來(lái)顯示內(nèi)容的div
          
                var reader=new FileReader();
                reader.onload=(function(imgEl) { 
                  return function(e) { imgEl.src=e.target.result; }; 
                })(img);
                reader.readAsDataURL(file);
              }
            }
          

          以上代碼可以在reviewBox容器中顯示已上傳好的圖片,當(dāng)然我們還可以基于此來(lái)擴(kuò)展,利用canvas將圖片畫到canvas上,然后進(jìn)行圖片壓縮,最后再把壓縮后的圖片上傳到服務(wù)器.這中方式其實(shí)目前很多工具型網(wǎng)站都在用,比如在線圖片處理網(wǎng)站,提供的批量壓縮圖片,批處理水印等功能,套路都差不多,感興趣的朋友可以嘗試研究一下. * 封裝文件上傳組件

          這塊筆者之前也寫過(guò)詳細(xì)的文章,這里就不一一舉例了.

          6. Fullscreen

          全屏API主要是讓網(wǎng)頁(yè)能在電腦屏幕中全屏顯示,它允許我們打開或者退出全屏模式,以便我們根據(jù)需要進(jìn)行對(duì)應(yīng)的操作,比如我們常用的網(wǎng)頁(yè)圖形編輯器或者富文本編輯器, 為了讓用戶專心于內(nèi)容設(shè)計(jì),我們往往提供切換全屏的功能供用戶使用.由于全屏API比較簡(jiǎn)單,這里我們直接上代碼:

          // 開啟全屏
          document.documentElement.requestFullscreen();
          // 退出全屏
          document.exitFullscreen();
          

          以上代碼的document.documentElement也可以換成任何一個(gè)你想讓其全屏的元素.默認(rèn)情況下我們還可以通過(guò)document.fullscreenElement來(lái)判斷當(dāng)前頁(yè)面是否處于全屏狀態(tài),來(lái)實(shí)現(xiàn)屏幕切換的效果.如果是react開發(fā)者,我們也可以將其封裝成一個(gè)自定義hooks來(lái)實(shí)現(xiàn)與業(yè)務(wù)相關(guān)的全屏切換功能.

          7. URL

          URL API是URL標(biāo)準(zhǔn)的組成部分,URL標(biāo)準(zhǔn)定義了構(gòu)成有效統(tǒng)一資源定位符的內(nèi)容以及訪問(wèn)和操作URL的API。

          我們利用URL組件可以做很多有意思的事情.比如我們有個(gè)需求需要提取url的參數(shù)傳給后臺(tái),傳統(tǒng)的做法是自己寫一個(gè)方法來(lái)解析url字符串,手動(dòng)返回一個(gè)query對(duì)象.但是利用URL對(duì)象,我們可以很方便的拿到url參數(shù),如下:

          let addr=new URL(window.location.href)
          let host=addr.host  // 獲取主機(jī)地址
          let path=addr.pathname  // 獲取路徑名
          let user=addr.searchParams.get("user")  // 獲取參數(shù)為user對(duì)應(yīng)的值
          

          以上代碼可知,我們?nèi)绻麑rl轉(zhuǎn)化為URL對(duì)象,那么我們就可以很方便的通過(guò)searchParams提供的api來(lái)拿到url參數(shù)而無(wú)需自己再寫一個(gè)方法了.

          另一方面,如果網(wǎng)站安全性比較高,我們還可以對(duì)參數(shù)進(jìn)行自然數(shù)排序然后再加密上傳給后端.具體代碼如下:

          function sortMD5WithParameters() {
              let url=new URL(document.location.href);
              url.searchParams.sort();
              let keys=url.searchParams.keys();
              let params={}
          
              for (let key of keys) {
                let val=url.searchParams.get(key);
                params[key]=val
              };
              // ...md5加密
              return MD5(params)
           }
          

          8. Geolocation

          地理位置 API 通過(guò) navigator.geolocation 提供, 這個(gè)瀏覽器API也比較實(shí)用, 我們?cè)诰W(wǎng)站中可以用此方式確定用戶的位置信息,從而讓網(wǎng)站有不同的展現(xiàn),增強(qiáng)用戶體驗(yàn).

          舉幾個(gè)有意思的例子可以讓大家感受一下: 根據(jù)不同地區(qū),網(wǎng)站展示不同的主題:

          根據(jù)用戶所在地區(qū),展示不同推薦內(nèi)容 這一點(diǎn)電商網(wǎng)站或者內(nèi)容網(wǎng)站用的比較多, 比如用戶在新疆,則給他推薦瓜果類廣告, 在北京,則給他推薦旅游景點(diǎn)類廣告等,雖然實(shí)際應(yīng)用中往往會(huì)更復(fù)雜,但是也是一種思路.

          其實(shí)應(yīng)用遠(yuǎn)遠(yuǎn)不止如此,程序員可以發(fā)揮想象來(lái)實(shí)現(xiàn)更有意思的事情,讓自己的網(wǎng)站更智能.接下來(lái)筆者就基于promise寫一段獲取用戶位置的代碼:

          function getUserLocation() {
              return new Promise((resolve, reject)=> {
                if (!navigator.geolocation) {
                  reject()
                } else {
                  navigator.geolocation.getCurrentPosition(success, error);
                }
          
                function success(position) {
                  const latitude=position.coords.latitude;
                  const longitude=position.coords.longitude;
                  resolve({latitude, longitude})
                }
          
                function error() {
                  reject()
                }
              })
            }
          

          使用方式和結(jié)果如下圖所示:

          我們基于獲取到的經(jīng)緯度調(diào)用第三方api(比如百度,高德)就可以獲取用戶所在為精確位置信息了.

          9. Notifications

          Notifications API 允許網(wǎng)頁(yè)或應(yīng)用程序在系統(tǒng)級(jí)別發(fā)送在頁(yè)面外部顯示的通知;這樣即使應(yīng)用程序空閑或在后臺(tái),Web應(yīng)用程序也會(huì)向用戶發(fā)送信息。

          我們舉個(gè)實(shí)際的例子,比如我們網(wǎng)站內(nèi)容有更新,通知用戶,效果如下:

          相關(guān)代碼如下:

          Notification.requestPermission( function(status) {
            console.log(status); // 僅當(dāng)值為 "granted" 時(shí)顯示通知
            var n=new Notification("趣談前端", {body: "從零搭建一個(gè)CMS全棧項(xiàng)目"}); // 顯示通知
          });
          

          當(dāng)然瀏覽器的Notification還給我們提供了4個(gè)事件觸發(fā)api方便我們做更全面的控制: onshow 當(dāng)通知被顯示給用戶時(shí)觸發(fā) (已廢棄, 但部分瀏覽器仍然能用) onclick 當(dāng)用戶點(diǎn)擊通知時(shí)觸發(fā) onclose 當(dāng)通知被關(guān)閉時(shí)觸發(fā)(已廢棄, 但部分瀏覽器仍然能用) onerror 當(dāng)通知發(fā)生錯(cuò)誤的時(shí)候觸發(fā)

          有了這樣的事件監(jiān)聽,我們就可以控制當(dāng)用戶點(diǎn)擊通知時(shí), 跳轉(zhuǎn)到對(duì)應(yīng)的頁(yè)面或者執(zhí)行相關(guān)的業(yè)務(wù)邏輯.如下代碼所示:

          Notification.requestPermission( function(status) {
            console.log(status); // 僅當(dāng)值為 "granted" 時(shí)顯示通知
            var n=new Notification("趣談前端", {body: "從零搭建一個(gè)CMS全棧項(xiàng)目"}); // 顯示通知
                n.onshow=function () { 
                  // 消息顯示時(shí)執(zhí)行的邏輯
                  console.log('show') 
                }
                n.onclick=function () { 
                  // 消息被點(diǎn)擊時(shí)執(zhí)行的邏輯
                  history.push('/detail/1232432')
                }
                n.onclose=function () { 
                  // 消息關(guān)閉時(shí)執(zhí)行的邏輯
                  console.log('close')
                }
          });
          

          當(dāng)然我們?cè)谑褂们靶枰@取權(quán)限,方式也很簡(jiǎn)單,大家可以在mdn上學(xué)習(xí)了解.

          10. Battery Status

          Battery Status API提供了有關(guān)系統(tǒng)充電級(jí)別的信息并提供了通過(guò)電池等級(jí)或者充電狀態(tài)的改變提醒用戶的事件。 這個(gè)可以在設(shè)備電量低的時(shí)候調(diào)整應(yīng)用的資源使用狀態(tài),或者在電池用盡前保存應(yīng)用中的修改以防數(shù)據(jù)丟失。

          之前的版本中Battery Status API提供了幾個(gè)事件監(jiān)聽函數(shù)來(lái)監(jiān)聽電量的變化以及監(jiān)聽設(shè)備是否充電,但是筆者看文檔時(shí)這些api都已經(jīng)廢棄,如下: chargingchange 監(jiān)聽設(shè)別是否充電 levelchange 監(jiān)聽電量充電等級(jí) chargingtimechange 充電時(shí)間變化 dischargingtimechange 放電時(shí)間變化

          雖然以上幾個(gè)看似有用的api已經(jīng)被棄用,但是筆者親測(cè)谷歌還是可以正常使用的,但是為了讓自己代碼更可靠,我們可以用其他方式代替,比如用定時(shí)器定期去檢測(cè)電量情況,進(jìn)而對(duì)用戶做出不同的提醒.

          接下來(lái)我們看看基本的用法:

          navigator.getBattery().then(function(battery) {
            console.log("是否在充電? " + (battery.charging ? "是" : "否"));
            console.log("電量等級(jí): " + battery.level * 100 + "%");
            console.log("充電時(shí)間: " + battery.chargingTime + " s");
            console.log("放電時(shí)間: " + battery.dischargingTime + "s");
          });
          

          我們可以通過(guò)getBattery拿到設(shè)備電池信息,這個(gè)api非常有用,比如我們可以在用戶電量不足時(shí)禁用網(wǎng)站動(dòng)畫或者停用一些耗時(shí)任務(wù),亦或者是對(duì)用戶做適當(dāng)?shù)奶嵝?改變網(wǎng)站顏色等,對(duì)于webapp中播放視頻或者直播時(shí),我們也可以用css畫一個(gè)電量條,當(dāng)電量告急時(shí)提醒用戶.作為一個(gè)優(yōu)秀的網(wǎng)站體驗(yàn)師,這一塊還是不容忽視的.

          參考文獻(xiàn)

          • ECMAScript 6 入門 - 阮一峰
          • MDN web English docs

          主站蜘蛛池模板: 国产在线精品一区二区三区不卡| 精品欧美一区二区在线观看| 日本一区二三区好的精华液 | 国产成人精品一区二区三区免费 | 在线观看一区二区精品视频| 天堂不卡一区二区视频在线观看| 亚洲AV日韩AV天堂一区二区三区| 国产伦精品一区二区三区四区 | 精品欧洲av无码一区二区| 天美传媒一区二区三区| 亚洲国产老鸭窝一区二区三区 | 精品一区二区在线观看| 在线免费观看一区二区三区| 亚洲无删减国产精品一区| 精品一区二区三区水蜜桃| 亚洲av乱码一区二区三区| 波多野结衣的AV一区二区三区 | 国产一区在线观看免费| 国产91精品一区二区麻豆网站| 中文字幕无线码一区| 国产精品一区二区四区| 日韩精品乱码AV一区二区| 日韩人妻无码一区二区三区综合部 | 亚洲香蕉久久一区二区三区四区| 亚洲无线码一区二区三区| 国产一区二区视频在线播放| 国产成人免费一区二区三区| 亚洲av无一区二区三区| 综合无码一区二区三区四区五区| 中文字幕一区二区三区久久网站| 东京热无码av一区二区| 夜夜精品视频一区二区| 波多野结衣AV无码久久一区| 激情久久av一区av二区av三区| 五月婷婷一区二区| 久久91精品国产一区二区| 精品乱人伦一区二区三区| 日韩中文字幕一区| 久久精品国产第一区二区三区| 国产美女精品一区二区三区| 国产成人综合精品一区|