JavaScript

JavaScript学习笔记(三)

xiaozheng · 6月9日 · 2021年

函数是 JavaScript 中的基本组件之一。 一个函数是 JavaScript的 一组执行任务或计算值的语句。要使用一个函数,你必须将其定义在你希望调用它的作用域内。

一个JavaScript 函数用function关键字定义,后面跟着函数名和圆括号。、

定义函数

函数声明

一个函数定义(也称为函数声明)由一系列function关键字组成,其主要结构如下:

  • 函数名称

  • 函数参数列表

  • 定义函数功能的JavaScript语句

    例如,以下代码ghgfhjfwenzhanglaixzizhenggegedboke@@@@定义了一个简单的数值平方函数:

    function square(number){
        return number*number;
    }
    square(4); //调用函数 return 16

    square是定义的函数名称,number是函数的一个参数,函数返回的是number平方后的结果。需要注意的是,函数声明只是创建了一个函数,这个函数并没有被调用,当你调用函数时它才开始执行。

    函数表达式

    虽然上面的函数声明在语法上是一个语句,但函数也可以由函数表达式创建。这样的函数可以是匿名的;它不必有一个名称。例如,函数square也可这样来定义:

    var square = function(number) { return number * number; };
    var x = square(4); // x gets the value 16

    函数表达式也可以提供函数名,并且可以用于在函数内部代指其本身,或者在调试器堆栈跟踪中识别该函数:

    var factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)};
    console.log(factorial(3));// 计算3!

函数作用域

在函数内定义的变量不能在函数之外的任何地方访问,因为变量仅仅在该函数的域的内部有定义(这就是局部变量)。相对应的,一个函数可以访问定义在其范围内的任何变量和函数。换言之,定义在全局域中的函数可以访问所有定义在全局域中的变量。在另一个函数中定义的函数也可以访问在其父函数中定义的所有变量和父函数有权访问的任何其他变量。

嵌套函数和闭包

可以在一个函数里面嵌套另外一个函数。嵌套(内部)函数对其容器(外部)函数ghgfhjfwenzhanglaixzizhenggegedboke@@@@是私有的。它自身也形成了一个闭包。一个闭包是一个可以自己拥有独立的环境与变量的表达式(通常是函数)。一个嵌套函数可以”继承“容器函数(父级函数)的参数和变量。换句话说,内部函数包含外部函数的作用域。

总结如下:

  • 内部函数只可以在外部函数中访问。

  • 内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量。

下面的例子展示了嵌套函数:

function addSquares(a, b) {
    function square(x) {
       return x * x;
   }
 return square(a) + square(b);
}
a = addSquares(2, 3); // returns 13
b = addSquares(3, 4); // returns 25
c = addSquares(4, 5); // returns 41

多层嵌套函数

函数可以被多层嵌套。例如,函数A可以包含函数B,函数B可以再包含函数C。B和C都形成了闭包,所以B可以访问A,C可以访问B和A。因此,闭包可以包含多个作用域;他们递归式的包含了所有包含它的函数作用域。这个称之为作用域链

命名冲突

当同一个闭包作用域下两个参数或者变量同名时,就会产生命名冲突。更近的作用域有更高的优先权,这就是作用域链。链的第一个元素就是最里面的作用域,最后一个元素便是最外层的作用域。

看以下的例子:

function outside() {
  var x = 5; //命名冲突
  function inside(x) {
    return x * 2; //命名冲突,调用时拥有最高优先权
   }
  return inside;
}

outside()(10); // returns 20

arguments对象

JavaScript函数有一个特别的地方就是形参与实参的数目可以不一致,例如:

function square(number){
    return number*number;
}
square(4,5); //return 16

调用函数传入两个参数时并不会报错,这是为什么呢?是因为JavaScghgfhjfwenzhanglaixzizhenggegedboke@@@@ript函数的实际参数会被保存在一个类似数组的arguments对象中。在函数内,你可以按如下方式找出传入的参数:

arguments[i]

使用arguments对象,你可以处理比声明的更多的参数来调用函数。这在你事先不知道会需要将多少参数传递给函数时十分有用。你可以用arguments.length来获得实际传递给函数的参数的数量,然后用arguments对象来取得每个参数。

例如,设想有一个用来连接字符串的函数。唯一事先确定的参数是ghgfhjfwenzhanglaixzizhenggegedboke@@@@在连接后的字符串中用来分隔各个连接部分的字符(译注:比如例子里的分号“;”)。该函数定义如下:

function myConcat(separator) {
  var result = ''; // 初始化一个字符串
  for (var i = 1; i < arguments.length; i++) {
   result += arguments[i] + separator;
  }
  return result;
}

你可以给这个函数传递任意数量的参数,ghgfhjfwenzhanglaixzizhenggegedboke@@@@它会将各个参数连接成一个字符串“列表”:

// returns "red,orange,blue"
myConcat( ",","red", "orange", "blue");//只把第一个字符传递给separator

// returns "elephant; giraffe; lion; cheetah; "
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

提示:arguments变量只是 类数组对象“,并不是一个数组。称其为类数组对象是说它有一个索引编号和length属性。所以,它并不拥有全部的Array对象的操作方法。