前端面試題-JavaScript

引用GitHub 上 ltadpoles的前端面試

https://github.com/ltadpolesjavascript

目錄

1. JavaScript 有哪些數據類型html

2. 怎麼判斷不一樣的JS數據類型前端

3. undefined 和 null 有什麼區別java

4. 數組對象有哪些經常使用方法nginx

5. Js 有哪幾種建立對象的方式git

6. 怎麼實現對對象的拷貝(淺拷貝與深拷貝)es6

7. 什麼是閉包,爲何要用它github

8. 介紹一下 JavaScript 原型,原型鏈,它們有何特色web

9. JavaScript 如何實現繼承面試

10. new 操做符具體幹了什麼

11. 同步和異步的區別,怎麼異步加載 JavaScript

12. 跨域問題的產生,怎麼解決它

13. 對 this 的理解

14. apply()、call()和 bind() 是作什麼的,它們有什麼區別

15. 什麼是內存泄漏,哪些操做會形成內存泄漏

16. 什麼是事件代理,它的原理是什麼

17. 對AMD和CMD的理解,它們有什麼區別

18. 對ES6的瞭解

19. 箭頭函數有什麼特色

20. Promise 對象的瞭解

21. async 函數以及 awit 命令

22. export 與 export default有什麼區別

23. 前端性能優化

24. 對JS引擎執行機制的理解

1. JavaScript 有哪些數據類型

6種原始數據類型:

  • Boolean: 布爾表示一個邏輯實體,能夠有兩個值:true 和 false
  • Number: 用於表示數字類型
  • String: 用於表示文本數據
  • Null: Null 類型只有一個值: null,特指對象的值未設置
  • Undefined: 一個沒有被賦值的變量會有個默認值 undefined
  • Symbol: 符號(Symbols)是ECMAScript第6版新定義的。符號類型是惟一的而且是不可修改的

引用類型:Object

詳見 JavaScript中的數據類型

2. 怎麼判斷不一樣的JS數據類型
  • typeof操做符:返回一個字符串,表示未經計算的操做數的類型

typeof 操做符對於簡單數據類型,返回其自己的數據類型,函數對象返回 function ,其餘對象均返回 Object

null 返回 Object

  • instanceof: 用來判斷A 是不是 B的實例,表達式爲 A instanceof B,返回一個Boolean類型的值

instanceof 檢測的是原型,只能用來判斷兩個對象是否屬於實例關係, 而不能判斷一個對象實例具體屬於哪一種類型

let a = []; a instanceof Array // true a instanceof Object // true

變量a 的 __proto__ 直接指向Array.prototype,間接指向 Object.prototype,因此按照 instanceof 的判斷規則,a 就是Object的實例.針對數組的這個問題,ES5 提供了 Array.isArray() 方法 。該方法用以確認某個對象自己是否爲 Array 類型

  • constructor: 當一個函數被定義時,JS引擎會爲其添加prototype原型,而後再在 prototype上添加一個 constructor屬性,並讓其指向該函數的引用

nullundefined是無效的對象,所以是不會有constructor存在的,這兩種類型的數據須要經過其餘方式來判斷

函數的constructor是不穩定的,這個主要體如今自定義對象上,當開發者重寫prototype後,原有的constructor引用會丟失,constructor會默認爲 Object

function F() {}; var f = new F; f.constructor == F // true F.prototype = {a: 1} var f = new F f.constructor == F // false 

在構造函數 F.prototype 沒有被重寫以前,構造函數 F 就是新建立的對象 f 的數據類型。當 F.prototype 被重寫以後,原有的 constructor 引用丟失, 默認爲 Object

所以,爲了規範開發,在重寫對象原型時通常都須要從新給 constructor 賦值,以保證對象實例的類型不被篡改

  • toString: Object 的原型方法,調用該方法,默認返回當前對象的 [[Class]] 。這是一個內部屬性,其格式爲 [object Xxx] ,其中 Xxx 就是對象的類型
Object.prototype.toString.call('') ; // [object String] Object.prototype.toString.call(11) ; // [object Number] Object.prototype.toString.call(true) ; // [object Boolean] Object.prototype.toString.call(Symbol()); //[object Symbol] Object.prototype.toString.call(undefined) ; // [object Undefined] Object.prototype.toString.call(null) ; // [object Null] Object.prototype.toString.call(new Function()) ; // [object Function] Object.prototype.toString.call([]) ; // [object Array]
3. undefined 和 null 有什麼區別

null表示"沒有對象",即該處不該該有值

典型用法:

  1. 做爲函數的參數,表示該函數的參數不是對象
  2. 做爲對象原型鏈的終點

undefined表示"缺乏值",就是此處應該有一個值,可是尚未定義

典型用法:

  1. 變量被聲明瞭,但沒有賦值時,就等於undefined
  2. 調用函數時,應該提供的參數沒有提供,該參數等於undefined
  3. 對象沒有賦值的屬性,該屬性的值爲undefined
  4. 函數沒有返回值時,默認返回undefined

詳見: undefined和null的區別-阮一峯

4. 數組對象有哪些經常使用方法

修改器方法:

  • pop(): 刪除數組的最後一個元素,並返回這個元素
  • push():在數組的末尾增長一個或多個元素,並返回數組的新長度
  • reverse(): 顛倒數組中元素的排列順序
  • shift(): 刪除數組的第一個元素,並返回這個元素
  • unshift(): 在數組的開頭增長一個或多個元素,並返回數組的新長度
  • sort(): 對數組元素進行排序,並返回當前數組
  • splice(): 在任意的位置給數組添加或刪除任意個元素

訪問方法:

  • concat(): 返回一個由當前數組和其它若干個數組或者若干個非數組值組合而成的新數組
  • join(): 鏈接全部數組元素組成一個字符串
  • slice(): 抽取當前數組中的一段元素組合成一個新數組
  • indeOf(): 返回數組中第一個與指定值相等的元素的索引,若是找不到這樣的元素,則返回 -1
  • lastIndexOf(): 返回數組中最後一個(從右邊數第一個)與指定值相等的元素的索引,若是找不到這樣的元素,則返回 -1

迭代方法:

  • forEach(): 爲數組中的每一個元素執行一次回調函數,最終返回 undefined
  • every(): 若是數組中的每一個元素都知足測試函數,則返回 true,不然返回 false
  • some(): 若是數組中至少有一個元素知足測試函數,則返回 true,不然返回 false
  • filter(): 將全部在過濾函數中返回 true 的數組元素放進一個新數組中並返回
  • map(): 返回一個由回調函數的返回值組成的新數組

更多方法請參考 MDN 傳送門

5. Js 有哪幾種建立對象的方式

對象字面量

var obj = {}

Object 構造函數

var obj = new Object()

工廠模式

function Person(name, age) { var o = new Object() o.name = name; o.age = age; o.say = function() { console.log(name) } return o }

缺點: 每次經過Person建立對象的時候,全部的say方法都是同樣的,可是卻存儲了屢次,浪費資源

構造函數模式

function Person(name, age) { this.name = name this.age = age this.say = function() { console.log(name) } } var person = new Person('hello', 18)

構造函數模式隱試的在最後返回return this 因此在缺乏new的狀況下,會將屬性和方法添加給全局對象,瀏覽器端就會添加給window對象,能夠根據return this 的特性調用call或者apply指定this

原型模式

function Person() {} Person.prototype.name = 'hanmeimei'; Person.prototype.say = function() { alert(this.name); } Person.prototype.friends = ['lilei']; var person = new Person();

實現了方法與屬性的共享,能夠動態添加對象的屬性和方法。可是沒有辦法建立實例本身的屬性和方法,也沒有辦法傳遞參數

構造函數和原型組合

function Person(name, age) { this.name = name this.age = age } Person.prototype.say = function() { console.log(this.name) } var person = new Person('hello')

還有好幾種模式,感興趣的小夥伴能夠參考 紅寶書,大家確定知道的了!

6. 怎麼實現對對象的拷貝(淺拷貝與深拷貝)

淺拷貝

  • 拷貝原對象引用
  • 可使用Array.prototype.slice()也能夠完成對一個數組或者對象的淺拷貝
  • Object.assign()方法

深拷貝

  • 最經常使用的方式就是 JSON.parse(JSON.stringify(目標對象),缺點就是隻能拷貝符合JSON數據標準類型的對象

更多參考 JavaScript 中的淺拷貝與深拷貝

7. 什麼是閉包,爲何要用它

簡單來講,閉包就是可以讀取其餘函數內部變量的函數

function Person() { var name = 'hello' function say () { console.log(name) } return say() } Person() // hello

因爲 JavaScript 特殊的做用域,函數外部沒法直接讀取內部的變量,內部能夠直接讀取外部的變量,從而就產生了閉包的概念

用途:

最大用處有兩個,一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中

注意點:

因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露

更多參考 JavaScript 中的閉包

8. 介紹一下 JavaScript 原型,原型鏈,它們有何特色

首先明確一點,JavaScript是基於原型的

每一個構造函數(constructor)都有一個原型對象(prototype),原型對象都包含一個指向構造函數的指針,而實例(instance)都包含一個指向原型對象的內部指針.

image

圖解:

  • 每個構造函數都擁有一個prototype屬性,這個屬性指向一個對象,也就是原型對象
  • 原型對象默認擁有一個constructor屬性,指向指向它的那個構造函數
  • 每一個對象都擁有一個隱藏的屬性[[prototype]],指向它的原型對象

那麼什麼是原型鏈:

JavaScript中全部的對象都是由它的原型對象繼承而來。而原型對象自身也是一個對象,它也有本身的原型對象,這樣層層上溯,就造成了一個相似鏈表的結構,這就是原型鏈

全部原型鏈的終點都是Object函數的prototype屬性。Objec.prototype指向的原型對象一樣擁有原型,不過它的原型是null,而null則沒有原型

image

更多參考 JavaScript 中的原型與原型鏈

9. JavaScript 如何實現繼承
  • 原型鏈繼承
function Animal() {} Animal.prototype.name = 'cat' Animal.prototype.age = 1 Animal.prototype.say = function() {console.log('hello')} var cat = new Animal() cat.name // cat cat.age // 1 cat.say() // hello

最簡單的繼承實現方式,可是也有其缺點

  1. 來自原型對象的全部屬性被全部實例共享
  2. 建立子類實例時,沒法向父類構造函數傳參
  3. 要想爲子類新增屬性和方法,必需要在new語句以後執行,不能放到構造器中
  • 構造繼承
function Animal() { this.species = "動物" } function Cat(name, age) { Animal.call(this) this.name = name this.age = age } var cat = new Cat('豆豆', 2) cat.name // 豆豆 cat.age // 2 cat.species // 動物

使用call或apply方法,將父對象的構造函數綁定在子對象上.

  • 組合繼承
function Animal() { this.species = "動物" } function Cat(name){ Animal.call(this) this.name = name } Cat.prototype = new Animal() // 重寫原型 Cat.prototype.constructor = Cat 

若是沒有Cat.prototype = new Animal()這一行,Cat.prototype.constructor是指向Cat的;加了這一行之後,Cat.prototype.constructor指向Animal.這顯然會致使繼承鏈的紊亂(cat1明明是用構造函數Cat生成的),所以咱們必須手動糾正,將Cat.prototype對象的constructor值改成Cat

  • extends 繼承 ES6新增繼承方式,Class 能夠經過extends關鍵字實現繼承
class Animal { } class Cat extends Animal { constructor() { super(); } }

使用 extends 實現繼承,必須添加 super 關鍵字定義子類的 constructor,這裏的super() 就至關於 Animal.prototype.constructor.call(this)

固然,還有不少種實現繼承的方式,這裏就很少說了。而後,再推薦一波 紅寶書

更多參考 JavaScript 中的繼承

10. new 操做符具體幹了什麼
  • 建立一個空對象,而且 this 變量引用該對象,同時還繼承了該函數的原型
  • 屬性和方法被加入到 this 引用的對象中
  • 新建立的對象由 this 所引用,而且最後隱式的返回 this
11. 同步和異步的區別,怎麼異步加載 JavaScript

同步模式

同步模式,又稱阻塞模式。javascript 在默認狀況下是會阻塞加載的。當前面的 javascript 請求沒有處理和執行完時,會阻止瀏覽器的後續處理

異步模式

異步加載又叫非阻塞,瀏覽器在下載執行 js 同時,還會繼續進行後續頁面的處理

異步加載 JavaScript

  • 動態添加 script 標籤
  • defer
  • async

defer屬性和async都是屬於 script 標籤上面的屬性,二者都能實現 JavaScript 的異步加載。不一樣之處在於:async 在異步加載完成的時候就立刻開始執行了,defer 會等到 html 加載完畢以後再執行

12. 跨域問題的產生,怎麼解決它

因爲瀏覽器的 同源策略,在出現 域名、端口、協議有一種不一致時,就會出現跨域,屬於瀏覽器的一種安全限制。

解決跨域問題有不少種方式,經常使用的就是如下幾種:

  • jsonp 跨域:動態建立script,再請求一個帶參網址實現跨域通訊.缺點就是隻能實現 get 一種請求
  • document.domain + iframe跨域:兩個頁面都經過js強制設置document.domain爲基礎主域,就實現了同域.可是僅限主域相同,子域不一樣的跨域應用場景
  • 跨域資源共享(CORS):只服務端設置Access-Control-Allow-Origin便可,前端無須設置,若要帶cookie請求:先後端都須要設置
  • nginx反向代理接口跨域:同源策略是瀏覽器的安全策略,不是HTTP協議的一部分。服務器端調用HTTP接口只是使用HTTP協議,不會執行JS腳本,不須要同源策略,也就不存在跨越問題
  • WebSocket協議跨域
13. 對 this 的理解

在 JavaScript 中,研究 this 通常都是 this 的指向問題,核心就是 this 永遠指向最終調用它的那個對象,除非改變 this 指向或者箭頭函數那種特殊狀況

function test() { console.log(this); } test() // window var obj = { foo: function () { console.log(this.bar) }, bar: 1 }; var foo = obj.foo; var bar = 2; obj.foo() // 1 foo() // 2 // 函數調用的環境不一樣,所獲得的結果也是不同的
14. apply()、call()和 bind() 是作什麼的,它們有什麼區別

相同點:三者均可以改變 this 的指向

不一樣點:

  • apply 方法傳入兩個參數:一個是做爲函數上下文的對象,另一個是做爲函數參數所組成的數組
var obj = { name : 'sss' } function func(firstName, lastName){ console.log(firstName + ' ' + this.name + ' ' + lastName); } func.apply(obj, ['A', 'B']); // A sss B 
  • call 方法第一個參數也是做爲函數上下文的對象,可是後面傳入的是一個參數列表,而不是單個數組
var obj = { name: 'sss' } function func(firstName, lastName) { console.log(firstName + ' ' + this.name + ' ' + lastName); } func.call(obj, 'C', 'D'); // C sss D
  • bind 接受的參數有兩部分,第一個參數是是做爲函數上下文的對象,第二部分參數是個列表,能夠接受多個參數
var obj = { name: 'sss' } function func() { console.log(this.name); } var func1 = func.bind(null, 'xixi'); func1();

applycall 方法都會使函數當即執行,所以它們也能夠用來調用函數

bind 方法不會當即執行,而是返回一個改變了上下文 this 後的函數。而原函數 func 中的 this 並無被改變,依舊指向全局對象 window

bind 在傳遞參數的時候會將本身帶過去的參數排在原函數參數以前

function func(a, b, c) { console.log(a, b, c); } var func1 = func.bind(this, 'xixi'); func1(1,2) // xixi 1 2
15. 什麼是內存泄漏,哪些操做會形成內存泄漏

內存泄漏:是指一塊被分配的內存既不能使用,又不能回收,直到瀏覽器進程結束

可能形成內存泄漏的操做:

  • 意外的全局變量
  • 閉包
  • 循環引用
  • 被遺忘的定時器或者回調函數

你可能還須要知道 垃圾回收機制 此外,高程上面對垃圾回收機制的介紹也很全面,有興趣的小夥伴能夠看看

16. 什麼是事件代理,它的原理是什麼

事件代理:通俗來講就是將元素的事件委託給它的父級或者更外級元素處理

原理:利用事件冒泡機制實現的

優勢:只須要將同類元素的事件委託給父級或者更外級的元素,不須要給全部元素都綁定事件,減小內存空間佔用,提高性能; 動態新增的元素無需從新綁定事件

17. 對AMD和CMD的理解,它們有什麼區別

AMDCMD都是爲了解決瀏覽器端模塊化問題而產生的,AMD規範對應的庫函數有 Require.jsCMD規範是在國內發展起來的,對應的庫函數有Sea.js

AMD和CMD最大的區別是對依賴模塊的執行時機處理不一樣

一、AMD推崇依賴前置,在定義模塊的時候就要聲明其依賴的模塊

二、CMD推崇就近依賴,只有在用到某個模塊的時候再去require

參考:AMD-中文版 CMD-規範

18. 對ES6的瞭解

ECMAScript 6.0 是 JavaScript 語言的下一代標準

新增的特性:

  • 聲明變量的方式 let const
  • 變量解構賦值
  • 字符串新增方法 includes() startsWith() endsWith() 等
  • 數組新增方法 Array.from() Array.of() entries() keys() values() 等
  • 對象簡潔寫法以及新增方法 Object.is() Object.assign() entries() keys() values()
  • 箭頭函數、rest 參數、函數參數默認值等
  • 新的數據結構: Set 和 Map
  • Proxy
  • Promise對象
  • async函數 await命令
  • Class
  • Module 體系 模塊的加載和輸出方式

瞭解更多,參考 ES6入門-阮一峯

19. 箭頭函數有什麼特色

ES6 容許使用「箭頭」(=>)定義函數

var f = v => v; // 等同於 var f = function (v) { return v; }

注意點:

  • 函數體內的 this 對象,就是定義時所在的對象,而不是使用時所在的對象
  • 不能夠看成構造函數,也就是說,不可使用 new 命令,不然會拋出一個錯誤
  • 不可使用 arguments 對象,該對象在函數體內不存在。若是要用,能夠用 rest 參數代替
20. Promise 對象的瞭解

Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大.所謂Promise,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果 --ES6入門-阮一峯

Promise 對象表明一個異步操做,有三種狀態:pending(進行中)、fulfilled(已成功)和 rejected(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態

特色:

  • 對象的狀態不受外界影響
  • 一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果
  • Promise 新建後就會當即執行
const promise = new Promise(function(resolve, reject) { // ... some code if (/* 異步操做成功 */){ resolve(value); } else { reject(error); } })

Promise實例生成之後,能夠用then方法分別指定resolved狀態和rejected狀態的回調函數

promise.then(function(value) { // success }, function(error) { // failure })

then 方法返回的是一個新的Promise實例

Promise.prototype.catch 用於指定發生錯誤時的回調函數,具備「冒泡」性質,會一直向後傳遞,直到被捕獲爲止。也就是說,錯誤老是會被下一個catch語句捕獲

getJSON('/post/1.json').then(function(post) { return getJSON(post.commentURL); }).then(function(comments) { // some code }).catch(function(error) { // 處理前面三個Promise產生的錯誤 });

catch 方法返回的仍是一個 Promise 對象,所以後面還能夠接着調用 then 方法

出去上述方法,Promise還有其餘用法,小夥伴們能夠在這裏查看大佬寫的文章 ES6入門-阮一峯

21. async 函數以及 awit 命令

async 函數是什麼?一句話,它就是 Generator 函數的語法糖

瞭解Generator函數的小夥伴,這裏 傳送門

async 特色:

async 函數返回一個 Promise 對象,可使用 then 方法添加回調函數。當函數執行的時候,一旦遇到 await 就會先返回,等到異步操做完成,再接着執行函數體內後面的語句

async 函數內部 return 語句返回的值,會成爲 then 方法回調函數的參數

async 函數返回的 Promise 對象,必須等到內部全部 await 命令後面的 Promise 對象執行完,纔會發生狀態改變,除非遇到 return 語句或者拋出錯誤

async 函數內部拋出錯誤,會致使返回的 Promise 對象變爲 reject 狀態。拋出的錯誤對象會被 catch 方法回調函數接收到

function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { await timeout(ms); console.log(value); } asyncPrint('hello world', 50);

await 命令: await 命令後面是一個 Promise 對象,返回該對象的結果。若是不是 Promise 對象,就直接返回對應的值

async function f() { // 等同於 // return 123; return await 123; } f().then(v => console.log(v)) // 123

await 命令後面是一個thenable對象(即定義then方法的對象),那麼await會將其等同於 Promise 對象.也就是說就算一個對象不是Promise對象,可是隻要它有then這個方法, await 也會將它等同於Promise對象

使用注意點:

  • await 命令後面的 Promise 對象,運行結果多是 rejected,因此最好把 await 命令放在 try...catch 代碼塊中
  • 多個 await 命令後面的異步操做,若是不存在繼發關係,最好讓它們同時觸發
  • await 命令只能用在 async 函數之中,若是用在普通函數,就會報錯

瞭解更多,請點擊 這裏

22. export 與 export default有什麼區別

export 與 export default 都可用於導出常量、函數、文件、模塊等

在一個文件或模塊中,exportimport 能夠有多個,export default 僅有一個

經過 export 方式導出,在導入時要加 { }export default 則不須要

使用 export default命令,爲模塊指定默認輸出,這樣就不須要知道所要加載模塊的變量名; export 加載的時候須要知道加載模塊的變量名

export default 命令的本質是將後面的值,賦給 default 變量,因此能夠直接將一個值寫在 export default 以後

23. 前端性能優化

參見 雅虎14條前端性能優化

24. 對JS引擎執行機制的理解

首選明確兩點:

JavaScript 是單線程語言

JavaScript 的 Event Loop 是 JS 的執行機制, 也就是事件循環

console.log(1) setTimeout(function(){ console.log(2) },0) console.log(3) // 1 3 2

JavaScript 將任務分爲同步任務和異步任務,執行機制就是先執行同步任務,將同步任務加入到主線程,遇到異步任務就先加入到 event table ,當全部的同步任務執行完畢,若是有可執行的異步任務,再將其加入到主線程中執行

視頻詳解,移步 這裏

setTimeout(function(){console.log(1);},0); new Promise(function(resolve){ console.log(2); for(var i = 0; i < 10000; i++){ i == 99 && resolve(); } }).then(function(){ console.log(3) }); console.log(4); // 2 4 3 1

在異步任務中,定時器也屬於特殊的存在。有人將其稱之爲 宏任務、微任務,定時器就屬於宏任務的範疇。

相關文章
相關標籤/搜索