App下載

JavaScript面試經(jīng),offer拿到手軟

猿友 2020-09-28 10:31:19 瀏覽數(shù) (2486)
反饋

文章來(lái)源于公眾號(hào):猴哥說(shuō)前端 作者:monkeysoft

本文給大家分享一些 JavaScript 面試經(jīng)驗(yàn),在這金九銀十的招聘季,希望大家都能找到滿意的工作。

JavaScript的數(shù)據(jù)類(lèi)型都有什么?

基本數(shù)據(jù)類(lèi)型:String,Boolean,Number,Undefined,Null

引用數(shù)據(jù)類(lèi)型:Object(Array,Date,RegExp,Function)

javascript中=====的區(qū)別是什么?

==會(huì)自動(dòng)進(jìn)行類(lèi)型轉(zhuǎn)換,===不會(huì)

例舉3種強(qiáng)制類(lèi)型轉(zhuǎn)換和2種隱式類(lèi)型轉(zhuǎn)換?

強(qiáng)制(parseInt,parseFloat,Number())

隱式(==) 1==”1”//true

null==undefined//true

原生 JS 中 call()、apply()、bind() 方法有什么區(qū)別?

三個(gè)方法都可以改變函數(shù)運(yùn)行時(shí)的 this 指向。

三個(gè)方法第一個(gè)參數(shù)都是函數(shù)調(diào)用執(zhí)行時(shí)this 指向的對(duì)象。

call() 方法第二個(gè)參數(shù)是個(gè)可變參數(shù),是函數(shù)調(diào)用執(zhí)行時(shí)本身所需要的參數(shù)。

apply() 方法第二個(gè)參數(shù)是數(shù)組或arguments。call()與apply()都是立即調(diào)用函數(shù)執(zhí)行,在運(yùn)行時(shí)修改this指向。

bind()是返回一個(gè)新的函數(shù),新函數(shù)的函數(shù)主體與原函數(shù)的函數(shù)主體一致,當(dāng)新函數(shù)被調(diào)用時(shí),函數(shù)體中 this 指向的是 bind() 方法第一個(gè)參數(shù)傳遞的對(duì)象,而bind() 方法不會(huì)影響原函數(shù)本身的 this 指向。

什么是閉包?特點(diǎn)是?

閉包,官方對(duì)閉包的解釋是:一個(gè)擁有許多變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個(gè)函數(shù)),因而這些變量也是該表達(dá)式的一部分。

閉包的特點(diǎn):

(1)作為一個(gè)函數(shù)變量的一個(gè)引用,當(dāng)函數(shù)返回時(shí),其處于激活狀態(tài)。

(2) 一個(gè)閉包就是當(dāng)一個(gè)函數(shù)返回時(shí),一個(gè)沒(méi)有釋放資源的棧區(qū)。

簡(jiǎn)單的說(shuō),JavaScript 允許使用內(nèi)部函數(shù)---即函數(shù)定義和函數(shù)表達(dá)式位于另一個(gè)函數(shù)的函數(shù)體內(nèi)。而且,這些內(nèi)部函數(shù)可以訪問(wèn)它們所在的外部函數(shù)中聲明的所有局部變量、參數(shù)和聲明的其他內(nèi)部函數(shù)。當(dāng)其中一個(gè)這樣的內(nèi)部函數(shù)在包含它們的外部函數(shù)之外被調(diào)用時(shí),就會(huì)形成閉包。

事件委托是什么?

符合W3C標(biāo)準(zhǔn)的事件綁定 addEventLisntener /attachEvent

讓利用事件冒泡的原理,讓自己的所觸發(fā)的事件,讓他的父元素代替執(zhí)行!

如何阻止事件冒泡和默認(rèn)事件

e. stopPropagation();//標(biāo)準(zhǔn)瀏覽器

event.canceBubble=true;//ie9之前

阻止默認(rèn)事件:

為了不讓a點(diǎn)擊之后跳轉(zhuǎn),我們就要給他的點(diǎn)擊事件進(jìn)行阻止

return false

e.preventDefault();

document load 和document ready的區(qū)別?

Document.onload 是在結(jié)構(gòu)和樣式加載完才執(zhí)行js

window.onload:不僅僅要在結(jié)構(gòu)和樣式加載完,還要執(zhí)行完所有的樣式、圖片這些資源文件,全部加載完才會(huì)觸發(fā)window.onload事件

Document.ready原生種沒(méi)有這個(gè)方法,jquery中有 $().ready(function)

為了保證頁(yè)面輸出安全,我們經(jīng)常需要對(duì)一些特殊的字符進(jìn)行轉(zhuǎn)義,請(qǐng)寫(xiě)一個(gè)函數(shù)escapeHtml,將, &, “進(jìn)行轉(zhuǎn)義

return str.replace(/[”&]/g, function(match) {
    switch (match) {
      case “”:
          return “>”;
      case “&”:
          return “&”;
      case “\””:
         return “"”;
     }
  });
}

簡(jiǎn)述創(chuàng)建函數(shù)的幾種方式

第一種(函數(shù)聲明):function sum1(num1,num2){ return num1+num2; }

第二種(函數(shù)表達(dá)式):var sum2 = function(num1,num2){ return num1+num2; }

第三種(函數(shù)對(duì)象方式):var sum3 = new Function("num1","num2","return num1+num2");

把 Script 標(biāo)簽 放在頁(yè)面的最底部的body封閉之前 和封閉之后有什么區(qū)別?瀏覽器會(huì)如何解析它們?

如果說(shuō)放在body的封閉之前,將會(huì)阻塞其他資源的加載

如果放在body封閉之后,不會(huì)影響body內(nèi)元素的加載

iframe的優(yōu)缺點(diǎn)?

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

  1. 解決加載緩慢的第三方內(nèi)容如圖標(biāo)和廣告等的加載問(wèn)題
  2. Security sandbox
  3. 并行加載腳本

缺點(diǎn):

  1. iframe會(huì)阻塞主頁(yè)面的Onload事件
  2. 即時(shí)內(nèi)容為空,加載也需要時(shí)間
  3. 沒(méi)有語(yǔ)意

Javascript如何實(shí)現(xiàn)繼承?

原型鏈繼承,借用構(gòu)造函數(shù)繼承,組合繼承,寄生式繼承,寄生組合繼承

請(qǐng)你談?wù)凜ookie的弊端?

缺點(diǎn):

1.Cookie 數(shù)量和長(zhǎng)度的限制。部分瀏覽器每個(gè) domain 最多只能有50條 cookie,基本所有瀏覽器中每個(gè) cookie 長(zhǎng)度不能超過(guò)4KB,否則會(huì)被截掉。

2.安全性問(wèn)題。如果 cookie 被人攔截了,那人就可以取得所有的 session 信息。即使加密也與事無(wú)補(bǔ),因?yàn)閿r截者并不需要知道 cookie 的意義,他只要原樣轉(zhuǎn)發(fā) cookie 就可以達(dá)到目的了。

3.有些狀態(tài)不可能保存在客戶(hù)端。例如,為了防止重復(fù)提交表單,我們需要在服務(wù)器端保存一個(gè)計(jì)數(shù)器。如果我們把這個(gè)計(jì)數(shù)器保存在客戶(hù)端,那么它起不到任何作用。

4.占用網(wǎng)絡(luò)上傳帶寬。每次請(qǐng)求服務(wù)器資源時(shí),都會(huì)攜帶 cookie 信息向服務(wù)器傳遞。

DOM操作——怎樣添加、移除、移動(dòng)、復(fù)制、創(chuàng)建和查找節(jié)點(diǎn)?

創(chuàng)建新節(jié)點(diǎn)

  • createDocumentFragment() // 創(chuàng)建一個(gè)DOM片段
  • createElement() // 創(chuàng)建一個(gè)具體的元素
  • createTextNode() // 創(chuàng)建一個(gè)文本節(jié)點(diǎn)

添加、移除、替換、插入

  • appendChild()
  • removeChild()
  • replaceChild()
  • insertBefore() // 在已有的子節(jié)點(diǎn)前插入一個(gè)新的子節(jié)點(diǎn)

查找

  • getElementsByTagName() // 通過(guò)標(biāo)簽名稱(chēng)
  • getElementsByName() // 通過(guò)元素的Name屬性的值(IE容錯(cuò)能力較強(qiáng),會(huì)得到一個(gè)數(shù)組,其中包括id等于name值的)
  • getElementById() // 通過(guò)元素Id,唯一性

js延遲加載的方式有哪些?

  1. defer和async
  2. 動(dòng)態(tài)創(chuàng)建DOM方式(創(chuàng)建script,插入到DOM中,加載完畢后callBack)
  3. 按需異步載入js

documen.write和 innerHTML 的區(qū)別?

document.write 只能重繪整個(gè)頁(yè)面

innerHTML 可以重繪頁(yè)面的一部分

哪些操作會(huì)造成內(nèi)存泄漏?

內(nèi)存泄漏指任何對(duì)象在您不再擁有或需要它之后仍然存在。垃圾回收器定期掃描對(duì)象,并計(jì)算引用了每個(gè)對(duì)象的其他對(duì)象的數(shù)量。如果一個(gè)對(duì)象的引用數(shù)量為 0(沒(méi)有其他對(duì)象引用過(guò)該對(duì)象),或?qū)υ搶?duì)象的惟一引用是循環(huán)的,那么該對(duì)象的內(nèi)存即可回收。

  1. setTimeout 的第一個(gè)參數(shù)使用字符串而非函數(shù)的話,會(huì)引發(fā)內(nèi)存泄漏。
  2. 閉包
  3. 控制臺(tái)日志
  4. 循環(huán)(在兩個(gè)對(duì)象彼此引用且彼此保留時(shí),就會(huì)產(chǎn)生一個(gè)循環(huán))

判斷一個(gè)字符串中出現(xiàn)次數(shù)最多的字符,統(tǒng)計(jì)這個(gè)次數(shù)?

var str = 'asdfssaaasasasasaa';
var json = {};
for (var i = 0; i < str.length; i++) {
        if(!json[str.charAt(i)]){
                json[str.charAt(i)] = 1;
        }else{
                json[str.charAt(i)]++;
        }
};
var iMax = 0;
var iIndex = '';
for(var i in json){
        if(json[i]>iMax){
                iMax = json[i];
                iIndex = i;
        }
}
alert('出現(xiàn)次數(shù)最多的是:'+iIndex+'出現(xiàn)'+iMax+'次');

數(shù)組扁平化

數(shù)組扁平化是指將一個(gè)多維數(shù)組變?yōu)橐粋€(gè)一維數(shù)組

const arr = [1, [2, [3, [4, 5]]], 6];
// => [1, 2, 3, 4, 5, 6]

方法一:使用flat()

const res1 = arr.flat(Infinity);

方法二:利用正則

const res2 = JSON.stringify(arr).replace(/\[|\]/g, '').split(',');

但數(shù)據(jù)類(lèi)型都會(huì)變?yōu)樽址?/p>

方法三:正則改良版本

const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g, '') + ']');

方法四:使用reduce

const flatten = arr => {
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
  }, [])
}
const res4 = flatten(arr);

方法五:函數(shù)遞歸

const res5 = [];
const fn = arr => {
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      fn(arr[i]);
    } else {
      res5.push(arr[i]);
    }
  }
}
fn(arr);

數(shù)組去重

const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]

方法一:利用Set

const res1 = Array.from(new Set(arr));

方法二:兩層for循環(huán)+splice

const unique1 = arr => {
  let len = arr.length;
  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1);
        // 每刪除一個(gè)樹(shù),j--保證j的值經(jīng)過(guò)自加后不變。同時(shí),len--,減少循環(huán)次數(shù)提升性能
        len--;
        j--;
      }
    }
  }
  return arr;
}

方法三:利用indexOf

const unique2 = arr => {
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (res.indexOf(arr[i]) === -1) res.push(arr[i]);
  }
  return res;
}

方法四:利用include

const unique3 = arr => {
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!res.includes(arr[i])) res.push(arr[i]);
  }
  return res;
}

方法五:利用filter

const unique4 = arr => {
  return arr.filter((item, index) => {
    return arr.indexOf(item) === index;
  });
}

方法六:利用Map

const unique5 = arr => {
  const map = new Map();
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!map.has(arr[i])) {
      map.set(arr[i], true)
      res.push(arr[i]);
    }
  }
  return res;
}

類(lèi)數(shù)組轉(zhuǎn)化為數(shù)組

類(lèi)數(shù)組是具有l(wèi)ength屬性,但不具有數(shù)組原型上的方法。常見(jiàn)的類(lèi)數(shù)組有arguments、DOM操作方法返回的結(jié)果。

方法一:Array.from

Array.from(document.querySelectorAll('div'))

方法二:Array.prototype.slice.call()

Array.prototype.slice.call(document.querySelectorAll('div'))

方法三:擴(kuò)展運(yùn)算符

[...document.querySelectorAll('div')]

方法四:利用concat

Array.prototype.concat.apply([], document.querySelectorAll('div'));

debounce(防抖)

觸發(fā)高頻時(shí)間后n秒內(nèi)函數(shù)只會(huì)執(zhí)行一次,如果n秒內(nèi)高頻時(shí)間再次觸發(fā),則重新計(jì)算時(shí)間。

const debounce = (fn, time) => {
  let timeout = null;
  return function() {
    clearTimeout(timeout)
    timeout = setTimeout(() => {
      fn.apply(this, arguments);
    }, time);
  }
};

防抖常應(yīng)用于用戶(hù)進(jìn)行搜索輸入節(jié)約請(qǐng)求資源,window觸發(fā)resize事件時(shí)進(jìn)行防抖只觸發(fā)一次。

throttle(節(jié)流)

高頻時(shí)間觸發(fā),但n秒內(nèi)只會(huì)執(zhí)行一次,所以節(jié)流會(huì)稀釋函數(shù)的執(zhí)行頻率。

const throttle = (fn, time) => {
  let flag = true;
  return function() {
    if (!flag) return;
    flag = false;
    setTimeout(() => {
      fn.apply(this, arguments);
      flag = true;
    }, time);
  }
}

節(jié)流常應(yīng)用于鼠標(biāo)不斷點(diǎn)擊觸發(fā)、監(jiān)聽(tīng)滾動(dòng)事件。

函數(shù)珂里化

指的是將一個(gè)接受多個(gè)參數(shù)的函數(shù) 變?yōu)?接受一個(gè)參數(shù)返回一個(gè)函數(shù)的固定形式,這樣便于再次調(diào)用,例如f(1)(2)

經(jīng)典面試題:實(shí)現(xiàn)add(1)(2)(3)(4)=10; 、 add(1)(1,2,3)(2)=9;

function add() {
  const _args = [...arguments];
  function fn() {
    _args.push(...arguments);
    return fn;
  }
  fn.toString = function() {
    return _args.reduce((sum, cur) => sum + cur);
  }
  return fn;
}

深拷貝

遞歸的完整版本(考慮到了Symbol屬性):

const cloneDeep1 = (target, hash = new WeakMap()) => {
  // 對(duì)于傳入?yún)?shù)處理
  if (typeof target !== 'object' || target === null) {
    return target;
  }
  // 哈希表中存在直接返回
  if (hash.has(target)) return hash.get(target);


  const cloneTarget = Array.isArray(target) ? [] : {};
  hash.set(target, cloneTarget);


  // 針對(duì)Symbol屬性
  const symKeys = Object.getOwnPropertySymbols(target);
  if (symKeys.length) {
    symKeys.forEach(symKey => {
      if (typeof target[symKey] === 'object' && target[symKey] !== null) {
        cloneTarget[symKey] = cloneDeep1(target[symKey]);
      } else {
        cloneTarget[symKey] = target[symKey];
      }
    })
  }


  for (const i in target) {
    if (Object.prototype.hasOwnProperty.call(target, i)) {
      cloneTarget[i] =
        typeof target[i] === 'object' && target[i] !== null
        ? cloneDeep1(target[i], hash)
        : target[i];
    }
  }
  return cloneTarget;
}

以上就是W3Cschool編程獅關(guān)于JavaScript面試經(jīng),offer拿到手軟的相關(guān)介紹了,希望對(duì)大家有所幫助。

0 人點(diǎn)贊