JS匯總---基礎(chǔ)&函數(shù)&閉包

基礎(chǔ)


Javascript是一種弱類型語(yǔ)言,它分別有什么優(yōu)點(diǎn)和缺點(diǎn)

弱類型語(yǔ)言:簡(jiǎn)單好用,更靈活多變。但是會(huì)犧牲性能,比如一些隱含的類型轉(zhuǎn)換
強(qiáng)類型語(yǔ)言:類型轉(zhuǎn)換的時(shí)候非常嚴(yán)格,,強(qiáng)類型語(yǔ)言是直接操縱內(nèi)存,容易出內(nèi)存越界和泄漏的問題。在類型的轉(zhuǎn)換方面是很多約束,甚至強(qiáng)制轉(zhuǎn)換也要很謹(jǐn)慎,一不小心就出大問題。

Javascript里面的數(shù)據(jù)類型有哪些

5個(gè)簡(jiǎn)單數(shù)據(jù)類型(基本數(shù)據(jù)類型)+ 1個(gè)復(fù)雜數(shù)據(jù)類型
undefiend, number string null boolean + object

有幾種方式可以判斷數(shù)據(jù)類型

typeof和intanceof
其實(shí)typeof和instanceof的目的都是檢測(cè)變量的類型,兩個(gè)的區(qū)別在于typeof一般是檢測(cè)的是基本數(shù)據(jù)類型,instanceof主要檢測(cè)的是引用類型!

typeof.png

基本類型和引用類型有什么區(qū)別

賦值的時(shí)候基本類型按值,引用類型按引用,就是基本類型會(huì)復(fù)制一份,引用類型就是一個(gè)新的指針
函數(shù)傳參的時(shí)候都是按值傳遞

{}=={}? []==[]? null==undefined?

{}=={}()
[]==[]()
null == undefined(對(duì))

寫個(gè)方法判斷一個(gè)變量的類型

函數(shù)


函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別

函數(shù)聲明會(huì)將那個(gè)函數(shù)提升到最前面,成為全局函數(shù)。函數(shù)聲明要指定函數(shù)名,而函數(shù)表達(dá)式不用,可以用作匿名函數(shù)。
創(chuàng)建函數(shù)的方式:函數(shù)聲明、函數(shù)表達(dá)式、還有一種不常見的方式就是Function構(gòu)造器。
函數(shù)聲明:

function add(a,b) {
    a = +a;
    b = +b;
    if (isNaN(a) || isNaN(b)) {
        return;
    }
    return a + b;
}

函數(shù)表達(dá)式的幾種方式:

// 函數(shù)表達(dá)式
var add = function(a, b) {
    // do sth
}


// 匿名函數(shù)定義的一個(gè)立即執(zhí)行函數(shù)表達(dá)式
(function() {
    // do sth
})();


// 作為返回值的函數(shù)表達(dá)式
return function() {
    // do sth
};


// 命名式函數(shù)表達(dá)式
var add = function foo (a, b) {
    // do sth
}

函數(shù)聲明與函數(shù)表達(dá)式的主要區(qū)別就是:函數(shù)聲明會(huì)被前置
函數(shù)聲明前置:

// function add(a,b) 已經(jīng)聲明前置了,可以正常調(diào)用
var num = add(1, 2);
console.log(num);  // 3

function add(a,b) {
    a = +a;
    b = +b;
    if (isNaN(a) || isNaN(b)) {
        return;
    }
    return a + b;
}

函數(shù)表達(dá)式前置:

// var add 變量聲明提前,此時(shí)變量的值是undefined
var num = add(1, 2);
console.log(num);  // TypeError:undefined is not a function

var add = function(a, b) {
    a = +a;
    b = +b;
    if (isNaN(a) || isNaN(b)) {
        return;
    }
    return a + b;
}

Function構(gòu)造器:

var func = new Function('a', 'b', 'console.log(a+b);');
fun(1, 2); // 3

// 和上面的方式?jīng)]有區(qū)別
var func = Function('a', 'b', 'console.log(a+b);');
func(1, 2); // 3

區(qū)別:


360截圖20170307114742162.png

原型與原型鏈

每一個(gè)構(gòu)造函數(shù)都有一個(gè)prototype,指向一個(gè)對(duì)象,這個(gè)對(duì)象的所有屬性和方法,都會(huì)被構(gòu)造函數(shù)的實(shí)例繼承。
原型鏈作為實(shí)現(xiàn)繼承的主要方法。其基本思想是利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。
在JavaScript中,用proto 屬性來(lái)表示一個(gè)對(duì)象的原型鏈。當(dāng)查找一個(gè)對(duì)象的屬性時(shí),JavaScript 會(huì)向上遍歷原型鏈,直到找到給定名稱的屬性為止!

函數(shù)有哪幾種調(diào)用方式

直接調(diào)用
作為對(duì)象的方法調(diào)用
apply,call

作用域

JS沒有塊作用域,只有函數(shù)作用域
作用域鏈的作用是保證執(zhí)行環(huán)境里有權(quán)訪問的變量和函數(shù)是有序的,作用域鏈的變量只能向上訪問,變量訪問到window對(duì)象即被終止,作用域鏈向下訪問變量是不被允許的。

作用域鏈?zhǔn)鞘裁?/h4>

A://說(shuō)的不是很清楚

閉包

閉包是什么

閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)

創(chuàng)建閉包的方式:
在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)

閉包的作用:
1.讓這些函數(shù)的值始終保存在內(nèi)存中

濫用閉包有什么副作用

由于閉包會(huì)攜帶包含它的函數(shù)的作用域鏈,因此會(huì)比其他函數(shù)占用更多的內(nèi)存。過(guò)度使用閉包可能會(huì)導(dǎo)致內(nèi)存占用過(guò)多,所以只在絕對(duì)必要時(shí)使用閉包。

閉包實(shí)現(xiàn)塊級(jí)作用域

(function() {

})();

閉包的作用/應(yīng)用

匿名自執(zhí)行函數(shù)、緩存、實(shí)現(xiàn)封裝(主要作用)、實(shí)現(xiàn)面向?qū)ο笾械膶?duì)象
1 匿名自執(zhí)行函數(shù)

我們知道所有的變量,如果不加上var關(guān)鍵字,則默認(rèn)的會(huì)添加到全局對(duì)象的屬性上去,這樣的臨時(shí)變量加入全局對(duì)象有很多壞處,
比如:別的函數(shù)可能誤用這些變量;造成全局對(duì)象過(guò)于龐大,影響訪問速度(因?yàn)樽兞康娜≈凳切枰獜脑玩溕媳闅v的)。
除了每次使用變量都是用var關(guān)鍵字外,我們?cè)趯?shí)際情況下經(jīng)常遇到這樣一種情況,即有的函數(shù)只需要執(zhí)行一次,其內(nèi)部變量無(wú)需維護(hù),
比如UI的初始化,那么我們可以使用閉包:

var datamodel = {    
    table : [],    
    tree : {}    
};    
     
(function(dm){    
    for(var i = 0; i < dm.table.rows; i++){    
       var row = dm.table.rows[i];    
       for(var j = 0; j < row.cells; i++){    
           drawCell(i, j);    
       }    
    }    
       
    //build dm.tree      
})(datamodel);   
```
我們創(chuàng)建了一個(gè)匿名的函數(shù),并立即執(zhí)行它,由于外部無(wú)法引用它內(nèi)部的變量,因此在執(zhí)行完后很快就會(huì)被釋放,關(guān)鍵是這種機(jī)制不會(huì)污染全局對(duì)象。

**2緩存**

再來(lái)看一個(gè)例子,設(shè)想我們有一個(gè)處理過(guò)程很耗時(shí)的函數(shù)對(duì)象,每次調(diào)用都會(huì)花費(fèi)很長(zhǎng)時(shí)間,那么我們就需要將計(jì)算出來(lái)的值存儲(chǔ)起來(lái),當(dāng)調(diào)用這個(gè)函數(shù)的時(shí)候,首先在緩存中查找,如果找不到,則進(jìn)行計(jì)算,然后更新緩存并返回值,如果找到了,直接返回查找到的值即可。閉包正是可以做到這一點(diǎn),因?yàn)樗粫?huì)釋放外部的引用,從而函數(shù)內(nèi)部的值可以得以保留。
```
var CachedSearchBox = (function(){    
    var cache = {},    
       count = [];    
    return {    
       attachSearchBox : function(dsid){    
           if(dsid in cache){//如果結(jié)果在緩存中    
              return cache[dsid];//直接返回緩存中的對(duì)象    
           }    
           var fsb = new uikit.webctrl.SearchBox(dsid);//新建    
           cache[dsid] = fsb;//更新緩存    
           if(count.length > 100){//保正緩存的大小<=100    
              delete cache[count.shift()];    
           }    
           return fsb;          
       },    
     
       clearSearchBox : function(dsid){    
           if(dsid in cache){    
              cache[dsid].clearSelection();      
           }    
       }    
    };    
})();    
     
CachedSearchBox.attachSearchBox("input1");    
```
這樣,當(dāng)我們第二次調(diào)用CachedSearchBox.attachSerachBox(“input1”)的時(shí)候,我們就可以從緩存中取道該對(duì)象,而不用再去創(chuàng)建一個(gè)新的searchbox對(duì)象。

**3 實(shí)現(xiàn)封裝**

可以先來(lái)看一個(gè)關(guān)于封裝的例子,在person之外的地方無(wú)法訪問其內(nèi)部的變量,而通過(guò)提供閉包的形式來(lái)訪問:
```
var person = function(){    
    //變量作用域?yàn)楹瘮?shù)內(nèi)部,外部無(wú)法訪問    
    var name = "default";       
       
    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
}();    
     
print(person.name);//直接訪問,結(jié)果為undefined    
print(person.getName());    
person.setName("abruzzi");    
print(person.getName());    
   
得到結(jié)果如下:  
   
undefined  
default  
abruzzi  
```
4 **實(shí)現(xiàn)面向?qū)ο笾械膶?duì)象**
傳統(tǒng)的對(duì)象語(yǔ)言都提供類的模板機(jī)制,這樣不同的對(duì)象(類的實(shí)例)擁有獨(dú)立的成員及狀態(tài),互不干涉。雖然JavaScript中沒有類這樣的機(jī)制,但是通過(guò)使用閉包,我們可以模擬出這樣的機(jī)制。還是以上邊的例子來(lái)講:
```
function Person(){    
    var name = "default";       
       
    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
};    
     
     
var john = Person();    
print(john.getName());    
john.setName("john");    
print(john.getName());    
     
var jack = Person();    
print(jack.getName());    
jack.setName("jack");    
print(jack.getName());    
   
運(yùn)行結(jié)果如下:  
   
default  
john  
default  
jack  
```
由此代碼可知,john和jack都可以稱為是Person這個(gè)類的實(shí)例,因?yàn)檫@兩個(gè)實(shí)例對(duì)name這個(gè)成員的訪問是獨(dú)立的,互不影響的。

####實(shí)現(xiàn)一個(gè)暴露內(nèi)部變量,而且外部可以訪問修改的函數(shù)
(get和set,閉包實(shí)現(xiàn)) 
```
var person = function(){    
    //變量作用域?yàn)楹瘮?shù)內(nèi)部,外部無(wú)法訪問    
    var name = "default";       
       
    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
}();    
     
print(person.name);//直接訪問,結(jié)果為undefined    
print(person.getName());    
person.setName("abruzzi");    
print(person.getName());    
   
得到結(jié)果如下:  
   
undefined  
default  
abruzzi  
```


####從幾個(gè)li中取下標(biāo)的閉包代碼
```
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>


var li=document.getElementsByTagName("li");

for(var i=0;i<li.length;i++) {
    (function(x) {
        li[x].onclick=function(){alert(x);}
    })(i);
}

```
####實(shí)現(xiàn)一個(gè)閉包的例子(實(shí)現(xiàn)了一個(gè)定時(shí)函數(shù)傳值的)
```
閉包:
for(var i = 0; i < 10; i++ ){
    (function(x){
        setTimeout(function(){
            console.log(x)
        },x*1000)
    })(i)
}
或者用全局變量實(shí)現(xiàn)

```
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容