JS中的函数

JS中的函数

函数的定义

一个功能模块或一段代码,只定义一次,但是可以在多个地方执行。

 

函数的参数化

形参 函数在定义的时候可以包含参数,称为形参,他可以在函数内部以局部变量的形式使用,如function sum(a,b){return a+b},a和b就是形参。

实参 函数在运行的时候接受的参数称为实参,sum(1,2)1.基础数据类型:值保存在变量中。

 

创建函数的六种方式

 

函数声明语句

function fn(){}

关键字 函数名 小括号 大括号 四个部分都是必填的,结尾不需要使用分号。

 

函数表达式语句

var fn=function(){}

函数名不是必填的,而且大部分情况下是不需要填写的,其他必填。

 

常用的三种情况

当作一个对象赋值给一个变量    

count = function(...) {…}

在一个对象上创建一个方法      

sum: function() {…}

当作一个回调函数            

reduce(function(…) {…})

 

声明和表达式的区别

声明语句 会发生声明提前,这里说的声明提前指的是整个函数的声明都提前,你可以先执行一个函数,并在之后的代码中进行声明,但是不建议这么做,而且建议将函数的定义都放在顶部,因为无论你将函数声明语句写在哪里,经过解释器的编译之后都会到达作用域的顶部。

表达式语句 相当于创建一个变量,并且把函数赋值给这个变量,这个时候只有变量会提升,在没有运行到赋值语句之前,这个变量的值为undefined。

 

微小的禁忌

在if for等语句块中定义函数只能使用表达式语句

原因1.在严格模式下不允许在语句块中使用函数声明语句。

原因2.由于涉及到声明提前的问题,所以不同的浏览器解析出来的结果可能是不相同的。    

 

(function() {

'use strict';

if (true) {
function ok() {

return 'true ok';

    }

  } else {

function ok() {

return 'false ok';

  }

}


console.log(typeof ok === 'undefined'); // => true

console.log(ok()); // ok is not defined"
})();

 (function() {

  'use strict';   
  var ok;

  if (true) {

    ok = function() {

    return 'true ok';

    };

  } else {

    ok = function() {
       return 'false ok';
    };

  }

  
console.log(typeof ok === 'function'); // => true

console.log(ok()); // => 'true ok'
})();

 

命名函数表达式

当函数没有一个名称(名称属性是一个空字符串)时这个函数是一个匿名函数。函数表达式创建的函数都是匿名函数,函数声明语句创建的函数都是命名函数

将函数声明语句和函数表达式语句写在一起就是命名函数表达式语句

exp:var a=functon b(){}

 

和函数表达式的区别

使用函数声明语句创建的函数会有name属性,值就是函数名。

使用匿名函数表达式将匿名函数赋值给变量,该变量没有name属性。

使用命名函数表达式将命名函数赋值给变量,该变量有name属性,值就是函数的名称。

通过命名函数表达式创建的函数,在函数体中具有和函数对象相同名称的一个变量,而且这个变量只能在函数体内访问到,如果在函数的外面,这个值为undefined。  

 

var a = function b(variable) {      
  console.log(typeof b === 'function'); // => true

  return typeof variable;
 }

console.log(a(3)); // => 'number'

console.log(a.name); // => 'b'

console.log(typeof b === 'undefined'); // => true

 

用途

函数内递归调用

在调试的时候通过这个名字来锁定问题出现的地方,减少在控制台输出anonoymous。

函数名有助于快速理解其功能

 

浏览器BUG

仅在IE8,命名函数表达式中的命名函数会像普通命名函数一样被解析,也就是说在IE8下的解析方式类似于先使用函数声明语句创建命名函数函数,然后再把这个函数赋值给一个变量,这会造成在同一个作用域下的命名污染

解决的办法:将函数的名称和前面变量或属性的名称写成同一个。

 

箭头函数

定义

使用一对小括号,括号内是一系列的参数(param1,param2,...,paramN),后面紧跟=>符号和{…},代码主体放置在这对大括号内。

 

使用

当箭头函数只有一个参数时,可以省略小括号

它只包含一个声明时,大括号可以省略。

即当创建的箭头函数只有一个参数并且主体只有一个声明时,小括号和大括号都可以省略。这有助于创建一个非常短的回调函数。

exp:numbers.some(item => item === 0)

 

特点

箭头函数不创建执行自己的上下文(没有this

箭头函数是一个匿名函数(没有name属性),所有的匿名函数都可以写成箭头函数

箭头函数内没有arguments对象

 

函数生成器

function* fn(){}    ES6提供的创建函数的方式。

 

区别

在function 后面增加了一个*

函数体中的return使用yield代替,而且可以存在多个yield(发音:又得)。

 

执行后的返回值

Generator对象(中文翻译:迭代器),该对象包含一个next()方法,运行next方法时,会从生成器的最顶端代码开始执行。一直执行到yield,并将结果以对象的形式返回,返回的对象包含value和done两个属性,value的值为yield后面的返回值,done是一个布尔类型,表示是否执行到最后。

 

为什么会有个done

函数生成器返回的Generator对象和普通函数最大的区别在于,每次执行完next,都会保存当时的状态,可以理解为代码运行的位置,下一次执行next的时候直接从这行代码开始执行,直到下一个yield,done表示的就是是否还有下一个yield。  

 

function* indexGenerator() {    var index = 0;    while(true) {        yield index++;    }}var g = indexGenerator();console.log(g.next().value); // => 0console.log(g.next().value); // => 1

 

用途

迭代器最大的特点就是保存状态,所以可以用于需要保存状态的任何地方

 

最简单的例子

聊天器,将多个句子放在数组里,每次点击页面就弹出一句。

传统的做法:创建一个全局变量来保存数组和对应的索引,这样会造成环境污染。

迭代器做法:使用函数生成器创建迭代器,然后一直next()。

 

ES5下的模拟

在ES5下可以通过闭包实现类似的功能,但是如果使用迭代器会让你的代码看起来更简洁。

使用babel等转换工具进行转换的结果就是通过闭包的来模拟迭代器。

 

函数构造器

var fn=new Function(一个字符串形式的参数)

 

最大的三个问题

1.函数体的内容要用字符串的形式作为参数,传递给构造函数,复杂而且容易出错。

2.因为函数体是以字符串的形式传入的,所以在执行的过程中会调用一次eval函数,eval函数对性能和安全性会造成很大的隐患。

3.创建出来的函数不能访问当前的作用域,一直是在全局创建的。

理解上述三点(尤其是第三点)比使用更加重要,实际开发中不会使用

 

开发中如何选择

 

构造器方式

绝对不推荐使用

 

函数生成器方式

需要保存状态的时候可以使用,但是记得需要使用对应工具转换

 

箭头函数

需要匿名函数,尤其是需要在闭包中使用this的时候推荐使用,可以大幅度减小代码量,也需要转换工具转换。

 

其余三种

声明函数,函数表达式和命名函数表达式,需要参考实际情况来定。

通常情况下,如果只是创建一个函数并且在后面进行调用,推荐使用声明函数,而且推荐在代码的顶部声明。

有些情况一定要使用函数表达式,如在if for等语句块中去创建函数,将对象的某个属性赋值为函数,或者在回调中使用,再次强调所有的函数表达式都可以使用箭头函数来代替

 

阅读原文下载《JS中的函数》

提取码234u

 
posted on 2019-06-13 16:32  悬弟  阅读(359)  评论(0编辑  收藏  举报