前端面試必問問題18道---一篇文章進大廠

談談數組Array在項目中常見的場景
  • 求和,求最大(小)值,排序,多個數組合並(concat),去重,分割,find,indexOf,filter,join, toString等等
  • 數組的concat,join,slice,toString方法不會改變原數組
  • 數組的splice,push,pop,unshift,shift,sort,reverse方法會改變原數組
類數組怎麼轉化爲數組

什麼是類數組,就是屬性要爲索引(數字)屬性,同時又必須有一個length屬性來控制對象邊界的一個特殊對象,特點:
1.不能使用數組的方法,他們不能使用Array的方法
2.擁有length屬性,可以使用下標來訪問元素,這兩點和數組相同。

var obj = {
  '0': 'a',
  '1': 'b',
  '2': 'c',
  'length': 3
}

轉化爲數組的方法有3種:

  • 第一種,採用es6中的新方法Array.from()
var myArray = ["value1", "value2", "value3"];
var mySet = new Set(myArray);
console.log(Array.from(mySet));  //第一種方法Array.from()
console.log([...mySet]);         //第二種方法Array.from()
  • 第二種,創建新數組,通過forEach遍歷把類數組的元素添加到新數組中

  • 第三種,通過Array.prototype.slice.call(arrayLike, 0)或者[].slice.call(arrayLike, 0)將類數組對象傳入即可

  • 第四種,擴展運算符 ... [...arguments]

// 將參數轉爲數組
var fn = function() {
  console.log([...arguments]);
}
fn(1, 2, 3)

// 展開Set值
[...set.values()]
common.js和ES6模塊化的區別

詳情可見:
js的4種模塊化的使用方法和區別
import、require、export、module.exports 混合使用詳解

不同規範產生的來由啊,隨着技術的發展,js這門語言模塊化的需要也越來迫切,隨之而來就是各種規範,什麼CMD、AMD各種規範也應運而生,現在在服務端形成CommonJS規範,前端呢ES6模塊化的概念也深入人心。
畢竟一個是用於後端,一個是用於前端,所以還是有很大的不同,比較一下common.js和ES6模塊化的區別:

  • 前者支持動態導入,也就是 require(${path}/xx.js),後者目前不支持。

  • 前者是同步導入,因爲用於服務端,文件都在本地,同步導入即使卡住主線程影響也不大。而後者是異步導入,因爲用於瀏覽器,需要下載文件,如果也採用同步導入會對渲染有很大影響。

  • 前者在導出時都是值拷貝,就算導出的值變了,導入的值也不會改變,所以如果想更新值,必須重新導入一次。但是後者採用實時綁定的方式,導入導出的值都指向同一個內存地址,所以導入值會跟隨導出值變化

  • require 在 ES6(bable將import轉化爲require) 和 CommonJS 中都支持,像Vue中一般引入CSS樣式的時候會用到require,是因爲樣式一般需要同步導入的原因所致

  • 即使我們使用了 ES6 的模塊系統,如果藉助 Babel 的轉換,ES6 的模塊系統最終還是會轉換成 CommonJS 的規範。

兩個數據互換,比如a=6&b=5,怎麼做能a=5&b=6
// 第一種方法
var a = 10;
var b = 20;
var tmp = a; // tmp = 10;
a = b;       // a = 20;
b = tmp;     // b = 10;
// 第二種方法
var a = 10;
var b = 20;
a = a + b;
b = a - b;
a = a - b;
TS中枚舉的定義

枚舉(Enum)類型用於取值被限定在一定範圍內的場景,比如一週只能有七天,顏色限定爲紅綠藍等,通過enum關鍵字定義

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days[0] === 'Sun'); //true
console.log(Days['Sat'] === 6); //true
Set和Map

JavaScript的默認對象表示方式{}可以視爲其他語言中的Map或Dictionary的數據結構,即一組鍵值對。

但是JavaScript的對象有個小問題,就是鍵必須是字符串。但實際上Number或者其他數據類型作爲鍵也是非常合理的。爲了解決這個問題,最新的ES6規範引入了新的數據類型Map和set.

  • Set [類數組] 集合是由一組無序且唯一(即不能重複)的項組成的,可以想象成集合是一個既沒有重複元素,也沒有順序概念的數組
var a = new Set([1, 2, 3, {"1": "2"}, ["3","4"]])
  • Map [類對象] 它類似於對象,也是鍵值對的集合,但是“鍵”的範圍不限於字符串,各種類型的值(包括對象)都可以當作鍵,是一種更完善的 Hash 結構實現。如果你需要“鍵值對”的數據結構,Map 比 Object 更合適
var m = new Map([['name', 'zhangsan'],['sex', 'male']]);
console.log(m); //Map {"name" => "zhangsan", "sex" => "male"}
防抖與節流的原理及實現

節流 (throttle) 先執行,過一段時間再執行第二次

function throttlePro(delay, action) {
    var tId;
    return function () {
        var context = this;
        var arg = arguments;
        if (tId) return;
        tId = setTimeout(function () {
            action.apply(context, arg);
            clearTimeout(tId);
            // setTimeout 返回一個整數,clearTimeout 之後,tId還是那個整數,setInterval同樣如此
            tId = null;
        }, delay);
    }
}

去抖 (debounce) 直至事件結束後,在處理,比如resize/scroll

function debounce(delay, action) {
    var tId;
    return function () {
        var context = this;
        var arg = arguments;
        if (tId) clearTimeout(tId);
        tId = setTimeout(function () {
            action.apply(context, arg);
        }, delay);
    }
}
window.onscroll = debounce(1000, print);

實現原理:利用定時器setTimeout

展開(擴展)運算符 ...的使用場景
  • 替換Apply的函數調用
// 使用擴展運算符
var array1 = [1, 2, 3];
Math.min(...array1);
  • 數組合並
// 數組合並
var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
var array3 = [...array1, ...array2, 7, 8]; //[1,2,3,4,5,6,7,8]
array1.push(...array2 )// 當然也可以使用concat等數組合並方法,但是擴展運算符提供了一種新的方式
  • 轉換類數組爲數組
// es5
var fn = function() {
  console.log(Array.prototype.slice.apply(arguments));
}
fn(1,2,3)

//擴展運算符
var fn = function() {
  console.log([...arguments]);
}
fn(1,2,3)
怎麼拍平一個二維數組(數組的扁平化)

核心原因, 用Array.isArray判斷每一項
js數組拍平(數組扁平化)的六種方式

怎麼判斷一個目標類型
  • typeof

    • 對於數組、函數、對象來說,其關係錯綜複雜,使用 typeof 都會統一返回 “object” 字符串,
    • null也會返回’object’
    • 對NaN返回是’number’
  • instanceof

    • 和typeof一樣, [] instanceof Array[] instanceof Object 都是返回true
  • Object.prototype.toString.call()

Object.prototype.toString.call("jerry");    //[object String]
Object.prototype.toString.call(12);         //[object Number]
Object.prototype.toString.call(true));      //[object Boolean]
function Person(){};
Object.prototype.toString.call(new Person); //[object Object]
// 判斷是否爲函數
function isFunction(it) {
  return Object.prototype.toString.call(it) === '[object Function]';
}

// 判斷是否爲數組:
function isArray(o) {
  return Object.prototype.toString.call(o) === '[object Array]';
}
babel常用到的工具包
* babel: ES6轉義的核心包
* babel-cli: 用於在終端使用babel,用命令行轉碼
* babel-core: 如果某些代碼需要調用Babel的API進行轉碼,就要使用`babel-core`模塊
* babel-loader: 執行轉義的核心包
* babel-plugin-react-transform: 代替react-hot-loader的插件
* babel-preset-es2015: 現在被babel-preset-env取代了,被babel未來不會過時的(future-proof)”解決方案
* babel-preset-react: 轉義react的jsx語法,
* babel-preset-stage-0: `stage-0`包含`stage-1`, `stage-2`以及`stage-3`的所有功能
Vue中一個頁面能否有多個插槽,怎麼區分

vue中的插槽(slot)
vue中的插槽,父組件向子組件傳遞填充的模板代碼,子組件中提供給父組件使用的一個佔位符,用<slot></slot>標籤表示,比如HTML、組件等,填充的內容會替換掉子組件的<slot></slot>標籤(替換佔位符)。

如果需要多個佔位符,則直接加name區分即可,即

// 子組件插槽
<div>
    <span>我是第一個插槽</span>
    <slot name="yanggb1"></slot>
    <span>我是第二個插槽</span>
    <slot name="yanggb2"></slot>
</div>

// 父組件插入模板
<template v-slot:yanggb1>
    <span>yanggb1</span>
</template>
<template v-slot:yanggb2>
    <span>yanggb2</span>
</template>
Vue中data爲啥不直接返回一個對象,而要return出去

不使用return包裹的數據會在項目的全局可見,會造成變量污染,使用return包裹後數據中變量只在當前組件中生效,不會影響其他組件

函數作用域和函數執行順序

作用域有全局變量局部變量(也稱爲函數作用域)兩類, ES6新增塊級作用域,通過新增命令letconst來體現

function func(args){
  if (true) {
    //let聲明i
    let i = 6;
    //在if內打印i值
    console.log('inside: ' + i);//6
  }
  //在if外,再次打印i值
  console.log('outside: ' + i);//i is not defined
};

關於執行順序,主要是由函數的定義方式中函數聲明式定義 function student(){}所引起的變量置頂提升

聊聊new Function這個陌生面孔及函數作用域和函數執行順序

js的深拷貝和淺拷貝

淺拷貝的時候如果數據是基本數據類型,那麼就如同直接賦值那種,會拷貝其本身,如果除了基本數據類型之外還有一層對象,那麼對於淺拷貝而言就只能拷貝其引用,對象的改變會反應到拷貝對象上;但是深拷貝就會拷貝多層,即使是嵌套了對象,也會都拷貝出來。

簡單的拷貝方法(即對象中的值沒有函數,數組,對象等複雜類型)

// 第一種 循環賦值
function simpleClone(initalObj) {
    var obj = {};
    for ( var i in initalObj) {
        obj[i] = initalObj[i];
    }
    return obj;
}

// 第二種 Object.assign
var obj2 = { a: 10, b: 20, c: 30 };
var cloneObj2 = Object.assign({}, obj2);
cloneObj2.b = 100;
console.log(obj2);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(cloneObj2);
// { a: 10, b: 100, c: 30 }

// 第三種 JSON.parse(JSON.stringify(obj1)) 一般處理json數據時纔會使用
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));

(終於弄清楚JS的深拷貝和淺拷貝了)[https://blog.csdn.net/QTFYING/article/details/90407551]

聊聊Promise

誕生的原因:在JavaScript的世界中,所有代碼都是單線程執行的。由於這個“缺陷”,導致JavaScript的所有網絡操作,瀏覽器事件,都必須是異步執行。異步執行可以用回調函數實現,最典型的就是ajax,最終被ES6納入標準,生成Promise

判斷下面函數的執行順序

console.log(1)
setTimeout(()=>{console.log(2)},1000)
async function fn(){
    console.log(3)
    setTimeout(()=>{console.log(4)},20)
    return Promise.reject()
}
async function run(){
    console.log(5)
    await fn()
    console.log(6)
}
run()
//需要執行150ms左右
for(let i=0;i<90000000;i++){}
setTimeout(()=>{
    console.log(7)
    new Promise(resolve=>{
        console.log(8)
        resolve()
    }).then(()=>{console.log(9)})
},0)
console.log(10)

你真的瞭解Promise嗎

閉包

定義:

  • 百度百科:
    閉包就是能夠讀取其他函數內部變量的函數。例如在javascript中,只有函數內部的子函數才能讀取局部變量,所以閉包可以理解成“定義在一個函數內部的函數“。

  • 《JavaScript高級編程指南》:
    閉包是指有權訪問另外一個函數作用域中的變量的函數。

  • MDN:
    閉包是由函數以及創建該函數的詞法環境組合而成,這個環境包含了這個閉包創建時所能訪問的所有局部變量

有什麼作用呢:

  • 模仿塊級作用域(匿名函數)
  • 儲存變量,延長變量生命週期
  • 封裝私有變量
  • 函數柯里化
// 正常正則驗證字符串 reg.test(txt)

// 函數封裝後
function check(reg, txt) {
    return reg.test(txt)
}

check(/\d+/g, 'test')       //false
check(/[a-z]+/g, 'test')    //true

// Currying後
function curryingCheck(reg) {
    return function(txt) {
        return reg.test(txt)
    }
}

var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)

hasNumber('test1')      // true
hasNumber('testtest')   // false
hasLetter('21212')      // false

關於閉包的this指向

var name = "聽風是風";
var obj = {
    name: "行星飛行",
    sayName: function () {
        var that = this;
        return function () {
            console.log(this.name); //this -> window -> 聽風是風
            console.log(that.name);
        };
    }
};
obj.sayName()(); // 行星飛行

一篇文章看懂JS閉包,都要2020年了,你怎麼能還不懂閉包?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章