前端高頻面試題(四)(附答案)

手寫發(fā)布訂閱

class EventListener {
    listeners = {};
    on(name, fn) {
        (this.listeners[name] || (this.listeners[name] = [])).push(fn)
    }
    once(name, fn) {
        let tem = (...args) => {
            this.removeListener(name, fn)
            fn(...args)
        }
        fn.fn = tem
        this.on(name, tem)
    }
    removeListener(name, fn) {
        if (this.listeners[name]) {
            this.listeners[name] = this.listeners[name].filter(listener => (listener != fn && listener != fn.fn))
        }
    }
    removeAllListeners(name) {
        if (name && this.listeners[name]) delete this.listeners[name]
        this.listeners = {}
    }
    emit(name, ...args) {
        if (this.listeners[name]) {
            this.listeners[name].forEach(fn => fn.call(this, ...args))
        }
    }
}

實(shí)現(xiàn)一個(gè) add 方法

題目描述:實(shí)現(xiàn)一個(gè) add 方法 使計(jì)算結(jié)果能夠滿足如下預(yù)期:
add(1)(2)(3)()=6
add(1,2,3)(4)()=10

其實(shí)就是考函數(shù)柯里化

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

function add(...args) {
  let allArgs = [...args];
  function fn(...newArgs) {
    allArgs = [...allArgs, ...newArgs];
    return fn;
  }
  fn.toString = function () {
    if (!allArgs.length) {
      return;
    }
    return allArgs.reduce((sum, cur) => sum + cur);
  };
  return fn;
}

同步和異步的區(qū)別

  • 同步指的是當(dāng)一個(gè)進(jìn)程在執(zhí)行某個(gè)請(qǐng)求時(shí),如果這個(gè)請(qǐng)求需要等待一段時(shí)間才能返回聊训,那么這個(gè)進(jìn)程會(huì)一直等待下去亥曹,直到消息返回為止再繼續(xù)向下執(zhí)行午乓。
  • 異步指的是當(dāng)一個(gè)進(jìn)程在執(zhí)行某個(gè)請(qǐng)求時(shí),如果這個(gè)請(qǐng)求需要等待一段時(shí)間才能返回护赊,這個(gè)時(shí)候進(jìn)程會(huì)繼續(xù)往下執(zhí)行,不會(huì)阻塞等待消息的返回,當(dāng)消息返回時(shí)系統(tǒng)再通知進(jìn)程進(jìn)行處理殖蚕。

說一下data為什么是一個(gè)函數(shù)而不是一個(gè)對(duì)象?

JavaScript中的對(duì)象是引用類型的數(shù)據(jù),當(dāng)多個(gè)實(shí)例引用同一個(gè)對(duì)象時(shí)沉迹,只要一個(gè)實(shí)例對(duì)這個(gè)對(duì)象進(jìn)行操作睦疫,其他實(shí)例中的數(shù)據(jù)也會(huì)發(fā)生變化。而在Vue中鞭呕,我們更多的是想要復(fù)用組件蛤育,那就需要每個(gè)組件都有自己的數(shù)據(jù),這樣組件之間才不會(huì)相互干擾葫松。所以組件的數(shù)據(jù)不能寫成對(duì)象的形式瓦糕,而是要寫成函數(shù)的形式。數(shù)據(jù)以函數(shù)返回值的形式定義进宝,這樣當(dāng)我們每次復(fù)用組件的時(shí)候刻坊,就會(huì)返回一個(gè)新的data,也就是說每個(gè)組件都有自己的私有數(shù)據(jù)空間党晋,它們各自維護(hù)自己的數(shù)據(jù)谭胚,不會(huì)干擾其他組件的正常運(yùn)行。

CDN的概念

CDN(Content Delivery Network未玻,內(nèi)容分發(fā)網(wǎng)絡(luò))是指一種通過互聯(lián)網(wǎng)互相連接的電腦網(wǎng)絡(luò)系統(tǒng)灾而,利用最靠近每位用戶的服務(wù)器,更快扳剿、更可靠地將音樂旁趟、圖片、視頻庇绽、應(yīng)用程序及其他文件發(fā)送給用戶锡搜,來提供高性能橙困、可擴(kuò)展性及低成本的網(wǎng)絡(luò)內(nèi)容傳遞給用戶。

典型的CDN系統(tǒng)由下面三個(gè)部分組成:

  • 分發(fā)服務(wù)系統(tǒng): 最基本的工作單元就是Cache設(shè)備耕餐,cache(邊緣cache)負(fù)責(zé)直接響應(yīng)最終用戶的訪問請(qǐng)求凡傅,把緩存在本地的內(nèi)容快速地提供給用戶。同時(shí)cache還負(fù)責(zé)與源站點(diǎn)進(jìn)行內(nèi)容同步肠缔,把更新的內(nèi)容以及本地沒有的內(nèi)容從源站點(diǎn)獲取并保存在本地夏跷。Cache設(shè)備的數(shù)量、規(guī)模明未、總服務(wù)能力是衡量一個(gè)CDN系統(tǒng)服務(wù)能力的最基本的指標(biāo)槽华。
  • 負(fù)載均衡系統(tǒng): 主要功能是負(fù)責(zé)對(duì)所有發(fā)起服務(wù)請(qǐng)求的用戶進(jìn)行訪問調(diào)度,確定提供給用戶的最終實(shí)際訪問地址趟妥。兩級(jí)調(diào)度體系分為全局負(fù)載均衡(GSLB)和本地負(fù)載均衡(SLB)猫态。全局負(fù)載均衡主要根據(jù)用戶就近性原則,通過對(duì)每個(gè)服務(wù)節(jié)點(diǎn)進(jìn)行“最優(yōu)”判斷煮纵,確定向用戶提供服務(wù)的cache的物理位置懂鸵。本地負(fù)載均衡主要負(fù)責(zé)節(jié)點(diǎn)內(nèi)部的設(shè)備負(fù)載均衡
  • 運(yùn)營管理系統(tǒng): 運(yùn)營管理系統(tǒng)分為運(yùn)營管理和網(wǎng)絡(luò)管理子系統(tǒng)偏螺,負(fù)責(zé)處理業(yè)務(wù)層面的與外界系統(tǒng)交互所必須的收集行疏、整理、交付工作套像,包含客戶管理酿联、產(chǎn)品管理、計(jì)費(fèi)管理夺巩、統(tǒng)計(jì)分析等功能贞让。

如何提?webpack的打包速度?

(1)優(yōu)化 Loader

對(duì)于 Loader 來說,影響打包效率首當(dāng)其沖必屬 Babel 了柳譬。因?yàn)?Babel 會(huì)將代碼轉(zhuǎn)為字符串生成 AST喳张,然后對(duì) AST 繼續(xù)進(jìn)行轉(zhuǎn)變最后再生成新的代碼,項(xiàng)目越大美澳,轉(zhuǎn)換代碼越多销部,效率就越低。當(dāng)然了制跟,這是可以優(yōu)化的舅桩。

首先我們優(yōu)化 Loader 的文件搜索范圍

module.exports = {
  module: {
    rules: [
      {
        // js 文件才使用 babel
        test: /\.js$/,
        loader: 'babel-loader',
        // 只在 src 文件夾下查找
        include: [resolve('src')],
        // 不會(huì)去查找的路徑
        exclude: /node_modules/
      }
    ]
  }
}

對(duì)于 Babel 來說,希望只作用在 JS 代碼上的雨膨,然后 node_modules 中使用的代碼都是編譯過的擂涛,所以完全沒有必要再去處理一遍。

當(dāng)然這樣做還不夠聊记,還可以將 Babel 編譯過的文件緩存起來撒妈,下次只需要編譯更改過的代碼文件即可恢暖,這樣可以大幅度加快打包時(shí)間

loader: 'babel-loader?cacheDirectory=true'

(2)HappyPack

受限于 Node 是單線程運(yùn)行的,所以 Webpack 在打包的過程中也是單線程的狰右,特別是在執(zhí)行 Loader 的時(shí)候胀茵,長時(shí)間編譯的任務(wù)很多,這樣就會(huì)導(dǎo)致等待的情況挟阻。

HappyPack 可以將 Loader 的同步執(zhí)行轉(zhuǎn)換為并行的琼娘,這樣就能充分利用系統(tǒng)資源來加快打包效率了

module: {
  loaders: [
    {
      test: /\.js$/,
      include: [resolve('src')],
      exclude: /node_modules/,
      // id 后面的內(nèi)容對(duì)應(yīng)下面
      loader: 'happypack/loader?id=happybabel'
    }
  ]
},
plugins: [
  new HappyPack({
    id: 'happybabel',
    loaders: ['babel-loader?cacheDirectory'],
    // 開啟 4 個(gè)線程
    threads: 4
  })
]

(3)DllPlugin

DllPlugin 可以將特定的類庫提前打包然后引入。這種方式可以極大的減少打包類庫的次數(shù)附鸽,只有當(dāng)類庫更新版本才有需要重新打包脱拼,并且也實(shí)現(xiàn)了將公共代碼抽離成單獨(dú)文件的優(yōu)化方案。DllPlugin的使用方法如下:

// 單獨(dú)配置在一個(gè)文件中
// webpack.dll.conf.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry: {
    // 想統(tǒng)一打包的類庫
    vendor: ['react']
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].dll.js',
    library: '[name]-[hash]'
  },
  plugins: [
    new webpack.DllPlugin({
      // name 必須和 output.library 一致
      name: '[name]-[hash]',
      // 該屬性需要與 DllReferencePlugin 中一致
      context: __dirname,
      path: path.join(__dirname, 'dist', '[name]-manifest.json')
    })
  ]
}

然后需要執(zhí)行這個(gè)配置文件生成依賴文件坷备,接下來需要使用 DllReferencePlugin 將依賴文件引入項(xiàng)目中

// webpack.conf.js
module.exports = {
  // ...省略其他配置
  plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      // manifest 就是之前打包出來的 json 文件
      manifest: require('./dist/vendor-manifest.json'),
    })
  ]
}

(4)代碼壓縮

在 Webpack3 中熄浓,一般使用 UglifyJS 來壓縮代碼,但是這個(gè)是單線程運(yùn)行的省撑,為了加快效率赌蔑,可以使用 webpack-parallel-uglify-plugin 來并行運(yùn)行 UglifyJS,從而提高效率竟秫。

在 Webpack4 中娃惯,不需要以上這些操作了,只需要將 mode 設(shè)置為 production 就可以默認(rèn)開啟以上功能肥败。代碼壓縮也是我們必做的性能優(yōu)化方案趾浅,當(dāng)然我們不止可以壓縮 JS 代碼,還可以壓縮 HTML馒稍、CSS 代碼皿哨,并且在壓縮 JS 代碼的過程中,我們還可以通過配置實(shí)現(xiàn)比如刪除 console.log 這類代碼的功能纽谒。

(5)其他

可以通過一些小的優(yōu)化點(diǎn)來加快打包速度

  • resolve.extensions:用來表明文件后綴列表证膨,默認(rèn)查找順序是 ['.js', '.json'],如果你的導(dǎo)入文件沒有添加后綴就會(huì)按照這個(gè)順序查找文件鼓黔。我們應(yīng)該盡可能減少后綴列表長度央勒,然后將出現(xiàn)頻率高的后綴排在前面
  • resolve.alias:可以通過別名的方式來映射一個(gè)路徑,能讓 Webpack 更快找到路徑
  • module.noParse:如果你確定一個(gè)文件下沒有其他依賴请祖,就可以使用該屬性讓 Webpack 不掃描該文件订歪,這種方式對(duì)于大型的類庫很有幫助

await 到底在等啥?

await 在等待什么呢肆捕? 一般來說刷晋,都認(rèn)為 await 是在等待一個(gè) async 函數(shù)完成。不過按語法說明,await 等待的是一個(gè)表達(dá)式眼虱,這個(gè)表達(dá)式的計(jì)算結(jié)果是 Promise 對(duì)象或者其它值(換句話說喻奥,就是沒有特殊限定)。

因?yàn)?async 函數(shù)返回一個(gè) Promise 對(duì)象捏悬,所以 await 可以用于等待一個(gè) async 函數(shù)的返回值——這也可以說是 await 在等 async 函數(shù)撞蚕,但要清楚,它等的實(shí)際是一個(gè)返回值过牙。注意到 await 不僅僅用于等 Promise 對(duì)象甥厦,它可以等任意表達(dá)式的結(jié)果,所以寇钉,await 后面實(shí)際是可以接普通函數(shù)調(diào)用或者直接量的刀疙。所以下面這個(gè)示例完全可以正確運(yùn)行:

function getSomething() {
    return "something";
}
async function testAsync() {
    return Promise.resolve("hello async");
}
async function test() {
    const v1 = await getSomething();
    const v2 = await testAsync();
    console.log(v1, v2);
}
test();

await 表達(dá)式的運(yùn)算結(jié)果取決于它等的是什么。

  • 如果它等到的不是一個(gè) Promise 對(duì)象扫倡,那 await 表達(dá)式的運(yùn)算結(jié)果就是它等到的東西谦秧。
  • 如果它等到的是一個(gè) Promise 對(duì)象,await 就忙起來了撵溃,它會(huì)阻塞后面的代碼疚鲤,等著 Promise 對(duì)象 resolve,然后得到 resolve 的值缘挑,作為 await 表達(dá)式的運(yùn)算結(jié)果集歇。

來看一個(gè)例子:

function testAsy(x){
   return new Promise(resolve=>{setTimeout(() => {
       resolve(x);
     }, 3000)
    }
   )
}
async function testAwt(){    
  let result =  await testAsy('hello world');
  console.log(result);    // 3秒鐘之后出現(xiàn)hello world
  console.log('cuger')   // 3秒鐘之后出現(xiàn)cug
}
testAwt();
console.log('cug')  //立即輸出cug

這就是 await 必須用在 async 函數(shù)中的原因。async 函數(shù)調(diào)用不會(huì)造成阻塞卖哎,它內(nèi)部所有的阻塞都被封裝在一個(gè) Promise 對(duì)象中異步執(zhí)行鬼悠。await暫停當(dāng)前async的執(zhí)行,所以'cug''最先輸出亏娜,hello world'和‘cuger’是3秒鐘后同時(shí)出現(xiàn)的。

深拷貝(考慮到復(fù)制 Symbol 類型)

題目描述:手寫 new 操作符實(shí)現(xiàn)

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

function isObject(val) {
  return typeof val === "object" && val !== null;
}

function deepClone(obj, hash = new WeakMap()) {
  if (!isObject(obj)) return obj;
  if (hash.has(obj)) {
    return hash.get(obj);
  }
  let target = Array.isArray(obj) ? [] : {};
  hash.set(obj, target);
  Reflect.ownKeys(obj).forEach((item) => {
    if (isObject(obj[item])) {
      target[item] = deepClone(obj[item], hash);
    } else {
      target[item] = obj[item];
    }
  });

  return target;
}

// var obj1 = {
// a:1,
// b:{a:2}
// };
// var obj2 = deepClone(obj1);
// console.log(obj1);

常見的圖片格式及使用場景

(1)BMP蹬挺,是無損的维贺、既支持索引色也支持直接色的點(diǎn)陣圖。這種圖片格式幾乎沒有對(duì)數(shù)據(jù)進(jìn)行壓縮巴帮,所以BMP格式的圖片通常是較大的文件溯泣。

(2)GIF是無損的、采用索引色的點(diǎn)陣圖榕茧。采用LZW壓縮算法進(jìn)行編碼垃沦。文件小,是GIF格式的優(yōu)點(diǎn)用押,同時(shí)肢簿,GIF格式還具有支持動(dòng)畫以及透明的優(yōu)點(diǎn)。但是GIF格式僅支持8bit的索引色,所以GIF格式適用于對(duì)色彩要求不高同時(shí)需要文件體積較小的場景池充。

(3)JPEG是有損的桩引、采用直接色的點(diǎn)陣圖。JPEG的圖片的優(yōu)點(diǎn)是采用了直接色收夸,得益于更豐富的色彩坑匠,JPEG非常適合用來存儲(chǔ)照片,與GIF相比卧惜,JPEG不適合用來存儲(chǔ)企業(yè)Logo厘灼、線框類的圖。因?yàn)橛袚p壓縮會(huì)導(dǎo)致圖片模糊咽瓷,而直接色的選用手幢,又會(huì)導(dǎo)致圖片文件較GIF更大。

(4)PNG-8是無損的忱详、使用索引色的點(diǎn)陣圖围来。PNG是一種比較新的圖片格式,PNG-8是非常好的GIF格式替代者匈睁,在可能的情況下监透,應(yīng)該盡可能的使用PNG-8而不是GIF,因?yàn)樵谙嗤膱D片效果下航唆,PNG-8具有更小的文件體積胀蛮。除此之外,PNG-8還支持透明度的調(diào)節(jié)糯钙,而GIF并不支持粪狼。除非需要?jiǎng)赢嫷闹С郑駝t沒有理由使用GIF而不是PNG-8任岸。

(5)PNG-24是無損的再榄、使用直接色的點(diǎn)陣圖。PNG-24的優(yōu)點(diǎn)在于它壓縮了圖片的數(shù)據(jù)享潜,使得同樣效果的圖片困鸥,PNG-24格式的文件大小要比BMP小得多。當(dāng)然剑按,PNG24的圖片還是要比JPEG疾就、GIF、PNG-8大得多艺蝴。

(6)SVG是無損的矢量圖猬腰。SVG是矢量圖意味著SVG圖片由直線和曲線以及繪制它們的方法組成。當(dāng)放大SVG圖片時(shí)猜敢,看到的還是線和曲線姑荷,而不會(huì)出現(xiàn)像素點(diǎn)盒延。這意味著SVG圖片在放大時(shí),不會(huì)失真厢拭,所以它非常適合用來繪制Logo兰英、Icon等。

(7)WebP是谷歌開發(fā)的一種新圖片格式供鸠,WebP是同時(shí)支持有損和無損壓縮的畦贸、使用直接色的點(diǎn)陣圖。從名字就可以看出來它是為Web而生的楞捂,什么叫為Web而生呢薄坏?就是說相同質(zhì)量的圖片,WebP具有更小的文件體積≌郑現(xiàn)在網(wǎng)站上充滿了大量的圖片胶坠,如果能夠降低每一個(gè)圖片的文件大小,那么將大大減少瀏覽器和服務(wù)器之間的數(shù)據(jù)傳輸量繁堡,進(jìn)而降低訪問延遲沈善,提升訪問體驗(yàn)。目前只有Chrome瀏覽器和Opera瀏覽器支持WebP格式椭蹄,兼容性不太好闻牡。

  • 在無損壓縮的情況下,相同質(zhì)量的WebP圖片绳矩,文件大小要比PNG小26%罩润;
  • 在有損壓縮的情況下,具有相同圖片精度的WebP圖片翼馆,文件大小要比JPEG小25%~34%割以;
  • WebP圖片格式支持圖片透明度,一個(gè)無損壓縮的WebP圖片应媚,如果要支持透明度只需要22%的格外文件大小严沥。

瀏覽器本地存儲(chǔ)方式及使用場景

(1)Cookie

Cookie是最早被提出來的本地存儲(chǔ)方式,在此之前珍特,服務(wù)端是無法判斷網(wǎng)絡(luò)中的兩個(gè)請(qǐng)求是否是同一用戶發(fā)起的祝峻,為解決這個(gè)問題,Cookie就出現(xiàn)了扎筒。Cookie的大小只有4kb,它是一種純文本文件酬姆,每次發(fā)起HTTP請(qǐng)求都會(huì)攜帶Cookie嗜桌。

Cookie的特性:

  • Cookie一旦創(chuàng)建成功,名稱就無法修改
  • Cookie是無法跨域名的辞色,也就是說a域名和b域名下的cookie是無法共享的骨宠,這也是由Cookie的隱私安全性決定的,這樣就能夠阻止非法獲取其他網(wǎng)站的Cookie
  • 每個(gè)域名下Cookie的數(shù)量不能超過20個(gè),每個(gè)Cookie的大小不能超過4kb
  • 有安全問題层亿,如果Cookie被攔截了桦卒,那就可獲得session的所有信息,即使加密也于事無補(bǔ)匿又,無需知道cookie的意義方灾,只要轉(zhuǎn)發(fā)cookie就能達(dá)到目的
  • Cookie在請(qǐng)求一個(gè)新的頁面的時(shí)候都會(huì)被發(fā)送過去

如果需要域名之間跨域共享Cookie,有兩種方法:

  1. 使用Nginx反向代理
  2. 在一個(gè)站點(diǎn)登陸之后碌更,往其他網(wǎng)站寫Cookie裕偿。服務(wù)端的Session存儲(chǔ)到一個(gè)節(jié)點(diǎn),Cookie存儲(chǔ)sessionId

Cookie的使用場景:

  • 最常見的使用場景就是Cookie和session結(jié)合使用痛单,我們將sessionId存儲(chǔ)到Cookie中嘿棘,每次發(fā)請(qǐng)求都會(huì)攜帶這個(gè)sessionId,這樣服務(wù)端就知道是誰發(fā)起的請(qǐng)求旭绒,從而響應(yīng)相應(yīng)的信息鸟妙。
  • 可以用來統(tǒng)計(jì)頁面的點(diǎn)擊次數(shù)

(2)LocalStorage

LocalStorage是HTML5新引入的特性,由于有的時(shí)候我們存儲(chǔ)的信息較大挥吵,Cookie就不能滿足我們的需求重父,這時(shí)候LocalStorage就派上用場了。

LocalStorage的優(yōu)點(diǎn):

  • 在大小方面蔫劣,LocalStorage的大小一般為5MB坪郭,可以儲(chǔ)存更多的信息
  • LocalStorage是持久儲(chǔ)存,并不會(huì)隨著頁面的關(guān)閉而消失脉幢,除非主動(dòng)清理歪沃,不然會(huì)永久存在
  • 僅儲(chǔ)存在本地,不像Cookie那樣每次HTTP請(qǐng)求都會(huì)被攜帶

LocalStorage的缺點(diǎn):

  • 存在瀏覽器兼容問題嫌松,IE8以下版本的瀏覽器不支持
  • 如果瀏覽器設(shè)置為隱私模式沪曙,那我們將無法讀取到LocalStorage
  • LocalStorage受到同源策略的限制,即端口萎羔、協(xié)議液走、主機(jī)地址有任何一個(gè)不相同,都不會(huì)訪問

LocalStorage的常用API:

// 保存數(shù)據(jù)到 localStorage
localStorage.setItem('key', 'value');

// 從 localStorage 獲取數(shù)據(jù)
let data = localStorage.getItem('key');

// 從 localStorage 刪除保存的數(shù)據(jù)
localStorage.removeItem('key');

// 從 localStorage 刪除所有保存的數(shù)據(jù)
localStorage.clear();

// 獲取某個(gè)索引的Key
localStorage.key(index)

LocalStorage的使用場景:

  • 有些網(wǎng)站有換膚的功能贾陷,這時(shí)候就可以將換膚的信息存儲(chǔ)在本地的LocalStorage中缘眶,當(dāng)需要換膚的時(shí)候,直接操作LocalStorage即可
  • 在網(wǎng)站中的用戶瀏覽信息也會(huì)存儲(chǔ)在LocalStorage中髓废,還有網(wǎng)站的一些不常變動(dòng)的個(gè)人信息等也可以存儲(chǔ)在本地的LocalStorage中

(3)SessionStorage

SessionStorage和LocalStorage都是在HTML5才提出來的存儲(chǔ)方案巷懈,SessionStorage 主要用于臨時(shí)保存同一窗口(或標(biāo)簽頁)的數(shù)據(jù),刷新頁面時(shí)不會(huì)刪除慌洪,關(guān)閉窗口或標(biāo)簽頁之后將會(huì)刪除這些數(shù)據(jù)顶燕。

SessionStorage與LocalStorage對(duì)比:

  • SessionStorage和LocalStorage都在本地進(jìn)行數(shù)據(jù)存儲(chǔ)凑保;
  • SessionStorage也有同源策略的限制,但是SessionStorage有一條更加嚴(yán)格的限制涌攻,SessionStorage只有在同一瀏覽器的同一窗口下才能夠共享欧引;
  • LocalStorage和SessionStorage都不能被爬蟲爬取

SessionStorage的常用API:

// 保存數(shù)據(jù)到 sessionStorage
sessionStorage.setItem('key', 'value');

// 從 sessionStorage 獲取數(shù)據(jù)
let data = sessionStorage.getItem('key');

// 從 sessionStorage 刪除保存的數(shù)據(jù)
sessionStorage.removeItem('key');

// 從 sessionStorage 刪除所有保存的數(shù)據(jù)
sessionStorage.clear();

// 獲取某個(gè)索引的Key
sessionStorage.key(index)

SessionStorage的使用場景

  • 由于SessionStorage具有時(shí)效性恳谎,所以可以用來存儲(chǔ)一些網(wǎng)站的游客登錄的信息芝此,還有臨時(shí)的瀏覽記錄的信息。當(dāng)關(guān)閉網(wǎng)站之后惠爽,這些信息也就隨之消除了癌蓖。

說一下SPA單頁面有什么優(yōu)缺點(diǎn)?

優(yōu)點(diǎn):

1.體驗(yàn)好婚肆,不刷新租副,減少 請(qǐng)求  數(shù)據(jù)ajax異步獲取 頁面流程;

2.前后端分離

3.減輕服務(wù)端壓力

4.共用一套后端程序代碼较性,適配多端

缺點(diǎn):

1.首屏加載過慢用僧;

2.SEO 不利于搜索引擎抓取

為什么函數(shù)的 arguments 參數(shù)是類數(shù)組而不是數(shù)組?如何遍歷類數(shù)組?

arguments是一個(gè)對(duì)象赞咙,它的屬性是從 0 開始依次遞增的數(shù)字责循,還有calleelength等屬性,與數(shù)組相似攀操;但是它卻沒有數(shù)組常見的方法屬性院仿,如forEach, reduce等,所以叫它們類數(shù)組速和。

要遍歷類數(shù)組歹垫,有三個(gè)方法:

(1)將數(shù)組的方法應(yīng)用到類數(shù)組上,這時(shí)候就可以使用callapply方法颠放,如:

function foo(){ 
  Array.prototype.forEach.call(arguments, a => console.log(a))
}

(2)使用Array.from方法將類數(shù)組轉(zhuǎn)化成數(shù)組:

function foo(){ 
  const arrArgs = Array.from(arguments) 
  arrArgs.forEach(a => console.log(a))
}

(3)使用展開運(yùn)算符將類數(shù)組轉(zhuǎn)化成數(shù)組

function foo(){ 
    const arrArgs = [...arguments] 
    arrArgs.forEach(a => console.log(a)) 
}

setTimeout排惨、setInterval、requestAnimationFrame 各有什么特點(diǎn)碰凶?

異步編程當(dāng)然少不了定時(shí)器了暮芭,常見的定時(shí)器函數(shù)有 setTimeoutsetInterval欲低、requestAnimationFrame辕宏。最常用的是setTimeout,很多人認(rèn)為 setTimeout 是延時(shí)多久砾莱,那就應(yīng)該是多久后執(zhí)行匾效。

其實(shí)這個(gè)觀點(diǎn)是錯(cuò)誤的,因?yàn)?JS 是單線程執(zhí)行的恤磷,如果前面的代碼影響了性能面哼,就會(huì)導(dǎo)致 setTimeout 不會(huì)按期執(zhí)行。當(dāng)然了扫步,可以通過代碼去修正 setTimeout魔策,從而使定時(shí)器相對(duì)準(zhǔn)確:

let period = 60 * 1000 * 60 * 2
let startTime = new Date().getTime()
let count = 0
let end = new Date().getTime() + period
let interval = 1000
let currentInterval = interval
function loop() {
  count++
  // 代碼執(zhí)行所消耗的時(shí)間
  let offset = new Date().getTime() - (startTime + count * interval);
  let diff = end - new Date().getTime()
  let h = Math.floor(diff / (60 * 1000 * 60))
  let hdiff = diff % (60 * 1000 * 60)
  let m = Math.floor(hdiff / (60 * 1000))
  let mdiff = hdiff % (60 * 1000)
  let s = mdiff / (1000)
  let sCeil = Math.ceil(s)
  let sFloor = Math.floor(s)
  // 得到下一次循環(huán)所消耗的時(shí)間
  currentInterval = interval - offset 
  console.log('時(shí):'+h, '分:'+m, '毫秒:'+s, '秒向上取整:'+sCeil, '代碼執(zhí)行時(shí)間:'+offset, '下次循環(huán)間隔'+currentInterval) 
  setTimeout(loop, currentInterval)
}
setTimeout(loop, currentInterval)

接下來看 setInterval,其實(shí)這個(gè)函數(shù)作用和 setTimeout 基本一致河胎,只是該函數(shù)是每隔一段時(shí)間執(zhí)行一次回調(diào)函數(shù)闯袒。

通常來說不建議使用 setInterval。第一游岳,它和 setTimeout 一樣政敢,不能保證在預(yù)期的時(shí)間執(zhí)行任務(wù)。第二胚迫,它存在執(zhí)行累積的問題喷户,請(qǐng)看以下偽代碼

function demo() {
  setInterval(function(){
    console.log(2)
  },1000)
  sleep(2000)
}
demo()

以上代碼在瀏覽器環(huán)境中,如果定時(shí)器執(zhí)行過程中出現(xiàn)了耗時(shí)操作访锻,多個(gè)回調(diào)函數(shù)會(huì)在耗時(shí)操作結(jié)束以后同時(shí)執(zhí)行褪尝,這樣可能就會(huì)帶來性能上的問題。

如果有循環(huán)定時(shí)器的需求期犬,其實(shí)完全可以通過 requestAnimationFrame 來實(shí)現(xiàn):

function setInterval(callback, interval) {
  let timer
  const now = Date.now
  let startTime = now()
  let endTime = startTime
  const loop = () => {
    timer = window.requestAnimationFrame(loop)
    endTime = now()
    if (endTime - startTime >= interval) {
      startTime = endTime = now()
      callback(timer)
    }
  }
  timer = window.requestAnimationFrame(loop)
  return timer
}
let a = 0
setInterval(timer => {
  console.log(1)
  a++
  if (a === 3) cancelAnimationFrame(timer)
}, 1000)

首先 requestAnimationFrame 自帶函數(shù)節(jié)流功能河哑,基本可以保證在 16.6 毫秒內(nèi)只執(zhí)行一次(不掉幀的情況下),并且該函數(shù)的延時(shí)效果是精確的龟虎,沒有其他定時(shí)器時(shí)間不準(zhǔn)的問題掏熬,當(dāng)然你也可以通過該函數(shù)來實(shí)現(xiàn) setTimeout

== 操作符的強(qiáng)制類型轉(zhuǎn)換規(guī)則绢掰?

對(duì)于 == 來說患朱,如果對(duì)比雙方的類型不一樣,就會(huì)進(jìn)行類型轉(zhuǎn)換旭斥。假如對(duì)比 xy 是否相同容达,就會(huì)進(jìn)行如下判斷流程:

  1. 首先會(huì)判斷兩者類型是否相同,相同的話就比較兩者的大写谷花盐;
  2. 類型不相同的話,就會(huì)進(jìn)行類型轉(zhuǎn)換菇爪;
  3. 會(huì)先判斷是否在對(duì)比 nullundefined算芯,是的話就會(huì)返回 true
  4. 判斷兩者類型是否為 stringnumber,是的話就會(huì)將字符串轉(zhuǎn)換為 number
1 == '1'
      ↓
1 ==  1

  1. 判斷其中一方是否為 boolean凳宙,是的話就會(huì)把 boolean 轉(zhuǎn)為 number 再進(jìn)行判斷
'1' == true
        ↓
'1' ==  1
        ↓
 1  ==  1

  1. 判斷其中一方是否為 object 且另一方為 string熙揍、number 或者 symbol,是的話就會(huì)把 object 轉(zhuǎn)為原始類型再進(jìn)行判斷
'1' == { name: 'js' }        ↓'1' == '[object Object]'

偽元素和偽類的區(qū)別和作用氏涩?

  • 偽元素:在內(nèi)容元素的前后插入額外的元素或樣式届囚,但是這些元素實(shí)際上并不在文檔中生成有梆。它們只在外部顯示可見,但不會(huì)在文檔的源代碼中找到它們意系,因此泥耀,稱為“偽”元素。例如:
p::before {content:"第一章:";}
p::after {content:"Hot!";}
p::first-line {background:red;}
p::first-letter {font-size:30px;}

  • 偽類:將特殊的效果添加到特定選擇器上蛔添。它是已有元素上添加類別的痰催,不會(huì)產(chǎn)生新的元素。例如:
a:hover {color: #FF00FF}
p:first-child {color: red}

總結(jié): 偽類是通過在元素選擇器上加?偽類改變?cè)貭顟B(tài)迎瞧,?偽元素通過對(duì)元素的操作進(jìn)?對(duì)元素的改變夸溶。

代碼輸出結(jié)果

var a, b
(function () {
   console.log(a);
   console.log(b);
   var a = (b = 3);
   console.log(a);
   console.log(b);   
})()
console.log(a);
console.log(b);

輸出結(jié)果:

undefined 
undefined 
3 
3 
undefined 
3

這個(gè)題目和上面題目考察的知識(shí)點(diǎn)類似,b賦值為3凶硅,b此時(shí)是一個(gè)全局變量缝裁,而將3賦值給a,a是一個(gè)局部變量咏尝,所以最后打印的時(shí)候压语,a仍舊是undefined。

什么是同源策略

跨域問題其實(shí)就是瀏覽器的同源策略造成的编检。

同源策略限制了從同一個(gè)源加載的文檔或腳本如何與另一個(gè)源的資源進(jìn)行交互胎食。這是瀏覽器的一個(gè)用于隔離潛在惡意文件的重要的安全機(jī)制。同源指的是:協(xié)議允懂、端口號(hào)厕怜、域名必須一致。

同源策略:protocol(協(xié)議)蕾总、domain(域名)粥航、port(端口)三者必須一致。

同源政策主要限制了三個(gè)方面:

  • 當(dāng)前域下的 js 腳本不能夠訪問其他域下的 cookie生百、localStorage 和 indexDB递雀。
  • 當(dāng)前域下的 js 腳本不能夠操作訪問操作其他域下的 DOM。
  • 當(dāng)前域下 ajax 無法發(fā)送跨域請(qǐng)求蚀浆。

同源政策的目的主要是為了保證用戶的信息安全缀程,它只是對(duì) js 腳本的一種限制,并不是對(duì)瀏覽器的限制市俊,對(duì)于一般的 img杨凑、或者script 腳本請(qǐng)求都不會(huì)有跨域的限制,這是因?yàn)檫@些操作都不會(huì)通過響應(yīng)結(jié)果來進(jìn)行可能出現(xiàn)安全問題的操作摆昧。

TCP 和 UDP的概念及特點(diǎn)

TCP 和 UDP都是傳輸層協(xié)議撩满,他們都屬于TCP/IP協(xié)議族:

(1)UDP

UDP的全稱是用戶數(shù)據(jù)報(bào)協(xié)議,在網(wǎng)絡(luò)中它與TCP協(xié)議一樣用于處理數(shù)據(jù)包,是一種無連接的協(xié)議伺帘。在OSI模型中昭躺,在傳輸層,處于IP協(xié)議的上一層曼追。UDP有不提供數(shù)據(jù)包分組窍仰、組裝和不能對(duì)數(shù)據(jù)包進(jìn)行排序的缺點(diǎn),也就是說礼殊,當(dāng)報(bào)文發(fā)送之后,是無法得知其是否安全完整到達(dá)的针史。

它的特點(diǎn)如下:

1)面向無連接

首先 UDP 是不需要和 TCP一樣在發(fā)送數(shù)據(jù)前進(jìn)行三次握手建立連接的晶伦,想發(fā)數(shù)據(jù)就可以開始發(fā)送了。并且也只是數(shù)據(jù)報(bào)文的搬運(yùn)工啄枕,不會(huì)對(duì)數(shù)據(jù)報(bào)文進(jìn)行任何拆分和拼接操作婚陪。

具體來說就是:

  • 在發(fā)送端,應(yīng)用層將數(shù)據(jù)傳遞給傳輸層的 UDP 協(xié)議频祝,UDP 只會(huì)給數(shù)據(jù)增加一個(gè) UDP 頭標(biāo)識(shí)下是 UDP 協(xié)議泌参,然后就傳遞給網(wǎng)絡(luò)層了
  • 在接收端,網(wǎng)絡(luò)層將數(shù)據(jù)傳遞給傳輸層常空,UDP 只去除 IP 報(bào)文頭就傳遞給應(yīng)用層沽一,不會(huì)任何拼接操作

2)有單播,多播漓糙,廣播的功能

UDP 不止支持一對(duì)一的傳輸方式铣缠,同樣支持一對(duì)多,多對(duì)多昆禽,多對(duì)一的方式蝗蛙,也就是說 UDP 提供了單播,多播醉鳖,廣播的功能捡硅。

3)面向報(bào)文

發(fā)送方的UDP對(duì)應(yīng)用程序交下來的報(bào)文,在添加首部后就向下交付IP層盗棵。UDP對(duì)應(yīng)用層交下來的報(bào)文壮韭,既不合并,也不拆分漾根,而是保留這些報(bào)文的邊界泰涂。因此,應(yīng)用程序必須選擇合適大小的報(bào)文

4)不可靠性

首先不可靠性體現(xiàn)在無連接上辐怕,通信都不需要建立連接逼蒙,想發(fā)就發(fā),這樣的情況肯定不可靠寄疏。

并且收到什么數(shù)據(jù)就傳遞什么數(shù)據(jù)是牢,并且也不會(huì)備份數(shù)據(jù)僵井,發(fā)送數(shù)據(jù)也不會(huì)關(guān)心對(duì)方是否已經(jīng)正確接收到數(shù)據(jù)了。

再者網(wǎng)絡(luò)環(huán)境時(shí)好時(shí)壞驳棱,但是 UDP 因?yàn)闆]有擁塞控制批什,一直會(huì)以恒定的速度發(fā)送數(shù)據(jù)。即使網(wǎng)絡(luò)條件不好社搅,也不會(huì)對(duì)發(fā)送速率進(jìn)行調(diào)整驻债。這樣實(shí)現(xiàn)的弊端就是在網(wǎng)絡(luò)條件不好的情況下可能會(huì)導(dǎo)致丟包,但是優(yōu)點(diǎn)也很明顯形葬,在某些實(shí)時(shí)性要求高的場景(比如電話會(huì)議)就需要使用 UDP 而不是 TCP合呐。

5)頭部開銷小,傳輸數(shù)據(jù)報(bào)文時(shí)是很高效的笙以。

UDP 頭部包含了以下幾個(gè)數(shù)據(jù):

  • 兩個(gè)十六位的端口號(hào)淌实,分別為源端口(可選字段)和目標(biāo)端口
  • 整個(gè)數(shù)據(jù)報(bào)文的長度
  • 整個(gè)數(shù)據(jù)報(bào)文的檢驗(yàn)和(IPv4 可選字段),該字段用于發(fā)現(xiàn)頭部信息和數(shù)據(jù)中的錯(cuò)誤

因此 UDP 的頭部開銷小猖腕,只有8字節(jié)拆祈,相比 TCP 的至少20字節(jié)要少得多,在傳輸數(shù)據(jù)報(bào)文時(shí)是很高效的倘感。

(2)TCP TCP的全稱是傳輸控制協(xié)議是一種面向連接的放坏、可靠的、基于字節(jié)流的傳輸層通信協(xié)議侠仇。TCP 是面向連接的轻姿、可靠的流協(xié)議(流就是指不間斷的數(shù)據(jù)結(jié)構(gòu))。

它有以下幾個(gè)特點(diǎn):

1)面向連接

面向連接逻炊,是指發(fā)送數(shù)據(jù)之前必須在兩端建立連接互亮。建立連接的方法是“三次握手”,這樣能建立可靠的連接余素。建立連接豹休,是為數(shù)據(jù)的可靠傳輸打下了基礎(chǔ)。

2)僅支持單播傳輸

每條TCP傳輸連接只能有兩個(gè)端點(diǎn)桨吊,只能進(jìn)行點(diǎn)對(duì)點(diǎn)的數(shù)據(jù)傳輸威根,不支持多播和廣播傳輸方式。

3)面向字節(jié)流

TCP不像UDP一樣那樣一個(gè)個(gè)報(bào)文獨(dú)立地傳輸视乐,而是在不保留報(bào)文邊界的情況下以字節(jié)流方式進(jìn)行傳輸洛搀。

4)可靠傳輸

對(duì)于可靠傳輸,判斷丟包佑淀、誤碼靠的是TCP的段編號(hào)以及確認(rèn)號(hào)留美。TCP為了保證報(bào)文傳輸?shù)目煽浚徒o每個(gè)包一個(gè)序號(hào),同時(shí)序號(hào)也保證了傳送到接收端實(shí)體的包的按序接收谎砾。然后接收端實(shí)體對(duì)已成功收到的字節(jié)發(fā)回一個(gè)相應(yīng)的確認(rèn)(ACK)逢倍;如果發(fā)送端實(shí)體在合理的往返時(shí)延(RTT)內(nèi)未收到確認(rèn),那么對(duì)應(yīng)的數(shù)據(jù)(假設(shè)丟失了)將會(huì)被重傳景图。

5)提供擁塞控制

當(dāng)網(wǎng)絡(luò)出現(xiàn)擁塞的時(shí)候较雕,TCP能夠減小向網(wǎng)絡(luò)注入數(shù)據(jù)的速率和數(shù)量,緩解擁塞挚币。

6)提供全雙工通信

TCP允許通信雙方的應(yīng)用程序在任何時(shí)候都能發(fā)送數(shù)據(jù)亮蒋,因?yàn)門CP連接的兩端都設(shè)有緩存,用來臨時(shí)存放雙向通信的數(shù)據(jù)忘晤。當(dāng)然宛蚓,TCP可以立即發(fā)送一個(gè)數(shù)據(jù)段,也可以緩存一段時(shí)間以便一次發(fā)送更多的數(shù)據(jù)段(最大的數(shù)據(jù)段大小取決于MSS)

說一下怎么把類數(shù)組轉(zhuǎn)換為數(shù)組?

//通過call調(diào)用數(shù)組的slice方法來實(shí)現(xiàn)轉(zhuǎn)換
Array.prototype.slice.call(arrayLike)

//通過call調(diào)用數(shù)組的splice方法來實(shí)現(xiàn)轉(zhuǎn)換
Array.prototype.splice.call(arrayLike,0)

//通過apply調(diào)用數(shù)組的concat方法來實(shí)現(xiàn)轉(zhuǎn)換
Array.prototype.concat.apply([],arrayLike)

//通過Array.from方法來實(shí)現(xiàn)轉(zhuǎn)換
Array.from(arrayLike)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末设塔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子远舅,更是在濱河造成了極大的恐慌闰蛔,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件图柏,死亡現(xiàn)場離奇詭異序六,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蚤吹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門例诀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人裁着,你說我怎么就攤上這事繁涂。” “怎么了二驰?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵扔罪,是天一觀的道長。 經(jīng)常有香客問我桶雀,道長矿酵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任矗积,我火速辦了婚禮全肮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘棘捣。我一直安慰自己辜腺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哪自,像睡著了一般丰包。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上壤巷,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天邑彪,我揣著相機(jī)與錄音,去河邊找鬼胧华。 笑死寄症,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的矩动。 我是一名探鬼主播有巧,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼悲没!你這毒婦竟也來了篮迎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤示姿,失蹤者是張志新(化名)和其女友劉穎甜橱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體栈戳,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岂傲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了子檀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镊掖。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖褂痰,靈堂內(nèi)的尸體忽然破棺而出亩进,到底是詐尸還是另有隱情,我是刑警寧澤脐恩,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布镐侯,位于F島的核電站,受9級(jí)特大地震影響驶冒,放射性物質(zhì)發(fā)生泄漏苟翻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一骗污、第九天 我趴在偏房一處隱蔽的房頂上張望崇猫。 院中可真熱鬧,春花似錦需忿、人聲如沸诅炉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涕烧。三九已至月而,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間议纯,已是汗流浹背父款。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瞻凤,地道東北人憨攒。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像阀参,于是被迫代替她去往敵國和親肝集。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容