2019前端面試題(js篇)

在此分享、整理前端面試題,若有解答錯誤的地方,煩請各位大佬指正,感謝!!javascript

javascript原型、原型鏈?有什麼特色

每一個函數都有一個 prototype 屬性,函數的 prototype屬性指向了一個對象,這個對象正是調用該構造函數而建立的實例的原型前端

那什麼是原型呢?你能夠這樣理解:每個JavaScript對象(null除外)在建立的時候就會與之關聯另外一個對象,這個對象就是咱們所說的原型,每個對象都會從原型"繼承"屬性。java

這是每個JavaScript對象(除了 null )都具備的一個屬性,叫__proto__,這個屬性會指向該對象的原型。git

原型鏈解決的主要是繼承問題。github

每一個對象擁有一個原型對象,經過 proto 指針指向其原型對象,並從中繼承方法和屬性,同時原型對象也可能擁有原型,這樣一層一層,最終指向 null(Object.proptotype.proto 指向的是null)。這種關係被稱爲原型鏈 (prototype chain),經過原型鏈一個對象能夠擁有定義在其餘對象中的屬性和方法。面試

juejin.im/post/5cf336…ajax

function Person() {

}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
複製代碼
function Person() {

}
console.log(Person === Person.prototype.constructor); // true
複製代碼
function Person() {

}

var person = new Person();

console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
// 順便學習一個ES5的方法,能夠得到對象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
複製代碼

參考:github.com/mqyqingfeng…算法

解釋javascript中的做用域和變量聲明提高

做用域是指程序源代碼中定義變量的區域。數組

做用域規定了如何查找變量,也就是肯定當前執行代碼對變量的訪問權限。promise

變量聲明提高:

foo;  // undefined
var foo = function () {
    console.log('foo1');
}

foo();  // foo1,foo賦值
複製代碼

能夠想象成:全部的聲明(變量和函數)都會被「移動」到各自做用域的最頂端

javascript有幾種類型的值?你能畫一下他們的內存圖嗎

JS 中分爲七種內置類型,七種內置類型又分爲兩大類型:基本類型和對象(Object)。

基本類型有六種: null,undefined,boolean,number,string,symbol。

  • 基本類型:--> 內存(不包含閉包中的變量)
  • 引用類型:--> 內存

引用類型有:Object、Array、Date、RegExp、Function

如何實現數組的隨機排序

var arr = [1,2,3,4,5,6,7,8,9,10];
var newArr = [];
let length=arr.length
for(let i=0;i<length;i++){
  let randomIndex = Math.floor(Math.random()*arr.length);
  newArr[i]=arr[randomIndex]
  arr.splice(randomIndex,1)
}
console.log(newArr)
複製代碼

談談this對象的理解,call()和apply()的區別

call和apply的區別在於傳入參數的不一樣; 第一個參數都是,指定函數體內this的指向;

第二個參數開始不一樣,apply是傳入帶下標的集合,數組或者類數組,apply把它傳給函數做爲參數,call從第二個開始傳入的參數是不固定的,都會傳給函數做爲參數。

call比apply的性能要好,日常能夠多用call,

call傳入參數的格式正是內部所須要的格式,

什麼是閉包?爲何要用它?

閉包:函數 A 返回了一個函數 B,而且函數 B 中使用了函數 A 的變量,函數 B 就被稱爲閉包。

做用有:

封裝私有變量 模仿塊級做用域(ES5中沒有塊級做用域) 實現JS的模塊

new操做符到底幹了什麼?

  • 建立一個新對象
  • 這個對象會連接到它的原型:obj.proto = Con.prototype
  • 綁定this(apply),屬性和方法被加入到 this 引用的對象中。並執行了構造函數中的方法.
  • 函數沒有返回其餘對象,那麼this指向這個新對象,不然this指向構造函數中返回的對象。

如何實現一個 new

function _new(fn, …arg) {
    const obj = Object.create(fn.prototype);
    const ret = fn.apply(obj, arg);
    return ret instanceof Object ? ret : obj;
}
複製代碼

談談你對ECMAScript6的理解

ECMAScript6是ES2015標準;

  • 新增了塊級做用域(let,const)
  • 提供了定義類的語法糖(class)
  • 新增了一種基本數據類型(Symbol)
  • 新增了變量的解構賦值
  • 函數參數容許設置默認值,引入了rest參數,新增了箭頭函數
  • 數組新增了一些API,如 isArray / from / of 方法;數組實例新增了 entries(),keys() 和 values() 等方法
  • 對象和數組新增了擴展運算符
  • ES6 新增了模塊化(import/export)
  • ES6 新增了 Set 和 Map 數據結構
  • ES6 原生提供 Proxy 構造函數,用來生成 Proxy 實例
  • ES6 新增了生成器(Generator)和遍歷器(Iterator)

AMD、CMD規範區別

AMD規範:是 RequireJS在推廣過程當中對模塊定義的規範化產出的,

CMD規範:是SeaJS 在推廣過程當中對模塊定義的規範化產出的。 區別

CMD 推崇依賴就近;AMD 推崇依賴前置

CMD 是延遲執行;AMD 是提早執行

CMD性能好,由於只有用戶須要的時候才執行;AMD用戶體驗好,由於沒有延遲,依賴模塊提早執行了

垃圾回收

垃圾回收是一種內存管理機制。咱們聲明一個變量和函數的時候都會佔用內存,可是內存容量有限,當一個變量離開執行環境的時候。考慮到它不會再使用的,就會被回收,釋放內存

方法:

  • 引用計數
  • 標記清除

內存泄漏

內存泄漏是指計算機可用的內存愈來愈少,主要是由於程序不能釋放那些再也不使用的內存。

無心的全局變量、循環、定時器、回調

瀏覽器緩存

  • 強緩存
  • 協商緩存

強緩存

強緩存表示在緩存期間不須要請求,state code 爲 200 expires(本地過時時間)和cache-control(單位是秒,多少秒後過時),cache-control優於expires

協商緩存

若是緩存過時了,咱們就可使用協商緩存來解決問題。協商緩存須要請求,若是緩存有效會返回 304。

  • Etag和if-none-match對比
  • last-modified和if-modified-since對比

算法

  • 冒泡排序

    重複遍歷全部的元素,兩個元素比較,若是大小順序不對,就交換它們的位置。重複遍歷直到沒有再須要交換,排序完成。

function bubbleSort(arr) {
    var len = arr.length;
    for (var i = 0; i < len; i++) {
        for (var j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j+1]) {        //相鄰元素兩兩對比
                var temp = arr[j+1];        //元素交換
                arr[j+1] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return arr;
}
複製代碼
  • 選擇排序 在待排序序列中找到最小(大)元素,放在序列的起始位置,而後,再從剩餘元素中尋找最小(大)元素,而後放到已排序序列的末尾。重複,直到全部元素均排序完畢。

function selectionSort(arr) {
    var len = arr.length;
    var minIndex, temp;
    for (var i = 0; i < len - 1; i++) {
        minIndex = i;
        for (var j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {     //尋找最小的數
                minIndex = j;                 //將最小數的索引保存
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}
複製代碼
  • 插入排序 原理:經過構建有序序列,對於未排序元素,在已排序序列中從後向前掃描,找到相應位置並插入。通常能夠將第一個元素做爲有序序列,用未排序的元素與之相比,插入,直到排序完畢。像打撲克牌整理本身的牌同樣。
function insertionSort(arr) {
    var len = arr.length;
    var preIndex, current;
    for (var i = 1; i < len; i++) {
        preIndex = i - 1;
        current = arr[i];
        while(preIndex >= 0 && arr[preIndex] > current) {
            arr[preIndex+1] = arr[preIndex];
            preIndex--;
        }
        arr[preIndex+1] = current;
    }
    return arr;
}
複製代碼
  • 快速排序的思想: 數組中指定一個元素做爲標尺,比它大的放到該元素後面,比它小的放到該元素前面,如此重複直至所有正序排列。 。

function quickSort(arr, left, right) {
    var len = arr.length,
        partitionIndex,
        left = typeof left != 'number' ? 0 : left,
        right = typeof right != 'number' ? len - 1 : right;

    if (left < right) {
        partitionIndex = partition(arr, left, right);
        quickSort(arr, left, partitionIndex-1);
        quickSort(arr, partitionIndex+1, right);
    }
    return arr;
}

function partition(arr, left ,right) {     //分區操做
    var pivot = left,                      //設定基準值(pivot)
        index = pivot + 1;
    for (var i = index; i <= right; i++) {
        if (arr[i] < arr[pivot]) {
            swap(arr, i, index);
            index++;
        }       
    }
    swap(arr, pivot, index - 1);
    return index-1;
}

function swap(arr, i, j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
複製代碼

繼承的方法

實現繼承 主要是依靠原型鏈來實現的.

  • 構造函數
    • 保證了原型鏈中引用類型值的獨立,再也不被全部實例共享;
    • 子類型建立時也可以向父類型傳遞參數.
  • 組合繼承 組合繼承, 有時候也叫作僞經典繼承,指的是將原型鏈和借用構造函數的技術組合到一塊,從而發揮二者之長的一種繼承模式.
    • 組合繼承其實調用了兩次父類構造函數,
  • 寄生式繼承
  • 寄生組合繼承

TypeScript的類型

  • 布爾值 let isDone: boolean = false;

  • 數字

    全部數字類型都是浮點數

    let decLiteral: number = 6;

  • 字符串

    模版字符串也是這個類型

    let name: string = "bob";

  • 數組

    • 數字數組:number[]、Array
    • 對象數組:Object[]
  • 元組

    元組類型容許表示一個已知元素數量和類型的數組,各元素的類型沒必要相同。 好比,你能夠定義一對值分別爲 string和number類型的元組。

    let x: [string, number];

  • 枚舉enum

    enum Color {Red, Green, Blue}
    let c: Color = Color.Green;
    複製代碼
  • nerver

  • Object

  • Null 和 Undefined

  • Void

  • Any

var、let 和 const 區別的實現原理是什麼

var 和 let 用以聲明變量,const 用於聲明只讀的常量;

var 聲明的變量,不存在塊級做用域,在全局範圍內都有效,let 和 const 聲明的,只在它所在的代碼塊內有效;

let 和 const 不存在像 var 那樣的 「變量提高」 現象,因此 var 定義變量能夠先使用,後聲明,而 let 和 const 只可先聲明,後使用;

let 聲明的變量存在暫時性死區,即只要塊級做用域中存在 let,那麼它所聲明的變量就綁定了這個區域,再也不受外部的影響。

let 不容許在相同做用域內,重複聲明同一個變量;

const 在聲明時必須初始化賦值,一旦聲明,其聲明的值就不容許改變,更不容許重複聲明;

如 const 聲明瞭一個複合類型的常量,其存儲的是一個引用地址,不容許改變的是這個地址,而對象自己是可變的。

變量與內存之間的關係,主要由三個部分組成:

變量名

內存地址

內存空間

JS 引擎在讀取變量時,先找到變量綁定的內存地址,而後找到地址所指向的內存空間,最後讀取其中的內容。當變量改變時,JS 引擎不會用新值覆蓋以前舊值的內存空間(雖然從寫代碼的角度來看,確實像是被覆蓋掉了),而是從新分配一個新的內存空間來存儲新值,並將新的內存地址與變量進行綁定,JS 引擎會在合適的時機進行 GC,回收舊的內存空間。

const 定義變量(常量)後,變量名與內存地址之間創建了一種不可變的綁定關係,阻隔變量地址被改變,當 const 定義的變量進行從新賦值時,根據前面的論述,JS 引擎會嘗試從新分配新的內存空間,因此會被拒絕,便會拋出異常。

github.com/Advanced-Fr…

input 搜索如何防抖,如何處理中文輸入

處理中文輸入,在input框綁定compositionsend,就能夠監聽中文輸入結束

promise、promise.all、promose.race

手寫一個ajax

簡化版

var request = new XMLHttpRequest();
request.open('GET', '/xxx');
request.onload = () => {console.log('請求成功'};
request.send();
複製代碼

詳細版

var request = new XMLHttpRequest();
request.open('GET', 'xxx');
request.onreadystatechange = function() {
  if(request.readyState === 4 ) {
    console.log('請求完成');
    if(request.respondse.status >=200 && request.respondse.status) {
      console.log('請求成功')
    }else{
      
    }
  }
}

request.send();
複製代碼

如何實現深拷貝

經常使用:使用JSON.parse(JSON.stringify(obj))

原理是把一個對象序列化成爲一個JSON字符串,將對象的內容轉換成字符串的形式再保存在磁盤上,再用JSON.parse()反序列化將JSON字符串變成一個新的對象

缺點是: 會忽略undefined、symbol、funciton

實現:遞歸+判斷類型

一個簡單的代碼

// 數字 字符串 function是不須要拷貝的
function deepClone(value) {  
    if (value == null) return value;  
    if (typeof value !== 'object') return value;
    if (value instanceof RegExp) return new RegExp(value);  
    if (value instanceof Date) return new Date(value);  
    // 我要判斷 value 是對象仍是數組 若是是對象 就產生對象 是數組就產生數組  
    let obj = new value.constructor;  
    for(let key in value){    
        obj[key] = deepClone(value[key]); // 看一看當前的值是否是一個對象  
    }  
    return obj;
}
複製代碼

juejin.im/post/5c400a…

手寫一個promise

function Promise(executor) {
    let self = this;
    self.status = 'pending'; //等待態
    self.value = undefined;  //成功的返回值
    self.reason = undefined; //失敗的緣由

    function resolve(value){
        if(self.status === 'pending'){
            self.status = 'resolved';
            self.value = value;
        }
    }
    function reject(reason) {
        if(self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
        }
    }
    try{
        executor(resolve, reject);
    }catch(e){
        reject(e);// 捕獲時發生異常,就直接失敗
    }
}
//onFufiled 成功的回調
//onRejected 失敗的回調
Promise.prototype.then = function (onFufiled, onRejected) {
    let self = this;
    if(self.status === 'resolved'){
        onFufiled(self.value);
    }
    if(self.status === 'rejected'){
        onRejected(self.reason);
    }
}
module.exports = Promise;
複製代碼

juejin.im/post/5aafe3…

window.onload和document.body.onload事件

window.onload--頁面上全部的DOM,樣式表,腳本,圖片都已經加載完成

document.onload/document.body.onload--僅當DOM加載完成,--也就是DOMContentLoaded的時間(打開devtools-network-DOMContentLoaded)

splice和slice的區別

splice做用於數組,會修改原數組,有刪除或替換、添加的功能,返回被刪除的元素,若是沒有元素被刪除,返回空數組

var months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// inserts at index 1
console.log(months);
// expected output: Array ['Jan', 'Feb', 'March', 'April', 'June']

months.splice(4, 1, 'May');
// replaces 1 element at index 4
console.log(months);
// expected output: Array ['Jan', 'Feb', 'March', 'April', 'May']
複製代碼

slice能夠做用於數組和字符串,不修改選數組,返回一個新數組

var animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];

console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]

console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]
複製代碼

參考:參考連接 JS家的排序算法

相關文章
相關標籤/搜索