JavaScript面試題

1. 談談對於閉包的理解

閉包就是有權訪問另外一個函數做用域中的變量的函數,MDN上面:閉包是一種特殊的對象,它由兩部分構成:函數,以及建立該函數的環境。環境由閉包建立時再做用域中的任何局部變量組成。javascript

建立閉包的最多見的方式就是在一個函數內建立另外一個函數,經過另外一個函數訪問這個函數的局部變量,利用閉包能夠延長做用域鏈,緩存數據java

  • 閉包的特性:
    • 函數內再嵌套函數
    • 內部函數能夠引用外層的參數和變量
    • 參數和變量不會被垃圾回收機制回收
  • 使用閉包主要是爲了設計私有的方法和變量。閉包的優勢是能夠避免全局變量的污染,缺點是閉包會常駐內存,會增大內存使用量,使用不當很容易形成內存泄露。
var getNum;
  function getCounter(){
    var n=1;
    var inner = function(){
        return n++;
    }
    return inner;
  }
  getNum = getCounter();
  console.log(getNum()); //1
  console.loga(getNum()); //2

2. 說說你對做用域鏈的理解

當查找變量的時候,會先從當前上下文的變量對象中查找,若是沒有找到,就會從父級執行上下文的變量對象中查找,一直找到全局上下文的變量對象,也就是全局對象window,這樣有多個執行上下文的變量對象構成的鏈條就叫作做用域鏈編程

  • 做用域鏈的做用是保證執行環境裏有權訪問的變量和函數是有序的,做用域鏈的變量只能向上訪問,變量訪問到window對象即被終止,做用域鏈向下訪問變量是不被容許的
  • 簡單的說,做用域就是變量域函數的可訪問訪問,即做用域控制着變量域函數的可見性和生命週期

3. 請解釋一下什麼是事件代理

  • 事件代理,又稱之爲事件委託。是JavaScript中經常使用綁定事件的經常使用技巧。"事件代理"便是把本來須要綁定的事件委託給父元素,讓父元素擔當事件監聽的職務。事件代理代理的原理是DOM元素的事件冒泡。使用事件代理的好處是能夠提升性能數組

  • 能夠大量節省內存佔用,減小事件註冊,好比在table上代理全部td的click事件緩存

  • 能夠實現當新增子對象時無需再次對其綁定閉包

4. 事件模型

  • 冒泡型事件: 當你使用事件冒泡時,子級元素先觸發,父級元素後觸發app

  • 捕獲型事件: 當你使用事件捕獲時,父級元素先觸發,子級元素後觸發函數

  • DOM事件流: 同時支持兩種事件模型: 捕獲型事件和冒泡型事件性能

  • 阻止冒泡:在w3c中,使用stopPropagation()方法,在IE下這隻cancelBubble = truethis

  • 阻止捕獲: 阻止事件的默認行爲,例如click 連接後跳轉。在w3c中使用 PreventDefault()方法,在IE下設置window.event.returnValue = false

5. 什麼是面向對象編程及面向過程編程,他們的異同和優缺點

  • 面向過程就是分析出解決問題所須要的步驟,而後用函數把這些步驟一步一步實現,使用的時候一個一個一次調用就能夠了

  • 面向對象是把構成問題事物分解成各個對象,創建對象的目的不是爲了完成一個步驟,而是爲了描述某個事物在整個解決問題的步驟中的行爲,向對象是以功能劃分問題,而不是步驟

  • 面向對象的基本思想是使用對象,類,繼承,封裝等基本蓋面來進行程序設計

優勢:

  • 易維護,採用面向對象思想設計的結構,可讀性高,因爲繼承的存在,即便改變需求,那麼維護也只是在局部模塊,因此維護起來是非誠方便和較低成本的

  • 易擴展

  • 縮短了開發週期

  • 開發工做的重用性,繼承性高,下降重複工做量

  • 縮短了開發週期

6. 講講事件監聽

綁定事件的另中方法就是用addEventListener()和attachEvent()來綁定事件監聽函數

語法:

element.addEventListener(event,function useCapture)

event: (必需)事件名,支持全部DOM事件
function: (必需)指定要事件觸發時執行的函數
useCapture: (可選)執行時間是否在捕獲或冒泡階段執行。true,捕獲。false,冒泡,默認是false

事件監聽的優勢:

  • 能夠綁定多個事件,常規的事件綁定只執行最後綁定的事件,可是使用事件監聽,兩個事件都執行
<input type="button" value="click me" id="btn3">
  <input type="button" value="click me" id="btn4">
  <script>
     var btn3 = document.getElementById("btn3");
     btn3.onclick = function(){
        alert("hello 1"); //不執行
     }
     btn3.onclick = function(){
        alert("hello 2");//執行
     }
     btn4.addEventListener('click',hello3);
     btn4.addEventListener('click',hello4);

     function hello3(){
        alert("hello 3")//執行
     }
     function hello4(){
        alert("hello 4")//執行
     }
  </script>
  • 能夠解除相應的綁定
<input type="button" value="click me" id="btn5">
<script>
var btn5 = document.getElementById("btn5");
btn5.addEventListener("click",hello1);//執行了
btn5.addEventListener("click",hello2);//不執行
btn5.removeEventListener("click",hello2);
function hello1(){
    alert("hello 1");
}
function hello2(){
    alert("hello 2");
}
</script>

7. 介紹JS的基本數據類型

Undefined,Null,Boolean,Number,String,ECMAscript2015新增了Symbol(建立後獨一無二且不可變的數據類型)

8. 介紹js有哪些內置對象

Object是JavaScript中全部對象的父對象

數據封裝類對象:OBject,Array,Boolean,Number和String
其餘對象: Function,Arguments,Math,Date,RegExp,Error

9. JavaScript原型,原型鏈,有什麼特色?

每一個獨享都會在其內部初始化一個屬性,就是prototype(原型),當咱們訪問一個對象的屬性時,若是這個對象內部不存在這個屬性,那麼他就是去prototype裏找這個屬性,這個prototype又會有本身的prototype,因而就這樣一直找下去,也就是咱們平說的原型鏈的該概念。關係:instance.constuctor.prototype = instance.__proto__

原型鏈:當咱們須要一屬性的時候,JavaScript引擎會先看當前對象是夠有這個屬性,若是沒有的話,就會查找它的prototype獨享時候有這個屬性,如此遞推下去,一直檢索到Object內建對象

10. javaScript有幾種類型,你能花一下他們的內存圖嗎?

棧:基本數據類型(Undefined,Null,Boolean,Number,String)
堆:引用數據類型(對象,數組和函數)

兩種類型的區別是:存儲位置不一樣:

原始數據類型直接存儲在棧中的簡單數據段,佔據空間小,大小固定,屬於被頻繁使用數據,因此放入棧中存儲

引用數據類型存儲在堆(heap)中的對象,佔據空間大、大小不固定。若是存儲在棧中,將會影響程序運行的性能;引用數據類型在棧中存儲了指針,該指針指向堆中該實體的起始地址。當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中得到實體

內存圖

11. JavaScript如何實現繼承

構造函數繼承

  • 構造繼承

經過apply和call方法,將父對象的構造函數綁定在子對象上

function Person(name,age){
    this.name =name;
    this.age =age;
  }
  
  function Child(name,age,sex){
    Person.call(this,name,age);
    this.sex =sex;
  }

  Person.prototype.sayHi = function(){
    console.log('hello')
  }

  var p1 =new Child('小明',18,"男");
  console.log(p1.name,p1.age,p1.sex)//'小明',18,"男"
  p1.sayHi();//報錯

構造函數繼承的方式能夠繼承到構造函數上的屬性和方法,可是原型prototype下的屬性和方法沒法繼承

  • 原型繼承

經過改變子函數原型指向繼而實現繼承父函數下的屬性和方法

function Person(name,age){
    this.name =name;
    this.age =age;
  }
  Person.prototype.sayHi = function(){
     console.log('hi') 
  }
  function Student(score){
    this.score =score
  }
  Student.prototype = new Person('小明',10)

  var stu1 = new Student(100);
  var stu2 =new Student(80);

  console.log(stu1.score,stu1.name)//100 "小明"
  console.log(stu2.score,stu2.name)//80 "小明"
  stu1.sayHi();//hi
  stu2.sayHi();//hi

原型繼承能夠繼承原型上的方法和屬性,但構造函數上的屬性和方法沒法被修改

  • 組合繼承

經過原型繼承加上構造函數繼承,完美實現繼承的方案

function Person(name,age){
    this.name =name
    this.age = age
 }

 Person.prototype.sayHi = function(){

  console.log('hi')
 }

 function Student(name,age,socre){
  Person.call(this,name,age);
  this.score = score
 }
 Student.prototype = new Person();

 Student.prototype.eat = function(){
  console.log('吃東西')
 }
 var stu = new Student("小明",20,"100")
 console.log(stu.name,stu.age,stu.score)//小明 20 100
 stu.sayHi();//hi
 stu.eat();//吃東西
  • 拷貝繼承

拷貝繼承就是把一個對象中的屬性或者方法直接複製到另外一個對象中

function Animal(){}
  Animal.prototype.species = "動物"

  function extend2(child,parent){
    var p = Parent.prototype
    var c = child.prototype;
    for(var i in p){
      c[i]=p[i]
    }
    c.uber =p ;
  }
  extend2(Cat,Animal);
  var cat1 = new Cat("大毛","黃色")
  console.log(cat1.species);//動物

一般構造函數使用組合繼承的方式實現繼承,是最完美的實現

非構造函數繼承

  • 淺拷貝

    淺拷貝就是把至關於把一個對象中的內容複製一份給另外一個對象,可是這種事是複製複製不完整的,只能複製對象下的方法和屬性,對象下的對象的方法和屬性沒法複製

    var obj1 = {
        age:10,
        sex:"男",
        car:["奔馳","寶馬","特斯拉","奧迪"]
      }
      var obj2 = {};
    
      function extend(a,b){
        for(var key in a){
          b[key] = a[key];
        }
      }
      extend(obj1,obj2);
      console.log(obj1);
      console.log(onj2);
  • 深拷貝

深拷貝,就是可以實現真正意義上的數組和對象的拷貝,須要遞歸調用淺拷貝

function deepCopy(p,c){
    var c =c ||{};
    for(var i in p){
      if(typeof p[i]==='object'){
        c[i] == (p[i].constructor === Array) ?[]:{}
        deepCopy(p[i],c[i])
      }else{
        c[i] = p[i]
      }
    }
    return c;
  }

  var Doctor = deep(Chinese)
  Chinese.birthPlaces = ['北京','上海','香港'];

  Doctor.birthPlaces.push('廈門');
alert(Doctor.birthPlaces); //北京, 上海, 香港, 廈門

  alert(Chinese.birthPlaces); //北京, 上海, 香港
相關文章
相關標籤/搜索