一,DOM事件
1.dom事件的級別 DOM0 element.onclilck = function(){} DOM2 element.addEventListener('click',function(){}) DOM2 element.addEventListener('keyup',function(){}),增加了鍵盤和鼠標事件 2.dom事件模型, 冒泡和捕獲 3.dom事件流 首先捕獲階段從祖先元素到達目標階段 然后冒泡階段,從目標階段到祖先元素的一個過程 4.dom事件捕獲的具體流程,
document 》html 》 body 》 (div)
5.事件委派 指將事件統一綁定給元素的共同的祖先元素,這樣當后代元素上的事件觸發時,會一直冒泡到祖先元素 從而通過祖先元素的響應函數來處理事件。
事件委派是利用了冒泡,通過委派可以減少事件綁定的次數,提高程序的性能 場景;商品分類移動移除事件
6.event對象的常見應用 event.preventDefault(),阻止默認行為,a標簽,form表單
event.stopPropagation(),阻止冒泡,場景,新聞中的的個人中心圖標點擊事件,不用跳轉到新聞詳情,跳轉到個人中心頁
event.target 和 event.currentTarget
event.target 是引發事件的 目標元素(原始點擊位置),它在冒泡過程中不會發生變化。
event.currentTarget 是當前正在執行的監聽函數所在的那個節點,在冒泡過程中值會發發生變化,
event.currentTarget === this(this 指向觸發事件的內部那個綁定監聽函數的元素節點對象)。
舉例
<ul id="ul"> <li class="item1">li item1</li> <li class="item2">li item2</li> <li class="item3">li item3</li> </ul> <script> var ul = document.getElementById("ul") ul.addEventListener('click', function (event) { console.log(this); console.log(event.target); // 點擊哪個li就獲取那個對應的li對象 console.log(event.currentTarget); // 每次都是最外層的ul對象,完全等於this console.log("------------------"); }, false); </script>
7.dom自定義事件
//創建事件, Event是無法傳遞參數的
var event = new Event('build');
//創建事件, CustomEvent是可以傳遞參數的
var event = new CustomEvent('build', { detail: elem.dataset.time });
// 監聽事件Listen for the event.
elem.addEventListener('build', function (e) { //... }, false);
// 分發/觸發事件Dispatch the event.
elem.dispatchEvent(event);
舉例
// 事件對象eve,事件名稱test var eve = new Event('test'); ev.addEventListener('test', function () { console.log('test dispatch'); }); setTimeout(function () { // 觸發事件對象 ev.dispatchEvent(eve); }, 1000);
二,原型鏈
創建對象有幾種方法
// 第一種方式:字面量 var o1 = {name: 'o1'}; var o2 = new Object({name: 'o2'}); // 第二種方式:構造函數 function M(name) { this.name = name; }; var o3 = new M('o3'); // 第三種方式:Object.create var p = {name: 'p'}; var o4 = Object.create(p);
原型鏈詳情;https://www.cnblogs.com/fsg6/p/12773775.html
三,面向對象
類的申明
/** * 類的聲明 */ var Animal = function () { this.name = 'Animal'; }; /** * es6中class的聲明 */ class Animal2 { constructor () { this.name = 'Animal2'; } }
類生成實例
/** * 實例化 */ console.log(new Animal(), new Animal2());
如何實現繼承,繼承的幾種方法,詳解;https://www.cnblogs.com/fsg6/p/12792788.html
4.JS有幾種數據類型,其中基本數據類型有哪些!
七種數據類型
- Boolean
- Null
- Undefined
- Number
- String
- Symbol (ECMAScript 6 新定義)
- Object
(ES6之前)其中5種為基本類型:string
,number
,boolean
,null
,undefined
,
ES6出來的Symbol
也是原始數據類型 ,表示獨一無二的值
Object
為引用類型(范圍挺大),也包括數組、函數,
5.null
和undefined
的差異
相同點:
- 在
if
判斷語句中,值都默認為false
- 大體上兩者都是代表無,具體看差異
差異:
null
轉為數字類型值為0,而undefined
轉為數字類型為NaN(Not a Number)
undefined
是代表調用一個值而該值卻沒有賦值,這時候默認則為undefined,函數沒有返回值時,默認返回undefined。
null
是一個很特殊的對象,最為常見的一個用法就是作為參數傳入(說明該參數不是對象)- 設置為
null
的變量或者對象會被內存收集器回收
6.JS 的DOM 操作(Node節點獲取及增刪查改);
- 獲取(太多了,有
document.getElementById/ClassName/Name/TagName 等,或者 querySelector
)
// example // get Node var element = document.querySelector('#test'); // 追加 element.appendChild(Node); // 刪除 element.removeChild(Node); // 查找 element.nextSibling // 獲取元素之后的兄弟節點 , 會拿到注釋文本,空白符這些 element.nextElementSibling // 等同, 獲取標簽(不會拿到注釋文本這些) element.previousSibling // 和上面同理,往前找兄弟節點 element.previousElementSibling // 改動,比如 屬性這些 element.setAttribute(name, value); // 增加屬性 element.removeAttribute(attrName); //刪除屬性
7.Javascript中,有一個函數,執行時對象查找時,永遠不會去查找原型,這個函數是?
hasOwnProperty
,這個更多的是用來區分自身屬性和原型鏈上的屬性。Object的hasOwnProperty()
方法返回一個布爾值,判斷對象是否包含特定的自身(非繼承)屬性。
function foo() { this.name = 'foo' this.sayHi = function () { console.log('Say Hi') } } foo.prototype.sayGoodBy = function () { console.log('Say Good By') } let myPro = new foo() console.log(myPro.name) // foo console.log(myPro.hasOwnProperty('name')) // true console.log(myPro.hasOwnProperty('toString')) // false,不會去查找原型中的屬性 console.log(myPro.hasOwnProperty('hasOwnProperty')) // fasle console.log(myPro.hasOwnProperty('sayHi')) // true console.log(myPro.hasOwnProperty('sayGoodBy')) // false console.log('sayGoodBy' in myPro) // true
8.談談你對ajax 的理解,以及用原生 JS 實現有哪些要點需要注意;
ajax
全稱是異步 javascript 和 XML
,用來和服務端進行數據交互的,讓無刷新替換頁面數據成了可能;
至於有哪些要要點,來一個簡短的ajax
請求
var xhr = new XMLHttpRequest(); // 聲明一個請求對象 xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ // readyState 4 代表已向服務器發送請求 if(xhr.status === OK){ // // status 200 代表服務器返回成功 console.log(xhr.responseText); // 這是返回的文本 } else{ console.log("Error: "+ xhr.status); // 連接失敗的時候拋出錯誤 } } } //發送http請求 xhr.open('GET', 'xxxx'); // 如何設置請求頭? xhr.setRequestHeader(header, value); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(null); // get方法 send null(亦或者不傳,則直接是傳遞 header) ,post 的 send 則是傳遞值
9.this對象的理解,詳情;https://www.cnblogs.com/dongcanliang/p/7054176.html
js的this指向是不確定的,也就是說是可以動態改變的。call/apply 就是用於改變this指向的函數,誰調用指向誰
(1)、處於全局作用域下的this: this;/*window*/ var a = {name: this}/*window*/ var b = [this];/*window*/ 在全局作用域下,this默認指向window對象。 (2)、處在函數中的this,又分為以下幾種情況: a、一般定義的函數,然后一般的執行: var a = function(){ console.log(this); } a();/*window*/ this還是默認指向window。 b、一般定義,用new調用執行: var a = function(){ console.log(this); } new a();/*新建的空對象*/ 這時候讓this指向新建的空對象,我們才可以給空對象初始化自有變量 c、作為對象屬性的函數,調用時: var a = { f:function(){ console.log(this) } } a.f();/*a對象*/ 這時候this指向調用f函數的a對象。 (3)、通過call()和apply()來改變this的默認引用: var b = {id: 'b'}; var a = { f:function(){ console.log(this) } } a.f.call(b);/*b*/ 所有函數對象都有的call方法和apply方法,它們的用法大體相似,f.call(b);的意思 是,執行f函數,並將f函數執行期活動對象里的this指向b對象,這樣標示符解析時,this就會是b對象了。
不過調用函數是要傳參的。所以,f.call(b, x, y); f.apply(b, [x, y]);好吧,以上就是用call方法執行f函數,與用apply方法執行f函數時傳參方式,它們之間的差異,大家一目了然:
apply通過數組的方式傳遞參數,call通過一個個的形參傳遞參數
function add(c, d){ return this.a + this.b + c + d; } var o = {a:1, b:3}; add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
bind方法
bind方法在ES5引入, 在Function的原型鏈上, Function.prototype.bind
。通過bind方法綁定后, 函數將被永遠綁定在其第一個參數對象上, 而無論其在什么情況下被調用。
function f() { return this; } var g = f.bind({ a: "azerty" }); console.log(g()); // { a: "azerty" }
(4)setTimeout & setInterval
對於延時函數內部的回調函數的this指向全局對象window(當然我們可以通過bind方法改變其內部函數的this指向)
//默認情況下代碼 function Person() { this.age = 0; setTimeout(function() { console.log(this); }, 3000); } var p = new Person();//3秒后返回 window 對象 ============================================== //通過bind綁定 function Person() { this.age = 0; setTimeout((function() { console.log(this); }).bind(this), 3000); } var p = new Person();//3秒后返回構造函數新生成的對象 Person{...}
箭頭函數中的 this
由於箭頭函數不綁定this, 它會捕獲其所在(即定義的位置)上下文的this值, 作為自己的this值
function Person() { this.age = 0; setTimeout(() => { // 回調里面的 `this` 變量就指向了期望的那個對象了 console.log(this);//實例對象p this.age++; }, 3000); } var p = new Person(); //實例對象p
var p = () => { return this; }; p()//window
以上箭頭函數都在方法內部,回去外層的函數找this值
箭頭函數作為對象屬性調用時,可以看到,作為方法調用的箭頭函數this指向全局window對象,而普通函數則指向調用它的對象
var obj = { i: 10, b: () => console.log(this.i, this), c: function() { console.log( this.i, this) } } obj.b(); // undefined window{...} obj.c(); // 10 Object {...}
b、dom模型中觸發事件的回調方法執行中活動對象里的this指向該dom對象。
10.JS 的作用域是什么?有什么特別之處么?
JS的作用域主要是說變量的作用范圍。作用域是函數定義好就存在的,而作用域鏈是函數調用的時候才有的;
JS的作用域從大類分的話是兩種:全局作用域和局部作用域
。內部會有變量聲明提升
作用域鏈描述的是程序查找變量的過程,首先在自己的作用域當中去查找,如果查找不到,去到上級作用域去查找,查找就用,查不到繼續往上查找,直到找到真正的全局
1.js作用域(全局變量,局部變量)內部可以訪問外部,但外部的不能訪問內部的
var a=10; function aaa(){ alert(a); }; aaa(); //a 為外部變量即全局變量,所以可以直接訪問到 結果為10
function aaa(){ var a=10; }; aaa(); alert(a); //a 為函數aaa()內部變量量即局部變量,所以無法訪問到
var a=10; function aaa(){ alert(a); }; function bbb(){ var a=20; aaa(); } bbb(); //結果為10,
//函數定義時候作用域就定死了,函數的作用域和調用沒關系
2.變量的查找是就近原則去尋找,定義的var變量;第二點,變量的聲明被提前到作用域頂部,賦值保留在原地
function aaa(){ alert(a); var a=20; } aaa(); //結果為:undefined /**************/ var a=10; function aaa(){ alert(a); var a=20; } aaa(); //結果為:undefined 可以解析為是: var a=10; function aaa(){ var a; //聲明提前了 alert(a); a=20; //賦值扔留着原地 } aaa();
3.如果函數內部沒有定義var這個變量,找形參,形參如果有,當做局部變量處理
var a=10; function aaa(a){ alert(a); var a=20; } aaa(a); //結果為:10
11.描述下cookie
,sessionStorage
,localStorage
的差異..
共同點:都是保存在瀏覽器端、且同源的
1.cookie大小4KB 左右,跟隨請求(請求頭),會占用帶寬資源,但是若是用來判斷用戶是否在線這些挺方便,sessionStorage和localStorage雖然也有存儲大小的限制,但比cookie大得多,可以達到5M或更大
2.數據有效期不同,sessionStorage:僅在當前瀏覽器窗口關閉之前有效;localStorage:始終有效,窗口或瀏覽器關閉也一直保存,因此用作持久數據;cookie:只在設置的cookie過期時間之前有效,即使窗口關閉或瀏覽器關閉
3.作用域不同,sessionStorage不在不同的瀏覽器窗口中共享,即使是同一個頁面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
12.javascript
的原型鏈你怎么理解?以及繼承怎么實現(手寫), 詳解;https://www.cnblogs.com/fsg6/p/12773775.html
原型鏈算是 JS 內一種獨有的機制,
所有對象都有一個內置[[proto]]
指向創建它的原型對象(prototype
)
原型鏈的基本用來實現繼承用的
13.ES6+你熟悉么,用過哪些特性?
- 箭頭函數
- 類及引入導出和繼承(
class
/import
/export
/extends
) - 字符串模板
- Promise
let
,const
async
/await
- 默認參數/參數或變量解構裝飾器
Array.inclueds
/String.padStart|String.padEnd
/Object.assign
14. var,let 和 const 有啥差異?
let
會產生塊級作用域,不會造成變量提升,無法重新聲明(但可以重新賦值);const
- 是只讀的,若是基本數據類型,具有不變性(無法重新賦值改動)
- 引用對象可以調整內部值(可能設計的時候沒有考慮周全!)
1.let
命令,用來聲明變量。只在塊級代碼塊有效。var是在全局變量
if (true) { let b= 2;
var a=1 } console.log(b);//報錯
console.log(a) //1
2.不存在變量提升,var
命令會發生”變量提升“現象,即變量可以在聲明之前使用,值為undefined
// var 的情況 console.log(a); // 輸出undefined var a = 2; // let 的情況 console.log(b); // 報錯ReferenceError let b = 2
3.let
不允許在相同作用域內,重復聲明同一個變量,但是可以更改值,var命令可以重復申明變量
let a =1 let a=2 //報錯
a=2//正常
var b=1 var b =2 //正常
const命令
1. const
聲明一個只讀的常量。一旦聲明,常量的值就不能改變。如果是引用對象,可修改內部的屬性
const a ;//報錯,一旦聲明變量,應該立即賦值!! const b = 2; b = 3//報錯,因為定義常量之后不能成重新賦值!! const names = []; names = [1,2,3] //出錯,因為變量names指向的地址不能發生改變,應始終指向[]所在的地址!!![1,2,3]與[]不是同一個地址 //不會報錯,因為names指向的地址不變,改變的只是內部數據 const names = []; names[0] = 1 names[1] = 2 names[2] = 3
2.const
命令聲明的常量也是不提升
3.const
的作用域與let
命令相同:只在聲明所在的塊級作用域內有效
15.async
和await
的用途
await有兩個作用,一是作為求值關鍵字,二是用同步方式展示異步代碼;如果方法中使用了await,那么在方法前面必須加上async
1.await可以阻塞當前線程,將異步操作變成同步,發送ajax請求,必須數據請求到了,執行下步操作
2.釋放異常(拋出異常)
await可以進行求值操作,當await后面跟的是Promise時,如果Promise中的操作是resolve(成功),那么await獲取的就是操作成功時的返回值;如果Promise中的操作是reject(失敗),await獲取的就是操作失敗時的返回值,並且如果在await上加了try catch,是可以捕捉到異常的
async function fun1() { try { // 必須加await,不然會報錯:UnhandledPromiseRejectionWarning await fun2(); } catch (error) { console.log("error") } } function fun2() { return new Promise((resolve, reject) => { reject("error"); }) } fun1();
3.async的作用是將方法的返回值封裝成Promise
async function t() { return "hello"; } console.log(t()); // 打印出 Promise{"hello"}
16.談談你對 Promise 的理解? 和 ajax 有關系么?,promise題目;https://juejin.cn/post/6844903509934997511
Promise
和ajax
沒有半毛錢直接關系.promise
只是為了解決"回調地獄"而誕生的;
1、Promise 是一個構造函數,我們可以通過該構造函數來生成Promise的實例。Promise 構造函數是同步執行的,promise.then
中的函數是異步執行的。
const promise = new Promise((resolve, reject) => { console.log(1) resolve() console.log(2) }) promise.then(() => { console.log(3) }) console.log(4)
//打印順序,1 2 4 3
2、Promise 狀態有三:pending(等待)、resolved(成功)、rejected(失敗)。狀態改變只能是 pending->resolved 或者 pending->rejected,狀態一旦改變則不能再變
3、Promise 是對回調函數的一種封裝,解決對調地獄的問題,我們可以通過Promise將自己的程序以同步的方式表達出來,從而可以解決代碼臃腫及可讀性差的問題。
4、axios采用Promise對象,發送ajax請求,獲取數據,利用async和awiat方式類同步獲取數據
5、Promise雖然解決了我們項目開發中的很多問題,但我們也不能無腦的濫用。比如Promise.all,如果參數中promise有一個失敗(rejected),則此實例回調必然失敗(reject),就不會再執行then方法的回調了。在實際中可能只是一個不關鍵的數據加載失敗,往往會導致其他所有的數據不會顯示,使得項目的容錯性大大降低。所以我個人在開發過程中只會在必須依賴這幾個步驟全部加載成功后才能繼續向下執行的場景中采用它,比如圖片的預加載功能。
基礎點
- 成功調用
resolve
,失敗調用reject
.then
獲取結果,.catch
捕獲異常。捕獲異常還可通過.then
的第二個參數.finally
無論成功失敗都一定會調用- 多個並發的請求,用
Promise.all()
- 只有
p1
、p2
、p3
的狀態都變成fulfilled
,p
的狀態才會變成fulfilled
,此時p1
、p2
、p3
的返回值組成一個數組,傳遞給p
的回調函數。 - 只要
p1
、p2
、p3
之中有一個被rejected
,p
的狀態就變成rejected
,此時第一個被reject
的實例的返回值,會傳遞給p
的回調函數。
- 只有