Maison  >  Article  >  interface Web  >  Explication détaillée des fonctions JavaScript

Explication détaillée des fonctions JavaScript

PHP中文网
PHP中文网original
2017-06-22 13:57:011026parcourir

Dans de nombreux langages traditionnels (C/C++/Java/C#, etc.), les fonctions existent en tant que citoyens de seconde classe. Vous ne pouvez déclarer une fonction qu'à l'aide de mots-clés du langage, puis l'appeler si vous en avez besoin. to Lorsqu'une fonction est passée en paramètre à une autre fonction, ou affectée à une variable locale, ou utilisée comme valeur de retour, des méthodes spéciales telles que des pointeurs de fonction et des délégués sont requises.

Dans le monde JavaScript, les fonctions sont des citoyens de premier ordre. Elles disposent non seulement de tous les moyens d'utiliser les fonctions traditionnelles (déclaration et appel), mais peuvent également être attribuées et transmises comme des simples. Valeurs Paramètres et retours, une telle fonction est également appelée fonction de première classe. De plus, la fonction en JavaScript agit également en tant que constructeur de la classe et est une instance de la classe Function. De telles identités multiples rendent les fonctions JavaScript très importantes.

1. Fonctions JavaScript d'entrée de gamme

Comme d'autres langages, les fonctions JavaScript suivent d'abord le principe de déclaration, puis utilisent uniquement des lettres, des chiffres, souligne ou $ et ne peut pas commencer par un chiffre. Il existe deux manières courantes de déclarer des fonctions :

Le code est le suivant :

 // 直接声明函数myfunc 
 function myfunc(/* arguments */) { 
 } 
   
 // 把匿名函数赋值给本地变量myfunc 
 var myfunc = function(/* arguments */) { 
 }

Notez qu'il existe des différences subtiles entre les deux manières ci-dessus de déclarer des fonctions : la première manière est une Les fonctions nommées, qu'elles soient déclarées avant l'appel, après l'appel, ou même dans une position qui ne sera pas exécutée (comme après l'instruction return ou dans une branche qui ne sera jamais vraie), sont accessibles dans toute la portée ; type La méthode consiste à attribuer une fonction anonyme à une variable à proprement parler, ce n'est pas une déclaration de fonction mais une expression de fonction. Cette fonction n'est accessible par aucun code avant l'affectation. lors de l'appel, sinon une erreur se produira lors de l'appel : "TypeError : undefined n'est pas une fonction". Par exemple :

Le code est le suivant :

 myfunc1(); // 能够正常调用,因为myfunc1采用直接声明的方式 
   
 function myfunc1() { 
 } 
   
 myfunc2(); // 出错 TypeError: undefined is not a function 
   
 var myfunc2 = function() { 
 };

La méthode d'appel de base de la fonction est la même que celle des langages traditionnels. Elle est appelée avec une paire de parenthèses : myfunc. (). Les fonctions JavaScript prennent également en charge les appels récursifs directs ou indirects. Par exemple, la fonction Fibonacci classique peut être implémentée en JavaScript comme ceci :

Le code est le suivant :

 function fib(n) { 
   if (n == 1 || n == 2) { 
     return 1; 
   } else { 
     return fib(n - 2) + fib(n - 1); 
   } 
 }

Les fonctions en JavaScript peuvent gérer des paramètres de longueur variable. À l'intérieur de la fonction, il y a une variable locale nommée arguments, qui est un objet de type tableau qui contient tous les paramètres transmis lors de l'appel. . Par exemple :

Le code est le suivant :

 function test() { 
   alert(arguments.length); 
 } 
   
 test(1); // 1 
 test(1, 'a'); // 2 
 test(true, [], {}); // 3 利用arguments可以实现类似C语言printf的功能,也可以用来实现方法的多态。

Fonctions JavaScript avancées

2.1 Fonctions anonymes et fonctions imbriquées

En JavaScript, vous pouvez déclarer une fonction sans nom, appelée fonction anonyme (Anonymouse Function). Dans le même temps, JavaScript permet également de déclarer des fonctions à l'intérieur de fonctions, appelées fonctions imbriquées. La portée d'une fonction imbriquée est la fonction parent entière.

Dans la section précédente sur la déclaration de fonction, nous avons vu une utilisation de fonctions anonymes et de fonctions imbriquées. Puisque les fonctions anonymes n'ont pas de nom, elles n'introduiront pas de nouvelles variables pour polluer le contexte, et apporteront de nouveaux effets de variable. , les fonctions anonymes sont donc souvent utilisées pour prévenir la pollution de l'environnement mondial.

Il existe un environnement global spécial (objet global) dans le runtime JavaScript. Cet objet stocke des fonctions et des variables globales. Dans le développement réel, plusieurs bibliothèques tierces ou plusieurs fichiers js sont souvent utilisés. introduire des déclarations répétées de variables ou de fonctions dans l'objet global, ce qui entraînera une confusion dans l'exécution du code. Par exemple, deux fichiers js sont introduits l'un après l'autre et chacun définit son propre journal de fonction pour un usage interne. La deuxième fonction introduite écrasera la première définition et ne générera aucune erreur. L'appel de la fonction log lors des exécutions ultérieures peut causer des problèmes. provoquant des erreurs. À l'heure actuelle, l'utilisation d'une fonction anonyme pour envelopper la logique dans l'intégralité du js peut éviter cette erreur. Cette méthode a été utilisée par la plupart des bibliothèques js open source.

Le code est le suivant :

 (function() { // 匿名函数 
   
 function log(msg) { 
     console.log(msg); 
 } 
   
 // 其他代码 
   
 }()); // 立即执行

Le code ci-dessus est un exemple simple. La portée de la fonction de journalisation est limitée à cette fonction anonyme. , et La fonction anonyme est entourée d'une paire de parenthèses (), formant une expression de fonction. La valeur de l'expression est une fonction. La paire de parenthèses suivante indique que la fonction sera exécutée immédiatement, permettant d'exécuter le code d'origine. normalement. Cependant, les fonctions déclarées de cette manière, les variables déclarées via var, etc. sont internes et ne sont accessibles par aucun code autre que les fonctions anonymes. Si vous devez exposer certaines fonctions comme interfaces, il existe plusieurs méthodes :

Le code est le suivant :

 var mylib = (function(global) { 
   
 function log(msg) { 
   console.log(msg); 
 } 
   
 log1 = log;  // 法一:利用没有var的变量声明的默认行为,在log1成为全局变量(不推荐) 
   
 global.log2 = log;  // 法二:直接在全局对象上添加log2属性,赋值为log函数(推荐) 
   
 return {  // 法三:通过匿名函数返回值得到一系列接口函数集合对象,赋值给全局变量mylib(推荐) 
    log: log
 }; 
   
 }(window));

2.2 Supérieur -fonctions d'ordre (fonction d'ordre élevé)

Si une fonction est utilisée comme paramètre ou valeur de retour, elle est appelée fonction d'ordre élevé. Toutes les fonctions en JavaScript peuvent être utilisées comme. fonctions d'ordre supérieur. C'est aussi des caractéristiques des fonctions du premier type. Ci-dessous, nous analyserons respectivement ces deux méthodes d’utilisation.

 代码如下:

 function negative(n) { 
   return -n; // 取n的相反值 
 } 
   
 function square(n) { 
   return n*n; // n的平方 
 } 
   
 function process(nums, callback) { 
   var result = []; 
   
   for(var i = 0, length = nums.length; i < length; i++) { 
     result[i] = callback(nums[i]); // 对数组nums中的所有元素传递给callback进行处理,将返回值作为结果保存 
   } 
   
   return result; 
 } 
   
 var nums = [-3, -2, -1, 0, 1, 2, 3, 4]; 
 var n_neg = process(nums, negative); 
 // n_neg = [3, 2, 1, 0, -1, -2, -3, -4]; 
 var n_square = process(nums, square); 
 // n_square = [9, 4, 1, 0, 1, 4, 9, 16];

以上代码展示了把函数作为参数传入另一个函数process调用的示例,在process函数的实现中,把callback作为一个黑盒子看待,负责把参数传给它,然后获取返回值,在调用之前并不清楚callback的具体实现。只有当执行到20行和22行时,callback才被分别代表negative或square,分别对每个元素进行取相反值或平方值的操作。

 代码如下:

 function generator() { 
   var i = 0; 
   return function() { 
     return i++; 
   }; 
 } 
   
 var gen1 = generator(); // 得到一个自然数生成器 
 var gen2 = generator(); // 得到另一个自然数生成器 
 var r1 = gen1(); // r1 = 0 
 var r2 = gen1(); // r2 = 1 
 var r3 = gen2(); // r3 = 0 
 var r4 = gen2(); // r4 = 1

上面的代码展示了把函数作为返回值的示例,generator是一个自然数生成器函数,返回值是一个自然数生成函数。每次调用generator时都会把一个匿名函数作为结果返回,这个匿名函数在被实际调用时依次返回每个自然数。在generator里的变量i在每次调用这个匿名函数时都会自增1,这其实就是一个闭包。下面我们来介绍一下闭包.
 
2.3 闭包(Closure)
闭包(Closure)并不是一个新鲜的概念,很多函数式语言中都使用了闭包。在JavaScript中,当你在内嵌函数中使用外部函数作用域内的变量时,就是使用了闭包。用一个常用的类比来解释闭包和类(Class)的关系:类是带函数的数据,闭包是带数据的函数。
闭包中使用的变量有一个特性,就是它们不在父函数返回时释放,而是随着闭包生命周期的结束而结束。比如像上一节中generator的例子,gen1和gen2分别使用了相互独立的变量i(在gen1的i自增1的时候,gen2的i并不受影响,反之亦然),只要gen1或gen2这两个变量没有被JavaScript引擎垃圾回收,他们各自的变量i就不会被释放。在JavaScript编程中,不知不觉就会使用到闭包,闭包的这个特性在带来易用的同时,也容易带来类似内存泄露的问题。例如:

 代码如下:

 var elem = document.getElementById(&#39;test&#39;); 
 elem.addEventListener(&#39;click&#39;, function() { 
   alert(&#39;You clicked &#39; + elem.tagName); 
 });

这段代码的作用是点击一个结点时显示它的标签名称,它把一个匿名函数注册为一个DOM结点的click事件处理函数,函数内引用了一个DOM对象elem,就形成了闭包。这就会产生一个循环引用,即:DOM->闭包->DOM->闭包...DOM对象在闭包释放之前不会被释放;而闭包作为DOM对象的事件处理函数存在,所以在DOM对象释放前闭包不会释放,即使DOM对象在DOM tree中删除,由于这个循环引用的存在,DOM对象和闭包都不会被释放。可以用下面的方法可以避免这种内存泄露:

代码如下:

 var elem = document.getElementById(&#39;test&#39;); 
 elem.addEventListener(&#39;click&#39;, function() { 
   alert(&#39;You clicked &#39; + this.tagName); // 不再直接引用elem变量 
 });

上面这段代码中用this代替elem(在DOM事件处理函数中this指针指向DOM元素本身),让JS运行时不再认为这个函数中使用了父类的变量,因此不再形成闭包。
闭包还会带来很多类似的内存泄露问题,只有在写代码的时候着重注意一下闭包,尽量避免此类的问题产生。
 
2.4 类构造函数
JavaScript的函数同时作为类的构造函数,因此只要声明一个函数就可以使用new关键字创建类的实例。

 代码如下:

function Person(name) { 
   this.name = name; 
   this.toString = function() { 
     return &#39;Hello, &#39; + this.name + &#39;!&#39;; 
   }; 
 } 
   
 var p = new Person(&#39;Ghostheaven&#39;);

 alert(p); // Hello, Ghostheaven! 在以上实例中Person函数作为类的构造函数使用,此时this指向新创建的实例对象,可以为实例增加属性和方法,关于详细的面向对象的JavaScript编程可以参考这篇文章。这里我想要说的是,JavaScript函数作为类构造函数使用时的返回值问题。

代码如下:

 function MyClass(name) { 
   this.name = name; 
   return name;  // 构造函数的返回值? 
 } 
   
 var obj1 = new MyClass(&#39;foo&#39;); 
 var obj2 = MyClass(&#39;foo&#39;); 
 var obj3 = new MyClass({}); 
 var obj4 = MyClass({});

上面的构造函数比较特别,有返回语句,那么obj1~obj4分别指向什么对象呢?实际结果是这样的:

复制代码 代码如下:

obj1 = MyClass对象
obj2 = &#39;foo&#39;
obj3 = {}
obj4 = {}

具体原因这篇文章有解释,本文不再赘述,由于带返回值的构造函数会产生奇怪的结果,因此不要在构造函数中调用有返回值的返回语句(空return可以)。

三、JavaScript函数妖怪级

欢迎来到妖怪级函数授课区,在这里会交给你如何淡定自如地面对老怪。。。
 
3.1 Function类
在JavaScript运行时中有一个内建的类叫做Function,用function关键字声明一个函数其实是创建Function类对象的一种简写形式,所有的函数都拥有Function类所有的方法,例如call、apply、bind等等,可以通过instanceof关键字来验证这个说法。
既然Function是一个类,那么它的构造函数就是Function(它本身也是Function类的对象),应该可以通过new关键字来生成一个函数对象。第一个妖怪来了,那就是如何用Function类构造一个函数。Function的语法如下:

代码如下:

new Function ([arg1[, arg2[, ... argN]],] functionBody)

其中arg1, arg2, ... argN是字符串,代表参数名称,functionBody也是字符串,表示函数体,前面的参数名称是可多可少的,Function的构造函数会把最后一个参数当做函数体,前面的都当做参数处理。

 代码如下:

 var func1 = new Function(&#39;name&#39;, &#39;return "Hello, " + name + "!";&#39;); 
 func1(&#39;Ghostheaven&#39;); // Hello, Ghostheaven!

以上方法就通过Function构造了一个函数,这个函数跟其他用function关键字声明的函数一模一样。
看到这儿,很多人可能会问为什么需要这样一个妖怪呢?“存在的即是合理的”,Function类有它独特的用途,你可以利用它动态地生成各种函数逻辑,或者代替eval函数的功能,而且能保持当前环境不会被污染*。
 
 
3.2 自更新函数(Self-update Function)
在很多语言中,函数一旦声明过就不能再次声明同名函数,否则会产生语法错误,而在JavaScript中的函数不仅可以重复声明,而且还可以自己更新自己。自己吃自己的妖怪来了!

 代码如下:

 function selfUpdate() { 
   window.selfUpdate = function() { 
     alert(&#39;second run!&#39;); 
   }; 
   
   alert(&#39;first run!&#39;); 
 } 
   
 selfUpdate(); // first run! 
 selfUpdate(); // second run! 这种函数可以用于只运行一次的逻辑,在第一次运行之后就整个替换成一段新的逻辑。

小结

JavaScript的函数灰常强大,在漂亮地解决很多问题的同时,也带来很多负面问题。妖怪级别的函数使用方法通常是一些鲜为人知的用法,除非特别必要不要轻易使用,否则会造成代码阅读困难,影响团队开发效率。
 
* 在新的ECMAScript中引入了严格模式,在严格模式下eval函数受到了很大的限制,也能够保证环境不被污染

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn