深入理解JavaScript函數

JavaScript 前端全棧架構丶 2019-05-15
深入理解JavaScript函數

JavaScript中的函數

1. 函數的定義

  • 兩種定義形式:
  • 通過函數定義表達式來定義
  • 通過函數聲明語句來定義

函數聲明語句定義一個函數

//計算階乘的遞歸函數

function factorial(x){

if (x<=1) return 1;

return x*factotial(x-1);

}

函數定義表達式定義一個函數

var s= function sum(x,y){
return x+y;
}

tips:以表達式方式定義的函數(特別適合用來定義那些只會用到一次的函數),函數名是可選的,

也就可以直接寫成這樣

var s= function(x,y){

return x+y;

}

2.函數命名

  • 函數的名稱通常是動詞或者以動詞為前綴的詞組 如:funciton saveMessage(){}
  • 函數名的第一個字符通常為小寫
  • 當我們命名的函數名比較長時,一種是駝峰式命名 如:function readSystemFile(){}
  • 另一種是以下劃線分割單詞,如:function make_products_iPhones(){}
  • 有一些函數是用作內部函數或者私有函數的,通常以一條下劃線為前綴。 如: _login()

3.函數的調用

  • 普通的函數調用

example:

printprops({x:1})
var total = distance(0,1,2)+ distance(3,4,5)
var probability = factorial(5)/factoral(10)
  • 對象方法的調用

概念:如果函數表達式是一個屬性訪問表達式(即該函數是一個對象的屬性或者數組的一個函數),

那麼該調用表達式就是一個方法調用表用表達式。

example:

//定義一個對象
var person = {
name: lihua ,
age: 18 ,
sex: 女 ,
send: function(){//返回 person的name、age、sex
this.message = this.name + this.age + this.age;
}
};
person.send();//這條語句就是函數的方法調用
person.message; //得到name、age、sex相關信息

tips:

1.函數返回的值倘若是因為解釋器到達結尾,返回值就是undefined;倘若函數返回是因為解釋器執行到一條return語句,

則返回return語句後面的值;如果return語句後面沒有值則返回undefined。

2.在ECMAScript3和在非嚴格模式下的ECMAScript5的函數調用規定中,this的值(調用上下文)的值是全局對象,但是在

嚴格模式下this的值卻是undefined,因此可也用this來判斷當前環境是否為嚴格模式,此外ECMAScript2015 (簡稱es6)嚴格模式請參考以下地址。

ECMAScipt的第六個版本 https://developer.mozilla.org/zh-CN/

3.方法調用和普通的函數調用的一個重要區別就是:調用上下文(即this的值),通常this關鍵字指向成為調用上下文的對象,

屬性訪問表達式:對象名.方法名(通常使用.運算符訪問屬性)或者"[]"進行屬性訪問操作。(this關鍵字的相關內容是十分重要的)

  • 構造函數的調用

構造函數就是用來初始化先創建的對象,通常使用關鍵字new來調用構造函數,當使用new關鍵字來調用構造函數的時候就會自動

創建一個新的的空對象,而構造函數只需要初始化這個新對象的狀態(屬性和方法),調用構造函數的話,新的對象的原型(prototype)等於

構造函數的原型(prototype)屬性,由此引出一個特性:通過同一個構造函數創建的所有對象都繼承同一個相同的對象。

凡是沒有形參的構造函數都可以省略圓括號,以下兩行代碼是等價的。

var fn = new Object();
var fn = new Object;

tips:

1.構造函數通常不使用return關鍵字,進行初始化新對象,執行完函數體,就調用構造函數表達式的計算結果作為新對象的值,顯示返回。

2.倘若構造函數使用return語句返回一個對象,那麼調用表達式的值就是這個對象,而不是this指向的對象。

3.構造函數裡沒有顯式調用return時,默認是返回this對象,也就是新創建的實例對象。

4.return的是五種簡單數據類型:String,Number,Boolean,Null,Undefined。這種情況下,忽視return值,依然返回this對象

  • 間接調用
  • JavaScript中的函數也是對象,所以函數對象也可以包含方法。函數的間接調用用到的call()和apply()方法。
  • 這兩個方法都能顯示指定調用所需的this的值,這就引出一個特性:任何函數都可以作為對象的方法來調用,
  • 哪怕這個函數不是那個對象的方法。(這也就是在實際開發中我們也會常用這兩個方法的原因之一)

1.call()方法調用一個函數, 其具有一個指定的this值和分別地提供的參數(參數的列表) this的值並不一定就是該函數執行真正的this值,在非嚴格模式下,倘若this的值指向nullundefined的話

會自動指向全局對象(瀏覽器中就是window對象),同時值為原始值(數字,字符串,布爾值)的this會指向該原始值的自動包裝對象。

2.apply()方法調用一個具有給定this值的函數,以及作為一個數組(或類似數組對象)提供的參數。

為this指定一個對象,使用apply時只需要寫一次這個方法然後再另一個對象中繼承它,繼而不用在這個新的對象重新來寫它。

tips:call()方法的作用和 apply() 方法類似,區別就是call()方法接受的是參數列表,而apply()方法接受的是一個參數數組。

4.函數中的形參與實參

  • 可選的形參
  • 可變長的實參列表:實參對象 arguments 它是一個類數組對象,通過數字下表就能夠訪問傳入函數的實參值,它包含length屬性,讓函數可以操作任意數量的實參。
  • callee和caller屬性
  • callee屬性指代當前正在執行的函數,caller指代調用當前正在執行的函數的函數,它可以訪問棧。而callee可以來遞歸調用自身。

5.函數的閉包(!important)

  • 概念:通俗地講函數的閉包就是在一個函數內部定義另一個函數,而這個內部的函數(子函數)可以調用包裹它的函數(父函數等、"爺爺、太爺爺...")的變量。
  • 也可以認為閉包就是能夠讀取其他函數內部變量的函數。
  • 變量作用域:
  • 全局變量:任何函數內部都可以訪問全局作用域
  • 局部變量:在函數外部無法讀取變量

tips

javascript是沒有像Java、C++那樣用一對“{}”括起來的塊級作用域,但是在es6中可以使用let關鍵字實現塊級作用域。

  • 詞法作用域:變量的作用域是在定義時決定而不是執行時決定,也就是說詞法作用域取決於源碼,通過靜態分析就能確定,因此詞法作用域也叫做靜態作用域。
  • with和eval除外,所以只能說JS的作用域機制非常接近詞法作用域(Lexical scope)。

var scope = "global scope";

function checkScope(){

var scope = "local scope";

function f(){return scope;}

return f();

}

checkScope(); //輸出可以得到 local scope

  • 閉包的用途
  • i. 讀取函數內部的變量、函數嵌套函數
  • ii. 讓這些變量的值始終保持在內存中(全局變量的值不會在被函數調用過後自動清除,由GC回收)
  • iii. 避免全局變量的汙染、讓私有成員存在
  • 閉包的注意事項
  • i. 由於閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存洩露
  • 解決方法是,在退出函數之前,將不使用的局部變量全部刪除。
  • ii. 閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),
  • 把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。
  • 以下例子來檢驗自己是否已經掌握了閉包的運行機制。
  • example 0ne:
  var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());

example Two:

  var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
  alert(object.getNameFunc()());

6.函數的屬性、方法以及構造函數

  • length屬性
  • argument.length屬性表示傳入函數的實參的個數,而函數本身的length屬性則有不同含義,它表示函數定義時的實際形參個數。
  • prototype(原型)屬性
  • 這個屬性指向對象的引用,而這個對象就被稱為原型對象(prototype object),每個函數都包含這個屬性,都包含不同的原型對象;
  • 在JavaScript中每個函數都有一個特殊的屬性叫作原型(prototype)

function doSomething(){}

console.log(doSomething.prototype);

-----------------------------------

結果: 這就是 原型對象

{

constructor: ƒ doSomething(), //構造函數

/* 這些又是這個構造函數裡面的方法或者屬性

arguments: null

caller: null

length: 0

name: "doSomething"

prototype: {constructor: ƒ} 構造函數的原型屬性

__proto__: ƒ ()*/

__proto__: { //原型屬性

constructor: ƒ Object(),

hasOwnProperty: ƒ hasOwnProperty(),

isPrototypeOf: ƒ isPrototypeOf(),

propertyIsEnumerable: ƒ propertyIsEnumerable(),

toLocaleString: ƒ toLocaleString(),

toString: ƒ toString(),

valueOf: ƒ valueOf()

}

}

-------------------------------

function doSomething(){}

doSomething.prototype.eat = "food" //doSomething函數的原型(prototype)屬性對象添加eat的屬性

var doSomeInstancing =new doSomething() //創建doSomething函數的實例 通過new關鍵字來調用該函數,它返回這個函數的實例化對象給doSomeInstancing

doSomeInstancing.prop = "add value"; //給對象doSomeInstancing添加一個“name為prop值為add value”的屬性

console.log(doSomeInstancing); //輸出這個doSomeIntancing對象 結果現實這個對象有兩個屬性,一個是:prop: "add value" 另一個是:__proto__: Object

console.log(doSomething.prototype); //輸出doSomething.prototype的值是一個對象{eat: "food",constructor: ƒ doSomething(), __proto__: Object

顯然doSomeInstancing.__proto__屬性與doSomething.prototype(構造函數的原型屬性的值是相同)它們的值是相等的。(可以通過===來判斷 __ptototype__為隱式原型)

  • call()、apply()和bind()方法 在前面的函數的間接調用中已經介紹了call()以及apply(),在這裡就不再敘說,就詳細介紹ECMAScript5新增的bind()方法

bind()方法中的bind翻譯過來就是捆綁、綁定之意,作用就是將函數綁定至某個對象中,倘若一個函數調用了bind()方法並傳入一個對象作為參數,那麼這個方法將返回新的函數。

function f(y){return this.x+y;} //待綁定的函數

var o = {x :1};//將要被綁定的對象

var g = f.bind(o); //將函數f綁定至對象o 相當於var o={x:1, f:function f(y){return this.x+y;}}

console.log(g(2)); //通過g(y)調用o.f(y) 輸出3

bind()方法除了除了第一個實參之外,傳入bind()的實參也會綁定至this.這種應用是一種常見的函數式編程技術,被稱為柯里化(currying)

var sum = function(x,y){return x+y;} 
var succ = sum.bind(null,1);
succ(2)
  • toString()方法
  • 該方法返回字符串
  • Function()構造函數
  • 高階函數
  • 記憶(mem

最後

以下是總結出來最全前端框架視頻,包含: javascript/vue/react/angualrde/express/koa/webpack 等學習資料。

深入理解JavaScript函數

【領取方式】

關注頭條 前端全棧架構丶第一時間獲取最新前端資訊學習

手機用戶可私信關鍵詞 【前端】即可獲取全棧工程師路線和學習資料!

相關推薦

推薦中...