《JavaScript權威指南第六版》學習筆記-函式

xf27發表於2017-10-30

第八章、函式

  • 函式是這樣的一段JavaScript程式碼,只定義一次,但可能被執行或呼叫任意次。

  • JavaScript函式是引數化的

  • 如果函式掛載在一個物件上,作為物件的一個屬性,就稱它為物件的方法。

  • 用於初始化一個新建立的物件的函式稱為建構函式(constructor)

  • 函式即物件,程式可以隨意操控它們。

  • JavaScript函式構成了一個閉包(closure),它給JavaScript帶來了強勁的程式設計能力

函式定義

1.函式使用function關鍵字來定義。

  • 函式名稱識別符號

  • 一對圓括號

  • 一對花括號

  • 以表示式定義的函式,函式的名稱是可選的

  • 函式定義表示式特別適合用來定義那些只會用到一次的函式

eg:

//輸出o的每個屬性的名稱和值,返回undefined
function printprops(o){
  for(var p in o){
    console.log(p + ":" + o[p] + "\n");
  }
}

//計算兩個笛卡爾座標(x1,y1)和(x2,y2)之間的距離
function distance(x1,y1,x2,y2){
  var dx = x2 - x1;
  var dy = y2 - y1;
  return Math.sqrt(dx*dx + dy*dy);
}

//計算階乘的遞迴函式(呼叫自身的函式)
//x的值是從x到x遞減(歩長為1)的值的累乘
function factorial(x){
  if(x < 1){
    return 1;
  }
  return x * factorial(x-1);
}

//這個函式表示式定義了一個函式用來求傳人蔘數的平方
//注意我們把它賦值給一個變數
var square = function(x){
  return x * x;
}

//函式表示式可以包含名稱,這在遞迴時很有用
var f = functon fact(x){
  if(x <= 1){
    return 1
  }else{
    return x*fact(x-1)
  }
}

//函式表示式也可以作為引數傳給其他函式
data.sort(function(a,b){
  return a-b;
  })

//函式表示式有時定義後立即呼叫
var tensquared = (function(x){return x *x;}(10))複製程式碼

2.函式宣告語句"被提前"到外部指令碼或外部函式作用域的頂部,所以以這種方式宣告的函式,可以被在它定義之前出現的程式碼所呼叫。

3.巢狀函式

function hypotenuse(a,b){
  function square(x){
    return x * x;
  }
  return Math.sqrt(square(a) + aquare(b))
}複製程式碼
函式呼叫

1.構成函式主體的JavaScript程式碼在定義之時並不會執行,只有呼叫該函式時,它們才會執行

  • 作為函式
  • 作為方法
  • 作為建構函式
  • 通過它們的call()和apply()方法間接呼叫

2.方法呼叫

(1).一個方法無非是個儲存在一個物件的屬性裡的JavaScript函式。

var calculator = {
  operand1:1,
  operand2:1,
  add: function(){
    this.result = this.operand1 + this.operand2;
  }

}
calculator.add();   //這個方法呼叫1+1的結果
calculator.result;  //=>2複製程式碼

(2).方法呼叫和函式呼叫有一個重要的區別,即:呼叫上下文。屬性訪問表示式由兩部分組成:一個物件和屬性名稱。函式體可以使用關鍵字this引用該物件。

(3).大多數方法呼叫使用點符號來訪問屬性,使用方括號(的屬性訪問表示式)也可以進行屬性訪問操作。

o["m"](x,y);  //o.m(x,y)
a[o](z);

customer.surname.toUpperCase();   //呼叫customer.surname的方法
f().m();         //在f()呼叫結束後繼續呼叫返回值中的方法m()複製程式碼

(4).任何函式只要作為方法呼叫實際上都會傳人一個隱式的實參——這個實參是一個物件,方法呼叫的母體就是這個物件。

rect.setSize(width,height);
setReactSize(rect,width,height);複製程式碼

(5).方法鏈

//找到所有的header,取得它們id的對映,轉換為陣列並對它們進行排序
$(":header").map(function(){
  return this.id
  }).get().sort()複製程式碼

(6).當方法不需要返回值時,最好直接返回this

(7).this是一個關鍵字,不是變數,也不是屬性名。JavaScript的語法不允許給this賦值

(8).和變數不同,關鍵字this沒有作用域的限制,巢狀的函式不會從呼叫它的函式中繼承this.如果巢狀函式作為方法呼叫,其this的值指向呼叫它的物件。如果巢狀函式作為函式呼叫,其this值不是全域性物件(

非嚴格模式下)就是undefined(嚴格模式下)。

很多人誤以為呼叫巢狀函式時this會指向呼叫外層函式的上下文。如果你想訪問這個外部函式的this值,需要將this的值儲存在一個變數裡,這個變數和內部函式都同在一個作用域內。通常使用self來儲存this

var o = {        //物件o
  m:function(){    //物件中的方法m()
    var self = this;    //將this的值儲存至一個變數中
    console.log(this === o);    //輸出true,this就是這個物件o
    f();    //呼叫輔助函式f()

    function f(){   //定義一個巢狀函式f()
      console.log(this === o);    //"false":this的值是全域性物件或undefined
      console.log(this === self);   //"true":selft指外部函式的this值
    }
  }
}
o.m()  //呼叫物件o的方法m()複製程式碼

3.建構函式呼叫

(1).如果函式或者方法呼叫之前帶有關鍵字new,它就構成建構函式呼叫

(2).建構函式呼叫建立一個新的空物件,這個物件繼承自建構函式的prototype屬性

4.間接呼叫

(1).call()和apply()可以用來間接地呼叫函式,兩個方法都允許顯式指定呼叫所需的this值

(2).call()方法使用它自有的實參列表作為函式的實參,apply()方法則要求以陣列的形式傳人蔘數

函式的實參和形參

1.可選形參

(1).當呼叫函式的時候傳人的實參比函式宣告時指定的形參個數要少,剩下的形參都將設定為undefined值

function getPropertyNames(o,/*optional*/a){
  if(a === undefined){
    a=[];    //如果未定義,則使用新陣列                        ----》a = a || [];
  }
  for(var property in o){
    a.push(property)
  }
  return a;
}

//這個函式呼叫可以傳人1個或2個實參
var a = getPropertyNames(o);    //將o的屬性儲存到一個新陣列中
getPropertyNames(p,a);   //將p的屬性追加至陣列a中複製程式碼

2.可變長的實參列表:實參物件

(1).在函式體內,識別符號arguments是指向實參物件的引用,實參物件是一個類陣列物件,這樣可以通過數字下標就能訪問傳人函式的實參值,而不用非要通過名字來得到實參。

function(x,y,z){
  //首先,驗證傳人實參的個數是否正確
  if(arguments.length!=3){
    throw new Error("function f called with" + arguments.length + "arguments,but it expects 3 arguments")
  }
  //再執行函式的其他邏輯...
}複製程式碼

(2).實參物件有一個重要的用處,就是讓函式可以操作任意數量的實參。

內建函式Max.max()

function max(/*...*/){
  var max = Number.NEGATIVE_INFINTY;
  //遍歷實參,查詢並記住最大值
  for(var i = 0;i < arguments.length;i++){
    if(arguments[i] > max){
      max = arguments[i];
    }
  }
  return max;
}   

var largest = max(1,10,100,98,10000)   //=>10000複製程式碼

這種函式可以接收任意個數的實參,這種函式也稱為"不定實參函式"(varargs function)

arguments並不是真正的陣列,它是一個實參物件

function f(x){
  console.log(x);    //輸出實參的初始值
  arguments[0] = null;  //修改實引數組的元素同樣會修改x的值
  console.log(x);  //輸出"null"
}複製程式碼

(3).callee和caller屬性

callee屬性指代當前正在執行的函式

caller指代呼叫當前正在執行的函式的函式。通過caller屬性可以訪問呼叫棧。

var factorial = function(x){
  if(x <= 1){
    return 1;
  }
  return x * arguments.callee(x-1)
}複製程式碼

3.將物件屬性用做實參

通過名/值對的形式來傳人蔘數

//將原始陣列的length元素複製至目標陣列
//開始複製原始陣列的from_start元素
//並且將其複製至目標陣列的to_start中
//要記住實參的順序並不容易
function arraycopy(/*array*/from,/*index*/from_start,
                  /*array*/to,/*index*/to_start,
                  /*inter*/length)
{
    //邏輯程式碼
}

//這個版本是實現效率稍微有些低,但你不必再記住實參的順序
//並且from_start和to_start都預設為0
function easycopy(args){
  arraycopy(args.from,
            args.from_start || 0,   //注意這裡設定了預設值
            args.to,
            args.to_start || 0,args.length);
}
//來看如何呼叫easycopy()
var a = [1,2,3,4],b = [];
easycopy({from:a,to:b,length:4})複製程式碼

4.實參型別

function max(/*number...*/){
  /*程式碼區*/
}


function sum(a){
  if(isArrayLike(a)){
    var total = 0;
    for(var i=0;i<a.length;i++){
      var element = a[i];
      if(element == null){
        continue;
      }
      if(isFinite(element)){
        total += element;
      }else{
        throw new Error("sum():elements must be finite numbers");
      }
    }
    return total;
  }else{
    throw new Error("sum():argument must be array-like");
  }
}複製程式碼

flexisum()可以接收任意數量的實參

function flexisum(a){
  var total = 0;
  for(var i = 0;i < arguments.length;i++){
    var element = arguments[i],n;
    if(element == null){  //忽略null和undefined實參
      continue;
    }
    if(isArray(element)){   //如果實參是陣列
      n = flexisum.apply(this,element);   //遞迴地計算累加和
    }else if(typeof element === "function"){   //否則,如果是函式。。。
      n = Number(element())  //呼叫它並做型別轉換
    }else{
      n = Number(element)   //否則直接做型別轉換
    }
    if(isNaN(n)){   //如果無法轉換為數字,則丟擲異常
      throw Error("flexisum():can't covert" + element + "to number")
    }
    total += n;   //否則,將n累加至total
  }
  return total;
}複製程式碼
作為值的函式

函式可以定義,也可以呼叫,這是函式最重要的特性。

square僅僅是變數的名字,這個變數指代函式物件。函式還可以賦值給其他的變數

function square(x){
  return x*x;
}

var s = square;  //現在s和square指代同一個函式
square(4);  //=>16
s(4);  //=>16複製程式碼

當函式作為物件的屬性呼叫時,函式就稱為方法:

var o = {         //物件直接量
  square:function(x){
    return x*x;
  }
}
var y = o.square(16);  //=>256複製程式碼

賦值給陣列元素時:

var a = [function(x){return x*x},20];   //陣列直接量
a[0](a[1]);  //=>400複製程式碼

將函式用做值

//在這裡定義一些簡單的函式
function add(x,y){return x+y;}
function subtract(x,y){return x-y;}
function multiply(x,y){return x*y;}
function divide(x,y){return x/y;}

//這裡的函式以上面的某個函式作為引數
//並給它傳人兩個運算元然後呼叫它
function operate(operator,operand1,operand2){
  return operator(operand1,operand2);
}

//這行程式碼所示的函式呼叫實際上計算了(2+3) + (4*5)的值
var i = operate(add,operate(add,2,3),operate(multiply,4,5));

//我們為這個例子重複實現一個簡單的函式
//這次實現使用函式直接量,這些函式直接量定義在一個物件直接量中
var operators={
  add:function(x,y){return x+y;},
  subtract:function(x,y){return x-y;},
  multiply:function(x,y){return x*y;},
  divide:function(x,y){return x/y;},
  pow:Math.pow  //使用預定義的函式
}

//這個函式接收一個名字作為運算子,在物件中查詢這個運算子
//然後將它作用於所提供的運算元
//注意這裡呼叫運算子函式的語法
function operate2(operation,operand1,operand2){
  if(typeof operators[operation] === "function"){
    return operators[operation](operand1,operand2);
  }else{
    throw "Unknown operator"
  }
}

//這樣來計算("hello" + " " + "world")的值
var j = operate("add","hello",operate2("add"," ","world"));
//使用預定義的函式Math.pow()
var k = operate2("pow",10,2)複製程式碼

自定義函式屬性

JavaScript中的函式並不是原始值,而是一種特殊的物件。

//初始化函式物件的計數器屬性
//由於函式宣告被提前了,因此這裡是可以在函式宣告之前給它的成員賦值的
uniqueInteger.counter = 0;

//每次呼叫這個函式都會返回一個不同的整數
//它使用一個屬性來記住下一次將要返回的值
function uniqueInteger(){
  return uniqueInteger.counter++;   //先返回計數器的值,然後計數器自增1
}複製程式碼

factorial()使用自身的屬性(將自身當做陣列來對待)來快取上一次的計算結果:

function factorial(n){
  if(isFinite(n) && n>0 && n==Math.round(n)){   //有限的正整數
    if(!(n in factorial)){  //如果沒有快取結果
      factorial[n] = n * factorial(n-1);    //計算結果並快取之
    }
    return factorial[n];   //返回快取結果
  }else{
    return NaN;  //如果輸入有誤
  }
}
factorial[1]=1;   //初始化快取以儲存這種基本情況複製程式碼
作為名稱空間的函式

在函式中宣告的變數在整個函式體內都是可見的(包括在巢狀的函式中),在函式的外部是不可見的。不在任何函式內宣告的變數是全域性變數,在整個JavaScript程式中都是可見的。

function mymodule(){
  //模組程式碼
  //這個模組所使用的所有變數都是區域性變數
  //而不是汙染全域性名稱空間
}
mymodule();  //不要忘了還要呼叫這個函式

(function(){   //mymodule()函式重寫為匿名的函式表示式
  //模組程式碼
  }());   //結束函式定義並立即呼叫它複製程式碼

特定場景下返回帶補丁的extend()版本

//定義一個擴充套件函式,用來將第二個以及以後續引數複製至第一個引數
//這裡我們處理了IE bug:在多數IE版本中
//如果o的屬性擁有一個不可列舉的同名屬性,則for/in迴圈
//不會列舉物件o的可列舉屬性,也就是說,將不會正確地處理諸如toString的屬性
//除非我們顯示檢測它
var extend = (function(){   //將這個函式的返回值賦值給extend
  //在修復它之前,首先檢查是否存在bug
  for(var p in {toString:null}){
    //如果程式碼執行到這裡,那麼for/in迴圈會正確工作並返回一個簡單版本的extend()函式
    return function extend(o){
      for(var i=1; i<arguments.length;i++){
        var source = arguments[i];
        //複製所有的可列舉屬性
        for(var prop in source){
          o[prop] = source[prop];
        }
      }
      return o;
    };
  }
  return function patched_extend(o){
    for(var i=1;i<arguments.length;i++){
      var source = arguments[i];
      //複製所有的可列舉屬性
      for(var prop in source){
        o[prop] = source[prop]
      }
      //現在檢查特殊屬性
      for(var j=0;j<protoprops.length;j++){
        prop = protoprops[j];
        if(source.hasOwnProperty(prop)){
          o[prop] = source(prop);
        }
      }
    }
    return o;
  };

  //這個列表列出了需要檢查的特殊屬性
  var protoprops = ["toString","valueOf","constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString"]
  }())複製程式碼
閉包

1.JavaScript也採用詞法作用域(lexical scoping)

2.函式物件可以通過作用域鏈相互關聯起來,函式體內部的變數都可以儲存在函式作用域內,這種特性在電腦科學文獻中稱為"閉包"

3.所有的JavaScript函式都是閉包。

var scope = "global scope";   //全域性變數
function checkscope(){
  var scope = "local scope";  //區域性變數
  function f(){return scope;}  //在作用域中返回這個值
  return f();
}
checkscope();   //=>"local scope"

var scope = "global scope";   //全域性變數
function checkscope(){
  var scope = "local scope";  //區域性變數
  function f(){return scope;}  //在作用域中返回這個值
  return f;
}
checkscope()();   //=>"local scope"複製程式碼

4.它們可以捕捉到區域性變數(和引數),並一直儲存下來

var uniqueInter = (function(){        //定義函式並立即呼叫
                    var counter =0;   //函式的私有狀態
                    return function(){return counter++;}
                  }())複製程式碼

5.巢狀的函式是可以訪問作用域內的變數的,而且可以訪問外部函式中定義的counter變數

function counter(){
  var n = 0;
  return {
    count:function(){return n++;},
    reset:function(){return n = 0;}
  }
}

var c = counter(), d = counter();  //建立兩個計數器
c.count()  //=>0
d.count();  //=>0:它們互不干擾
c.reset();  //reset()和count()方法共享狀態
c.count();  //=>0:因為我們重置了c
d.count();   //=>1:而沒有重置d複製程式碼

6.counter()函式返回了一個"計數器"物件,這個物件包含兩個方法:count()返回下一個整數,reset()將計數器重置為內部狀態

這兩個方法都可以訪問私有變數n

每次呼叫counter()都會建立一個新的作用域鏈和一個新的私有變數

如果呼叫counter()兩次,則會得到兩個計數器物件,而且彼此包含不同的私有變數,呼叫其中一個計數器物件的count()或reset()不會影響另外一個物件。

function counter(n){   //函式引數n是一個私有變數
  return{
    //屬性get()方法返回並給私有計算器var遞增1
    get count(){return n++;}
    //屬性set不允許n遞減
    set count(m){
      if(m > n){
        n = m;
      }else{
        throw Error("count can only be set to a larger value")
      }
    }
  }
}
var c = counter(1000);
c.count;   //=>1000
c.count;   //=>1001
c.count=2000;  //=>2000
c.count;  //=>2000
c.count = 2000;  //=>Error複製程式碼

7.利用閉包實現的私有屬性存取器方法

//這個函式給物件o增加了屬性存取器方法
//方法名稱為get<name>和set<name>.如果提供了一個判斷函式setter方法就會用它來檢測引數的合法性,然後在儲存它
//如果判斷函式返回false,setter方法丟擲一個異常
//這個函式有一個非同尋常之處,就是getter和setter函式
//所操作的屬性值並沒有儲存在物件o中,相反,這個值僅僅是儲存在函式中的區域性變數中
//getter和setter方法同樣是區域性函式,因此可以訪問這個區域性變數
//也就是說,對於兩個存取器方法來說這個變數是私有的沒有辦法繞過存取器方法來設定或修改這個值

function addPrivateProperty(o,name,predicate){
  var value;  //這是一個屬性值

  //get方法簡單地將其返回
  o["get" + name] = function(){return value;};

  //set方法首先檢查值是否合法,若不合法就丟擲異常
  o["set" + name] = function(v){
    if(predicate && !predicate(v)){
      throw Error("set" + name + ":invalid value" +v);
    }else{
      value = v;
    }
  }
}

var o = {};
addPrivateProperty(o,"Name",function(x){return typeof x == "string"});

o.setName("Frank");   //設定屬性值
console.log(o.getName());  //得到屬性值
o.setName(o);  //檢視設定一個錯誤型別的值複製程式碼

8.在同一個作用域鏈中定義兩個閉包,這兩個閉包共享同樣的私有變數或變數

//這個函式返回一個總是返回v的函式
function constfunc(v){return function(){return v;}}

//建立一個陣列用來儲存常數函式
var funcs = [];
for(var i = 0; i < 10;i++){
  funcs[i] = constfunc(i);
}

funcs[5]();  //=>5複製程式碼

9.this是JavaScript的關鍵字,而不是變數。每個函式呼叫都包含一個this值,如果閉包在外部函式裡是無法訪問this的,除非外部函式將this轉存為一個變數

var self = this;  //將this儲存至一個變數中,以便巢狀的函式能夠訪問它複製程式碼

10.閉包具有自己所繫結的arguments,因此閉包內無法直接訪問外部函式的引數陣列,除非外部函式將引數陣列儲存到另外一個變數中

var outerArguments = arguments;  //儲存起來以便巢狀的函式能使用它複製程式碼
函式屬性、方法和建構函式

對函式執行typeof運算會返回字串"function",但是函式是JavaScript中特殊的物件。

1.length屬性

arguments.length表示傳人函式的實參的個數

函式的length屬性是隻讀屬性,它代表函式實參的數量

//這個函式使用arguments.callee,因此它不能在嚴格模式下工作
function check(args){
  var actual = args.length;   //實參的真實個數
  var expected = args.callee.length;  //期望的實參個數
  if(actual !== expected){  //如果不同則丟擲異常
    throw Error("Expected" + expected + "args;got" + actual);
  }
}

function f(x,y,z){
  check(arguments);  //檢查實參個數和期望的實參個數是否一致
  return x + y + z;  //再執行函式的後續邏輯
}複製程式碼

2.prototype屬性

每個函式都包含一個prototype屬性,這個屬性是指向一個物件的引用,這個物件稱做"原型物件"(prototype

object).每一個函式都包含不同的原型物件。當將函式用做建構函式的時候,新建立的物件會從原型物件上繼承屬性。

3.call()方法和apply()方法

call()和apply()看做是某個物件的方法,通過呼叫方法的形式來間接呼叫函式

call()方法來呼叫一個物件的Object.prototype.toString方法,用以輸出物件的類。

call()和apply()的第一個實參是呼叫函式的物件,它是呼叫上下文,在函式體內通過this來獲得對它的引用。

以物件o的方法來呼叫函式f()

f.call(o);
f.apply(o);

o.m=f; //將f儲存為o的臨時方法
o.m(); //呼叫它,不傳人蔘數
delete o.m;  //將臨時方法刪除複製程式碼

call()和apply()的第一個實參都會變為this的值

f.call(0,1,2);   //以物件o的方法的形式呼叫函式f(),並傳入兩個引數
f.apply(0,[1,2]);  //實參都放入一個陣列當中複製程式碼

如果一個函式的實參可以是任意數量,給apply()傳人的引數陣列可以是任意長度的。

var biggest = Math.max.apply(Math,array_of_numbers);

//將物件o中名為m()的方法替換為另一個方法
//可以在呼叫原始的方法之前和之後記錄日誌訊息
function trace(o,m){
  var original = o[m];  //在閉包中儲存原始方法
  o[m]=function(){  //定義新的方法
    console.log(new Date(),"Entering:",m);
    var result = original.apply(this,arguments);
    console.log(new Date(),"Exiting:",m);
    return result;
  }
}複製程式碼

trace()函式接收兩個引數,一個物件和有一個方法名,它將指定的方法替換為一個新方法,這個新方法是"包裹"原始方法的另一個泛函式。

4.bind()方法

這個方法的主要作用就是將函式繫結至某個物件。

function f(y){   //這個是待繫結的函式
  return this.x +y;  
}
var o={x:1}; //將要繫結的物件
var g=f.bind(o);  //通過呼叫g(x)來呼叫o.f(x)
g(2);  


//返回一個函式,通過呼叫它來呼叫o中的方法f(),傳遞它所有的實參
function bind(f,o){
  if(f.bind){
    return f.bind(o);   //如果bind()方法存在的話,使用bind()方法
  }else{
    return function(){  //否則,這樣繫結
      return f.apply(o,arguments);
    }
  }
}複製程式碼

除了第一個實參之外,傳人bind()的實參也會繫結至this

var sum = function(x,y){return x+y};   //返回兩個實參的和值
//建立一個類似sum的新函式,但this的值繫結到null
//並且第一個引數繫結到1,這個新的函式期望只傳人一個實參
var succ = sum.bind(null,1);
succ(2);  //=>3:x繫結到1,並傳入2作為實參y

function f(y,z){return this.x+y+z};  //另外一個做累加計算的函式
var g = f.bind({x:1},2);  //繫結this和y
g(3)   //=》6:this.x繫結到1,y繫結到2,z繫結到3複製程式碼

我們將這個方法另存為Function.prototype.bind,以便所有的函式物件都繼承它

if(!Function.prototype.bind){
  Function.prototype.bind = function(o/*,args*/){
    //將this和arguments的值儲存至變數中以便在後面巢狀的函式中可以使用它
    var self = this,boundArgs = arguments;

    //bind()方法的返回值是一個函式
    return function(){

      //建立一個實參列表,將傳人bind()的第二個及後續的實參都傳人這個函式
      var args = [],i;
      for(i=1;i<boundArgs.length;i++){
        args.push(boundArgs[i]);
      }
      for(i=0;i<arguments.length;i++){
        args.push(arguments[i]);
      }
      //現在將self作為o的方法來呼叫,傳人這些實參
      return self.apply(o,args);
    }
  }
}複製程式碼

bind()方法返回的函式是一個閉包

5.toString()方法

函式也有toString()方法,返回一個字串

6.Function()建構函式

不管是通過函式定義語句還是函式直接量表示式,函式的定義都要使用function關鍵字。但函式還可以通過Function()建構函式來定義

var f = new Function("x","y","return x*y;");
var f = function(x,y){return x*y;}複製程式碼

Function()建構函式可以傳人任意數量的字串實參。

var scope = "global";
function constructFunction(){
  var scope = "local";
  return new Function("return scope")  //無法捕獲區域性作用域
}
//這一行程式碼返回global,因為通過Function()建構函式所返回的函式使用的不是區域性作用域
constructFunction()();  //=>"global"複製程式碼

7.可呼叫物件

"可呼叫物件"(callable object)是一個物件,可以在函式呼叫表示式中呼叫這個物件。所有的函式都是可呼叫的,但並非所有的可呼叫物件都是函式

如果想檢測一個物件是否是真正的函式物件

function isFunction(x){
  return Object.prototype.toStirng.call(x) === "[object Function]"
}複製程式碼

8.函數語言程式設計

JavaScript並非函數語言程式設計語言

1.使用函式處理陣列

非函數語言程式設計風格

var data = [1, 1, 3, 5, 5];
var total = 0;
for (var i = 0; i < data.length; i++) {
    total += data[i];
}
var mean = total / data.length; //平均值

//計算標準差,首先計算每個資料減去平均數之後偏差的平方然後求和
total = 0;
for (var i = 0; i < data.length; i++) {
    var deviation = data[i] - mean;
    total += deviation * deviation;
}
var stddev = Math.sqrt(total / (data.length - 1));複製程式碼

使用陣列方法map()和reduce()來實現同樣的計算

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

var data = [1, 2, 3, 4, 1];
var mean = data.reduce(sum) / data.length;
var deviations = data.map(function(x) { return x - mean });
var stddev = Math.sqrt(deviations.map(square).reduce(sum) / (data.length - 1));複製程式碼

|版權宣告:本文為summer博主原創文章,未經博主允許不得轉載。

相關文章