真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

JavaScript函數(shù)的特性有哪些

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)JavaScript函數(shù)的特性有哪些,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

創(chuàng)新互聯(lián)是一家從事企業(yè)網(wǎng)站建設(shè)、成都網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、外貿(mào)網(wǎng)站建設(shè)、行業(yè)門戶網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)制作的專業(yè)的建站公司,擁有經(jīng)驗(yàn)豐富的網(wǎng)站建設(shè)工程師和網(wǎng)頁(yè)設(shè)計(jì)人員,具備各種規(guī)模與類型網(wǎng)站建設(shè)的實(shí)力,在網(wǎng)站建設(shè)領(lǐng)域樹立了自己獨(dú)特的設(shè)計(jì)風(fēng)格。自公司成立以來(lái)曾獨(dú)立設(shè)計(jì)制作的站點(diǎn)近1000家。

1 函數(shù)對(duì)象

JavaScript 函數(shù)就是對(duì)象。對(duì)象是名值對(duì)的集合,它還擁有一個(gè)連接到原型對(duì)象的鏈接。對(duì)象字面量產(chǎn)生的對(duì)象連接到 Object.prototype,而函數(shù)對(duì)象連接到 Function.prototype(這個(gè)對(duì)象本身連接到 Object.prototype)。每個(gè)函數(shù)在創(chuàng)建時(shí)會(huì)附加兩個(gè)隱藏屬性:函數(shù)的上下文以及實(shí)現(xiàn)函數(shù)的代碼。

函數(shù)對(duì)象在創(chuàng)建后會(huì)有一個(gè) prototype 屬性,它的值是一個(gè)擁有 constructor 屬性、且值既是該函數(shù)的對(duì)象。

因?yàn)楹瘮?shù)是對(duì)象,所以可以被當(dāng)做參數(shù)傳遞給其他函數(shù)。它也可以再返回函數(shù)。

2 函數(shù)字面量

函數(shù)可以通過(guò)字面量進(jìn)行創(chuàng)建:

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

這里沒有給函數(shù)命名,所以稱它為匿名函數(shù)。

一個(gè)內(nèi)部函數(shù)除了可以訪問(wèn)自己的參數(shù)和變量之外,還可以訪問(wèn)它的父函數(shù)的參數(shù)和變量。通過(guò)函數(shù)字面量創(chuàng)建的函數(shù)對(duì)象包含一個(gè)連接到外部上下文的連接,這被稱為閉包。它是 JavaScript 強(qiáng)大表現(xiàn)力的來(lái)源。

3 調(diào)用

調(diào)用一個(gè)函數(shù)會(huì)暫停當(dāng)前函數(shù)的執(zhí)行,它會(huì)傳遞控制權(quán)和參數(shù)給這個(gè)被調(diào)用的函數(shù)。

當(dāng)函數(shù)的實(shí)際參數(shù)的個(gè)數(shù)與形式參數(shù)的個(gè)數(shù)不匹配時(shí),不會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。如果實(shí)際參數(shù)的個(gè)數(shù)過(guò)多,那么超出的參數(shù)會(huì)被忽略;如果實(shí)際參數(shù)的個(gè)數(shù)過(guò)少,那么缺失的值會(huì)是 undefined。不會(huì)對(duì)參數(shù)類型進(jìn)行檢查,所以任何類型的值都可以被傳遞給任何參數(shù)。

3.1 方法調(diào)用模式

當(dāng)一個(gè)函數(shù)被保存為對(duì)象的一個(gè)屬性時(shí),就稱它為方法。當(dāng)方法被調(diào)用時(shí),this 被綁定到這個(gè)對(duì)象。如果調(diào)用表達(dá)式包含一個(gè)提取屬性的動(dòng)作(即包含一個(gè)”.” 點(diǎn)表達(dá)式或 “[]” 下標(biāo)表達(dá)式),那么它就是被當(dāng)做一個(gè)方法被調(diào)用。

var myObject = {
  value: 0,//屬性
  increment: function (inc) {//方法
    this.value += typeof inc === 'number' ? inc : 1;
  }
};
myObject.increment();
console.log(myObject.value);//1
myObject.increment(2);
console.log(myObject.value);//3

這里可以使用 this 來(lái)訪問(wèn)自己所屬的對(duì)象。通過(guò) this 可以取得它們所屬對(duì)象的上下文的方法被稱為公共方法。

3.2 函數(shù)調(diào)用模式

var add = function (a, b) {
  return a + b;
}
var sum = add(3, 4);//7;this 被綁定到全局對(duì)象

這里的 this 被綁定到全局對(duì)象,這其實(shí)是語(yǔ)言設(shè)計(jì)上的失誤!如果設(shè)計(jì)正確,那么當(dāng)內(nèi)部函數(shù)被調(diào)用時(shí),this 應(yīng)該被綁定到外部函數(shù)的 this 變量才是??梢赃@樣解決:為這個(gè)方法定義一個(gè)變量并給它賦值為 this,這樣內(nèi)部函數(shù)就可以通過(guò)這個(gè)變量訪問(wèn)到 this 啦,一般把這個(gè)變量命名為 that:

myObject.double = function () {
  var that = this;//讓內(nèi)部函數(shù)可以通過(guò)這個(gè)變量訪問(wèn)到 this (myObject)
  var helper = function () {
    that.value = add(that.value, that.value);
  };
  helper();//以函數(shù)形式調(diào)用 helper
};
myObject.double();//以方法形式調(diào)用 helper
console.log(myObject.value);//6

3.3 構(gòu)造器調(diào)用模式

JavaScript 是基于原型繼承的語(yǔ)言,所以對(duì)象可以從其他對(duì)象繼承它們的屬性。

如果在函數(shù)之前加上 new ,那么 JavaScript 就會(huì)創(chuàng)建一個(gè)連接到該函數(shù)的 prototype 屬性的新對(duì)象,而 this 會(huì)綁定到這個(gè)新對(duì)象。

/**
 * 構(gòu)造器調(diào)用模式(不推薦)
 */
var Quo = function (string) {//定義構(gòu)造器函數(shù);按照約定,變量名首字母必須大寫
  this.status = string;//屬性
};
/**
 * 為 Quo 的所有實(shí)例提供一個(gè)名為 get_status 的公共方法
 * @returns {*}
 */
Quo.prototype.get_status = function () {
  return this.status;
};
var myQuo = new Quo("confused");//定義一個(gè) Quo 實(shí)例
console.log(myQuo.get_status());//"confused"

按照約定,構(gòu)造器函數(shù)被保存在以大寫字母命名的變量中。因?yàn)槿绻{(diào)用構(gòu)造器函數(shù)時(shí)沒有加上 new,問(wèn)題很大,所以才以大寫字母的命名方式讓大家記住調(diào)用時(shí)要加上 new。

3.4 Apply 調(diào)用模式

因?yàn)?JavaScript 是函數(shù)式的面向?qū)ο笳Z(yǔ)言,所以函數(shù)可以擁有方法。

apply 方法可以構(gòu)建一個(gè)參數(shù)數(shù)組,然后再傳遞給被調(diào)用的函數(shù)。這個(gè)方法接收兩個(gè)參數(shù):要綁定給 this 的值以及參數(shù)數(shù)組。

//相加
var array = [3, 4];
var sum = add.apply(null, array);//7
console.log(sum);
//調(diào)用 Quo 的 get_status 方法,給 this 綁定 statusObject 上下文
var statusObject = {
  status: 'A-OK'
};
var status = Quo.prototype.get_status.apply(statusObject);
console.log(status);//'A-OK'

4 參數(shù)

當(dāng)函數(shù)被調(diào)用時(shí),會(huì)有一個(gè) arguments 數(shù)組。它是函數(shù)被調(diào)用時(shí),傳遞給這個(gè)函數(shù)的參數(shù)列表,包含那些傳入的、多出來(lái)的參數(shù)??梢岳眠@一點(diǎn),編寫一個(gè)無(wú)須指定參數(shù)個(gè)數(shù)的函數(shù):

//構(gòu)造一個(gè)能夠接收大量參數(shù),并相加的函數(shù)
var sum = function () {
  var i, sum = 0;
  for (i = 0; i < arguments.length; i += 1) {
    sum += arguments[i];
  }
  return sum;
};
console.log(sum(4, 5, 6, 7, 8, 9));//39

arguments 不是一個(gè)真正的數(shù)組,它只是一個(gè)類數(shù)組的對(duì)象,它擁有 length 屬性,但沒有數(shù)組的相關(guān)方法。

5 返回

return 語(yǔ)句可以讓函數(shù)提前返回。return 被執(zhí)行時(shí),函數(shù)會(huì)立即返回。

一個(gè)函數(shù)總會(huì)返回一個(gè)值,如果沒有指定這個(gè)值,它就會(huì)返回 undefined。

如果使用 new 前綴來(lái)調(diào)用一個(gè)函數(shù),那么它的返回值是:創(chuàng)建的一個(gè)連接到該函數(shù)的 prototype 屬性的新對(duì)象。

6 異常

異常是干擾程序正常流程的事故。發(fā)生事故時(shí),我們要拋出一個(gè)異常:

var add = function (a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw{
      name: 'TypeError',
      message: 'add needs numbers'
    };
  }
  return a + b;
}

throw 語(yǔ)句會(huì)中斷函數(shù)的執(zhí)行,它要拋出一個(gè) exception 對(duì)象,這個(gè)對(duì)象包含一個(gè)用來(lái)識(shí)別異常類型的 name 屬性和一個(gè)描述性的 message 屬性。也可以根據(jù)需要,擴(kuò)展這個(gè)對(duì)象。

這個(gè) exception 對(duì)象會(huì)被傳遞到 try 語(yǔ)句的 catch 從句:

var try_it = function () {
  try {
    add("seven");
  } catch (e) {
    console.log(e.name + ": " + e.message);
  }
};
try_it();

一個(gè) try 語(yǔ)句只會(huì)有一個(gè)捕獲所有異常的 catch 從句。所以如果處理方式取決于異常的類型,那么我們就必須檢查異常對(duì)象的 name 屬性,來(lái)確定異常的類型。

7 擴(kuò)充類型的功能

可以給 Function.prototype 增加方法來(lái)使得這個(gè)方法對(duì)所有的函數(shù)都可用:

/**
 * 為 Function.prototype 新增 method 方法
 * @param name 方法名稱
 * @param func 函數(shù)
 * @returns {Function}
 */
Function.prototype.method = function (name, func) {
  if (!this.prototype[name])//沒有該方法時(shí),才添加
    this.prototype[name] = func;
  return this;
};

通過(guò)這個(gè)方法,我們給對(duì)象新增方法時(shí),就可以省去 prototype 字符啦O(∩_∩)O~

有時(shí)候需要提取數(shù)字中的整數(shù)部分,我們可以為 Number.prototype 新增一個(gè) integer 方法:

Number.method('integer', function () {
  return Math[this < 0 ? 'ceil' : 'floor'](this);
});

它會(huì)根據(jù)數(shù)字的正負(fù)來(lái)決定是使用 Math.ceiling 還是 Math.floor

然后再為 String 添加一個(gè)移除字符串首尾空白的方法:

String.method('trim', function () {
  return this.replace(/^\s+|\s+$/g, '');
});

這里使用了正則表達(dá)式。

通過(guò)為基本類型增加方法,可以極大地提高 JavaScript 的表現(xiàn)力。因?yàn)樵屠^承的動(dòng)態(tài)本質(zhì),新的方法立刻被賦予所有的對(duì)象實(shí)例上(甚至包括那些在方法被增加之前的那些對(duì)象實(shí)例)

基本類型的原型是公用的,所以在使用其他類庫(kù)時(shí)要小心。一個(gè)保險(xiǎn)的做法是:只在確定沒有該方法時(shí)才添加它。

Function.prototype.method = function (name, func) {
  if (!this.prototype[name])//沒有該方法時(shí),才添加
    this.prototype[name] = func;
  return this;
};

8 遞歸

遞歸函數(shù)是會(huì)直接或間接地調(diào)用自身的函數(shù)。它會(huì)把一個(gè)問(wèn)題分解為一組相似的子問(wèn)題,而每一個(gè)子問(wèn)題都會(huì)用一個(gè)尋常的解來(lái)解決。

漢諾塔的游戲規(guī)則是:塔上有 3 根柱子和一套直徑不相同的空心圓盤。開始時(shí),源柱子上的所有圓盤都是按照從小到大的順序堆疊的。每次可以移動(dòng)一個(gè)圓盤到另一個(gè)柱子,但不允許把較大的圓盤放置在嬌小的圓盤之上。最終的目標(biāo)是把一堆圓盤移動(dòng)到目標(biāo)柱子上。我們可以用遞歸解決這個(gè)問(wèn)題:

/**
 * 漢若塔
 * @param disc 圓盤編號(hào)
 * @param src 源柱子
 * @param aux 輔助用的柱子
 * @param dst 目的柱子
 */
var hanoi = function (disc, src, aux, dst) {
  if (disc > 0) {
    hanoi(disc - 1, src, dst, aux);
    console.log('Move disc ' + disc + ' from ' + src + ' to ' + dst);
    hanoi(disc - 1, aux, src, dst);
  }
};
hanoi(3, 'Src', 'Aux', 'Dst');

這里演示了如果圓盤數(shù)為 3 的解法:

JavaScript函數(shù)的特性有哪些

這個(gè)問(wèn)題可以分解為 3 個(gè)子問(wèn)題。首先移動(dòng)一對(duì)圓盤中較小的圓盤到輔助的柱子上,從而露出下面較大的圓盤;然后再移動(dòng)下面的圓盤到目標(biāo)柱子。最后再將較小的圓盤從輔助柱子再移動(dòng)到目標(biāo)柱子上。通過(guò)遞歸調(diào)用自身來(lái)處理圓盤的移動(dòng),就可以解決這些子問(wèn)題。

上面這個(gè)函數(shù)最終會(huì)以一個(gè)不存在的圓盤編號(hào)被調(diào)用,但它不執(zhí)行任何操作,所以不會(huì)導(dǎo)致死循環(huán)。

遞歸函數(shù)可以非常高效地操作樹型結(jié)構(gòu)。比如文檔對(duì)象模型(DOM),我們可以在每次遞歸調(diào)用時(shí)處理指定樹的一小段:

/**
 * 從某個(gè)節(jié)點(diǎn)開始,按照 HTML 源碼中的順序,訪問(wèn)該樹的每一個(gè)節(jié)點(diǎn)
 * @param node 開始的節(jié)點(diǎn)
 * @param func 被訪問(wèn)到的每一個(gè)節(jié)點(diǎn),會(huì)作為參數(shù)傳入這個(gè)函數(shù),然后這個(gè)函數(shù)被調(diào)用
 */
var walk_the_DOM = function walk(node, func) {
  func(node);
  node = node.firstChild;
  while (node) {
    walk(node, func);
    node = node.nextSibling;
  }
};
/**
 * 查找擁有某個(gè)屬性的元素
 * @param att 屬性名稱字符串
 * @param value 匹配值(可選)
 * @return 匹配的元素?cái)?shù)組
 */
var getElementsByAttributes = function (att, value) {
  var results = [];
  walk_the_DOM(document.body, function (node) {
    var actual = node.nodeType === 1 && node.getAttribute(attr);
    if (typeof actual === 'string' && (actual === value) || typeof value != 'string') {
      results.push(node);
    }
  });
  return results;
};

注意: 深度遞歸的函數(shù)會(huì)因?yàn)槎褩R绯龆\(yùn)行失敗。比如一個(gè)會(huì)返回自身調(diào)用函數(shù)結(jié)果的函數(shù),它被稱為尾遞歸函數(shù)。

/**
 * 求階乘(帶尾遞歸的函數(shù))
 *
 * JavaScript 當(dāng)前沒有對(duì)尾遞歸進(jìn)行優(yōu)化,所以如果遞歸過(guò)深會(huì)導(dǎo)致堆棧溢出
 * @param i
 * @param a
 * @returns {*} 返回自身調(diào)用的結(jié)果
 */
var factorial = function factorial(i, a) {
  a = a || 1;
  if (i < 2) {
    return a;
  }
  return factorial(i - 1, a * i);
};
console.log(factorial(4));

9 作用域

作用域控制著變量和參數(shù)的可見性以及生命周期,它減少了名稱沖突,而且提供自動(dòng)內(nèi)存管理機(jī)制。

var foo = function () {
  var a = 3, b = 5;
  var bar = function () {
    var b = 7, c = 11;
    console.log("a:" + a + ";b:" + b + ";c:" + c);//a:3;b:7;c:11
    a += b + c;
    console.log("a:" + a + ";b:" + b + ";c:" + c);//a:21;b:7;c:11
  };
  console.log("a:" + a + ";b:" + b);//a:3;b:5
  bar();
  console.log("a:" + a + ";b:" + b);//a:21;b:5
};
foo();

JavaScript 支持函數(shù)作用域,但要注意一點(diǎn),就是在一個(gè)函數(shù)內(nèi)部的任何位置定義的變量,都這個(gè)函數(shù)的任何地方都是可見的!

**注意:**JavaScript 不支持塊級(jí)作用域。所以最好的做法是在函數(shù)體的頂部,聲明函數(shù)中可能會(huì)用到的所有變量。

10 閉包

作用域的好處是:內(nèi)部函數(shù)可以訪問(wèn)定義它們外部函數(shù)的參數(shù)和變量(除了 this 和 arguments)。

注意:內(nèi)部函數(shù)擁有比它的外部函數(shù)更長(zhǎng)的生命周期。

var myObject = (function () {
  var value = 0;//只對(duì) increment 與 getValue 可見
  return {
    increment: function (inc) {
      value += typeof inc === 'number' ? inc : 1;
    },
    getValue: function () {
      return value;
    }
  }
}());

通過(guò)一個(gè)函數(shù)的形式來(lái)初始化 myObject 對(duì)象,它會(huì)返回一個(gè)對(duì)象字面量。函數(shù)定義了一個(gè) value 變量,這個(gè)變量對(duì) increment 和 getValue 方法總是可見的,但它的函數(shù)作用域卻使得這個(gè)變量對(duì)其他的代碼來(lái)說(shuō)是不可見的!

/**
 * quo 構(gòu)造函數(shù)
 * @param status 私有屬性
 * @returns {{get_status: Function}}
 */
var quo = function (status) {
  return {
    get_status: function () {//方法
      return status;
    }
  };
};
var myQuo = quo("amazed");
console.log(myQuo.get_status());//amazed

當(dāng)我們調(diào)用 quo 時(shí),它會(huì)返回一個(gè)包含 get_status 方法的新對(duì)象,它的引用保存在 myQuo 中。所以即使 quo 函數(shù)已經(jīng)返回了,但 get_status 方法仍然享有訪問(wèn) quo 對(duì)象的 status 屬性的特權(quán)。get_status 方法訪問(wèn)的可是 status 屬性本身,這就是閉包哦O(∩_∩)O~

再看一個(gè)例子:

/**
 * 設(shè)置一個(gè) DOM 節(jié)點(diǎn)為黃色,然后漸變?yōu)榘咨?
 * @param node
 */
var fade = function (node) {
  var level = 1;
  var step = function () {
    var hex = level.toString(16);//轉(zhuǎn)換為 16 位字符
    node.style.backgroundColor = '#FFFF' + hex + hex;
    if (level < 15) {
      level += 1;
      setTimeout(step, 100);
    }
  };
  setTimeout(step, 100);
};
fade(document.body);

fade 函數(shù)在最后一行被調(diào)用后已經(jīng)返回,但只要 fade 的內(nèi)部函數(shù)又需要,它的變量就會(huì)持續(xù)保留。

注意:內(nèi)部函數(shù)能夠訪問(wèn)外部函數(shù)的實(shí)際變量:

var add_the_handlers_error = function (nodes) {
  var i;
  for (i = 0; i < nodes.length; i += 1) {
    nodes[i].onclick = function (e) {
      alert(i);//綁定的是變量 i 本身,而不是函數(shù)在構(gòu)造時(shí)的變量 i 的值?。?!
    }
  }
};

add_the_handlers_error 函數(shù)的本意是:想傳遞給每個(gè)事件處理器一個(gè)唯一的 i 值,但因?yàn)槭录幚砥骱瘮?shù)綁定了變量 i 本身,而不是它的值!

/**
 * 給數(shù)組中的節(jié)點(diǎn)設(shè)置事件處理程序(點(diǎn)擊節(jié)點(diǎn),會(huì)彈出一個(gè)顯示節(jié)點(diǎn)序號(hào)的對(duì)話框)
 * @param nodes
 */
var add_the_handlers = function (nodes) {
  var helper = function (i) {//輔助函數(shù),綁定了當(dāng)前的 i 值
    return function (e) {
      alert(i);
    };
  };
  var i;
  for (i = 0; i < nodes.length; i += 1) {
    nodes[i].onclick = helper(i);
  }
};

我們?cè)谘h(huán)之外向構(gòu)造一個(gè)輔助函數(shù),讓這個(gè)函數(shù)返回一個(gè)綁定了當(dāng)前 i 值的函數(shù),這樣就可以解決問(wèn)題啦O(∩_∩)O~

11 回調(diào)

假設(shè)用戶觸發(fā)了一個(gè)請(qǐng)求,瀏覽器向服務(wù)器發(fā)送這個(gè)請(qǐng)求,然后最終顯示服務(wù)器的響應(yīng)結(jié)果:

request = prepare_the_request();
response = send_request_synchronously(request);
display(response);

這種方式的問(wèn)題在于,網(wǎng)絡(luò)上的同步請(qǐng)求可能會(huì)導(dǎo)致客戶端進(jìn)入假死狀態(tài)。

所以建議使用異步請(qǐng)求,并為服務(wù)端的響應(yīng)創(chuàng)建一個(gè)回調(diào)函數(shù)。這個(gè)異步請(qǐng)求函數(shù)會(huì)立即返回,這樣我們的客戶端就不會(huì)被阻塞啦:

request = prepare_the_request();
send_request_asynchronously(request, function(response){
 display(response);
)};

一旦接收到服務(wù)端的響應(yīng),傳給 send_request_asynchronously 的匿名函數(shù)就會(huì)被調(diào)用啦O(∩_∩)O~

12 模塊模式

模塊是一個(gè)提供接口但卻隱藏狀態(tài)與實(shí)現(xiàn)的函數(shù)??梢允褂煤瘮?shù)和閉包來(lái)構(gòu)建模塊。通過(guò)函數(shù)來(lái)生成模塊,就可以不用全局變量啦。

假設(shè)我們想給 String 增加一個(gè) deentityify 方法。它會(huì)尋找字符串中的 HTML 字符,并把它們替換為對(duì)應(yīng)的字符。這就需要在對(duì)象中保存字符實(shí)體的名字和它對(duì)應(yīng)的字符。不能用全局變量,因?yàn)樗悄Ч?!如果定義在函數(shù)內(nèi)部,那么就會(huì)帶來(lái)運(yùn)行時(shí)的損耗,因?yàn)槊看螆?zhí)行函數(shù)時(shí),這個(gè)字面量就會(huì)被求值一次。所以理想的方式是把它放入閉包:

String.method('deentityify', function () {
  //字符實(shí)體表:映射字符實(shí)體的名字到對(duì)應(yīng)的字符
  var entity = {
    quot: '"',
    lt: '<',
    gt: '>'
  };
  //返回 deentityify 方法
  return function () {
    /**
     * 返回以'&'開頭 和 以';'結(jié)尾的子字符串
     */
    return this.replace(/&([^$;]+);/g, function (a, b) {
      var r = entity[b];//b:映射字符實(shí)體名字
      return typeof r === 'string' ? r : a;//a:原始字符串
    });
  };
}());

注意:最后一行是 (),所以我們是立即調(diào)用剛剛創(chuàng)建的函數(shù)!

模塊模式利用函數(shù)的作用域和閉包來(lái)創(chuàng)建被綁定對(duì)象和私有成員的關(guān)聯(lián)。這個(gè)例子中只有 deentityify 方法才有權(quán)訪問(wèn) entity (字符實(shí)體表)。

模塊模式是一個(gè)定義了私有變量和函數(shù)的函數(shù)。先利用閉包創(chuàng)建一個(gè)可以訪問(wèn)私有變量和函數(shù)的特權(quán)函數(shù),最后返回這個(gè)函數(shù),或者把它保存到一個(gè)可以被訪問(wèn)到的地方。

模塊模式做到了信息隱藏,是一種優(yōu)秀的設(shè)計(jì)實(shí)踐,特別適合于封裝應(yīng)用程序或者構(gòu)造單例哦O(∩_∩)O~

模塊模式還可以產(chǎn)生安全對(duì)象。比如我們想構(gòu)造一個(gè)能夠產(chǎn)生序列號(hào)的對(duì)象:

/**
 * 產(chǎn)生唯一字符串的對(duì)象(安全的對(duì)象)
 * 唯一字符串由 (前綴 + 序列號(hào)) 組成
 * @returns {{set_prefix: Function, set_seq: Function, gensym: Function}}
 */
var serial_number = function () {
  var prefix = '';
  var seq = 0;
  return {
    /**
     * 設(shè)置前綴
     * @param p
     */
    set_prefix: function (p) {
      prefix = String(p);
    },
    /**
     * 設(shè)置序列號(hào)
     * @param s
     */
    set_seq: function (s) {
      seq = s;
    },
    /**
     * 產(chǎn)生唯一的字符串
     * @returns {string}
     */
    gensym: function () {
      var result = prefix + seq;
      seq += 1;
      return result;
    }
  };
};
var seqer = serial_number();
seqer.set_prefix('Q');
seqer.set_seq(1000);
var unique = seqer.gensym();
console.log(unique);//Q1000

sequer 包含的方法沒有用到 this 或 that,所以很安全。sequer 是一組函數(shù)的集合,只有那些特權(quán)函數(shù)才能夠獲取或修改私有屬性哦O(∩_∩)O~

如果把 sequer.gensym 作為值傳遞給第三方函數(shù),那么那個(gè)函數(shù)可以使用它產(chǎn)生唯一的字符串,但卻不能通過(guò)這個(gè)函數(shù)改變 prefix 或 seq 的值。因?yàn)檫@個(gè)函數(shù)的功能只是“產(chǎn)生唯一的字符串”呀!

13 級(jí)聯(lián)

如果我們讓某些方法返回 this,那么就會(huì)啟動(dòng)級(jí)聯(lián)。在級(jí)聯(lián)中,我們可以在單條語(yǔ)句中依次調(diào)用同一個(gè)對(duì)象的多個(gè)方法,最著名的例子就是 jQuery 哦O(∩_∩)O~

形如:

getElement('myDiv')
 .move(350,150)
 .width(100);

級(jí)聯(lián)可以產(chǎn)生出極富表現(xiàn)力的接口。

14 柯里化

柯里化指的是:把函數(shù)與傳遞給它的參數(shù)結(jié)合,產(chǎn)生出新的函數(shù):

Function.method('curry', function () {
  var slice = Array.prototype.slice,
    args = slice.apply(arguments),//創(chuàng)建一個(gè)真正的數(shù)組
    that = this;
  return function () {
    return that.apply(null, args.concat(slice.apply(arguments)));
  };
});
var add1 = add.curry(1);
console.log(add1(6));//7

因?yàn)?arguments 不是真正的數(shù)組,所以沒有 concat 方法,因此我們使用 slice 創(chuàng)建出了真正的數(shù)組。

15 記憶

有時(shí)候可以把先前計(jì)算過(guò)的結(jié)果記錄在某個(gè)對(duì)象中,避免重復(fù)計(jì)算。

假設(shè)我們想通過(guò)遞歸來(lái)計(jì)算 Fibonacci 數(shù)列。一個(gè) Fibonacci 數(shù)字是之前的兩個(gè) Fibonacci 數(shù)字之和,最前面的兩個(gè)數(shù)字是 0 和 1:

var fiboncci = function () {
  var memo = [0, 1];//存儲(chǔ)結(jié)果(已經(jīng)計(jì)算過(guò)的值)
  var fib = function (n) {
    var result = memo[n];
    if (typeof result !== 'number') {
      result = fib(n - 1) + fib(n - 2);
      memo[n] = result;
    }
    return result;
  };
  return fib;
}();
for (var i = 0; i <= 10; i += 1) {
  console.log(i + ": " + fiboncci(i));
}

我們把計(jì)算結(jié)果存儲(chǔ)在 memo 數(shù)組中,它被隱藏在閉包里。函數(shù)被調(diào)用時(shí),它會(huì)先檢查計(jì)算結(jié)果是否已存在,如果存在就立即返回。

我們對(duì)這個(gè)例子進(jìn)行擴(kuò)展,構(gòu)造一個(gè)帶記憶功能的函數(shù):

/**
 * 帶記憶功能的函數(shù)
 * @param memo 初始的 memo 數(shù)組
 * @param formula 公式函數(shù)
 * @returns {Function}
 */
var memoizer = function (memo, formula) {
  var recur = function (n) {
    var result = memo[n];
    if (typeof result != 'number') {
      result = formula(recur, n);
      memo[n] = result;
    }
    return result;
  };
  return recur;
};

這個(gè)函數(shù)會(huì)返回一個(gè)可以管理 meno 存儲(chǔ)參數(shù)和在需要時(shí)調(diào)用 formula 函數(shù)的 recur 函數(shù)。recur 函數(shù)和它的參數(shù)會(huì)被傳遞給 formula 函數(shù)(就是公式)。是不是感覺有點(diǎn)繞,我們來(lái)看看實(shí)例就會(huì)清楚啦。

現(xiàn)在我們使用 memoizer 函數(shù)來(lái)重新定義 fibonacci 函數(shù):

/**
 * 斐波那契
 * @type {Function}
 */
var fibonacci = memoizer([0, 1], function (recur, n) {
  return recur(n - 1) + recur(n - 2);
});
console.log(fibonacci(10));//55

這樣清楚了吧,使用這個(gè)函數(shù)可以極大地減少我們的工作量哦,比如下面的這個(gè)階乘函數(shù):

/**
 * 階乘
 * @type {Function}
 */
var factorial = memoizer([1, 1], function (recur, n) {
  return n * recur(n - 1);
});
console.log(factorial(10));//3628800

上述就是小編為大家分享的JavaScript函數(shù)的特性有哪些了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


本文題目:JavaScript函數(shù)的特性有哪些
鏈接分享:http://weahome.cn/article/pseied.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部