什么是閉包?閉包的用途是什么鸠澈?閉包的缺點是什么白对?
什么是閉包
閉包:內(nèi)部函數(shù)總是可以訪問其所在的外部函數(shù)中聲明的參數(shù)和變量。聲明在一個函數(shù)中的函數(shù)握联,叫做閉包函數(shù)。
存在不被釋放或者互相引用的場景都可以叫做閉包每瞒。
代碼舉例:
function Counter(){
let num = 0
function add(){
num++
console.log(num)
}
return add
}
let addOne = Counter()
addOne()//1
addOne()//2
在Counter函數(shù)中創(chuàng)建了一個add函數(shù)金闽,且add函數(shù)中用到了Counter函數(shù)的num變量,這就使得num和add函數(shù)構(gòu)成了一個閉包剿骨。這個變量num一直存在于內(nèi)中代芜,我們很難在外部訪問num變量,但是我們可以通過操作內(nèi)部的函數(shù)來控制它浓利。
閉包的用途
- 隱藏變量挤庇,只通過制定接口訪問數(shù)據(jù)
const cache = (() => {
const store = {}
return {
get(key) {
return store[key]
},
set(key, val) {
store[key] = val
},
remove(key) {
delete store[key]
}
}
})()
這個箭頭函數(shù)我們會得到一個對象钞速,我會得到它的get,set和remove方法嫡秕,我們打印這個cache的時候只會看到get渴语,set和remove三個屬性,store就被我們隱藏起來了昆咽。但是我們可以通過get驾凶,set和remove來操作store。
- 存儲臨時變量
function sum(a){
return function(b){
return a+b
}
}
const sum1 = sum(1)
const sum2 = sum(2)
console.log(sum1(3))//4
console.log(sum2(3))//5
聲明好的sum1和sum2就會隱藏在閉包里掷酗,當(dāng)我們在調(diào)用sum1(3)的時候就自動加上了sum1调违,同理在調(diào)用sum2(3)的時候,也自動加上了sum2汇在。sum1和sum2就存儲了一個臨時的數(shù)據(jù)給我們用翰萨。
- 讓對象擁有私有屬性
constructor() {
let name
Object.assign(this,{
setName(newName){
name = newName
},
getName(){
return name
}
})
}
sayHi(){
console.log(`Hi,${this.getName()}`)
}
}
let p1 = new People
p1.setName('qyk')
p1.getName()//'qyk'
p1.sayHi()//"Hi,qyk"
sayHi()調(diào)用了上層的name,是個閉包糕殉。同時也讓p1有了私有屬性。
- 調(diào)用一個函數(shù)返回另一個函數(shù)
閉包的缺點
- 由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中殖告,內(nèi)存消耗很大阿蝶,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題黄绩,在IE中可能導(dǎo)致內(nèi)存泄露羡洁。解決方法是,在退出函數(shù)之前爽丹,將不使用的局部變量全部刪除筑煮。
- 閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值粤蝎。所以真仲,如果你把父函數(shù)當(dāng)作對象使用,把閉包當(dāng)作它的公用方法初澎,把內(nèi)部變量當(dāng)作它的私有屬性秸应,這時一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值碑宴。
call软啼、apply、bind 的用法分別是什么延柠?
call
call 方法第一個參數(shù)是要綁定給this的值祸挪,后面?zhèn)魅氲氖且粋€參數(shù)列表。當(dāng)?shù)谝粋€參數(shù)為null贞间、
undefined的時候贿条,默認指向window雹仿。
function add(a,b){
return a+b;
}
function.call(add,1,2)//3 其中1和2是連續(xù)的參數(shù)
apply
apply接受兩個參數(shù),第一個參數(shù)是要綁定給this的值闪唆,第二個參數(shù)是一個參數(shù)數(shù)組盅粪。當(dāng)?shù)谝粋€參數(shù)為
null、undefined的時候悄蕾,默認指向window票顾。
function add(a,b){
return a+b;
}
funciton.apply(add,[1,2]//3 其中1和2數(shù)參數(shù)數(shù)組
當(dāng)函數(shù)需要傳遞多個變量時, apply 可以接受一個數(shù)組作為參數(shù)輸入, call 則是接受一系列的單獨變量。
bind
第一個參數(shù)是this的指向帆调,從第二個參數(shù)開始是接收的參數(shù)列表奠骄。bind 方法不會立即執(zhí)行,而是返回一個改變了上下文 this 后的函數(shù)番刊。
fn.bind(x,y,z)
不會執(zhí)行 fn含鳞,而是會返回一個新的函數(shù)
新的函數(shù)執(zhí)行時,會調(diào)用 fn芹务,調(diào)用形式為fn.call(x, y, z)
蝉绷,其中 x 是 this,y 和z 是其他參數(shù)
function add(a, b){
return a+b;
}
var foo1 = add.bind(add, 1,2);
foo1(); //3 只有調(diào)用才會執(zhí)行
在 ES6 的箭頭函數(shù)下, call 和 apply 將失效枣抱。
請說出至少 10 個 HTTP 狀態(tài)碼熔吗,并描述各狀態(tài)碼的意義。
- 狀態(tài)碼100表示繼續(xù)佳晶∥荩客戶端應(yīng)繼續(xù)其請求
- 狀態(tài)碼101表示切換協(xié)議。服務(wù)器根據(jù)客戶端的請求切換協(xié)議
- 狀態(tài)碼200表示請求成功轿秧。一般用于GET與POST請求
- 狀態(tài)碼201表示已創(chuàng)建中跌。成功請求并創(chuàng)建了新的資源
- 狀態(tài)碼202表示已接受。已經(jīng)接受請求菇篡,但未處理完成
- 狀態(tài)碼203表示非授權(quán)信息漩符。請求成功。但返回的meta信息不在原始的服務(wù)器逸贾,而是一個副本
- 狀態(tài)碼300表示多種選擇陨仅。請求的資源可包括多個位置,相應(yīng)可返回一個資源特征與地址的列表用于用戶終端(例如:瀏覽器)選擇
- 狀態(tài)碼303表示查看其它地址
- 狀態(tài)碼305表示使用代理铝侵。所請求的資源必須通過代理訪問
- 狀態(tài)碼306表示已經(jīng)被廢棄的HTTP狀態(tài)碼
- 狀態(tài)碼400表示客戶端請求的語法錯誤灼伤,服務(wù)器無法理解
- 狀態(tài)碼403表示服務(wù)器理解請求客戶端的請求,但是拒絕執(zhí)行此請求
- 狀態(tài)碼404表示服務(wù)器無法根據(jù)客戶端的請求找到資源(網(wǎng)頁)
如何實現(xiàn)數(shù)組去重咪鲜?
假設(shè)有數(shù)組 array = [1,5,2,3,4,2,3,1,3,4]
你要寫一個函數(shù) unique狐赡,使得
unique(array) 的值為 [1,5,2,3,4]
也就是把重復(fù)的值都去掉,只保留不重復(fù)的值疟丙。
使用indexOf
思路:新建一個空的結(jié)果數(shù)組颖侄,for 循環(huán)原數(shù)組鸟雏,判斷結(jié)果數(shù)組是否存在當(dāng)前元素,
如果有相同的值則跳過览祖,不相同則push進數(shù)組孝鹊。
let arr = [1, 5, 2, 3, 4, 2, 3, 1, 3, 4]
function unique(arr) {
let array = [];
for (let i = 0; i < arr.length; i++) { // 首次遍歷數(shù)組
if (array.indexOf(arr[i]) === -1) { // 判斷索引有沒有等于
array.push(arr[i])
}
}
return array
}
console.log(unique(arr));
缺點:無法對NaN和對象去重
使用set
let arr = [1, 5, 2, 3, 4, 2, 3, 1, 3, 4]
function unique (arr) {
return Array.from(new Set(arr))
}
console.log(unique(arr))
缺點:無法去重對象,且有兼容性問題展蒂。API太新又活,舊瀏覽器不支持
使用map
function unique(arr) {
const map = new Map()
const newArr = []
arr.forEach(item => {
if (!map.has(item)) { // has()用于判斷map是否包為item的屬性值
map.set(item, true) // 使用set()將item設(shè)置到map中,并設(shè)置其屬性值為true
newArr.push(item)
}
})
return newArr
}
console.log(unique(arr))
缺點:API 太新锰悼,舊瀏覽器不支持柳骄。
DOM 事件相關(guān)
什么是事件委托
由于事件會在冒泡階段向上傳播到父節(jié)點,因此可以把子節(jié)點的監(jiān)聽函數(shù)定義在父節(jié)點上箕般,由父節(jié)點的監(jiān)聽函數(shù)統(tǒng)一處理多個子元素的事件耐薯。這種方法叫做事件委托。 通俗點講就是委托一個元素幫我監(jiān)聽我本該監(jiān)聽的元素丝里。
怎么阻止默認動作
w3c的方法是e.preventDefault()
曲初,IE則是使用e.returnValue = false
<a id="testA" >caibaojian.com</a>
var a = document.getElementById("testA");
a.onclick =function(e){
if(e.preventDefault){
e.preventDefault();//W3C
}else{
window.event.returnValue = false;//IE
}
}
怎么阻止事件冒泡
w3c使用e.stopPropagation()
,IE使用e.cancelBubble = true
function stopBubble(e) {
if ( e && e.stopPropagation ){
e.stopPropagation(); //W3C
}else{
window.event.cancelBubble = true; //IE
}
}
如何理解 JS 的繼承杯聚?
基于原型的繼承
實例化一個新的函數(shù)复斥,子類的原型指向了父類的實例,子類就可以調(diào)用其父類原型對象上的共有屬性械媒。
function Parent() {
this.parentName = '父類';
}
Parent.prototype.getParentName = function() {
return this.parentName;
};
function Child() {
this.childName = '子類';
}
Child.prototype = new Parent();//繼承Parent
Child.prototype.getChildName = function() {
return this.childName
};
let c = new Child();
console.log(c.getParentName()); // '父類'
function Parent(name1){
this.name1 = name1
}
Parent.prototype.pMethod = function(){
console.log(this.name1)
}
function Child(name2, name1){
Parent.call(this, name1) // 得分點
this.name2 = name2
}
Child.prototype.__proto__ = Parent.prototype
Child.prototype.cMethod = function(){
console.log(this.name2)
}
缺點:子類的實例可以訪問父類的私有屬性,子類的實例還可以更改該屬性评汰,不安全
基于class的繼承
如需創(chuàng)建類繼承纷捞,使用 extends 關(guān)鍵字。
class Parent{
constructor(name1){
this.name1 = name1
}
pMethod(){
console.log(this.name1)
}
}
class Child extends Parent{
constructor(name2, name1){
super(name1) // 得分點
this.name2 = name2
}
cMethod(){
console.log(this.name2)
}
}
數(shù)組排序
給出正整數(shù)數(shù)組 array = [2,1,5,3,8,4,9,5]
請寫出一個函數(shù) sort被去,使得 sort(array) 得到從小到大排好序的數(shù)組 [1,2,3,4,5,5,8,9]
新的數(shù)組可以是在 array 自身上改的主儡,也可以是完全新開辟的內(nèi)存。
let min = (numbers)=>{
if(numbers.length>2){
return min([numbers[0],min(numbers.slice(1))])
}else {
return Math.min.apply(null,numbers)
}
}//求出最小值的算法
let minIndex = (numbers) =>
numbers.indexOf(min(numbers))//標(biāo)記最小值
let sort = (numbers)=>{
if(numbers.length>2){
let index = minIndex(numbers)
let min = numbers[index]
numbers.splice(index,1)
return [min].concat(sort(numbers))
}else{
return numbers[0]<numbers[1]? numbers :numbers.reverse()
}
}
let array =[2,1,5,3,8,4,9,5]
sort(array)
對 Promise 的了解惨缆?
promise的用途
Promise 用于避免回調(diào)地域糜值,讓代碼看起來更同步
創(chuàng)建一個 new Promise
function fn(){
return new Promise((resolve, reject)=>{
成功時調(diào)用 resolve(data)
失敗時調(diào)用 reject(reason)
})
}
使用 Promise.prototype.then
const promise1 = fn() // 得到 promise1 對象
fn().then(success, fail).then(success2, fail2).catch(fail3)
或者
promise1.then(success, fail).then(success2, fail2).catch(fail3)
均可
使用Promise.all
Promise.all([promise1, promise2]) 并行,等待所有 promise 成功坯墨。
如果都成功了寂汇,則 all 對應(yīng)的 promise 也成功;如果有一個失敗了捣染,則 all 對應(yīng)的 promise 失敗骄瓣。
使用 Promise.race
Promise.race([promise1, promise2]),返回一個
promise耍攘,一旦數(shù)組中的某個promise解決或拒絕榕栏,返回的 promise就會解決或拒絕畔勤。
說說跨域
什么是同源
源:協(xié)議+域名+端口號
Window.origin或者location.origin可以得到當(dāng)前的源
兩個URL的協(xié)議、域名和端口號完全一致那么這兩個URL就是同源
同源策略就是扒磁,瀏覽器規(guī)定:如果JS運行在源A里庆揪,那么就只能獲取源A的數(shù)據(jù),不能獲取源B的數(shù)據(jù)妨托,
即不允許跨域缸榛。這是瀏覽器的功能。瀏覽器為了主動預(yù)防偷數(shù)據(jù)的問題始鱼,設(shè)置了嚴(yán)格的同源策略
什么是跨域
跨域仔掸,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。
它是由瀏覽器的同源策略造成的医清,是瀏覽器對JavaScript實施的安全限制起暮。
當(dāng)一個請求url的協(xié)議、域名会烙、端口三者之間任意一個與當(dāng)前頁面url不同即為跨域负懦。
JSONP跨域
我們在跨域的時候,由于當(dāng)前瀏覽器不支持CORS柏腻,或者因為某些條件不支持CORS纸厉,我們必須使用一種方法來進行跨域。于是我們請求一個JS文件五嫂,這個JS文件回執(zhí)行一個回調(diào)颗品,回調(diào)里面有我們的數(shù)據(jù)∥衷担回調(diào)的名字可以通過隨機數(shù)生成的躯枢,我們把這個隨機數(shù)以callback的參數(shù)傳給后臺,后臺會把函數(shù)返回給我們并且執(zhí)行槐臀。
缺點:由于它是一個script標(biāo)簽锄蹂,所以讀不到AJAX那么精確的status值,無法知道狀態(tài)碼是什么水慨,也只能發(fā)送GET請求得糜,JSONP不支持POST
CORS跨域
CORS(跨域資源共享)
跨源資源共享 (CORS)是一種基于 HTTP 頭的機制,該機制通過允許服務(wù)器標(biāo)示除了它自己以外的其它 origin(域晰洒,協(xié)議和端口)朝抖,使得瀏覽器允許這些 origin 訪問加載自己的資源。瀏覽器默認不同源之間不能互相訪問數(shù)據(jù)欢顷,但是我們想讓兩個網(wǎng)站互相訪問槽棍。我們就用CORS,如果要共享數(shù)據(jù)就需要提前聲明。例如炼七,源B要訪問源A缆巧,源A就要在響應(yīng)頭里聲明源B可以訪問:
response.setHeader("Access-Control-Allow-Origin","http://foo.example")