前端JS初級面試題二 (。•ˇ‸ˇ•。)老鐵們!快來瞧瞧自己都會了麼

zoyoy發表於2020-02-16

目錄

  1. 傳統事件繫結和符合W3C標準的事件繫結有什麼區別?
  2. IE 和標準下有哪些相容性的寫法
  3. JavaScript 如何實現繼承?
  4. JavaScript 建立物件的幾種方式?
  5. this指標,閉包,作用域
  6. 如何阻止事件冒泡和預設事件
  7. JavaScript 的同源策略
  8. JavaScript是一門什麼樣的語言,它有哪些特點?
  9. 如何檢測陣列的資料型別?
  10. 新增 刪除 替換 插入到某個節點的方法
  11. javascript的本地物件,內建物件和宿主物件
  12. 已知ID的Input輸入框,希望獲取這個輸入框的輸入值,怎麼做?(不使用第三方框架)
  13. 希望獲取到頁面中所有的checkbox怎麼做?(不使用第三方框架)
  14. 設定一個已知ID的DIV的html內容為xxxx,字型顏色設定為黑色(不使用第三方框架)
  15. 當一個DOM節點被點選時候,我們希望能夠執行一個函式,應該怎麼做?
  16. Javascript的事件流模型都有什麼?
  17. 列舉瀏覽器物件模型BOM裡常用的至少4個物件,並列舉window物件的常用方法至少5個
  18. 簡述建立函式的幾種方式
  19. iframe的優缺點?
  20. 請你談談Cookie的弊端?
  21. js延遲載入的方式有哪些?
  22. documen.write和 innerHTML 的區別?
  23. 哪些操作會造成記憶體洩漏?
  24. 解釋jsonp的原理,以及為什麼不是真正的ajax
  25. javascript 中的垃圾回收機制?
  26. BOM物件有哪些,列舉window物件?
  27. 簡述readyonly與disabled的區別?
  28. 為什麼擴充套件javascript內建物件不是好的做法?

1. 傳統事件繫結和符合W3C標準的事件繫結有什麼區別?

傳統事件繫結

<div onclick="">123</div>
div1.onclick = function(){};
<button onmouseover=""></button>

注意:
如果給同一個元素繫結了兩次或多次相同型別的事件,那麼後面的繫結會覆蓋前面的繫結
(不支援DOM事件流 :事件捕獲階段=>目標元素階段=>事件冒泡階段)

符合W3C標準的事件繫結的方式 addEventListener/attachEvent
  • 非IE瀏覽器:addEventListener / removeEventListener

    所有的DOM節點都包含這兩個方法,並且他們都接受三個引數:
    1.事件型別
    2.事件處理方法
    3.布林引數,預設false
    (true捕獲階段呼叫事件處理方法;false冒泡階段呼叫事件處理方法。)

    //addEventListener
    let box = document.querySelector('.box')
    box.addEventListener('click',function(){
      console.log('box clicked...')
    })
    
    
    function xxx(){console.log('box clicked...')}
    box.addEventListener('click',xxx)   //新增事件
    box.removeEventListener('click',xxx)   //刪除事件
    

    注意:
    如果給同一個元素繫結了兩次或多次相同型別的事件,繫結依次觸發
    支援DOM事件流
    進行事件繫結不需要on字首

  • IE瀏覽器:attachEvent / detachEvent

    這兩個方法都接受兩個相同的引數。
    1.事件處理程式名稱
    2.事件處理程式方法

    IE只支援事件冒泡
    let box = document.querySelector('.box')
    function xxx(){console.log('box clicked...')}
    box.attachEvent('onclick',xxx) // 新增事件
    box.detachEvent('onclick',xxx) // 刪除事件
    

    注意:
    進行事件型別傳參需要帶上on字首
    這種方式只支援事件冒泡,不支援事件捕獲

相容性:
attachEvent——相容:IE7、IE8;不相容firefox、chrome、IE9、IE10、IE11、safari、opera
addEventListener——相容:firefox、chrome、IE、safari、opera;不相容IE7、IE8

2. IE 和標準下有哪些相容性的寫法

ev = event || window.event 獲取觸發事件的物件
var target = ev.srcElement || ev.target 獲取事件的源物件
document.documentElement.clientWidth/Height || document.body.clientWidth/Height 獲取瀏覽器視窗尺寸

3. JavaScript 如何實現繼承?

?(1) 原型鏈繼承

function Animals() {
    this.age = 1;
}
Animals.prototype.getAge = function(){
    return this.age;
};

function Cat() {
    this.name = '咪咪'
}

// 繼承,用 Animals 型別的一個例項來重寫 Cat 型別的原型物件
// 這一步,讓Cat的物件擁有了Animals物件的屬性,方法
Cat.prototype = new Animals(); 
var cat = new Cat();
console.log(cat.name) // '咪咪'
console.log(cat.age)  // 1
console.log(cat.getAge()) // 1

特點:

  • 非常純粹的繼承關係,例項是子類的例項,也是父類的例項
  • 父類新增原型方法/原型屬性,子類都能訪問到
  • 簡單,易於實現

缺點:

  • 要想為子類新增屬性和方法,必須要在new Cat()這樣的語句之後執行,不能放到構造器中
  • 無法實現多繼承
  • 來自原型物件的引用屬性被所有例項共享
  • 建立子類例項時,無法向父類建構函式傳參

?(2) 借用建構函式繼承(也稱偽造物件或經典繼承)
在子型別建構函式的內部呼叫超型別建構函式;使用 apply()call() 方法將父物件的建構函式繫結在子物件上

function Animals() {
    // 定義引用型別值屬性
    this.colors = ["red","green","blue"];
}
function Cat() {
    // 繼承 Animals,在這裡還可以給超型別建構函式傳參
    Animals.call(this);
}

var cat1 = new Cat();
cat1.colors.push('yellow');
console.log(cat1.colors);  //  ["red", "green", "blue", "yellow"]

var cat2 = new Cat();
console.log(cat2.colors); // ["red", "green", "blue"]

通過使用 apply() 或 call() 方法,我們實際上是在將要建立的 Cat 例項的環境下呼叫了 Animals 建構函式。這樣一來,就會在新 Cat 物件上執行 Animals() 函式中定義的所有物件初始化程式碼。結果 Cat 的每個例項就都會具有自己的 colors 屬性的副本了

特點:

  • 解決了原型鏈繼承中,子類例項共享父類引用屬性的問題
  • 建立子類例項時,可以向父類傳遞引數
  • 可以實現多繼承(call多個父類物件)

缺點:

  • 例項並不是父類的例項,只是子類的例項
  • 只能繼承父類的例項屬性和方法,不能繼承原型屬性/方法
  • 無法實現函式複用,每個子類都有父類例項函式的副本,影響效能

?(3)原型+建構函式組合繼承 (也稱偽經典繼承)
將原型鏈和借用建構函式的技術組合到一塊。使用原型鏈實現對原型屬性和方法的繼承,而通過借用建構函式來實現對例項屬性的繼承。這樣,既通過在原型上定義方法實現了函式複用,又能夠保證每個例項都有自己的屬性。

function Animals(age) {
    this.age = age;
    this.colors = ["red","green","blue"];
}
Animals.prototype.getAge = function(){
    return this.age;
};

function Cat(age, name) {
    // 借用建構函式方式繼承屬性
    Animals.call(this,age);
    this.name = name;
}

// 原型鏈方式繼承方法
Cat.prototype = new Animals(); 
Cat.prototype.constructor = Cat;
Cat.prototype.getName = function(){
    return this.name;
};

var cat1 = new Cat(1,"咪咪");
cat1.colors.push("yellow");
console.log(cat1.colors);    //  ["red", "green", "blue", "yellow"]
console.log(cat1.getAge());  //  1
console.log(cat1.getName()); //  咪咪

var cat2 = new Cat(2, "小白")
console.log(cat2.colors);    // ["red", "green", "blue"]
console.log(cat2.getAge());  // 2
console.log(cat2.getName()); // 小白

特點:

  • 組合繼承避免了原型鏈和借用建構函式的缺陷,融合了它們的優點(可以繼承例項屬性/方法,也可以繼承原型屬性/方法),成為 javascript 中最常用的繼承模式。
  • 既是子類的例項,也是父類的例項
  • 不存在引用屬性共享問題
  • 可傳參
  • 函式可複用
  • 使用 instanceof 操作符和 isPrototype() 方法也能夠用於識別基於組合繼承建立的物件。

缺點:

  • 無論在什麼情況下,都會呼叫兩次超型別建構函式:一次是在建立子型別原型的時候,另一次是在子型別建構函式內部。

?(4)原型式繼承
使用 Object.create() 方法實現原型式繼承

這個方法接收兩個引數:一是用作新物件原型的物件和一個為新物件定義額外屬性的物件。在傳入一個引數的情況下,此方法與 object() 方法作用一致。 在傳入第二個引數的情況下,指定的任何屬性都會覆蓋原型物件上的同名屬性。

var person = {
            name: "luochen",
            colors: ["red","green","blue"]
}; 
var anotherPerson1 = Object.create(person,{
            name: {
                    value: "tom"
            }
});
var anotherPerson2 = Object.create(person,{
            name: {
                    value: "jerry"
            }
});
anotherPerson1.colors.push("purple");
alert(anotherPerson1.name);     // "tom"
alert(anotherPerson2.name);     // "jerry"
alert(anotherPerson1.colors);    // "red,green,blue,purple"
alert(anotherPerson2.colors);    // "red,green,bule,purple";

只是想讓一個物件與另一個物件類似的情況下,原型式繼承是完全可以勝任的。
缺點: 包含引用型別值的屬性始終都會共享相應的值

?(5)寄生式繼承

// 建立一個僅用於封裝繼承過程的函式,該函式在內部以某種方式來增強物件,最後返回這個物件
function createPerson(original){
    // 通過 Object.create() 函式建立一個新物件
    var clone = Object.create(original);
    // 增強這個物件
    clone.sayGood = function(){           
        alert("hello world!!!");
    };
    // 返回這個物件 
    return clone;                          
}

在主要考慮物件而不是自定義型別和建構函式的情況下,寄生式繼承也是一種有用的模式。
缺點:

  • 做不到函式複用,都是每次建立物件都會建立一遍方法,記憶體佔用大

?(6) 寄生組合式繼承
通過借用建構函式來繼承屬性,通過原型鏈的混成形式來繼承方法。本質上,就是使用寄生式繼承來繼承超型別的原型,然後再將結果指定給子型別的原型

function Animals(age) {
    this.age = age;
    this.colors = ["red","green","blue"];
}
Animals.prototype.getAge = function(){
    return this.age;
};

function Cat(age, name) {
    // 借用建構函式方式繼承屬性
    Animals.call(this,age);
    this.name = name;
}

// 建立超型別原型的一個副本
var animalsCopy = Object.create(Animals.prototype);
// 重設因重寫原型而失去的預設的 constructor 屬性
animalsCopy.constructor = Cat;
// 將新建立的物件賦值給子型別的原型
Cat.prototype = animalsCopy;

Cat.prototype.getName = function() {
    return this.name;
}

var cat1 = new Cat(1,"咪咪");
cat1.colors.push("yellow");
console.log(cat1.colors);    //  ["red", "green", "blue", "yellow"]
console.log(cat1.getAge());  //  1
console.log(cat1.getName()); //  咪咪

var cat2 = new Cat(2, "小白")
console.log(cat2.colors);    // ["red", "green", "blue"]
console.log(cat2.getAge());  // 2
console.log(cat2.getName()); // 小白

這個例子的高效率體現在它只呼叫一次 Animals 建構函式,並且因此避免了在 Cat.prototype上面建立不必要,多餘的屬性。與此同時,原型鏈還能保持不變;因此還能夠正常使用 instance 操作符isPrototype() 方法
缺點:程式碼比較複雜

4. JavaScript 建立物件的幾種方式?

?(1) 使用物件字面量建立

var Person = {
  name:'小白',
  age:23,
  say: function() {
    console.log('我是:' + this.name); 
  }
}
Person.say();  // 我是:小白

?(2) 使用Object 建構函式建立

var Person = new Object();
Person.name = '小白';
Person.age = 23;
Person.say = function() {
	console.log('我是:' + this.name); 
}
Person.say();  // 我是:小白

推薦使用物件字面量{}形式建立,效率比較高

?(3) 使用工廠模式建立物件

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

var person1 = createPerson('name',29)
person1.say();

建立createPerson的時候,返回的是一個物件,那麼我們就無法判斷返回的物件究竟是一個什麼樣的型別。於是出現了使用建構函式建立物件。

?(4) 使用建構函式建立物件

function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.say = function() {
  console.log(this.name + ':' + this.age + '歲');
};

var person1 = new Person('小白', '12');
person1.say();     // 小白:12歲
var person2 =  new Person('小花', '11');
person2.say();     // 小花:11歲

這種方式與工廠模式相比:

  • 沒有顯示地構建物件
  • 直接將屬性和方法賦給了this物件
  • 沒有return語句
  • 可以通過instanceof識別物件的型別

使用內建物件建立物件
如:var str = new String("例項初始化String");
var str1 = "直接賦值的String";
var func = new Function("x","alert(x)");// 示例初始化func
var obj = new Object(); // 示例初始化一個Object

5. this指標,閉包,作用域

this: 指向呼叫上下文
閉包: 內層作用域可以訪問外層作用域的變數
作用域: 定義一個函式就開闢了一個區域性作用域,整個JS執行環境有一個全域性作用域

6. 如何阻止事件冒泡和預設事件

防止冒泡和捕獲
w3c的方法是e.stopPropagation(),IE則是使用e.cancelBubble = true

取消預設事件
w3c的方法是e.preventDefault(),IE則是使用e.returnValue = false

7. JavaScript 的同源策略

同源策略

  • ajax 請求時,瀏覽器要求當前網頁和server必須同源(安全)
  • 同源:協議,域名,埠,三者必須一致

詳情請點選檢視另一篇部落格 同源策略和跨域 (ノ°▽°)ノ 衝鴨!征服她!!!

8. JavaScript是一門什麼樣的語言,它有哪些特點?

js就是物件導向的弱型別語言
語言特性:物件導向(要掌握建立物件的多種方式,繼承的多種方式、原型鏈),動態/弱型別語言

動態語言的特性
var num=10;//num是一個數字型別
num="jim";//此時num又變成一個字串型別
//我們把一個變數用來儲存不同資料型別的語言稱之為一個動態語言,或者說是一個弱型別語言

9. 如何檢測陣列的資料型別?

檢測陣列的幾種方式:

Array.isArray([1,2,3]); // true   (ES5)
toString.call([]);      // "[object Array]"
var arr = [];
arr.constructor;       // ƒ Array() { [native code] }   不推薦使用,因為constructor是可以修改的。
[] instanceof Array    // true

JS基礎知識:變數的型別和計算

10. 新增 刪除 替換 插入到某個節點的方法

原生:
appendChild() // 新增
removeChild() // 刪除節點
replaceChild(新節點,舊節點) // 替換(前替換後)
insertBefore(插入節點,被插節點) // 插入(前插後)

jq:
// 新增
append()(舊節點新增新節點後面)
appendTo()(新節點新增到舊節點後面)
prepend()(舊節點新增新節點的前面)
prependTo() (新節點新增到舊節點的前面)

remove()// 刪除
empty() // 清空

// 插入
after() // 舊節點後面插入 新節點
before() // 舊節點前面插入 新節點
insertAfter() // 新節點插到舊節點後
insertBefore() // 新節點插到舊節點前

replaceWith() // 替換

11. javascript的本地物件,內建物件和宿主物件

本地物件array obj regexp等可以new例項化
內建物件gload Math 等不可以例項化的
宿主物件為瀏覽器自帶的document, window

12. 已知ID的Input輸入框,希望獲取這個輸入框的輸入值,怎麼做?(不使用第三方框架)

	document.getElementById(ID).value

13. 希望獲取到頁面中所有的checkbox怎麼做?(不使用第三方框架)

var domList = document.getElementsByTagName(‘input’)
var checkBoxList = [];
var len = domList.length;  //快取到區域性變數
while (len--) {  //使用while的效率會比for迴圈更高
  if (domList[len].type == ‘checkbox’) {
      checkBoxList.push(domList[len]);
  }
}

14. 設定一個已知ID的DIV的html內容為xxxx,字型顏色設定為黑色(不使用第三方框架)

var dom = document.getElementById(ID);
dom.innerHTML = “xxxx”
dom.style.color = “#000

15. 當一個DOM節點被點選時候,我們希望能夠執行一個函式,應該怎麼做?

直接在DOM裡繫結事件: <div onclick=”test()”></div>
在JS裡通過onclick繫結: xxx.onclick = test
通過事件新增進行繫結: addEventListener(xxx, ‘click’, test)

16. Javascript的事件流模型都有什麼?

事件冒泡: 事件開始由最具體的元素接受,然後逐級向上傳播
事件捕捉: 事件由最不具體的節點先接收,然後逐級向下,一直到最具體的
DOM事件流: 三個階段:事件捕捉,目標階段,事件冒泡

17. 列舉瀏覽器物件模型BOM裡常用的至少4個物件,並列舉window物件的常用方法至少5個

物件:window, document, location, screen, history, navigator
方法: alert(), confirm(), prompt(), open(), close()

18. 簡述建立函式的幾種方式

第一種(函式宣告):

function sum1(num1,num2){
   return num1+num2;
}

第二種(函式表示式)

var sum2 = function(num1,num2){
   return num1+num2;
}

第三種(函式物件方式):

var sum3 = new Function("num1","num2","return num1+num2");

19. iframe的優缺點?

優點:

  1. 解決載入緩慢的第三方內容如圖示和廣告等的載入問題
  2. Security sandbox
  3. 並行載入指令碼

缺點:

  1. iframe會阻塞主頁面的Onload事件
  2. 即時內容為空,載入也需要時間
  3. 沒有語意

20. 請你談談Cookie的弊端?

缺點:

  1. Cookie數量和長度的限制。每個domain最多隻能有20條cookie,每個cookie長度不能超過4KB,否則會被截掉。
  2. 安全性問題。如果cookie被人攔截了,那人就可以取得所有的session資訊。即使加密也與事無補,因為攔截者並不需要知道cookie的意義,他只要原樣轉發cookie就可以達到目的了。
  3. 有些狀態不可能儲存在客戶端。例如,為了防止重複提交表單,我們需要在伺服器端儲存一個計數器。如果我們把這個計數器儲存在客戶端,那麼它起不到任何作用。

更多詳情,請點選我的這篇部落格檢視:٩(๑❛ᴗ❛๑)۶
cookie localStorage sessionStorage (¦3」∠) 看完你居然就懂了!!!

21. js延遲載入的方式有哪些?

  1. defer和async
  2. 動態建立DOM方式(建立script,插入到DOM中,載入完畢後callBack)
  3. 按需非同步載入js

22. documen.write和 innerHTML 的區別?

  • document.write只能重繪整個頁面
  • innerHTML可以重繪頁面的一部分

23. 哪些操作會造成記憶體洩漏?

記憶體洩漏指任何物件在您不再擁有或需要它之後仍然存在。

垃圾回收器定期掃描物件,並計算引用了每個物件的其他物件的數量。如果一個物件的引用數量為 0(沒有其他物件引用過該物件),或對該物件的惟一引用是迴圈的,那麼該物件的記憶體即可回收。

造成記憶體洩露的操作有:

  1. setTimeout的第一個引數使用字串而非函式的話,會引發記憶體洩漏。
  2. 閉包
  3. 控制檯日誌
  4. 迴圈(在兩個物件彼此引用且彼此保留時,就會產生一個迴圈)

24. 解釋jsonp的原理,以及為什麼不是真正的ajax

JSONP:
動態建立script標籤,回撥函式
jsonp可以用來解決跨域問題,詳情請點選檢視:同源策略和跨域 (ノ°▽°)ノ 衝鴨!征服她!!!

Ajax:
Ajax是頁面無重新整理請求資料操作
Ajax, jQuery ajax, fetch,axios ヾ(⌐■_■)ノ看完 你就明白了~~

25. javascript 中的垃圾回收機制?

  • 在Javascript中,如果一個物件不再被引用,那麼這個物件就會被GC回收。
  • 如果兩個物件互相引用,而不再 被第3者所引用,那麼這兩個互相引用的物件也會被回收。
  • 因為函式a被b引用,b又被a外的c引用,這就是為什麼 函式a執行後不會被回收的原因。

26. BOM物件有哪些,列舉window物件?

  • window物件,是JS的最頂層物件,其他的BOM物件都是window物件的屬性;
  • document物件,文件物件;
  • location物件,瀏覽器當前URL資訊;
  • navigator物件,瀏覽器本身資訊;
  • screen物件,客戶端螢幕資訊;
  • history物件,瀏覽器訪問歷史資訊;

27. 簡述readyonly與disabled的區別

ReadOnlyDisabled的作用是使使用者不能夠更改表單域中的內容.

區別:

  • Readonly只針對input(text/password)textarea有效,而disabled對於所有的表單元素有效,包括select,radio,checkbox,button等。
  • 在表單元素使用了disabled後,我們將表單以POST或者GET的方式提交的話,這個元素的值不會被傳遞出去,而readonly會將該值傳遞出去

28. 為什麼擴充套件javascript內建物件不是好的做法?

因為你不知道哪一天瀏覽器或javascript本身就會實現這個方法,而且和你擴充套件的實現有不一致的表現。到時候你的javascript程式碼可能已經在無數個頁面中執行了數年,而瀏覽器的實現導致所有使用擴充套件原型的程式碼都崩潰了。。。

在這裡插入圖片描述

相關文章