面試題
1.js基本類型有哪些?
-
string
number
boolean
undefined
null
2.null和undefined的區別?
-
null是一個表示"無"的對象,轉為數值時為0;undefined是一個表示"無"的 原始值,轉為數值時為NaN;
-
typeof null 返回的是object,typeof undefined返回的是undefined
3.談談你對NaN的理解?
Not a Number 不是一個數字,但是數值類型
+> NaN 與其他數值進行比較的結果總是不相等的,包括它自身在內
4.談談你對this的理解?
-
this只能在作用域內使用,在全局作用域內調用this指向全局window
-
this在局部作用域內使用,誰調用函數,this就指向誰
5.類型轉換
-
轉數值
Number
parseInt
parseFloat
-
轉字符串
toString
String
-
轉布爾
Boolean
-
''
undefined
NaN
0
null
都會轉換成false
6.值傳遞和引用傳遞的區別
-
基礎數據類型和復雜的數據類型在賦值的時候因為存儲方式不一樣,導致賦值的內容也不一樣。
-
基礎的數據類型之間賦值是直接將一個變量的數據拷貝給另外一個變量,一個變量的變化不會改變另外一個變量的數據,被稱為值傳遞的過程。
-
復雜數據類型之間的賦值是將一個變量的地址拷貝給另外一個變量,因為地址(引用)指向的數據是同一個位置,所以一個變量變化會導致另一個也發生變化,這種賦值過程被稱為引用傳遞。
7.談談你對js作用域的理解
-
作用域,即變量(變量作用域又稱上下文)和函數生效(能被訪問)的區域
-
我們一般將作用域分成:
-
全局作用域
任何不在函數中或是大括號中聲明的變量,都是在全局作用域下,全局作用域下聲明的變量可以在程序的任意位置訪問
-
局部作用域
-
塊級作用域
ES6引入了let和const關鍵字,和var關鍵字不同,在大括號中使用let和const聲明的變量存在於塊級作用域中。在大括號之外不能訪問這些變量
-
函數作用域
函數作用域也叫局部作用域,如果一個變量是在函數內部聲明的它就在一個函數作用域下面。這些變量只能在函數內部訪問,不能在函數以外去訪問
-
-
8.js的內置構造函數有哪些,他們都有哪些共性
-
Array
Object
String
Date
Function
RegExp
Number
Boolean
-
使用的時候都要用new關鍵字去實例化
9.構造函數和普通函數以及箭頭函數區別
-
構造函數在定義的時候和普通函數沒有區別,只是定義的時候要求名稱首字母大寫。
-
箭頭函數不可以當成構造函數使用,因為箭頭函數的this指向問題
-
普通函數this指向看誰在調用這個函數,如果沒有找到,一般是window的隱式調用
-
箭頭函數this有兩種說法:一種認為箭頭函數沒有this,一種認為箭頭有this和外層的this一樣
-
構造函數this指向當前的實例對象
10.var let const的區別
- var聲明的變量會掛載在window上,而let和const聲明的變量不會
- var聲明變量存在變量提升,let和const不存在變量提升
- let和const聲明形成塊作用域
- 同一作用域下let和const不能聲明同名變量,而var可以
- 使用let/const聲明的變量在當前作用域存在暫存死區
- const一旦聲明必須賦值,不能使用null占位,聲明后不能再修改,如果聲明的是復合類型數據,可以修改其屬性
11. 閉包的含義,閉包的使用場景,閉包的優缺點
- 閉包的含義
- 全局作用域(window)隨着頁面打開就會自動生成,一直到頁面關閉才會被銷毀
- 局部作用域在函數執行時會生成,等函數執行完成之后就會被銷毀,局部作用域銷毀之后局部的變量也會被銷毀。如果我們想要局部作用域函數執行之后不被銷毀,我們可以在函數內部返回一個引用數據類型。閉包使用的就是返回一個函數,這樣函數作用域就不會被銷毀,局部變量會在內存空間內。
- 只要我們在調用這個函數,該函數內部有一個局部變量,返回的函數內部使用着這個局部變量,此時就可以訪問到局部變量。
- 閉包的使用場景
- 閉包一般在我們希望避免全局變量污染時會使用,我們使用一個局部變量,這樣別人就無法覆蓋我使用的變量
- 閉包的優缺點
- 優點
- 可以訪問一個局部變量
- 可以延長一個局部變量的生命周期
- 可以避免全局變量污染問題
- 缺點
- 占用內層空間 大量使用閉包會造成 棧溢出
- 由於閉包會一直占用內存空間,直到頁面銷毀,我們可以主動將已使用的閉包銷毀:
只有我們主動切斷鏈接,將這個鏈接地址置為null,閉包才會被垃圾回收機制回收內存空間
- 優點
12. this關鍵字以及修改this指向
- this指向問題
- 普通函數內部的this看調用的對象,有人調用,this就指向調用者,如果沒有找到誰在調用默認全局在調用,this指向window。
- 箭頭函數內部的this有兩種說法:一種認為箭頭函數沒有this,一種認為箭頭有this和外層的this一樣。總之箭頭函數的this指向看它定義位置作用域的this
- 構造函數內部的this指向當前創建出來的實例
- 修改this的指向
- call/apply/bind都可以修改this的指向
- call和apply在改變函數this指向的同時會調用這個函數,如果要傳參,call放在第二個參數后面,apply第二個參數里面傳一個數組
- bind改變函數this指向不會執行函數,會返回一個新的函數
13.new關鍵字做了哪些事情?
- new會創建一個空對象
- new會將構造函數內部this指向到這個創建出來的對象
- new會將創建對象的__proto__指向構造函數的prototype
- new會返回這個創建出來的對象
14.瀏覽器緩存的認識
- 緩存機制是瀏覽器自帶的機制,瀏覽器會將get請求訪問到的資源緩存到本地,這樣做的目的是為了提高用戶的訪問速度和節約用戶的流量。
- 但是緩存也會造成一些問題,有時候服務端文件發生變化名稱還是之前老的名字,瀏覽器無法識別這個文件的變化,還會使用緩存中的文件。
- 為了解決這個問題我們有時候需要在請求的過程中告訴瀏覽器我們不使用緩存,針對於get請求只要每次攜帶的參數變化瀏覽器就不會使用緩存文件,認為你要獲取新的數據。
15.跨域和跨域的解決方案
什么是跨域?
- 因為瀏覽器出於安全考慮,有同源策略。也就是說,如果協議、域名或者端口有一個不同就是跨域,Ajax 請求會失敗。
- 那么是出於什么安全考慮才會引入這種機制呢? 其實主要是用來防止 CSRF 攻擊的。簡單點說,CSRF 攻擊是利用用戶的登錄態發起惡意請求。
- 也就是說,沒有同源策略的情況下,A 網站可以被任意其他來源的 Ajax 訪問到內容。如果你當前 A 網站還存在登錄態,那么對方就可以通過 Ajax 獲得你的任何信息。當然跨域並不能完全阻止 CSRF。
- 然后我們來考慮一個問題,請求跨域了,那么請求到底發出去沒有? 請求必然是發出去了,但是瀏覽器攔截了響應。你可能會疑問明明通過表單的方式可以發起跨域請求,為什么 Ajax就不會。因為歸根結底,跨域是為了阻止用戶讀取到另一個域名下的內容,Ajax 可以獲取響應,瀏覽器認為這不安全,所以攔截了響應。但是表單並不會獲取新的內容,所以可以發起跨域請求。同時也說明了跨域並不能完全阻止 CSRF,因為請求畢竟是發出去了。
跨域的解決方案
- JSONP
- JSONP 的原理很簡單,就是利用script標簽沒有跨域限制的漏洞。通過script標簽訪問需要訪問的后端的地址,后端返回一個函數調用的方式,將需要發送給前端的數據當成這個函數的參數,前端只需要定義一個同名的函數接受后端返回的數據即可。
- JSONP使用簡單且兼容性不錯,但是只限於get請求,無法發送post請求
- JSONP使用還需要后端的配合
- 此種記住已經不是ajax請求了,是script請求
- 配置反向代理
- 因為同源策略是限制瀏覽器訪問別人的服務器,我們可以繞過直接請求,先讓我們瀏覽器請求自己的服務器,由自己的服務器請求別人的,拿到數據之后返回給我們的瀏覽器。這種只需要簡單的配置即可(ajax請求)
- 后端配置CORS
- 無需了解,一般也不建議這樣配置。
16.事件委托
什么是事件委托
- 在開發中,如果我們想要給一個元素綁定事件,需要獲取到這個元素進行綁定,但如果元素是動態生成,無法獲取從而無法綁定,此時我們就需要利用冒泡原理把這個事件委托給這個元素的頁面存在的父元素來綁定
使用場景
- ajax請求到的數據動態渲染到頁面
- js動態創建插入頁面的
如何實現事件委托
- 獲取頁面已存在的父元素,給這個父元素綁定同類型的事件,利用冒泡原理子元素也會觸發這個事件,在通過event.target來縮小事件觸發的范圍
17. 原型和原型鏈
原型概念
- 每一個函數都有一個prototype屬性,構造函數也有這個屬性
- 每一個對象都有一個__proto__屬性,實例化對象也有這個屬性
- 每個實例化對象的__proto__指向構造函數的prototype
原型鏈概念
- 每一個對象訪問屬性和方法的時候會優先使用自己的屬性和方法
- 自身沒有這個屬性和方法會去它的__proto__里面找這個屬性和方法,這一層的__proto__就是它構造函數的prototype
- 如果這一層沒有會繼續往下一層__proto__里面找 下一層的__proto__就是它父類構造函數的prototype
- 一直找到最上層構造函數Object,它的原型指向null,所以原型鏈頂層是null,這是屬性會出現undefined方法會報錯
18.cookie的特點
- 存儲大小有限制,一般是4KB左右,數量有限制,一般是 50 條左右
- 有時效性,也就是有過期時間,默認的過期是session級別
- 有域名限制,只能在域下面存儲,無法在本地存儲,當前域只能使用自己域下面的cookie,無法訪問其他域的cookie
- 服務端和客戶端公用的空間,一般存放一些前后端公用的數據
19.cookie和localStorage還有sessionStorage區別
- cookie一般存儲比較重要的數據,前后端公用的數據,有一定的大小限制(4kb),有過期時間
- localStorage和sessionStorage存儲的數據只能前端使用,相對於比較大(5M)
- localStorage存儲的數據是永久的,除非用戶自己手動清除緩存,否則會一直存在。sessionStorage存儲的數據是會話級別的,關閉瀏覽器就會自動刪除
20. 防抖和節流
21.js實現繼承的方式
- 借助構造函數實現繼承
- 在子類的構造函數里面調用父類的構造函數,利用call或者apply改變父類構造函數this的指向
- 優點:
- 子類實例可以使用父類的屬性和方法
- 缺點:
- 子類實例無法使用父類原型上的屬性和方法
- 原型鏈繼承
- 將子類構造函數的原型指向父類的某個實例
- 優點:
- 子類實例可以使用父類原型上的屬性和方法
- 缺點:
- 子類實例無法使用父類的屬性和方法
- 子類實例訪問constructor屬性得到的不是子類的構造函數,而是父類的構造函數
- 組合繼承
- 借助構造函數實現繼承 + 原型鏈繼承
- 這樣子類的實例既可以使用父類的屬性和方法也可以使用父類原型上的屬性和方法
- 將子類構造函數原型的constructor屬性重新指向子類的構造函數
- 缺點
- 會調用父類構造函數兩次
- 一次是在子類的構造函數內部
- 一次是在將子類構造函數原型指向父類實例
- 雖然會調用兩次但是沒有太大影響
- 會調用父類構造函數兩次
- 使用es6的class和extends關鍵字實現繼承