前端面試資料整理【javascript篇】

donggg 發表於 2022-05-12
前端 面試 JavaScript

塊級作用域(執行結果題)

塊級作用域
阮一峰 塊級作用域

var a = 1999;
{
    console.log(a); // function a(){}
    a = 2020;
    function a() {}
    a = 2021;
}
console.log(a); // 2020

我的理解:(原理可能錯誤,但是便於理解,錯誤點在於塊級不能宣告函式,瀏覽器會有自己的支援行為,類似於函式表示式宣告——參考阮一峰。宣告也不要用函式宣告的方式,用函式表示式的方式。)
在塊級中,編譯過程,函式宣告變數提升,執行過程無影響。因此是 function a(){}
在塊級外部,編譯過程,外部有a,無影響。執行過程時,開始查詢,由於 a 的查詢順序是從詞法環境({})到變數環境(var),查詢到最近的,因此是 2020(注意此處是執行階段,function a(){} 的變數提升是編譯階段)

ps.函式和變數相比,會被優先提升。(我的理解:這意味著函式會替換掉變數提升)

事件冒泡、事件捕獲、事件代理(事件委託)

事件冒泡:略
事件捕獲:略
事件代理:利用事件冒泡,將事件繫結在父元素中

target.addEventListener(type, listener, useCapture); ,其中useCapture 為 false 時,為事件冒泡,預設是 false。

Object

常見的方法:

  • Object.defineProperty 定義的 description: { value, writable, configurable, emunable },或者 { set, get, configurable, emunable }
  • Object.createObject.keysObject.valuesObject.entriesObject.toString
  • Object.preventExtensions 組織擴充套件物件

作用域、作用域鏈和上下文

作用域是指在函式定義時,宣告變數的空間。
作用域鏈是指在變數查詢過程中,從當前上下文查詢,逐層往父級,直至全域性。

函式宣告時,會有個 scope 屬性,包含所有父級的變數。此時 VO物件,包含內部函式、變數、形參,儲存在上下文中。
函式執行時,AO物件,包含內部函式、變數、形參、內部this,掛載到作用域鏈上,

作用域、作用域鏈與執行上下文棧入門瞭解

原形鏈

待補充

Promise 與非同步

常見的非同步請求:

原生

var request = new HttpXMLRequest()
request.open('GET', url);
request.responseType = 'json'
request.onload = function(){}
request.send()

實現Promise
待補充

// Promise

// Promise.prototype.then

// Promise.prototype.all

// Promise.prototype.resolve

// Promise.prototype.race

事件迴圈與事件佇列

事件迴圈
組成:

  • 事件佇列(單位是訊息,訊息關聯著回撥函式,從訊息佇列中彈出後會呼叫回撥函式,形成執行幀)
  • 執行棧(單位是幀,包含函式中的變數與引數)
  • 堆(儲存物件結構)

同步任務與非同步任務

window.requestAnimationFrame() 既不是巨集任務也不是微任務,而是在瀏覽器下次重繪的時候執行

閉包

兩個主要的特點:

  • 通過函式阻止外部函式對內部變數的引用
  • 函式可以使用外部的變數

參考

閉包中的this
由於閉包是執行在記憶體中,所以 this 通常指向全域性,可以通過 call 改變。

閉包的作用

  • 通過立即執行函式,模擬塊級作用域,減少向全域性作用域宣告變數,另外由於立即執行函式在執行完後外部沒有引用,那麼記憶體會立即釋放
  • 使用 var 宣告時,利用立即執行函式遍歷時 i 能準確獲取
  • 實現物件導向程式設計(不是通過 new 構造)
function Person(){
  var name = 'default';
  return {
    getName : function(){
      return name;
    },
    setName : function(newName){
      name = newName;
    }
  }
};
var p1 = Person();
console.log(p1.getName()); // default
p1.setName('wang');
console.log(p1.getName()); // wang

閉包的問題

  • 在閉包中引用 dom 會導致迴圈引用,無法 GC(引用計數)
// IE9 之前甚至無法銷燬 dom
function closure(){
  var element = document.getElementById(ID);
  element.onclick = function(){
    console.log(element.id);
  }
  // 銷燬 element
  // element = null
}
  • this 指向
  • 閉包返回區域性作用域變數時,記憶體不會立即釋放

巨集任務 和 微任務

常見非同步考題

es5/es6/es7/es8

待補充

class 中的 super

super 既可以當函式也可以當物件使用。
當函式時,相當於是父類的建構函式,只能用在子類的建構函式中,this 指向子類例項。

class A {
  constructor() {
    this.show();
  }
  show(){
    console.log('A 例項');
  }
}
class B extends A {
  constructor() {
    super();
  }
  show(){
    console.log('B 例項');
  }
}
new B() // B 例項

當物件時,在一般函式使用時,super 相當於父類原型物件,this 指向子類例項 。

class A {
  constructor() {  
    this.x = 'A';
  }
  say() {
    console.log(this.x)
  }
}
class B extends A {
  constructor() {
    super();
    this.x = 'B'
  }
}
let b = new B();
console.log(b.say()) // B
ps. 注意該提醒
class A {
  constructor() {  // 在建構函式上定義的屬性和方法相當於定義在父類例項上的,而不是原型物件上
    this.p = 2;
  }
}
class B extends A {
  get m() {
    return super.p;
  }
}
let b = new B();
console.log(b.m) // undefined

// 引申題
function A(x) {
    this.p = x
}
A.prototype.p = 2
// 此時的 p 通過建構函式已經宣告
new A().p // undefined

super 在靜態方法 this 指向父類

常見的程式碼

防抖與節流

防抖是指,在一段時間內累計後觸發,例如輸入框輸入文字時,監聽 onChange。

function debounce(fn, delay) {
    let timer
    return function () {
        const self = this;
        const args = arguments;
        if (timer) {
            clearTimeout(timer);
        }

        timer = setTimeout(function(){
            fn.apply(self, args);
        }, delay)
    }
}

let log = debounce(function(){ console.log('!')}, 5000)

window.addEventListener('resize',log)

節流是指,在一段時間內多次重複觸發僅執行一次,例如重複點選。

function throttle(fn, delay) {

    let timer
    
    return function () {
        const self = this;
        const args = arguments;
        if (timer) {
            return;
        }

        timer = setTimeout(function() {
            self.apply(fn, args)
            timer = null;
        }, delay)
    }
}

let log = throttle(function(){ console.log('!')}, 3000)

window.addEventListener('click',log)

形成這種區別的原因:
節流當第一次執行是 arg 就固定了,也就是說如果用節流放到輸入框 onChange 場景時,值將是第一個輸入的數字。
防抖,通過不斷的 clearTimeout,來更新要執行的函式,直到不觸發後,等待 delay 後執行,delay 的作用是在此期間如果再次觸發,則會再次 clearTimeout

手寫new

// Object.create 會更新 __proto__,也就是 [[Prototype]],維持原形鏈
function create (proto) {
    if (typeof proto !== 'object' && typeof proto !== 'function' ) {
        throw new TypeError("原型只能是物件")
    }
    if (proto === null) {
        throw new TypeError("不能為空")
    }

        // function F() {}
        //F.prototype = proto;
        // return new F();
    proto.constructor.prototype = proto
    return new proto.constructor()
}

function newOperator(ctor) {
    if (typeof ctor !== 'function') {
        throw '建構函式必須是方法'
    }

    newOperator.target = ctor;

    // es6 可以直接使用 Object.create
    // const instance = Object.create(ctor.prototype)
    const instance = create(ctor.prototype)
    const args = [].slice.call(arguments, 1)
    // 繫結 this,並執行建構函式
    const r = ctor.apply(instance, args);
    // 如果建構函式有返回,則返回建構函式
    if (r) {
        return r;
    }

    // 例項
    return instance;
}

function Person (name) {
    this.name = name
}


const w = newOperator(Person, "zs")

console.log(w)

手寫bind

function bind(fn, obj) {
    const args = [].slice.call(arguments, 1);
    const selef = this;
    return function bound() {
        return fn.call(obj, [].slice.call(arguments, 1).concat(args))
    }
}

const h = {
    name: 'zs',
}

function say() {
    console.log(this.name)
}

const bound = bind(say, h)
bound()

Object.is Polyfill

if (!Object.is) {
  Object.defineProperty(Object, "is", {
    value: function (x, y) {
        if (x === y) {
            // 1. 如果 x === y,且均不為0時
            // 2. 如果 x,y 均為 0 ,判斷符號是否相等
            return x !== 0 || 1 / x === 1 / y; 
        } else {
            // NaN 與自己比較。 包含:Number.NaN, 0/0, NaN
            return x != x && y != y;
        }
    }
  })
}

如何實現 Array.reduce()

待補充

curry 與 compose

待補充

Object.assign()

待補充

實現字串 repeat


// 原生repeat 'ni'.repeat(3); 
// 'ninini' 
// 實現一 
String.prototype.repeatString1 = function (n) { 
    return Array(n + 1).join(this); 
} 
console.log('ni'.repeatString1(3)); 
// 實現二 
String.prototype.repeatString2 = function (n) { 
    return Array(n).fill(this).join('');
}
console.log('ni'.repeatString2(3));

js 模板引擎

Function('let a = 1');

其他

其他

相關文章