前端面試題 -- JavaScript (一)

前言

前兩天總結了一下HTML+CSS方面的面試題 (傳送門),今天翻看了一些 JavaScript 面試中常見的幾個問題(只是一部分,會持續更新),分享給有須要的小夥伴,歡迎star關注html

若是文章中有出現紕漏、錯誤之處,還請看到的小夥伴留言指正,先行謝過git

如下 ↓github

1. JavaScript 有哪些數據類型

6種原始數據類型:web

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

引用類型:Object面試

詳見 JavaScript中的數據類型segmentfault

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表示"沒有對象",即該處不該該有值

典型用法:app

  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

後記

這裏如今只是JavaScript 面試題中的一部分,後面是持續更新, 有須要的小夥伴能夠關注哦好了,週末愉快 [啊!結束了……]

相關文章
相關標籤/搜索