天天看點

【JS函數】JS函數之普通、構造、閉包函數

自我介紹:大家好,我是吉帥振的網絡日志;微信公衆号:吉帥振的網絡日志;前端開發工程師,工作4年,去過上海、北京,經曆創業公司,進過大廠,現在鄭州敲代碼。

JS函數專欄

1【JS函數】JS函數之普通、構造、閉包函數

2【JS函數】JS函數之定時器函數

3【JS函數】JS函數之防抖、節流函數

4【JS函數】JS函數之排序函數,對象數組根據某屬性排序

5【JS函數】JS函數之高階函數、組合函數、函數柯裡化 

一、普通函數

關于函數基礎内容建議檢視w3school:JavaScript 函數

二、構造函數

1.定義:

構造函數就是初始化一個執行個體對象,對象的prototype屬性是繼承一個執行個體對象

2.主要内容:

a.預設函數首字母大寫

b.構造函數并沒有顯示傳回任何東西。new 操作符會自動建立給定的類型并傳回他們,當調用構造函數時,new會自動建立this對象,且類型就是構造函數類型。

c.也可以在構造函數中顯示調用return.如果傳回的值是一個對象,它會代替新建立的對象執行個體傳回。如果傳回的值是一個原始類型,它會被忽略,新建立的執行個體會被傳回。  

function Person( name){
    this.name =name;
}
var p1=new Person('John');
           

等同于:

function person(name ){
     Object obj =new Object();
     obj.name =name;
      return obj;
}
var p1= person("John");
           

d.因為構造函數也是函數,是以可以直接被調用,但是它的傳回值為undefine,此時構造函數裡面的this對象等于全局this對象。this.name其實就是建立一個全局的變量name。在嚴格模式下,當你補通過new 調用Person構造函數會出現錯誤。

【JS函數】JS函數之普通、構造、閉包函數

e.也可以在構造函數中用Object.defineProperty()方法來幫助我們初始化:

function Person( name){
    Object.defineProperty(this, "name"{
        get :function(){
           return name;
        },
         set:function (newName){
          name =newName;
        },
        enumerable :true, //可枚舉,預設為false
         configurable:true //可配置
       });
}  
var p1=new Person('John');
           

f.在構造函數中使用原型對象      

//比直接在構造函數中寫的效率要高的多
Person.prototype.sayName= function(){
    console.log(this.name);
};
           

但是如果方法比較多的話,大多人會采用一種更簡潔的方法:直接使用一個對象字面形式替換原型對象,如下:      

Person.prototype ={
    sayName :function(){
        console.log(this.name);
    },
    toString :function(){
        return "[Person "+ this.name+"]" ;
    }
};
           

這種方式非常流行,因為你不用多次鍵入Person.prototype,但有一個副作用你一定要注意:

【JS函數】JS函數之普通、構造、閉包函數

使用字面量形式改寫了原型對象改變了構造函數的屬性,是以他指向Object而不是Person。這是因為原型對象具有一個constructor屬性,這是其他對象執行個體所沒有的。當一個函數被建立時,它的prototype屬性也被建立,且該原型對象的constructor屬性指向該函數。當使用對象字面量形式改寫原型對象時,其constructor屬性将被置為泛用對象Object.為了避免這一點,需要在改寫原型對象的時候手動重置constructor,如下:

Person.prototype ={
    constructor :Person,
    sayName :function(){
        console.log(this.name);
    },        
    toString :function(){
        return "[Person "+ this.name+"]" ;
    }
};

//再次測試:
p1.constructor===Person
//true 
p1.constructor===Object
//false
p1 instanceof Person
//true
           

三、閉包函數

1.定義:

閉包函數指有權通路另一個函數作用域中的變量的函數。在本質上,閉包就是将函數内部和函數外部連接配接起來的橋梁。閉包是一種保護私有變量的機制,在函數執行時形成私有的作用域,保護裡面的私有變量不受外界幹擾。

2.主要内容:

要了解JavaScript閉包,就要先了解JavaScript的變量作用域。
變量的作用域有兩種:全局的和局部的(全局變量和局部變量)
JavaScript中,在函數内部可以直接讀取到全局變量。

var n=10
function fn(){
    alert(n)
}
fn()      //10

而在函數外部無法讀取到函數内部的變量。

function fn(){
    var n=10;
}
fn()
alert(n)   //n is not defined    函數外部無法讀取到函數内部的n


注意:函數内部使用var聲明變量的時候,這個變量是局部變量,如果不使用var,那麼這個變量就是一個全局變量。

例如:
function fn(){
    n=10;
}
fn()
alert(n)   //10
另外,函數的參數也是局部性的,隻在函數内部起作用。

在正常情況下,我們是無法得到函數内部的局部變量的,隻有變通方法才可以——在函數内部再聲明一個函數。

function f1(){
    var n=10;
    function f2(){
        alert(n)
    }
}

f2函數可以得到f1函數内的所有局部變量,但是f1函數卻無法得到f2函數内部的局部變量——JavaScript語言特有的“鍊式作用域”結構。(即子對象會一級一級地向上尋找所有父對象的變量),是以,父對象的所有變量,對于子對象都是可見的。

f2函數可以擷取到父級函數f1的局部變量,那麼如果把f2()函數傳回,在函數f1外部就可以通路到f1()函數内部的變量了。

例如:
function f1(){
    var n=10;
    function f2(){
        alert(n)
    }
    return f2()
}
f1()           //頁面彈出10

例子中的f2()函數就是一個閉包函數。
           

3.用途

(1)可以讀取父級作用域函數内部的變量;

(2)讓變量的值始終儲存在記憶體中(讓局部變量變成全局變量),不被垃圾回收機制清除。

繼續閱讀