p
標籤,標題要用h1-h6
標籤.<!DOCTYPE> 文檔聲明,它不是HTML標籤,是一個指示web瀏覽關於頁面使用哪一個HTML版本編寫的指令。<!DOCTYPE> 聲明必須位於文檔的第一行,位於<html>標籤以前。<!DOCTYPE html>
html
<html lang='en'>lang屬性設定文檔語言。node
做用:SEO搜索引擎優化;有助於閱讀障礙人士,經過讀屏器閱讀頁面jquery
還能夠是 <html lang="zh-CN">
web
5.meta標籤的幾種用法。面試
//這行代碼的意思是,文檔用UTF-8編碼的,瀏覽器解析的時候用UTF-8編碼解析。 <meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="description" content="騰訊網(www.qq.com)是中國最瀏覽量最大的門戶網站">
6.你用過哪些 HTML5標籤。ajax
內容性的標籤:編程
<header> 網頁的頭部 <nav> 網頁的導航 <section> 標籤訂義文檔中的節(好比章節、頁眉、頁腳或文檔中的其餘部分。) <article> 標籤的內容獨立於文檔的其他部分。好比外部的一篇文章,一個博客,論文等。 <aside> 網頁側邊欄 <footer> 網頁的頁腳
功能性的標籤json
<canvas> 經過腳本繪製圖像 <Audio> 播放音頻 <Video> 播放視頻
box-sizing:content-box; box-sizing:border-box; 目前使用此屬性,須要加前綴,Mozilla須要加上-moz-,Webkit內核須要加上-webkit-,Presto內核-o-,IE8-ms- -webkit-box-sizing:content-box; -moz-box-sizing:birder-box;
標準盒模型的缺點是,盒子的寬度和高度計算複雜(兩子元素並排例子)
怪異盒模型的優勢,當肯定了width/height以後,能夠隨意修改padding和border的厚度值,而不用擔憂父容器被撐爆。canvas
//行內元素 text-align:center //塊級元素 margin-left: auto; margin-right:auto;
//方案1 position-----元素固定寬高的狀況下 <div class="out"> out <div class="in">in</div> </div> <style> .out{ width:300px; height:300px; background:#ccc; color:red; position:relative; } .in{ width:100px; height:100px; background:pink; color:blue; position:absolute; left:50%; top:50%; margin-left:-50px; margin-top:-50px; } </style>
image.png跨域
//方案2 元素寬高不固定的狀況下,把上邊的 margin-left:(寬度/2); margin-top:(高度/2) 換成 transform:translate(-50%,-50%); <div class="out"> out <div class="in">測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測</div> </div> <style> .out{ width:300px; height:300px; background:#ccc; color:red; position:relative; } .in{ background:pink; color:blue; position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); } </style>
image.png
//方案3 flex <div class="out"> <div class="in"></div> </div> <style> .out{ width:300px; height:300px; background:#ccc; display:flex; //flex佈局 justify-content:center; //規定項目在主軸上居中對齊(水平居中) align-items:center; //規定項目在交叉軸上居中對齊(垂直居中) } .in{ width:100px; height:100px; background:pink; } </style>
image.png
//方案4 table-cell 給父元素設置 display:table; 父元素變成塊級表格元素(至關於table); 給子元素設置 display:table-cell;使子元素變成表格單元格(至關於td),而後設置 vertical-align:center; text-align:center; <div class="out"> <div class="in">哈哈哈哈哈哈哈哈哈哈</div> </div> <style> .out{ width:300px; height:300px; background:#ccc; display:table; } .in{ background:pink; display:table-cell; vertical-align:middle; text-align:center; } </style>
image.png
.box{ display:flex; } //行內元素也能夠設置爲flex .box{ display:inline-flex; } 注意設爲flex佈局後,子元素的float,clear,vertical-align屬性將失效。
採用flex佈局的元素,成爲flex容器。它的全部子元素自動成爲容器成員,稱爲flex項目。
經常使用的,設置到容器上的屬性有:
1.flex-direction:屬性決定主軸的方向(即項目的排列方向)。 .box{ flex-direction:row | row-reverse | column | column-reverse } -------------------------------------------------------------------------------- 2. flex-wrap:默認狀況下,項目都排在一條線上。這個屬性定義,若是一條軸線拍不下,如何換行。 .box{ flex-wrap:nowrap | wrap | wrap-reverse } -------------------------------------------------------------------------------- 3. justify-content:屬性定義了項目在主軸上的對齊方式。 .box{ justify-content:flex-start | flex-end | center | space-between | space-around } -------------------------------------------------------------------------------- 4.align-items:屬性定義項目在交叉軸上如何對齊 .box{ align-items:flex-start | flex-end | center | baseline | stretch } baseline 項目的第一行文字的基線對齊 stretch(默認值) 若是項目未設置高度或設爲auto,將佔滿整個容器的高度。 -------------------------------------------------------------------------------- 5. align-content:屬性定義了多根軸線的對齊方式。若是項目只有一根軸線,該屬性不起做用。 .box { align-content: flex-start | flex-end | center | space-between | space-around | stretch; } -------------------------------------------------------------------------------- 6. flex-flow 是flex-direction和 flex-wrap的簡寫,默認是 row nowrap
設置到項目上的屬性:
1.order 屬性定義項目的排列順序。數值越小,排列越靠前。默認爲0 .item { order: 1; } 2.flex-grow屬性定義項目的放大比例,默認爲0.即若是有剩餘空間,也不放大。 .item { flex-grow: <number>; /* default 0 */ } 3.flex-shrink 屬性定義了項目的縮小比例。默認爲1,若是空間不足,項目將縮小。 .item { flex-shrink: <number>; /* default 1 */ } 若是全部項目的flex-shrink屬性都爲1,當空間不足時,都將等比例縮小。若是一個項目的flex-shrink屬性爲0,其餘項目都爲1,則空間不足時,前者不縮小。 負值對該屬性無效。 4. flex-basis 屬性定義了在分配多餘空間以前,項目佔據的主軸空間。默認爲auto.即項目原本的大小。它能夠設爲跟width或height屬性同樣的值(好比350px),則項目將佔據固定空間。 .item { flex-basis: <length> | auto; /* default auto */ } 5. flex 屬性是flex-grow,flex-shrink,flex-basis,默認值爲0 1 auto 該屬性有兩個快捷值:auto (1 1 auto) 和 none (0 0 auto)。 建議優先使用這個屬性,而不是單獨寫三個分離的屬性,由於瀏覽器會推算相關值。 6.align-self 屬性 容許單個項目有與其餘項目不同的對齊方式,可覆蓋align-items屬性,默認值爲auto,表示繼承父元素的align-items屬性,若是沒有父元素,則等同於stretch。 .item { align-self: auto | flex-start | flex-end | center | baseline | stretch; }
1. 根元素 即HTML元素 2. float的值,不爲none 3. overflow的值,不爲visible 4. display的值,爲inline-block,table-cell ,table-caption 5. position 的值,爲absolute 或fixed
!important 優先級最高 權值無窮大 style 寫在元素行的內聯樣式其次 權值1000 id 權值100 class/僞類/屬性 權值10 標籤選擇器 權值1 通配符 權值0
更準確的說法 1.越具體,優先級越高 2.寫在後面的,會覆蓋寫在前面的 3.important優先級最高,可是要少用
6.清除浮動的方法:
方法 1:給父元素設置高度 2:給父元素設置overflow:hidden 3: 最佳實踐 .clearfix::after{ content:''; display:block; clear:both; } .clearfix加到容器上,裏邊的子元素浮動就被清除了
1. ES6語法知道哪些?分別怎麼用
A-------新增聲明命令 let 和 const, let表示變量 const表示常量
特色: 1. 都是塊級做用域,以{}代碼塊做爲做用域範圍,只能在代碼塊裏面使用。 2. 不存在變量提高,只能先聲明再使用,不然會報錯。在代碼塊內,在聲明變量以前,該變量都是不可用的,這在語法上成爲'暫時性死區' 3. 在一個代碼塊內,不容許重複聲明 4. const聲明的是一個只讀常量,在聲明的時候就要賦值,不然會擺錯,(若是const聲明的是一個對象,對象所包含的值是能夠修改的,抽象一點說,對象所指的地址是不可以改變的,變量成員是能夠修改的)
B--------模板字符串
用一對反引號(``)標識,它能夠當作普通字符串使用,也能夠用來定義多行字符串。也能夠在字符串中嵌入變量,JS表達式,或函數。須要寫在${}中
var str = `abc def gh`; console.log(str); let name = '明'; function a(){ return 'ming' } console.log(`個人名字叫作${name},年齡${17+5}歲,個人性別是${'男'},遊戲ID:${a()})
C------函數的擴展
function A(a,b=1){ console.log(a+b) console.log(a); console.log(b); } A(1); // 2, 1,1 A(2,3); //5,2,3
//只有一個參數時候,能夠省略參數的括號。 //若是沒有參數,或者參數在2個及兩個以上,必須帶括號 //當代碼只有一行,而且有當即返回值的時候,能夠省略花括號{} var f = a => a+1 var sayHi = ()=>{ console.log('hi') } var sum = (num1,num2)=>{ console.log(num1+mun2) }
箭頭函數的特色
箭頭函數的this是在定義函數的時候綁定,而不是在執行函數的時候綁定。
所謂在定義函數的時候綁定的意思是,箭頭函數的this是繼承自父執行上下文。箭頭函數沒有本身的this.
D---------對象的擴展
var foo = 'bar'; var baz = {foo}; //至關於 var bar={foo:foo}
var o = { sayHi:function(){ console.log('hi') } } 至關於 var o = { sayHi(){ console.log('hi') } }
var person = {name:"john",age:20,study(){alert('study')}}; console.log(Object.keys(person)) // ["name", "age", "study"] console.log(Object.keys(['aa','bb','cc']); //["0", "1", "2"] console.log(Object.keys('abcdef')); //["0", "1", "2", "3", "4", "5"]
var target ={} var obj1 = { name:'petter', age:20, sex:'女' } var obj2 = { sex:'男', score:100 } Object.assign(target,obj1,obj2); console.log(target); //{age: 20,name: "petter", score: 100, sex: "男"}
E---------- for...of循環
是遍歷全部數據結構的統一方法。for...of循環可使用的範圍包括數組,Set,Map結構,某寫類數組對象(arguments, DOM NodeList對象)以及字符串。
var arr = ["水星","金星","地球","火星"]; for(var s of arr){ console.log(s); //"水星" "金星" "地球" "火星" }
F--------import 和 export
ES6標準中,JavaScript 原生支持模塊(module)了,這種將JS代碼分割成不一樣功能的小塊進行模塊化,將不一樣功能的代碼分別寫在不一樣的文件中,各模塊只須要導出公共接口部分,而後經過模塊的導入方式能夠在其餘地方使用。
export 用於對外輸出本模塊(一個文件能夠看作是一個模塊)的變量接口
import 用於在一個模塊中加載另一個含有export接口的模塊
import和export命令只能在模塊的頂部,不能在代碼塊之中。
G-----Promise對象
Promise是異步編程的一種解決方案,將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。
它有三種狀態,分別是 pending-進行中,resolved-已完成,rejected-已失敗
Promise構造函數接收一個參數,這個參數是函數,而且這個函數傳入兩個參數,分別是 resolve 和 reject,分別是異步操做執行成功後的回調函數,和異步操做執行失敗後的回調函數。(按照標準來說,resolve是將Promise的狀態置爲fullfiled,reject是將Promise的狀態置爲rejected。)
因此咱們用Promise的時候通常是包在一個函數中,在須要的時候去運行這個函數,如:
function runAsync(){ var p = new Promise((resolve,reject)=>{ setTimeout(function(){ console.log('執行完成'); resolve('隨便什麼數據') },2000) }) return p; } runAsync();
在咱們包裝好的函數最後,會return出Promise對象,也就是說,執行這個函數咱們獲得了一個Promise對象。
Promise對象上有then 和 catch方法。
runAsync().then(function(data){ console.log(data) //'隨便什麼數據' //後面能夠用傳過來的數據作些其餘操做 //...... })
在runAsync()的返回上直接調用then方法,then接收一個參數,是函數,而且會拿到咱們在runAsync中調用resolve時傳的的參數。
這時候你應該有所領悟了,原來then裏面的函數就跟咱們平時的回調函數一個意思,可以在runAsync這個異步任務執行完成以後被執行。這就是Promise的做用了,簡單來說,就是能把原來的回調寫法分離出來,在異步操做執行完後,用鏈式調用的方式執行回調函數。
而Promise的優點在於,能夠在then方法中繼續寫Promise對象並返回,而後繼續調用then來進行回調操做。
鏈式操做的用法
從表面上看,Promise只是可以簡化層層回調的寫法,而實質上,Promise的精髓是「狀態」,用維護狀態、傳遞狀態的方式來使得回調函數可以及時調用,它比傳遞callback函數要簡單、靈活的多。因此使用Promise的正確場景是這樣的:
function runAsync1(){ var p = new Promise(function(resolve, reject){ //作一些異步操做 setTimeout(function(){ console.log('異步執行1完成'); resolve('隨便什麼數據1'); }, 2000); }); return p; } function runAsync2(){ var p = new Promise(function(resolve, reject){ //作一些異步操做 setTimeout(function(){ console.log('異步執行2完成'); resolve('隨便什麼數據2'); }, 2000); }); return p; } function runAsync3(){ var p = new Promise(function(resolve, reject){ //作一些異步操做 setTimeout(function(){ console.log('異步執行3完成'); resolve('隨便什麼數據3'); }, 2000); }); return p; } runAsync1() .then(function(data){ console.log(data); return runAsync2() }) .then(function(data){ console.log(data); return runAsync3() }) .then(function(data){ console.log(data) }) //2秒後打印 "異步執行1完成" "隨便什麼數據1" //又2秒後打印 "異步執行2完成" "隨便什麼數據2" //又2秒後打印 "異步執行3完成" "隨便什麼數據3"
在then方法中,能夠直接return 數據,而不是Promise對象,在後邊的then中就能夠接受到數據了。
runAsync1() .then(function(data){ console.log(data); return runAsync2() }) .then(function(data){ console.log(data); return "直接返回數據" }) .then(function(data){ console.log(data) }) //2秒後打印 "異步執行1完成" "隨便什麼數據1" //又2秒後打印 "異步執行2完成" "隨便什麼數據2" "直接返回數據"
reject的用法
咱們前面的例子都是隻有「執行成功」的回調,尚未「失敗」的狀況
reject就是把Promise的狀態設置爲 rejected,這樣咱們在then中就能捕捉到,而後執行失敗狀況的回調。
function getNumber(){ return new Promise((resolve,reject)=>{ setTimeout(function(){ var num = Math.ceil(Math.random()*10) //生成0-10之間的整數 if(num<=5){ resolve(num) }else{ reject('數字太大了') } },2000) }) } getNumber() .then( function(data){ console.log('resolved') console.log(data); }, function(reason,data){ console.log('rejected') console.log(reason) } )
getNumber函數用來異步獲取一個數字,2秒後執行完成,若是數字小於等於5,咱們認爲是「成功」了,調用resolve修改Promise的狀態。不然咱們認爲是「失敗」了,調用reject並傳遞一個參數,做爲失敗的緣由。 運行getNumber而且在then中傳了兩個參數,then方法能夠接受兩個參數,第一個對應resolve的回調,第二個對應reject的回調。因此咱們可以分別拿到他們傳過來的數據
catch 的用法
咱們知道 Promise除了有一個then方法,還有一個catch方法,它是幹什麼的呢?
其實它跟then方法的第二個參數的做用是同樣的。用來指定reject的回調。
它的用法:
getNumber() .then( function(data){ console.log('resolved') console.log(data); } ) .catch( function(reason){ console.log('rejected') console.log(reason) } )
效果和寫在then的第二個參數裏面同樣。不過它還有另一個做用,在執行resolve的回調(也就是then方法的第一個參數)時,若是拋出異常(代碼出錯了),那麼並不會報錯卡死JS,而是會進入到catch方法中
getNumber() .then(function(data){ console.log('resolved'); console.log(data); console.log(somedata); //此處的somedata未定義 }) .catch(function(reason){ console.log('rejected'); console.log(reason);
在 resolve的會調用,咱們console.log(somedata),而somedata是未定義的,若是不用Promise,代碼運行到這裏,就直接在控制檯報錯了,不往下運行了。可是在這裏會獲得這樣的結果:
image.png
也就是說進到catch方法裏面去了,並且把錯誤緣由傳到了reason參數中。即使是有錯誤的代碼也不會報錯了,這與咱們的try/catch語句有相同的功能。
all方法
Promise.all()提供了並行執行異步操做的能力。而且在全部異步操做執行完成之後,才執行回調。
Promise .all([runAsync1(), runAsync2(), runAsync3()]) .then(function(results){ console.log(results); });
Promise.all(),接收一個數組做爲參數,數組裏的元素最終都返回一個Promise對象。這樣,三個異步的操做就並行執行了,等到他們都執行完之後,纔會進入到then裏邊,那麼三個異步操做執行之後返回的數據哪裏去了呢? all會把全部異步操做的結果放進一個數組,而且把數組傳遞給then,就是上邊的results.
因此上邊代碼輸出的結果是:
image.png
有了all方法,你就能夠並行執行多個異步操做,而且在一個回調中處理全部的返回數據,有一個場景很適合用這個方法。
好比一些遊戲類的素材比較多的應用,打開網頁時預先加載,須要用到各類資源,好比圖片,flash,以及各類靜態文件。全部都加載完之後,再進行頁面的初始化。
race的用法
Promise.all方法,其實是誰跑的慢,以誰爲準執行回調。那麼相對的就有另一個方法,以誰跑的塊,以誰爲準執行回調。
就是race方法,這個詞原本就是賽跑的意思。race的用法與all同樣。咱們修改下上邊的計時器的時間:
function runAsync1(){ var p = new Promise(function(resolve, reject){ //作一些異步操做 setTimeout(function(){ console.log('異步執行1完成'); resolve('隨便什麼數據1'); }, 1000); }); return p; } function runAsync2(){ var p = new Promise(function(resolve, reject){ //作一些異步操做 setTimeout(function(){ console.log('異步執行2完成'); resolve('隨便什麼數據2'); }, 3000); }); return p; } function runAsync3(){ var p = new Promise(function(resolve, reject){ //作一些異步操做 setTimeout(function(){ console.log('異步執行3完成'); resolve('隨便什麼數據3'); }, 2000); }); return p; } Promise .race(runAsync1(),runAsync2(),runAsync3()) .then(function(results){ console.log(results); }) //1秒後打印 "異步執行1完成" //再過1秒後打印 "異步執行3完成" //再過2秒後打印 "異步執行2完成"
這個race有什麼用呢?使用場景仍是不少的,好比咱們能夠用race給某個異步請求設置超時時間,而且在超時後執行相應的操做,代碼以下:
//請求某個圖片資源 function requestImg(){ var p = new Promise(function(resolve, reject){ var img = new Image(); img.onload = function(){ resolve(img); } img.src = 'xxxxxx'; }); return p; } //延時函數,用於給請求計時 function timeout(){ var p = new Promise(function(resolve, reject){ setTimeout(function(){ reject('圖片請求超時'); }, 5000); }); return p; } Promise .race([requestImg(), timeout()]) .then(function(results){ console.log(results); }) .catch(function(reason){ console.log(reason); });
requestImg函數會異步請求一張圖片,我把地址寫爲"xxxxxx",因此確定是沒法成功請求到的。timeout函數是一個延時5秒的異步操做。咱們把這兩個返回Promise對象的函數放進race,因而他倆就會賽跑,若是5秒以內圖片請求成功了,那麼遍進入then方法,執行正常的流程。若是5秒鐘圖片還未成功返回,那麼timeout就跑贏了,則進入catch,報出「圖片請求超時」的信息。運行結果以下:
image.png
H-------解構賦值
ES6容許按照必定的模式,從數組和對象中提取值,對變量進行賦值。這杯成爲解構賦值。
let [a,b,c,d] = [1,2,3]; console.log(a); 1 console.log(b); 2 console.log(c); 3 console.log(d); undefined
-對象的結構賦值
對象的結構賦值與數組有一個重要的不一樣,數組的元素是按照次序排列的,變量的取值由它的位置決定。而對象的屬性是沒有次序的,變量必須與屬性同名,才能取到正確的值。
var person = {name:'小明',age:20,sex:'男'}; var {name,age,sex} = person; console.log(name); console.log(age); console.log(sex); //若是想要變量名和屬性名不一樣,能夠這樣寫 let {name:foo,age:baz} = {name:'小明',age:20,sex:'男'} console.log(foo); '小明' console.log(baz); 20
I--------set數據結構
Set的數據結構,相似數組,全部的數據都是惟一的,沒有重複的值,它自己是一個構造函數;
var arr = [1,2,2,3,4,4]; var s = new Set(arr); console.log(s); //{1,2,3,4} 屬性和方法: size 數據的長度 add() 添加某個值,返回 Set 結構自己。 delete() 刪除某個值,返回一個布爾值,表示刪除是否成功。 has() 查找某條數據,返回一個布爾值。 clear() 清除全部成員,沒有返回值。 console.log(s.size); //4 console.log(s.add(5)); //{1,2,3,4,5} console.log(s.delete(1)); //true console.log(s.has(2)); //true s.clear();
前提條件:一個函數在短期內被屢次執行。
可是這種短期內屢次重複執行,一般狀況下是沒有必要的。咱們須要優化這種高頻執行的JS。
優化的方法就是 函數節流 和 函數防抖
函數節流:
讓函數在特定的時間以內只執行一次。特定時間內若是屢次觸發函數,只有一次生效。
應用場景 :下拉加載。
函數防抖:
頻繁觸發某一事件,其中兩次觸發間隔時間大於等於特定時間,才執行代碼一次。若是在特定時間內,又觸發了一次事件,那麼從新開始計算函數執行時間。簡單的說,一個動做連續觸發,只執行最後一次。
應用場景:搜索框搜索輸入,好比用戶輸入字母間隔超過2秒再發送請求。
函數節流的例子 <button id="btn">大招<button> <script> var cd = false; //cd用來記錄大招是否冷卻,默認是沒有冷卻 function attack(){ console.log('發起攻擊') } var button = document.queryselector('#btn'); button.onclick = function(){ if(cd){ //點擊大招按鈕的時候判斷一下,若是大招冷卻,給出提示。 console.log('大招冷卻,沒法攻擊!') }else{ //若是大招沒有冷卻,發起攻擊 attack(); cd = true; //發起攻擊後,技能狀態調整爲冷卻 var timer = setTimeout(function(){ cd = false; //3秒鐘後,技能冷卻結束 },3000) } } </script>
//函數防抖例子 //公交關門 function close(){ console.log('關門'); } var button = document.querySelector('#btn'); var timer = null ; //定時器一開始是空的 button.onclick = function(){ //若是點擊了按鈕,發現上一個計時器還存在,那麼就把它清除掉。 if(timer){ window.clearTimeout(timer); } //若是5秒種以內沒有再點,就設置一個定時器,並執行關門函數,而且把定時器清除掉。 timer = setTimeout(function(){ close(); timer= null; },5000) }
var xhr = new XMLHttpRequest(); xhr.open('GET','/xxx',true); xhr.onreadystatechange = function(){ if(xhr.readystate === 4){ console.log('請求完成') if(xhr.status>=200&&xhr.status<300||xhr.status === 304){ console.log('請求成功') }else{ console.log('請求失敗') } } } xhr.send();
主要是看函數怎麼調用的!
fn() // this指向 window/global obj.fn() // this 指向 obj fn.call(xx) //this 指向 xx fn.bind(xx) //this指向 xx new Fn() //this指向 新的對象 fn = ()=>{} //this 指向外邊的this 判斷的通用方法 function fn(a,b){ console.log(this) } fn(1,2);//這句等價於下邊 fn.call(undefined,1,2); fn.apply(undefined,[1,2]); 注意: 在嚴格模式下 'use strict' ,此時 fn裏的this,就是call和apply的第一個參數,也就是 undefined 在非嚴格模式下,也就是不用 'use strict' ,call和apply裏的第一個參數若是是undefined或者是null,那麼this會默認替換爲 window 在看一個例子: var obj = { fn:function(a,b){ console.log(this) }, child:{ fn2:function(){ console.log(this) } } } obj.fn(1,2); 等價於 obj.fn.call(obj,1,2); obj.fn.apply(obj,[1,2]); //因此this是obj obj.child.fn2();等價於 obj.child.fn2.call(obj.child); obj.child.fn2.apply(obj.child); //因此this是 obj.child
閉包:
就是能讀取其餘函數內部變量的函數,在JS中,只有函數內部的子函數,可以讀取函數內部的局部變量。因此閉包能夠理解爲,定義在一個函數內部的函數。
閉包的缺點:
讓變量始終保持在內容中,內存消耗會很大。
什麼是詞法做用域?
詞法做用域就是函數建立的時候所在的做用域。
因此,函數在執行的時候,使用的變量,會先從本身內部去找,若是本身內部沒有定義,就到本身的詞法做用域(scopes)去找。
function car(){ var speed = 0; function fn(){ speed++; console.log(speed); } return fn; } var speedUp = car(); speedUp(); //1 speedUp(); //2 // 若是在car函數裏,再聲明一個函數fn,並返回fn,fn()內部又使用了car聲明的變量,而後調用car函數並賦值給 一個全局變量speedUp // 由於speedUp 屬於全局做用域,而全局做用域在頁面沒有關閉的時候,是不會銷燬的。因此也就致使了fn函數不會被銷燬 // 執行speedUp就至關於 執行fn(),而fn()函數內部有用到局部變量speed,按照做用域鏈來尋找,fn()函數內沒有聲明 // 繼續往上一級找,fn()函數是聲明在car()內的,而speed是在car內聲明的 // 因此第一次執行 speedUp() 的時候,結果是1;執行以後,speed是沒有被銷燬的 // 再次執行就是2 //再看個例子 var fnArr = []; for(var i=0;i<10;i++){ fnArr[i] = function(){ return i; } } console.log(fnArr[1]); // 這個時候數組裏存的都是函數 function(){ return i} console.log(fnArr[2]()) //10 console.log(fnArr[5]()) //10 console.log(fnArr[7]()) //10 //爲何會輸出10呢? 分析一下 fnArr[2]自己是一個函數,加括號,表示執行它指向的那個函數。 函數內部 return i,用到了i這個變量,因此會從本身內部去找這個變量,發現沒有聲明i,而後在去函數聲明的做用域去找,函數是在for裏聲明的,可是for自己並非函數,因此function函數聲明的做用域是全局做用域。而全局做用域裏,for循環完之後,i已經變爲10了,因此fnArr[i]()會輸出10. 那麼若是咱們想輸出 0,1,2,3,4,5,....怎麼辦呢? 用當即執行函數 var fnArr = []; for(var i=0;i<10;i++){ fnArr[i] =( function(j){ return j; })(i) } console.log(fnArr[0]) //0 console.log(fnArr[5]) //5 console.log(fnArr[7]) //7 再看個例子 for(var i=0;i<5;i++){ setTimeout(function(){ console.log(i) },2000) } //這裏2秒後會輸出5個5 for(var i=0;i<5;i++){ (function(j){ setTimeout(function(){ console.log(j) },2000) })(i) } //這裏2秒後會輸出 0,1,2,3,4
再熟悉一下做用域鏈
var x = 10 function foo(){ console.log(x); } foo(); //10 function bar(){ var x = 20; foo(); } bar(); //10 執行bar就是執行foo,foo輸出x,先從本身內部去找 發現沒有聲明x,而後到foo聲明的做用域去找 foo是聲明在全局做用域的,而全作做用域下,有聲明變量x,因此輸出10 //下邊之因此輸出30 是由於foo是在bar內部聲明的。 var x = 10; bar(); //30 function bar(){ var x = 30; function foo(){ console.log(x); } foo(); }
什麼是跨域?
--------是指不一樣源的網站之間的訪問。
同源策略
---------提到跨域,就不得不說一下同源策略,同源策略是NetScape提出的瀏覽器的一種安全策略,也就是指a網站,不能隨便讀取b網站的內容。
所謂同源:
--------指的是,協議、域名、端口、相同
最多見的應用是,當咱們調用ajax接口時,若是不設置跨域,瀏覽器會報錯。這證實使用XMLHttpRequest對象不能發送跨域請求。
有疑惑的小夥伴確定會問,那我用a標籤請求其餘網站,是否是就是跨域了呢?
這裏要明白跨域是指在當前域下調用其餘域下的東西,而連接則是直接跳轉到對方的域下了,跟你以前的域名毫無關係。
若是想從你的網站跨域去另外一個網站拿到一些資源,有一個前提是另一個網站的服務器支持跨域,這個須要在服務器端設置,不一樣服務器的設置方法不同,咱們在這裏很少說,就看客戶端跨域如何解決?
解決跨域最多見的方式是 jsonp。
先弄清楚 json和jsonp的區別
json (JavaScript Object Notation)是一種輕量級的數據交換格式,用來在瀏覽器和服務器之間交換數據。
jsonp (JSON With Padding) 就是打包在函數中調動的json,或者包裹的json
json 是一種數據格式;jsonp是一種數據調用方式
//json { "name":"小明" } //jsonp callback({ "name":"小明" })
jsonp是jquery給咱們封裝的一個方法,使用方法以下:
$.ajax({ ulr:'xxx', type:'GET', dataType:'jsonp', success:function(data){ console.log(data) } })
上邊的代碼是當咱們調用外部的一個接口時,經過設置jquery裏的ajax方法裏的datatype屬性的值爲jsonp,就能夠成功調用這個接口了。
首先咱們須要明白,在頁面上直接發起一個跨域的ajax請求是不能夠的,可是,在頁面上引入不一樣域上的js腳本倒是能夠的,就像你能夠在本身的頁面上使用<img src=""> 標籤來隨意顯示某個域上的圖片同樣。
看下怎麼利用<script src="">來完成一個跨域請求。
<div class="container"> <ul class="news"></ul> <button class="show">show news</button> </div> $('.show').addEventListener('click', function () { var script = document.createElement('script'); script.src = 'http://127.0.0.1:8080/getNews?callback=appendHtml'; document.head.appendChild(script); // document.head.removeChild(script); //爲了保持代碼的整潔,直接刪除了新增的標籤,可是標籤的做用已經起到了 }) function appendHtml(news) { var html = ''; for (var i = 0; i < news.length; i++) { html += '<li>' + news[i] + '</li>'; } console.log(html); $('.news').innerHTML = html; }
jsonp跨域的原理:
缺點: jsonp只能解決get方式的跨域。若是傳輸的數據比較大,這種方式就不行了。
cors跨域
cors (cross origin resource sharing) 全稱 '跨域資源共享'
相對於jsonp, cors支持全部類型的HTTP請求,而且實施起來很是簡單。
cors背後的基本思想是,使用自定義的HTTP頭部容許瀏覽器和服務器互相瞭解對方,從而決定請求和響應成功與否。
cors原理:
當使用XMLHttpRequest發送請求的時候,瀏覽器發現該請求不符合同源策略,會給該請求加一個請求頭 origin,後臺會進行一系列的處理,若是肯定接受請求,則在返回結果中,加入一個響應頭 Access-Control-allow-origin;瀏覽器判斷該響應頭中是否包含 origin的值,若是有,則瀏覽器會處理響應,咱們就拿到數據。若是沒有,則瀏覽器會直接駁回,咱們就拿不到數據。
IE10以上支持,現代瀏覽器也都支持cors
JSONP和CORS的區別?
async
是異步的縮寫。而await 能夠認爲是 async wait的縮寫。
async
做爲一個關鍵字放在函數前面,用來聲明這個函數是一個異步函數
。既然是異步函數就意味着該函數的執行不會阻塞後邊的代碼;async函數返回的是一個Promise對象
。
await用於等待一個異步方法執行完成
,await等待的是一個表達式,而表達式的計算結果是一個Promise對象或者其餘值。
(await至關於運算符,用於組成表達式,await表達式的運算結果取決於它等到的東西)
若是等到的不是一個Promise對象,那麼await表達式的運算結果就是它等到的東西。
若是它等到的是一個Promise對象,await就忙起來了,它會阻塞後面的代碼,等待Promise對象resolve,而後獲得resolve的值。做爲await表達式的運算結果。
await只能用到async函數中。
function sayHi(name){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('Hi'+name); },2000) }) } async function test(){ var result = await sayHi('小明'); console.log(result); } test();
async和 await捕獲異常
,須要用到 try/catch
的方式:
由於await後面跟着的是 Promise對象,當有異常的狀況下,會被Promise對象內部的catch捕獲。而await就是一個then的語法糖,並不會捕獲異常,那麼就須要藉助
try/catch
來捕獲異常,並進行相應的邏輯處理。
function sayHi(name){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ //生成0-10的隨機整數 var num = Math.ceil(Math.random()*10); if(num>5){ //若是隨機數大於5,就resolve出結果 resolve('Hi'+name); }else{ //不然就拋出錯誤 reject('出錯了'); } },2000) }) } async function test(){ try{ var result = await sayHi('小明'); console.log(result); }catch(error){ console.log(error) } } test();
關鍵字: 一、遞歸 二、判斷類型 三、檢查循環引用(環) 四、不可能拷貝 _proto_
拷貝分爲淺拷貝和深拷貝
在這以前,咱們先弄清楚兩個概念。
Number ,String,Boolean,Null,Undefined 都是基本數據類型 基本數據類型時按值訪問的,由於能夠直接操做保存在保存在變量中的實際值。 var a = 10; var b = a; //b只是保存了a複製的一個副本。因此 a和b是互相獨立的。 console.log(a); //10 console.log(b); //10 //a值的改變不會影響b的值 a = 20; console.log(a); //20 console.log(b); //10
引用數據類型也就是對象類型 Object type.好比 Object,Array,Function,Data等。 JavaScript的引用數據類型是保存在堆內存中的對象。 與其餘語言不一樣的是,你不能夠直接訪問堆內存空間中的位置和操做堆內存空間。只能操做對象在棧內存中的引用地址。 因此,引用類型數據在棧內存中保存的是對象在堆內存中的引用地址,經過這個引用地址能夠快速查找到保存在堆內存中的對象。 var obj1 = new Object(); var obj2 = obj1; obj2.name = '小明'; console.log(obj1.name); 說明這兩個引用數據類型指向了同一個堆內存對象。obj1賦值給onj2,實際上這個堆內存對象在棧內存的引用地址複製了一份給了obj2,可是實際上他們共同指向了同一個堆內存對象。 實際上改變的是堆內存對象。
下面咱們來演示這個引用數據類型賦值過程:
image.png
好了,上邊兩個概念瞭解清楚之後,咱們進入正題。
對象淺拷貝比較簡單,就是將一個變量賦值給另一個變量
var obj1 = { name:"小明", age:20 } var obj2 = obj1; console.log(obj2.age) //20 obj2通過淺拷貝,擁有了obj1的屬性
封裝淺拷貝方法
var easyCopy = function(oldObj){ //constructor 屬性應用了該對象的構造函數 var newObj = oldObj.constructor === Array?[]:{}; if(typeof oldObj != 'object') return; for(var key in oldObj){ if(oldObj.hasOwnProperty(key)){ newObj[key] = oldObj[key] } } return newObj; } var obj1 = { name:'小明', age:20 } var obj2 = easyCopy(obj1); console.log(obj2)
淺拷貝存在的問題:
咱們知道,引用數據類型的賦值,實際上是變量a把對象在堆內存中的地址,複製了一份給變量b,這個時候,a和b指向的都是同一個對象。經過a或者b均可以改變堆內存中的對象(好比添加刪除屬性),a和b是能夠相互影響的。因此這種淺拷貝會帶來BUG。
深拷貝的目的就是爲了實現 複製出2個徹底相同,卻又徹底獨立,互不干擾的對象。
var deepCopy = function(obj){ var cloneObj = Array.isArray(obj)? []:{}; if(obj && typeof obj === 'object' ){ for(var key in obj){ if(obj.hasOwnProperty(key)){ cloneObj[key] = typeof obj[key] === 'object'?deepCopy(obj[key]):obj[key] } } } return cloneObj; } var obj = { name:'小紅', age:18, friend:{ name:'小明', sex:'男', study:["數學","英文","物理"] } } var obj2 = deepCopy(obj); console.log(obj2) obj.friend = { name:'小明明明', sex:"女" } console.log(obj); console.log(obj2);
深拷貝的缺點:雖然深拷貝可以避免淺拷貝出現的問題。可是卻會帶來性能上的問題,若是一個對象很是複雜且數據龐大,所消耗的性能將會是很可觀的。
for..in能夠用來遍歷任何一個對象,它會將對象上的全部的屬性所有遍歷出來,包裹原型鏈上的屬性。因此上邊代碼中須要hasOwnProperty來判斷這個屬性究竟是不是對象自己的屬性。由於數組也是對象,for..in也能夠用來遍歷數組,可是for in損耗性能較多,因此儘可能不要用它來遍歷數組。
一個方法,重複調用自身的狀況,叫作遞歸。 須要注意的是,必定要有一個條件來結束遞歸,不然將會陷入無限的循環。 var num = 0; function recursion(){ if(num < 50){ num++; recursion() } } recursion()
trim()方法去掉字符串兩端的空格,不管有多少個空格都會去掉,字符串中間的空格不會被去掉。 function trim(string){ return string.replace(/^\s+|\s+$/g); } var str = ' ab cd ef '; var res = replace(str); console.log(res); //'ab cd ef'
//不用class function Person(name,age){ this.name = name; this.age = age; } Person.prototype.printName =function(){ console.log(this.name); } // 繼承屬性 function Mail(name,age,sex){ Person.call(this,name,age); this.sex = sex; } // 繼承方法 //Object.create 建立一個空對象,空對象的_proto_指向Person.prototype Mail.prototype = Object.create(Person.prototype); Mail.prototype.printSex = function(){ console.log(this.sex); } //由於Mail.prototype此時是指向Person.prototype的,因此Mail.prototype.contructor是指向 Person的。咱們須要修改它的指向。 Mail.prototype.contructor = Mail var john = new Mail('約翰',20,'男'); console.log(john.name); john.printName(); john.printSex();
//用class class Person { constructor(name,age){ this.name = name; this.age = age; } sayAge(){ console.log(`i am ${this.age}`) } } class Student extends Person{ constructor(name,age,score){ supre(name,age); this.socre = score; } sayScore(){ console.log(`i get ${this.score}`); } }
//雙循環去重 function fn(arr){ if(Array.isArray(arr)){ //聲明一個新的數組,而後把要遍歷的數組的第一個元素放入這個新數組 var newArr = [arr[0]]; //而後從第二個元素開始遍歷老的數組,同時遍歷新數組 //把老數組和新數組的已有元素作對比,若是不相等,就放入新數組。 for(let i=1;i<arr.length;i++){ let flag = true; for(let j=0;j<newArr.length;j++){ if(arr[i] === newArr[j]){ flag = false; break; } } if(flag){ newArr.push(arr[i]) } } } return newArr } var arr = [1,1,2,2,3,4,5,5]; var arr2 = fn(arr); console.log(arr2) [1, 2, 3, 4, 5] //indexOf去重 //兩個關鍵點 一、聲明新數組 二、判斷老數組裏的元素是否在新數組裏,沒有就push進新數組 function fn(arr){ if(!Array.isArray(arr)){ console.log('type error'); return } var newArr = []; for(let i=0;i<arr.length;i++){ if(newArr.indexOf(arr[i]) == -1){ newArr.push(arr[i]) } } return newArr; } var arr = [1,1,2,2,3,4,5,5]; var arr2 = fn(arr); console.log(arr2) [1, 2, 3, 4, 5] //set 去重 //ES6?新增了一個數據類型 set,set的一個最大的特色就是數據不重複。 //Set()函數能夠接收一個數組(或者類數組對象)做爲參數來初始化。 function fn(arr){ if(!Array.isArray(arr)){ console.log('type error'); return; } return [...new Set(arr)] } var arr = [1,1,2,2,3,4,5,5]; var arr2 = fn(arr); console.log(arr2) [1, 2, 3, 4, 5]
1. DOM事件模型是什麼?
Opera、Firefox、Chrome、Safari都支持DOM事件流,IE不支持事件流,只支持事件冒泡
事件處理程序是什麼?
響應某個事件的函數,就叫作事件處理程序
//HTML事件處理程序 <input type="button" name="btn" value="點擊" onclick="alert('clicked')" //DOM0級事件處理程序 <button id="box">點擊</button> var box = document.querySelector('#box'); box.onclick = function(){ console.log('DOM0級事件處理程序') } box.onclick = null //刪除綁定的事件 //DOM2級事件處理程序 <button id="box">點擊</button> var box = document.querySelector('#box'); box.addEventListener('click',function(){ console.log('DOM2級事件處理程序') },true) //刪除綁定的事件 box.removeEventListener('click',函數名,boolean) //IE事件處理程序 function showMes(){ console.log('我是IE事件處理程序') } var box = document.getElementById('box'); box.attatchEvent('onclick',showMes); //刪除綁定的事件處理程序 box.detachEvent('onclick',showMes);
跨瀏覽器事件處理程序
function showMes(){ console.log('我被點擊了') } var box = document.getElementById('box'); var EventUtil = { addHandler:function(element,type,handle){ if(element.addEventListener){ element.addEventListener(type,handle,false); }else if(element.attatchEvent){ element.attatchEvent('on'+type,handle) }else{ element['on'+type] = handle } }, removeHandler:function(element,type,handle){ if(element.removeEventListener){ element.removeEventListener(type,handle,false); }else if(element.detachEvent){ element.detachEvent('on'+type,handle); }else{ element['on'+type] = null; } } } EventUtil.addHandler(box,'click',showMes); EventUtil.removeHandler(box,'click',showMes);
事件對象
在觸發DOM上的事件時,都會產生一個事件對象Event
Event 對象表明事件的狀態,好比事件在其中發生的元素、鍵盤按鍵的狀態、鼠標的位置、鼠標按鈕的狀態。
兼容DOM的瀏覽器,會將一個對象,傳入事件處理程序中。
例如:
var box = document.querySelector('#box'); box.addEventListener('click',function(event){ console.log(event.type) //輸出事件的類型 }) event對象的屬性 event.target 獲取事件的類型 event.type 獲取事件的目標(就是時間綁定到哪一個元素上了) ...等等 event對象的屬性 event.stopPropagation : 再也不派發事件。就是終於事件在傳播過程的捕獲,目標處理,冒泡階段的傳播,事件再也不被派到其餘節點。 event.preventDefault : 通知瀏覽器不要執行與事件相關的默認動做。好比a標籤 IE事件對象 window.event 常見的屬性 window.event.type 至關於 event.type window.event.srcElement 至關與 event.target window.event.cancleBubble 至關於 event.stopPropagation() window.event.returnValue 至關於 event.preventDefault()
touch 觸摸事件,有4種之分 1. touch start 手指觸摸到屏幕會出發 2. touch move 當手指在屏幕上移動時會觸發 3. touch end 當手指離開屏幕時會觸發 4. touch cancel 可由系統進行的觸發,好比手指觸摸屏幕的時候,忽然alert了一下,則能夠觸發該事件。 tap事件 觸碰事件,通常用於代替click tap:手指碰一下屏幕會觸發 longTap :手指長按屏幕會觸發 singleTap:手指碰一下屏幕會觸發 doubleTap:手指雙擊屏幕會觸發 swipe事件 滑動事件 swipe:手指在屏幕上滑動時觸發 swipeLeft:手指在屏幕上向左滑動時會觸發 swipeRight:手指在屏幕上向右滑動時會觸發 swipeUp:手指在屏幕上向上滑動時會觸發 swipeDown:手指在屏幕上向下滑動時會觸發 模擬swipe事件:思路 記錄兩次touchmove的位置差,若是後一次在前一次的右邊,說明向右滑了。
事件委託就是利用事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。
舉個取快遞的例子:
有三個同事預計會在週一收到快遞。爲簽收快遞,有兩種辦法:一是三我的在公司門口等快遞;二是委託給前臺MM代爲簽收。
前臺MM收到快遞後,她會判斷收件人是誰,而後按照收件人的要求籤收,甚至代爲付款。
這種方案還有一個優點,那就是即便公司裏來了新員工(無論多少),前臺MM也會在收到寄給新員工的快遞後覈實並代爲簽收。
這個例子包含2層意思
1.如今委託前臺的同事,是能夠代爲簽收的(即程序中,現有的DOM節點是有事件的)
2.新員工也是能夠被前臺代爲簽收的(即程序中,新添加的DOM節點也是有事件的)。
<div class="container"> <div class="box">box1</div> <div class="box">box2</div> </div> <button class="add">add</button> var con = document.querySelector('.container'); var box = document.querySelectorAll('.box'); var addBtn = document.querySelector('.add'); //給每一個box都綁定一個事件 // box.forEach(function(node){ // node.onclick = function(){ // console.log(this.innerText); // } // }) //用父級div作事件代理 con.onclick = function(e){ if(e.target.classList.contains('box')){ console.log(e.target.innerText) } } //有了事件代理之後,哪怕新增的box,也會被綁定事件。 var i = 4; addBtn.onclick =function(){ var box = document.createElement('div'); box.classList.add('box'); box.innerText = 'box' + i++; con.appendChild(box); }