1. 談談對於閉包的理解
閉包就是有權訪問另一個函數作用域中的變量的函數,MDN上面:閉包是一種特殊的對象,它由兩部分構成:函數,以及創建該函數的環境。環境由閉包創建時再作用域中的任何局部變量組成。
創建閉包的最常見的方式就是在一個函數內創建另一個函數,通過另一個函數訪問這個函數的局部變量,利用閉包可以延長作用域鏈,緩存數據
-
閉包的特性:
- 函數內再嵌套函數
- 內部函數可以引用外層的參數和變量
- 參數和變量不會被垃圾回收機制回收
-
使用閉包主要是為了設計私有的方法和變量。閉包的優點是可以避免全局變量的污染,缺點是閉包會常駐內存,會增大內存使用量,使用不當很容易造成內存泄露。
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. 事件模型
-
冒泡型事件: 當你使用事件冒泡時,子級元素先觸發,父級元素后觸發
-
捕獲型事件: 當你使用事件捕獲時,父級元素先觸發,子級元素后觸發
-
DOM事件流: 同時支持兩種事件模型: 捕獲型事件和冒泡型事件
-
阻止冒泡:在w3c中,使用stopPropagation()方法,在IE下這只cancelBubble = true
-
阻止捕獲: 阻止事件的默認行為,例如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); //北京, 上海, 香港