成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Javascript函數聲明與遞歸調用

開發 前端
Javascript的函數的聲明方式和調用方式已經是令人厭倦的老生常談了,但有些東西就是這樣的,你來說一遍然后我再說一遍。每次看到書上或博客里寫的Javascript函數有四種調用方式,我就會想起孔乙己:茴字有四種寫法,你造嗎?

Javascript的函數的聲明方式和調用方式已經是令人厭倦的老生常談了,但有些東西就是這樣的,你來說一遍然后我再說一遍。每次看到書上或博客里寫的Javascript函數有四種調用方式,我就會想起孔乙己:茴字有四種寫法,你造嗎?

盡管缺陷有一堆,但Javascript還是令人著迷的。Javascript眾多優美的特性的核心,是作為***對象(first-class objects)的函數。函數就像其他普通對象一樣被創建、被分配給變量、作為參數被傳遞、作為返回值以及持有屬性和方法。函數作為***對象,賦予了Javascript強大的函數式編程能力,也帶來了不太容易控制的靈活性。

1、函數聲明

變量式聲明先創建一個匿名函數,然后把它賦值給一個指定的變量:

  1. var f = function () { // function body }; 

通常我們不必關心等號右邊表達式的作用域是全局還是某個閉包內,因為它只能通過等號左邊的變量f來引用,應該關注的是變量f的作用域。如果f指向函數的引用被破壞(f = null),且函數沒有被賦值給任何其它變量或對象屬性,匿名函數會因為失去所有引用而被垃圾回收機制銷毀。

也可以使用函數表達式創建函數:

  1. function f() { // function body } 

與變量式不同的是,這種聲明方式會為函數的一個內置屬性name賦值。同時把函數賦值給當前作用域的一個同名變量。(函數的name屬性,configurable、enumerable和writable均為false)

  1. function f() { // function body }  
  2. console.log(f.name); // "f"  
  3. console.log(f); // f()  

Javascript變量有一個的特別之處,就是會把變量的聲明提前,表達式式的函數聲明,也會把整個函數的定義前置,因此你可以在函數定義之前使用它:

  1. console.log(f.name); // "f"  
  2. console.log(f); // f()  
  3. function f() { // function body }  

函數表達式的聲明會被提升到作用域頂層,試試下面的代碼,它們不是本文的重點:

  1. var a = 0;  
  2. console.log(a); // 0 or a()?  
  3. function a () {}  

Crockford建議永遠使用***種方式聲明函數,他認為第二種方式放寬了函數必須先聲明后使用的要求從而會導致混亂。(Crockford是一個類似于羅素口中用來比喻維特根斯坦的"有良心的藝術家"那樣的"有良心的程序員",這句話很拗口吧)

函數式聲明

  1. function f() {} 

看起來是

  1. var f = function f(){}; 

的簡寫。

  1. var a = function b(){}; 

的表達式,創建一個函數并把內置的name屬性賦值為"b",然后把這個函數賦值給變量a,你可以在外部使用a()來調用它,但卻不能使用b(),因為函數已被賦值給a,所以不會再自動創建一個變量b,除非你使用var b = a聲明一個變量b。當然這個函數的name是"b"而不是"a"。

使用Function構造函數也可用來創建函數:

  1. var f = new Function("a,b,c","return a+b+c;"); 

這種方式其實是在全局作用域內生成一個匿名函數,并把它賦值給變量f。

2、遞歸調用

遞歸被用來簡化許多問題,這需要在一個函數體中調用它自己:

  1. // 一個簡單的階乘函數  
  2. var f = function (x) {  
  3.     if (x === 1) {  
  4.         return 1;  
  5.     } else {  
  6.         return x * f(x - 1);  
  7.     }  
  8. };  

Javascript中函數的巨大靈活性,導致在遞歸時使用函數名遇到困難,對于上面的變量式聲明,f是一個變量,所以它的值很容易被替換:

  1. var fn = f;  
  2. f = function () {};  

函數是個值,它被賦給fn,我們期待使用fn(5)可以計算出一個數值,但是由于函數內部依然引用的是變量f,于是它不能正常工作了。

函數式的聲明看起來好些,但很可惜:

  1. function f(x) {  
  2.     if (x === 1) {  
  3.         return 1;  
  4.     } else {  
  5.         return x * f(x - 1);  
  6.     }  
  7. }  
  8. var fn = f;  
  9. f = function () {}; // may been warning by browser  
  10. fn(5); // NaN  

看起來,一旦我們定義了一個遞歸函數,便須注意不要輕易改變變量的名字。

上面談論的都是函數式調用,函數還有其它調用方式,比如當作對象方法調用。

我們常常這樣聲明對象:

  1. var obj1 = {  
  2.     num : 5,  
  3.     fac : function (x) {  
  4.         // function body  
  5.     }  
  6. };  

聲明一個匿名函數并把它賦值給對象的屬性(fac)。

如果我們想要在這里寫一個遞歸,就要引用屬性本身:

  1. var obj1 = {  
  2.     num : 5,  
  3.     fac : function (x) {  
  4.         if (x === 1) {  
  5.             return 1;  
  6.         } else {  
  7.             return x * obj1.fac(x - 1);  
  8.         }  
  9.     }  
  10. };  

當然,它也會遭遇和函數調用方式一樣的問題:

  1. var obj2 = {fac: obj1.fac};  
  2. obj1 = {};  
  3. obj2.fac(5); // Sadness  

方法被賦值給obj2的fac屬性后,內部依然要引用obj1.fac,于是…失敗了。

換一種方式會有所改進:

  1. var obj1 = {  
  2.      num : 5,  
  3.      fac : function (x) {  
  4.         if (x === 1) {  
  5.             return 1;  
  6.         } else {  
  7.             return x * this.fac(x - 1);  
  8.         }  
  9.     }  
  10. };  
  11. var obj2 = {fac: obj1.fac};  
  12. obj1 = {};  
  13. obj2.fac(5); // ok  

通過this關鍵字獲取函數執行時的context中的屬性,這樣執行obj2.fac時,函數內部便會引用obj2的fac屬性。

可是函數還可以被任意修改context來調用,那就是***的call和apply:

  1. obj3 = {};  
  2. obj1.fac.call(obj3, 5); // dead again  

于是遞歸函數又不能正常工作了。

我們應該試著解決這種問題,還記得前面提到的一種函數聲明的方式嗎?

  1. var a = function b(){};  

這種聲明方式叫做內聯函數(inline function),雖然在函數外沒有聲明變量b,但是在函數內部,是可以使用b()來調用自己的,于是

  1. var fn = function f(x) {  
  2.     // try if you write "var f = 0;" here  
  3.     if (x === 1) {  
  4.         return 1;  
  5.     } else {  
  6.         return x * f(x - 1);  
  7.     }  
  8. };  
  9. var fn2 = fn;  
  10. fn = null;  
  11. fn2(5); // OK  
  1. // here show the difference between "var f = function f() {}" and "function f() {}"  
  2. var f = function f(x) {  
  3.     if (x === 1) {  
  4.         return 1;  
  5.     } else {  
  6.         return x * f(x - 1);  
  7.     }  
  8. };  
  9. var fn2 = f;  
  10. f = null;  
  11. fn2(5); // OK  
  1. var obj1 = {  
  2.     num : 5,  
  3.     fac : function f(x) {  
  4.         if (x === 1) {  
  5.             return 1;  
  6.         } else {  
  7.             return x * f(x - 1);  
  8.         }  
  9.     }  
  10. };  
  11. var obj2 = {fac: obj1.fac};  
  12. obj1 = {};  
  13. obj2.fac(5); // ok  
  14.  
  15. var obj3 = {};  
  16. obj1.fac.call(obj3, 5); // ok  

就這樣,我們有了一個可以在內部使用的名字,而不用擔心遞歸函數被賦值給誰以及以何種方式被調用。

Javascript函數內部的arguments對象,有一個callee屬性,指向的是函數本身。因此也可以使用arguments.callee在內部調用函數:

  1. function f(x) {  
  2.     if (x === 1) {  
  3.         return 1;  
  4.     } else {  
  5.         return x * arguments.callee(x - 1);  
  6.     }  
  7. }  

但arguments.callee是一個已經準備被棄用的屬性,很可能會在未來的ECMAscript版本中消失,在ECMAscript 5中"use strict"時,不能使用arguments.callee。

***一個建議是:如果要聲明一個遞歸函數,請慎用new Function這種方式,Function構造函數創建的函數在每次被調用時,都會重新編譯出一個函數,遞歸調用會引發性能問題——你會發現你的內存很快就被耗光了。

原文鏈接:http://my.oschina.net/JackSparrow/blog/222221

責任編輯:林師授 來源: oschina
相關推薦

2023-12-04 07:09:53

函數遞歸python

2020-03-09 17:18:47

JavaScript技術函數

2011-05-30 16:11:46

Javascript

2009-07-31 14:26:38

JavaScript函C#函數

2024-06-25 15:31:21

2016-09-06 20:46:53

JavaScript遞歸與數列Web

2017-08-01 00:19:15

Javascript函數函數聲明

2012-03-27 10:08:08

JavaScript

2022-11-11 10:23:04

2009-03-13 09:39:34

JavaScript函數調用規則

2009-08-31 09:21:38

JavaScript調

2010-07-30 12:56:02

Flex調用JavaS

2023-02-01 08:11:40

系統調用函數

2024-01-23 11:21:24

2022-01-04 19:21:04

函數TypeScript重載

2024-03-04 22:35:07

Currying類型函數

2010-01-05 16:12:55

Javascript匿

2009-11-27 16:20:22

PHP遞歸調用

2010-10-27 13:55:01

memoization遞歸JavaScript

2023-10-26 11:39:54

Linux系統CPU
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 黄色在线免费网站 | 久久久精品高清 | 成人在线精品视频 | 九一在线 | av天空| 国际精品鲁一鲁一区二区小说 | 精品久久久久久一区二区 | 久久成人亚洲 | 老司机久久 | 国产成人99| 天天干天天爽 | 日韩精品久久久久 | 99精品欧美一区二区三区 | 免费视频99| 久久久久国产一级毛片 | 可以看黄的视频 | 成人美女免费网站视频 | 成人一区二区视频 | 欧美日韩精品在线一区 | 一本一道久久a久久精品综合 | 中文字幕一区在线 | 中文字幕不卡一区 | 成人欧美一区二区 | 国产亚洲精品一区二区三区 | 亚洲日韩中文字幕一区 | 国产激情视频网站 | 欧美一级二级在线观看 | 91 在线| 午夜精品一区二区三区在线 | 国内精品伊人久久久久网站 | 男女午夜免费视频 | 成人日批视频 | 九九九久久国产免费 | 一区二区三区中文 | 精品国产91| av不卡一区| 国产欧美在线 | 国产一区二区三区四区 | 黄视频网站免费观看 | 欧美aⅴ片 | 国产网站在线播放 |