国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript || 函數(shù)

learn_shifeng / 2162人閱讀

摘要:每個函數(shù)表達(dá)式包括函數(shù)對象括號和傳入的實(shí)參組成。和作用都是動態(tài)改變函數(shù)體內(nèi)指向,只是接受參數(shù)形式不太一樣。在定義函數(shù)時(shí),形參指定為一個對象調(diào)用函數(shù)時(shí),將整個對象傳入函數(shù),無需關(guān)心每個屬性的順序。

函數(shù)

JavaScript中,函數(shù)指只定義一次,但可以多次被多次執(zhí)行或調(diào)用的一段JavaScript代碼。與數(shù)組類似,JavaScript中函數(shù)是特殊的對象,擁有自身屬性和方法

每個函數(shù)對象都有prototypelength屬性,bindapply()call()方法。函數(shù)的特殊性在于:可以通過函數(shù)調(diào)用執(zhí)行函數(shù)體中的語句。

函數(shù)是對象,所以可以賦值給變量、作為參數(shù)傳遞進(jìn)其他函數(shù)、掛載到對象上作為方法

1 函數(shù)定義

函數(shù)定義總共有三種方法:函數(shù)定義表達(dá)式、函數(shù)聲明語句和new Function()

但是new Function()使用很少,因?yàn)橥ㄟ^它創(chuàng)建的函數(shù)不使用詞法作用域,創(chuàng)建的函數(shù)都在全局作用域被調(diào)用。

函數(shù)定義表達(dá)式和函數(shù)聲明語句都利用關(guān)鍵字function來定義函數(shù)

// 函數(shù)聲明語句
function funcName([arg1 [, arg2] [..., argn]]) {
  statements
}
//函數(shù)定義表達(dá)式
var funcName = function([arg1 [, arg2] [..., argn]]) {
  statements
}

函數(shù)名標(biāo)識符funcName:引用新定義的函數(shù)對象

參數(shù)列表:函數(shù)中的參數(shù)與函數(shù)體中的局部變量相同,function(x)相當(dāng)于在函數(shù)體中var x;

{ statments }:構(gòu)成函數(shù)體的語句,調(diào)用函數(shù)后執(zhí)行的語句

1.1 變量提升

JavaScript中由var關(guān)鍵字聲明的變量存在變量提升:將變量聲明提升到作用域的頂部,但賦值仍保留在原處。所以函數(shù)聲明語句和函數(shù)定義表達(dá)式有本質(zhì)的區(qū)別

函數(shù)聲明語句:將函數(shù)聲明和函數(shù)的賦值都提升到作用域的頂部,在同一個作用域中可以出現(xiàn)調(diào)用在函數(shù)定義之前;

ECMAScript允許函數(shù)聲明語句作為頂級語句,可以出現(xiàn)在全局作用域中、也可以出現(xiàn)在嵌套函數(shù)中,但不能出現(xiàn)在循環(huán)、判斷、try-catch-finallywith語句中。函數(shù)定義表達(dá)式?jīng)]有限制

函數(shù)定義表達(dá)式:與var聲明的普通變量相同,只是將變量聲明提升到作用域頂部,但賦值仍然保留在原處,不能在定義前使用

//沒有顯式指明返回值的函數(shù),默認(rèn)返回undefined
//輸出對象o的每個屬性的名稱
function printPrps(o) {
  for(var prop in o) {
    console.log(prop + ": " + o[prop] + "
");
  }
}

//計(jì)算笛卡爾坐標(biāo)系中兩點(diǎn)間的距離
function distance(x1, y1, x2, y2) {
  var dx = x2 - x1;
  var dy = y2 - y1;
  return Math.sqrt(dx * dx + dy * dy);
}

// 計(jì)算階乘的遞歸函數(shù),x!是x到1間(步長1)的累乘
function factorial(x) {
  //遞歸結(jié)束標(biāo)志
  if(x <= 1) {
    return 1;
  }
  return x * factorial(x - 1);
}

//將函數(shù)表達(dá)式賦值給變量
var square = function (x) {return x * x;};
// 函數(shù)表達(dá)式可以包含函數(shù)名,在遞歸時(shí)很有用
//
var f = function fact(x) {
  if(x <= 1) {return 1;}
  return x * fact(x -1);
};

// 函數(shù)表達(dá)式可以作為參數(shù)傳遞給其他函數(shù)
data.sort(function(a, b) {return a  - b;});

//定義后立即調(diào)用函數(shù)表達(dá)式
var tensquare = (function(x) {return x * x;})(10);

1.2 嵌套函數(shù)

JavaScript中,函數(shù)可以嵌套在其他函數(shù)中。內(nèi)部函數(shù)可以訪問外部函數(shù)的局部變量和參數(shù)。

//  內(nèi)部函數(shù)square可以訪問到外部函數(shù)的參數(shù)a、b和局部變量c
function hypotenuse(a, b) {
  var c = 10;
  function square(x) {return x * x;}
  return Math.sqrt(square(a) + square(b) + square(c));
}
2 函數(shù)的調(diào)用

在定義函數(shù)時(shí),函數(shù)體中的代碼不會執(zhí)行,只有在調(diào)用函數(shù)時(shí),才執(zhí)行函數(shù)體中的語句。有四種方式可以調(diào)用函數(shù):

作為普通函數(shù)

作為對象的方法

作為構(gòu)造器函數(shù)

使用函數(shù)的call()apply()方法間接調(diào)用

2.1 調(diào)用函數(shù)

使用調(diào)用表達(dá)式來調(diào)用普通函數(shù),每個調(diào)用表達(dá)式由多個函數(shù)表達(dá)式組成。每個函數(shù)表達(dá)式包括函數(shù)對象、括號和傳入的實(shí)參組成。

每次調(diào)用會擁有本次調(diào)用的上下文this;在ES5非嚴(yán)格模式下,普通函數(shù)的this值是全局對象;在嚴(yán)格模式下是undefined

以函數(shù)形式調(diào)用的函數(shù)通常不使用this關(guān)鍵字

如果函數(shù)沒有顯式return語句返回一個值,默認(rèn)返回undefined

傳入的實(shí)參是由逗號分隔的0個或多個函數(shù)表達(dá)式

// 調(diào)用printProps()函數(shù),傳入對象作為實(shí)參即可
printPrps({x: 1});  
// 調(diào)用distance()函數(shù)
var total = distance(0, 0, 2, 1) + distance(2, 1, 3, 5);
// 調(diào)用factorial()函數(shù)
var probability = factorial(5) / factorial(13);

2.2 方法調(diào)用

方法是保存在JavaScript對象屬性中的函數(shù)。

對方法調(diào)用的參數(shù)和返回值處理與函數(shù)調(diào)用相同

方法調(diào)用由兩個部分組成:對象.屬性名(),其中屬性名是值為函數(shù)的屬性

方法調(diào)用中:調(diào)用上下文指調(diào)用方法的對象,使用this關(guān)鍵字引用

printProps({x: 1});
var total = distance(0, 0, 2, 1) + distance(2, 1, 3, 5);
var probability = factorial(5) / factorial(13);


var calculator = {  //對象字面量
  operand1: 1,
  operand2: 2,
  add: function() {
    //用this關(guān)鍵字指代當(dāng)前對象calculator
    this.result = this.operand1 + this.operand2;
  }
};
calculator.add();      //調(diào)用其add方法,使calculator對象獲得result屬性
calculator.result;   //   ==> 3

方法和this關(guān)鍵字是面向?qū)ο蟮暮诵模魏魏瘮?shù)作為方法調(diào)用時(shí)會傳入一個隱式實(shí)參(指代調(diào)用方法的對象this),基于this的方法可以執(zhí)行多種操作。

this是一個關(guān)鍵字,不是變量名、屬性名,JavaScript不允許為this賦值,但是可以將其賦值給其他變量

this沒有作用域限制,但是嵌套的函數(shù)不會從調(diào)用它的函數(shù)中繼承this

嵌套函數(shù)如果作為方法調(diào)用,this的值指向調(diào)用它的對象;

嵌套函數(shù)如果作為函數(shù)調(diào)用,this不是全局變量(ES5非嚴(yán)格模式),就是undefined(ES5嚴(yán)格模式)

嵌套函數(shù)的this并不指向調(diào)用它的外層函數(shù)的上下文

在外層函數(shù)中使用變量將外層函數(shù)的this對象及arguments屬性保存下來,在嵌套函數(shù)中便可以訪問

var o = {
  m: function() {
    var self = this;             // 保存this(指向o對象)在變量self中
    console.log(this === o);    // ==> true,this指向o對象
    f();                        //將f()作為函數(shù)調(diào)用

    function f() {
      console.log(this);  //  ==> window嚴(yán)格模式下,嵌套函數(shù)作為函數(shù)來調(diào)用,其this是undefined;非嚴(yán)格模式下是全局對象
      console.log(this === o);   //false,此處的this指向全局對象或undefined
      console.log(self === o);   //true,self指向外部函數(shù)的this值
    }
  }
};
o.m();       //調(diào)用對象o的方法m()

2.3 構(gòu)造函數(shù)調(diào)用

如果函數(shù)或方法調(diào)用前有關(guān)鍵字new,函數(shù)或者方法便作為構(gòu)造函數(shù)來調(diào)用。構(gòu)造函數(shù)會創(chuàng)建一個新對象,新對象繼承構(gòu)造函數(shù)的prototype屬性。

作為構(gòu)造器函數(shù)的調(diào)用,會將新創(chuàng)建的對象作為其調(diào)用上下文(this指向新創(chuàng)建的對象),在構(gòu)造器函數(shù)中使用this引用新創(chuàng)建的對象。

2.4 間接調(diào)用call()apply()

函數(shù)是對象,每個函數(shù)都有call()apply()兩個方法,作用是改變函數(shù)運(yùn)行時(shí)的上下文context--改變函數(shù)體內(nèi)部this的指向
,因?yàn)镴avaScript中有函數(shù)定義時(shí)上下文函數(shù)運(yùn)行時(shí)上下文函數(shù)中上下文可以改變的概念。

call()apply()作用都是動態(tài)改變函數(shù)體內(nèi)this指向,只是接受參數(shù)形式不太一樣。

call()需要將參數(shù)按順序傳遞進(jìn)函數(shù),并且知道參數(shù)的數(shù)量(參數(shù)數(shù)量確定時(shí)使用)

apply()將參數(shù)放在數(shù)組中傳進(jìn)函數(shù)(參數(shù)數(shù)量不確定時(shí)使用)

call()apply()存在的意義

在JavaScriptOOP中,使用原型實(shí)現(xiàn)繼承,call()apply()是用于不同對象間的方法復(fù)用。當(dāng)一個object沒有某個方法,但是另一個objAnother對象有,可以借助call()apply()使object可以操作objAnother對象的方法。

function Cat() {}
function Dog() {}

Cat.prototype = {
  food: "fish",
  say: function () {
    console.log("I love " + this.food);
  }
};

Dog.prototype = {food: "bone"};

var bCat = new Cat();
var bDog = new Dog();
bCat.say();                 //  ==> "I love fish"
bCat.say.call(bDog);          //==>"I love bone",bDog對象使用bCat對象的say方法,輸出自身的`this.food`屬性
3 函數(shù)的實(shí)參和形參

實(shí)參和形參是相對的概念,在函數(shù)定義時(shí)指定的參數(shù)叫做形參;在函數(shù)調(diào)用時(shí)傳入的參數(shù)叫做實(shí)參。對于需要省略的實(shí)參,可以使用null或undefined`作為占位符。

3.1 參數(shù)默認(rèn)值

如果調(diào)用函數(shù)時(shí),傳入的實(shí)參個數(shù)arguments.length小于定義時(shí)形參的個數(shù)arguments.callee.length,剩余的形參都被設(shè)置為undefined。對可以省略的值應(yīng)該賦一個合理的默認(rèn)值。

//  將對象obj中可枚舉的自身屬性追加到數(shù)組a中,并返回?cái)?shù)組a
//  如果省略a,則創(chuàng)建一個新數(shù)組,并返回這個新數(shù)組
function getPropertyNames(obj, /*optional*/ a) {
  if(!a) { a = []; }       //如果未傳入a,則使用新數(shù)組。
  // a = a || [];代替寫法更有語義
  for(var prop in obj) {
    if(!obj.hasOwnProperty(prop)) {continue;}
    a.push(prop);
  }
  return a;
}
// 調(diào)用,出入一個參數(shù)或兩個參數(shù)
var a = getPropertyNames(obj);   //將obj的屬性存儲到一個新數(shù)組中
getPropertyNames(obj, arr);   //將obj的屬性追加到arr數(shù)組中

函數(shù)中的參數(shù)等同于函數(shù)體內(nèi)的局部變量,具有函數(shù)的作用域。

3.2 參數(shù)對象

函數(shù)體內(nèi),標(biāo)識符arguments指向?qū)崊ο蟮囊茫瑢?shí)參對象是一個類數(shù)組對象,可以通過下標(biāo)訪問每個傳入的參數(shù)。

arguments僅是一個標(biāo)識符,嚴(yán)格模式下不能賦值;

應(yīng)用場景:函數(shù)包含固定個數(shù)的必須參數(shù),隨后包含不定數(shù)量的可選參數(shù)

// 可以接收任意個數(shù)的實(shí)參,
// 接收任意數(shù)量的實(shí)參,返回傳入實(shí)參的最大值,內(nèi)置的Math.max()方法功能類似
function max(/*...optional*/) {  //實(shí)參個數(shù)不能為0
  var maxNum = Number.NEGATIVE_INFINITY;   //將保存最大值的變量初始化
  for(var i in arguments) {
    maxNum = (arguments[i] > maxNum) ? arguments[i] : maxNum;
  }
  return maxNum;
}

3.3 calleecaller屬性

callee是ECMAScript規(guī)范中arguments對象的屬性:代表當(dāng)前正在執(zhí)行的函數(shù)。

caller是非標(biāo)準(zhǔn)的,只是瀏覽器基本都實(shí)現(xiàn)了這個屬性:帶表調(diào)用當(dāng)前函數(shù)的函數(shù)。

在嚴(yán)格模式中,對這兩個屬性讀寫都會產(chǎn)生錯誤

// arguments的callee屬性用在匿名函數(shù)的遞歸實(shí)現(xiàn)
var factorial = function(x) {
  if(x <= 1) {return 1;}
  return x * arguments.callee(x - 1);
}

3.4 將對象屬性作為參數(shù)

在定義一個函數(shù)時(shí),如果傳入的參數(shù)多于3個,在調(diào)用時(shí)按順序傳入會變得很麻煩。一種解決方式是傳入key/value形式的參數(shù),無需關(guān)注參數(shù)的順序。

在定義函數(shù)時(shí),形參指定為一個對象;

調(diào)用函數(shù)時(shí),將整個對象傳入函數(shù),無需關(guān)心每個屬性的順序。(性能會差,參數(shù)需要在對象中去查找值)

// 將原始數(shù)組的length復(fù)制到目標(biāo)數(shù)組
// 開始復(fù)制原始數(shù)組的from_start元素
// 并且將其復(fù)制至目標(biāo)數(shù)組的to_start中
// 參數(shù)復(fù)雜,調(diào)用時(shí)順序難以控制
function arrayCopy(array, from_start, target_arr, to_start, length) {
  // (原始數(shù)組, index, 目標(biāo)數(shù)組, index, length)
  {
    // 實(shí)現(xiàn)邏輯
  }
}

// 無需關(guān)心參數(shù)順序的版本,效率略低
// from_start和to_start默認(rèn)為0
function easyCopy(args) {
  arrayCopy(args.array,
            args.from_start || 0,
            args.target_arr,
            args.to_start || 0,
            args.length);
}
// easyCopy()的調(diào)用
var a = [1, 2, 3, 4];
var b = [];
easyCopy({array: a, target_arr: b, length: 4});

3.5 實(shí)參類型

JavaScript在定義函數(shù)時(shí)并未聲明形參類型,形參整體傳入函數(shù)體前不會做類型檢查,如果對傳入的實(shí)參有某種限制,最好在函數(shù)體內(nèi)增加類型檢查的代碼。

// 返回?cái)?shù)組或類數(shù)組a中元素的累加和
// 數(shù)組a中的元素必須是數(shù)字,null和undefined被忽略
// 類型檢查嚴(yán)格,但是靈活性很差
function sum(a) {
  if(isArrayLike(a)) {  // a是數(shù)組或類數(shù)組
    var result = 0;
    for(var i in a) {
      var element = a[i];
      if(element == null) {continue;}   //跳過null和undefined
      if(isFinite(element)) {
        result += element;
      } else {
        throw new Error("sum(): elements must be finite number");
      }
    }
    return result;
  } else {
    throw new Error("sum(): arguments must be array-like");
  }
}
4 函數(shù)作為值

函數(shù)定義及調(diào)用是JavaScript中的詞法特性;同時(shí)JavaScript中函數(shù)是一個對象:

可以賦值給變量

存儲在對象的屬性中或者數(shù)組的元素中

作為參數(shù)傳入另一個函數(shù):例如Array.sort()方法,用來對數(shù)組元素進(jìn)行排序。但是排序的規(guī)則有很多中,將具體規(guī)則封裝在函數(shù)中,傳入sort()。函數(shù)實(shí)現(xiàn)對任意兩個值都返回一個值,指定它們在排序好數(shù)組中的先后順序

// 簡單函數(shù)
function add(x, y) {return x + y;}
function subtract(x, y) {return x - y;}
function mutiply(x, y) {return x * y;}
function divide(x, y) {return x / y;}

// 這個函數(shù)以上面一個函數(shù)作為參數(shù),并傳入兩個操作數(shù),使用傳入的函數(shù)來調(diào)用
// 過程抽象:兩個數(shù)可以執(zhí)行加、減、乘、除四個操作,將四個運(yùn)算抽象為操作符,根據(jù)操作符不同,執(zhí)行不同的函數(shù)
function operate(operator, operand1, operand2) {
  return operator(operand1, operand2);
}
// 執(zhí)行(2 + 3) + (4 * 5)
var i = operate(add, 2, 3) + operate(mutiply, 4, 5);   // ==>25

// 另外一種實(shí)現(xiàn)
var  operators = {
  add: function(x, y) {return x + y;},
  subtrack: function(x, y) {return x + y;},
  mutiply: function(x, y) {return x + y;},
  divide: function(x, y) {return x + y;},
  pow: Math.pow
};
function operate2(operator, operand1, operand2) {
  if(typeof operators[operator] === "function") {
    return operators[operator](operand1, operand2);
  } else {
    throw "unknown operator";
  }
}
// 計(jì)算("hello" + " " + "world")的值
operate2("add", "hello", operate2("add", " ", "world"));   //  ==> "hello world"
operate2("pow", 10, 2);    // ==> 100

自定義屬性

函數(shù)是對象,可以擁有屬性。對于函數(shù)中的靜態(tài)變量,可以直接存入函數(shù)的屬性中。

// 初始化函數(shù)對象的計(jì)數(shù)器屬性,函數(shù)聲明會被提前,所以可以先給他的屬性賦值
uniqueInteger.counter = 0;
// 每次調(diào)用這個函數(shù),都會返回一個不同的整數(shù),使用counter屬性保存下次要返回的值
function uniqueInteger() {
  return uniqueInteger.counter++;   // 先返回計(jì)數(shù)器的值,再自增1
}
5 函數(shù)作為命名空間

JavaScript中只存在函數(shù)作用域和全局作用域,沒有塊級作用域。可以使用自執(zhí)行函數(shù)用作臨時(shí)命名空間,這樣不會污染全局變量。

(function() {/* 模塊代碼 */})();  //注意調(diào)用括號的位置,兩種寫法均可
(function() {/* 模塊代碼 */} ());
6 閉包

編程界崇尚優(yōu)雅簡潔唯美,很多時(shí)候如果你覺得一個概念很復(fù)雜,那么可能是你理錯了

閉包在JavaScript中,指內(nèi)部函數(shù)總是可以訪問其所在的外部函數(shù)中聲明的變量和參數(shù),即使外部函數(shù)被返回(調(diào)用結(jié)束)。

Closure使JavaScript使當(dāng)前作用域能夠訪問到外部作用域中的變量;

函數(shù)是JavaScript中唯一擁有自身作用域的結(jié)構(gòu),所以Closure的創(chuàng)建依賴于函數(shù)

6.1 如何理解
var scope = "global scope";
function checkScope() {
  var scope = "local scope";
  function f() {return scope;}
  return f;   //將函數(shù)對象返回
}
checkScope()();    //  ==>  "local scope"

在JavaScript中,每個函數(shù)在定義時(shí)會創(chuàng)建一個與之相關(guān)的作用域鏈,并且在程序執(zhí)行期間一直存在

外部函數(shù)checkScope有自身的作用域鏈,內(nèi)部函數(shù)f有自身多帶帶的的作用域鏈)

每次調(diào)用函數(shù)會創(chuàng)建一個新對象來保存參數(shù)和局部變量,并將其添加到作用域鏈。

當(dāng)函數(shù)返回時(shí),將綁定的新對象從作用域鏈上刪除。如果沒有其他變量引用該對象、或該對象沒有保存在某個對象的屬性中,它會被當(dāng)做垃圾回收。

如果沒有外部變量引用checkScope調(diào)用函數(shù)時(shí)創(chuàng)建的臨時(shí)對象,函數(shù)return后便被垃圾回收

如果checkScope定義有嵌套函數(shù)f,并將f作為返回值或保存在某個對象的屬性中。相當(dāng)于有一個外部引用指向嵌套函數(shù)。

f有自身的作用域鏈和保存參數(shù)與局部變量的對象

fcheckScope函數(shù)體內(nèi),可以訪問外部函數(shù)中所有的變量和參數(shù)

綜上所述:JavaScript中的函數(shù),通過作用域鏈和詞法作用域兩者的特性,將該函數(shù)定義時(shí)的所處的作用域中的相關(guān)函數(shù)進(jìn)行捕獲和保存,從而可以在完全不同的上下文中進(jìn)行引用

6.2 注意點(diǎn)

每個函數(shù)調(diào)用都有一個this值和arguments對象,需要在外部函數(shù)中用變量保存this值和arguments對象,Closure才可以訪問到外部函數(shù)的這兩個值。that = thisouterArguments = arguments

Closure是通過調(diào)用外部函數(shù)返回內(nèi)部嵌套函數(shù)創(chuàng)建的,每次調(diào)用外部函數(shù)都會創(chuàng)建一個Closure但是每個Closure共享外部函數(shù)聲明的變量,不會為每個Closure多帶帶創(chuàng)建一份外部作用域的副本

// 函數(shù)返回一個返回v的函數(shù)
function constFunc(v) {
  return function() {return v;};
}
//創(chuàng)建一個數(shù)組用來保存常數(shù)
var funcs = [];
for(var i=0; i<10; i++) {
  funcs[i] = constFunc(i);  // 創(chuàng)建了10個Closure,每個Closure的值不同,因?yàn)槊看蝹魅胪鈱雍瘮?shù)constFunc的值不同
}
console.log(funcs[6]());   //  ==> 6

function  constFuncs() {
  var funcs = [];
  for(var i=0; i<10; i++) {
    funcs[i] = function() {return i;};   // 創(chuàng)建10個Closure,但10個Closure在同一個外層函數(shù)constFuncs內(nèi),共享它的局部變量。
  }                                     // 10個Closure創(chuàng)建完畢后,i的值變?yōu)?,所以每個Closure返回的值都是0
  return funcs;
}
var foo = constFuncs();
console.log(foo[4]());    //  ==> 10

CLosure中部分資源不能自動釋放,容易造成內(nèi)存泄漏

內(nèi)存泄漏指由于疏忽或錯誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存(即不再利用的值或?qū)ο笠廊徽紦?jù)內(nèi)存空間)

7 函數(shù)的屬性、方法和構(gòu)造函數(shù)

JavaScript中函數(shù)是對象,每個函數(shù)都有lenghtprototype屬性;每個函數(shù)都有call()apply()bind()方法,并且可以利用函數(shù)的構(gòu)造函數(shù)Function()來創(chuàng)建函對象。

7.1 length屬性

函數(shù)對象的length屬性是只讀的,用于獲取定義函數(shù)時(shí)指定的形參個數(shù)。可以用來檢驗(yàn)定義的參數(shù)與傳入的參數(shù)是否相同。

// arguments.callee不能在嚴(yán)格模式下工作
function check(args) {
  var actual = args.length;
  var expected = args.callee.length;      // arguments.callee指代函數(shù)本身
  if(expected !== actual) {
    throw Error("Expected:" + expected + " args; got " + actual + "args;");
  }
}
// 測試函數(shù),只有傳入三個函數(shù)才不會報(bào)錯
function f(x, y, z) {
  check(arguments);
  return x + y + z;
}
7.2 prototype屬性

每個函數(shù)都有一個prototype屬性,指向一個原型對象的引用,每個函數(shù)的原型對象都不同

將函數(shù)用作創(chuàng)建對象的構(gòu)造器函數(shù)使用時(shí),新創(chuàng)建的對象會從函數(shù)的原型對象上繼承屬性

7.3 call()apply()

call()apply()用于動態(tài)改變this的指向,使對象的方法可以借用給別的對象使用。

7.4 bind()

bind()方法的作用是將函數(shù)綁定至某個對象,bind()方法的返回值是一個新的函數(shù)對象

f()函數(shù)調(diào)用bind()方法綁定至對象o,用變量g來接收bind()返回的函數(shù),(以函數(shù)調(diào)用形式)調(diào)用g時(shí),會將原始函數(shù)f當(dāng)做對象o的方法來使用。

var f = function(y) {return this.x + y;};
var o = {x: 2};
var g = f.bind(o);   // 將f()綁定到o對象上
console.log(g(6));    //  ==>  以函數(shù)調(diào)用的方式調(diào)用g(x),相當(dāng)于調(diào)用o.f(x)

// 實(shí)現(xiàn)bind()綁定
function bind(f, o) {
  if(f.bind) {   //如果bind()方法存在,使用bind()方法
    return f.bind(o);
  } else {
    return function() {  //利用apply()使o對象來調(diào)用f()方法,并且傳入類數(shù)組對象參數(shù)arguments
      return f.apply(o, arguments);  //arguments是調(diào)用綁定函數(shù)時(shí)傳入的參數(shù)
    };
  }
}

bind()第一個實(shí)參是要綁定方法的對象(本質(zhì)是將函數(shù)的this指向改為傳入的對象),同時(shí)后面的實(shí)參也會綁定至this,函數(shù)式編程中的currying 柯里化。

var sum = function(x, y) {return x + y;};
// 創(chuàng)建一個類似sum的新函數(shù),但是this綁定到null
// 并且第一個參數(shù)綁定為1,新的函數(shù)只期望傳入一個參數(shù)
var g = sum.bind(null, 1);  //  將sum的第一個參數(shù)x綁定為1
console.log(g(3));   // ==> 4,因?yàn)閤綁定為1,將3作為參數(shù)傳入y

function f(y, z) {return this.x + y + z;}
var g = f.bind({x: 2}, 3); // 將f函數(shù)綁定到對象{x: 2},將3綁定到函數(shù)的第一個參數(shù)y,新創(chuàng)建的函數(shù)傳入一個參數(shù)
console.log(g(1));   // ==>6

模擬實(shí)現(xiàn)bind()方法:bind()方法返回的是一個Closure

if(!Function.prototype.bind) {  //不支持bind方法
  Function.prototype.bind = function (o) {
    var self = this;     // 保存bind()中的this與arguments,便于在嵌套函數(shù)中使用
    var boundArgs = arguments;
    // bind()方法返回一個函數(shù)對象
    return function() {
      // 創(chuàng)建一個實(shí)參列表,將傳入bind()的第二個及以后的實(shí)參都傳入這個函數(shù)
      var args = [];
      // 傳入bind()函數(shù)的參數(shù)處理,從第二位開始
      for(var i=1; i

注意點(diǎn)

bind()方法的某些特性是上述模擬方法不能替代的。

bind()方法返回一個真正的函數(shù)對象,函數(shù)對象的length屬性是綁定函數(shù)的形參個數(shù)減去綁定的實(shí)參個數(shù)(length的值不能小于0

function f(y, z) {return this.x + y + z;}  // 綁定函數(shù)f的形參個數(shù)時(shí)2
var g = f.bind({x: 2}, 3); // 綁定的實(shí)參個數(shù)是1(從第二位開始是傳入綁定函數(shù)的實(shí)參),即將3傳遞給f的第一個參數(shù)y
g(1);   // ==> 6,繼續(xù)將1傳遞給函數(shù)f的形參z

ES5的bind()方法可以順帶做構(gòu)造函數(shù),此時(shí)將會忽略傳入bind()方法的this,原始函數(shù)以構(gòu)造函數(shù)的形式調(diào)用,其實(shí)參已經(jīng)綁定。

bind()方法返回的函數(shù)并不包含prototype屬性(普通函數(shù)的固有prototype屬性是不能刪除的);并且將綁定的函數(shù)用作構(gòu)造器函數(shù)時(shí)所創(chuàng)建的對象,從原始為綁定的構(gòu)造器函數(shù)中繼承prototype

如果將g()作為構(gòu)造函數(shù),其創(chuàng)建的對象與直接利用f當(dāng)做構(gòu)造函數(shù)創(chuàng)建的對象原型是同一個prototype

7.5 toString()方法

根據(jù)ECMAScript規(guī)范,函數(shù)的toString()方法返回一個字符串,字符串與函數(shù)聲明語句的語法有關(guān)。

大多數(shù)函數(shù)的toString()方法都返回函數(shù)的完整源碼

內(nèi)置函數(shù)的toString()方法返回一個類似"[native code]"的字符串作為函數(shù)體

7.6 Function()構(gòu)造函數(shù)

Function()構(gòu)造函數(shù)運(yùn)行JavaScript在運(yùn)行時(shí)動態(tài)創(chuàng)建并編譯函數(shù)

每次調(diào)用Function()構(gòu)造函數(shù)都會解析函數(shù)體,并創(chuàng)建新的函數(shù)對象。如果在循環(huán)中執(zhí)行Function(),會影響效率;

Function()創(chuàng)建的函數(shù)不使用詞法作用域,函數(shù)體的代碼編譯總在全局作用域執(zhí)行

Function()在實(shí)際編程中使用很少。

8 函數(shù)式編程

JavaScript并非函數(shù)式編程語言,但JavaScript中函數(shù)是對象,可以像對象一樣操控,所以可以應(yīng)用函數(shù)式編程技術(shù)

8.1 使用函數(shù)處理數(shù)組

假設(shè)有一個數(shù)組,元素都是數(shù)字,要計(jì)算所有元素的平均值與標(biāo)準(zhǔn)差。

非函數(shù)式編程風(fēng)格

var data = [1, 1, 3, 5, 5];
var total = 0;  //平均數(shù)是所有元素的和除以元素的個數(shù)
data.forEach(function(value) {
  total += value;
});
var mean = total / data.length;
//標(biāo)準(zhǔn)差:先計(jì)算每個元素與平均值的差的平方的和
var sum = 0;
data.forEach(function(value) {
  var tmp = value - mean;
  sum += tmp * tmp;
});
//標(biāo)準(zhǔn)差stddev
var stddev = Math.sqrt(sum / data.length-1);

函數(shù)式編程風(fēng)格,利用map()reduce()來實(shí)現(xiàn),抽象出兩個過程:

求平均值和標(biāo)準(zhǔn)差會用到求一個數(shù)組中所有元素的和:使用reduce()

求數(shù)組中每個元素的平方:使用map()

// 定義求和、求積兩個過程
var add = function(x, y) {return x + y;};
var square = function(x) {return x * x;};

var data = [1, 1, 3, 5, 5];
// reduc()實(shí)現(xiàn)數(shù)組求和
var avg = data.reduce(add) / data.length;
// map()實(shí)現(xiàn)差的平方,返回操作后的數(shù)組,再調(diào)用reduce()
var sum = data.map(function(value) {return value - avg;});
var stddev = Math.sqrt(sum.map(square).reduce(add) / (data.length - 1));

8.2 高階函數(shù)

高階函數(shù)higher-order function指操作函數(shù)的函數(shù),接收一個或多個函數(shù)作為參數(shù),并返回一個新函數(shù)。

// 高階函數(shù)not()返回一個新函數(shù),新函數(shù)將它的實(shí)參傳入f()
function not(f) {
  return function() {    // 返回一個新函數(shù)
    var result = f.apply(this, arguments);    // 調(diào)用f()
    return !result;   // 對結(jié)果求反
  };
}
var even = function (x) {   //判斷一個數(shù)是否是偶數(shù)
  return x % 2 === 0;
};
var odd = not(even);     // 一個新函數(shù),所做的事情與even()相反
[1, 1, 3, 5, 5].every(odd);    // ==> true每個元素都是奇數(shù)

// mapper()返回的函數(shù)的參數(shù)是數(shù)組,對每個元素執(zhí)行函數(shù)f()
// 返回所有計(jì)算結(jié)果組成的數(shù)組
function mapper(f) {
  return function(a) {
    return map(a, f);
  };
}
var increment = function(x) {return x + 1;};
var incrementer = mapper(increment);
incrementer([1, 2, 3]);
8.3 不完全函數(shù)

將一次完整的函數(shù)調(diào)用拆分為多次函數(shù)調(diào)用,每次傳入的實(shí)參都是完整實(shí)參的一部分,每個拆分開的函數(shù)叫做不完全函數(shù)partial function,每次函數(shù)調(diào)用叫做不完全函數(shù)調(diào)用partial application特點(diǎn)是每次調(diào)用都返回一個函數(shù),知道得到最終運(yùn)行結(jié)果為止

   if(!Function.prototype.bind) {  //不支持bind方法
  Function.prototype.bind = function (o) {
    var self = this;     // 保存bind()中的this與arguments,便于在嵌套函數(shù)中使用
    var boundArgs = arguments;
    // bind()方法返回一個函數(shù)對象
    return function() {
      // 創(chuàng)建一個實(shí)參列表,將傳入bind()的第二個及以后的實(shí)參都傳入這個函數(shù)
      var args = [];
      // 傳入bind()函數(shù)的參數(shù)處理,從第二位開始
      for(var i=1; i

函數(shù)f()bind()方法返回一個新函數(shù),給新函數(shù)傳入特定的上下文和一組指定的參數(shù),然后調(diào)用函數(shù)f()。傳入bind()的實(shí)參都是放在傳入原始參數(shù)的實(shí)參列表開始的位置。
但有時(shí)希望將傳入bind()的實(shí)參放在完整實(shí)參列表的右側(cè):

// 實(shí)現(xiàn)一個工具函數(shù),將類數(shù)組對或?qū)ο筠D(zhuǎn)化為真正的數(shù)組
// 將arguments對象轉(zhuǎn)化為真正的數(shù)組
function array(a, n) {return Array.prototype.slice.call(a, n || 0);}

// 這個函數(shù)的實(shí)參傳遞至左側(cè)
function partialLeft(f) {
  var args = arguments;   // 保存外部的實(shí)參數(shù)組
  return function() {    // 返回一個函數(shù)
    var a = array(args, 1);  // 開始處理外部的第一個args
    a = a.concat(array(arguments));  //然后增加所有的內(nèi)部實(shí)參
    return f.apply(this, a);   // 基于這個實(shí)參列表調(diào)用f()
  };
}

// 這個函數(shù)的實(shí)參傳遞至右側(cè)
function partialRight(f) {
  var args = arguments;   // 保存外部的實(shí)參數(shù)組
  return function() {    // 返回一個函數(shù)
    var a = array(arguments);  // 從內(nèi)部參數(shù)開始
    a = a.concat(array(args, 1));  //然后從外部第一個args開始添加
    return f.apply(this, a);   // 基于這個實(shí)參列表調(diào)用f()
  };
}

// 這個函數(shù)的實(shí)參被用作模板,實(shí)參列表中的undefined值都被填充
function partial(f) {
  var args = arguments;
  return function() {
    var a = array(args, 1);
    var i = 0, j = 0;
    // 遍歷args,從內(nèi)部實(shí)參填充undefined值
    for(; i -2:綁定第一個實(shí)參 2*(3-4)
partialRight(f, 2)(3, 4);   //  ==> 6:綁定最后一個實(shí)參 3*(4-2)
partial(f, undefined, 2)(3, 4);   //  ==> -6:綁定中間的實(shí)參 3*(2-4)

利用不完全函數(shù)的編程技巧,可以利用已有的函數(shù)來定義新的函數(shù)

8.4 記憶

在函數(shù)式編程中,把將上次計(jì)算記過緩存的技術(shù)叫做記憶memerization

本質(zhì)上是犧牲算法的空間復(fù)雜度以換取更優(yōu)的時(shí)間復(fù)雜度。因?yàn)樵诳蛻舳酥蠮avaScript代碼的執(zhí)行速度往往成為瓶頸。

// 返回f()的帶有記憶功能的版本(緩存上次計(jì)算結(jié)果)
// 只有在f()的實(shí)參字符串表示都不相同時(shí)才工作
function memorize(f) {
  var cache = {};   //將值保存在閉包內(nèi)
  return function() {
    // 將實(shí)參轉(zhuǎn)為字符串形式,并將其用作緩存的鍵
    var key = arguments.length + Array.prototype.join.call(arguments, ",");
    if(key in cache) {
      return cache[key];
    } else {
      return cache[key] = f.apply(this, arguments);
    }
  };
}
// memorize()創(chuàng)建新對象cache并將其保存在局部變量中,對于返回的函數(shù)來說它是私有的(在閉包中)。
// 返回的函數(shù)將它的實(shí)參數(shù)組轉(zhuǎn)化為字符串,并將字符串用作緩存對象的屬性名。如果在緩存中有這個值,則直接返回
// 如果沒有,調(diào)用既定函數(shù)對實(shí)參進(jìn)行計(jì)算,將計(jì)算結(jié)果緩存并返回

// 返回兩個整數(shù)的最大公約數(shù)
function gcd(a, b) {
  var t;
  if(a < b) { t= b; b = a; a = t; }
  while(b !== 0) {
    t = b;
    b = a %  b;
    a = t;
  }
  return a;
}
var gcdmemo = memorize(gcd);
gcdmemo(85, 187);   //  ==> 17
//注意寫一個遞歸函數(shù)時(shí),往往需要記憶功能
// 調(diào)用實(shí)現(xiàn)了記憶功能的遞歸函數(shù)
var factorial = memorize(function(n) {
  return (n <= 1)? 1 : n * factorial(n - 1);
});
factorial(5);   // ==> 120,同時(shí)緩存了1~4的值。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/81883.html

相關(guān)文章

  • JavaScript專題系列文章

    摘要:專題系列共計(jì)篇,主要研究日常開發(fā)中一些功能點(diǎn)的實(shí)現(xiàn),比如防抖節(jié)流去重類型判斷拷貝最值扁平柯里遞歸亂序排序等,特點(diǎn)是研究專題之函數(shù)組合專題系列第十六篇,講解函數(shù)組合,并且使用柯里化和函數(shù)組合實(shí)現(xiàn)模式需求我們需要寫一個函數(shù),輸入,返回。 JavaScript 專題之從零實(shí)現(xiàn) jQuery 的 extend JavaScritp 專題系列第七篇,講解如何從零實(shí)現(xiàn)一個 jQuery 的 ext...

    Maxiye 評論0 收藏0
  • JS程序

    摘要:設(shè)計(jì)模式是以面向?qū)ο缶幊虨榛A(chǔ)的,的面向?qū)ο缶幊毯蛡鹘y(tǒng)的的面向?qū)ο缶幊逃行┎顒e,這讓我一開始接觸的時(shí)候感到十分痛苦,但是這只能靠自己慢慢積累慢慢思考。想繼續(xù)了解設(shè)計(jì)模式必須要先搞懂面向?qū)ο缶幊蹋駝t只會讓你自己更痛苦。 JavaScript 中的構(gòu)造函數(shù) 學(xué)習(xí)總結(jié)。知識只有分享才有存在的意義。 是時(shí)候替換你的 for 循環(huán)大法了~ 《小分享》JavaScript中數(shù)組的那些迭代方法~ ...

    melody_lql 評論0 收藏0
  • JavaScript深入淺出

    摘要:理解的函數(shù)基礎(chǔ)要搞好深入淺出原型使用原型模型,雖然這經(jīng)常被當(dāng)作缺點(diǎn)提及,但是只要善于運(yùn)用,其實(shí)基于原型的繼承模型比傳統(tǒng)的類繼承還要強(qiáng)大。中文指南基本操作指南二繼續(xù)熟悉的幾對方法,包括,,。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。 怎樣使用 this 因?yàn)楸救藢儆趥吻岸耍虼宋闹兄豢炊?8 成左右,希望能夠給大家?guī)韼椭?...(據(jù)說是阿里的前端妹子寫的) this 的值到底...

    blair 評論0 收藏0
  • 10分鐘了解Javascript-天碼營

    摘要:然后將構(gòu)造函數(shù)的原型設(shè)為,便實(shí)現(xiàn)了對象繼承。首先,我們定義一個構(gòu)造函數(shù),并在其中定義一個局部變量。這里的是局部變量,其作用域仍然存在是閉包現(xiàn)象,而非對象屬性。 Javascript是動態(tài)的,弱類型的,解釋執(zhí)行的程序設(shè)計(jì)語言。 Javascript極其靈活,支持多種程序設(shè)計(jì)范式:面向?qū)ο蟆⒅噶钍健⒑瘮?shù)式。JavaSCript最初被用于瀏覽器腳本,現(xiàn)在已經(jīng)是所有主流瀏覽器的默認(rèn)腳本語言。瀏...

    trigkit4 評論0 收藏0
  • 學(xué)習(xí)React之前你需要知道的的JavaScript基礎(chǔ)知識

    摘要:和類在開始時(shí)遇到類組件,只是需要有關(guān)類的基礎(chǔ)。畢竟,中的條件呈現(xiàn)僅再次顯示大多數(shù)是而不是特定的任何內(nèi)容。 在我的研討會期間,更多的材料是關(guān)于JavaScript而不是React。其中大部分歸結(jié)為JavaScript ES6以及功能和語法,但也包括三元運(yùn)算符,語言中的簡寫版本,此對象,JavaScript內(nèi)置函數(shù)(map,reduce,filter)或更常識性的概念,如:可組合性,可重用...

    bitkylin 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<