1. 程式人生 > 其它 >前端JS面試題-基礎-作用域和閉包

前端JS面試題-基礎-作用域和閉包

寫在前面:本文內容主要根據慕課網雙越老師的付費課程“一天時間迅速準備前端面試 快速構建初級前端知識體系 ”進行編寫,主要是為了自己在面試前總結學習,歡迎留言指教。

本文包括如下內容:

每一部分包括題目知識點兩部分。

作用域和閉包

題目

  1. this的不同應用場景,如何取值
  2. 手寫bind函式
  3. 實際開發中閉包的應用場景,舉例說明
  4. 場景題

1. this的不同應用場景,如何取值

有5種應用場景,如下:

  • 作為普通函式——返回window
  • 使用 call apply bind——傳入什麼繫結什麼
  • 作為物件方法被呼叫——返回物件本身
  • 在class方法中呼叫——當前例項本身
  • 箭頭函式——上級作用域

2. 手寫bind函式

bind() 方法會建立一個新函式,當這個新函式被呼叫時,它的 this 值是傳遞給 bind() 的第一個引數, 它的引數是 bind() 的其他引數和其原本的引數。

//模擬 bind
Function.prototype.bind1 = function () {
  // 將引數拆解為陣列
  const args = Array.prototype.slice.call(arguments)

  //獲取 this (陣列第一項)
  const t = args.shift()

  // fn1.bind(···)中的 fn1
  const self = this

  //返回一個函式
  return function () {
    return self.apply(t, args)
  }
}

// 使用
function fn1(a, b, c) {
  console.log('this', this)
  console.log(a, b, c)
  return 'this is fn1'
}

const fn2 = fn1.bind1({x: 100}, 10, 20, 30)
console.log(fn2())

// 結果
this {x: 100}
10 20 30
this is fn1

3. 實際開發中閉包的應用場景,舉例說明

閉包的應用場景

  • 函式作為返回值,以cache快取為例:
function createCache() {
  const data = {} //閉包中的資料被隱藏,不被外界訪問
  return {
    set: function (key, val) {
      data[key] = val
    },
    get: function (key) {
      return data[key]
    }
  }
}

const c = createCache()
c.set('a', 100)
console.log(c.get('a'))
  • 函式作為引數被傳遞,以setTimeout定時器為例:
function fn() {
	alert()
}
const func = fn()
// 第一個引數是一個函式,或者是一段執行的js程式碼,第二引數是第一個引數執行的時間間隔。
setTimeout(func, 1000)
被面試官問到什麼是閉包,最好的回答就是把兩個 demo 寫一遍,然後說出閉包的使用場景【cache快取 setTimeout定時器 非同步操作 等】。不用說什麼概念。

4. 場景題

這個程式碼打印出來的序號都是 10,應該在for裡定義let i,這樣需要都是遞增的。

知識點

1. 作用域

  • 全域性作用域
  • 函式作用域
  • 塊級作用域(ES6新增)【大括號內是一個塊級作用域】

2. 自由變數【當前作用域未定義的變數】

  • 一個變數在當前作用域沒有定義,但被使用了
  • 向上級作用域,一層一層一次尋找,直至找到為止
  • 如果到全域性作用域都沒找到,則報錯 xx is not defined

所有的自由變數的查詢,是在函式定義的地方,向上級作用域查詢,不是在執行的地方!!!

3. 閉包

作用域引用的特殊情況,有兩種表現:

  • 函式作為引數被傳遞
  • 函式作為返回值被返回

4. this

有5種應用場景,如下:

  • 作為普通函式——返回window
  • 使用 call apply bind——傳入什麼繫結什麼
  • 作為物件方法被呼叫——返回物件本身
  • 在class方法中呼叫——當前例項本身
  • 箭頭函式——上級作用域
// 普通函式:返回window
function fn1() {
	console.log(this)
}
fn1() //window
// call apply bind:傳入什麼繫結什麼
fn1.call({x: 100}) //{x: 100}
const fn2 = fn1.bind({x:200})
fn2() //{x:200}

// 作為物件方法被呼叫:返回物件本身
const zhangsan = {
  name: '張三',
  sayhi(){
    console.log(this)
  }
}
zhangsan.sayhi() //{name: "張三", sayhi: ƒ}

// 在class方法中呼叫:返回例項本身
class People{
  constructor(name){
    this.name = name
  }
  sayhi(){
    console.log(this)
  }
}
const zhangsan = new People('張三')
zhangsan.sayhi() //People{name: "張三"}

// 箭頭函式:上級作用域
const zhangsan = {
  name: '張三',
  wait(){
    setTimeout(()=>{
      console.log(this)
    })
  },
  waitAgain(){
    setTimeout(function () {
      console.log(this)
    })
  }
  // 兩個函式進行對比
}
zhangsan.wait() //{name: "張三", wait: ƒ, waitAgain: ƒ}
zhangsan.waitAgain() //window

this取什麼值,是在函式執行的時候確認的,不是在函式定義的時候確認的