JavaScript学习(六)数据类型–函数

  • Post author:
  • Post category:java


函数声明

  • 函数声明有三种方式: function命令、函数表达式和function构造函数
  • 重复声明的函数会被覆盖
//function命令
function print(s) {
  console.log(s);
  //函数表达式
  var print = function(s) {
  console.log(s);
};
//函数具名表达式
var print = function x(){
  console.log(typeof x);//x是只可在函数内部使用的该函数的别名
};
//function构造函数,接收多个参数,最后一个参数是函数体
var add = new Function(
  'x',
  'y',
  'return x + y'
);
}
  • 函数体内return返回函数的返回值
  • 在函数内部调用自身–递归
  • 函数可以被赋值给变量和对象
  • 函数名同变量名一样,在编译时会被提升到代码头部

函数的属性和方法

  • name属性,返回函数名或具名函数的别名

name可以用来获取参数的函数的名字,如果一个函数的某个参数是函数,可以通过name获得该参数函数的函数名

* length,返回函数定义需要传入的参数个数

* toString()方法返回函数的源码字符串(包括注释)

函数作用域

ES5中只有全局作用域和函数作用域,函数作用域中可以访问全局作用域的变量,函数作用域中的局部变量无法被外部访问,且函数作用域中定义的变量会覆盖全局作用域中的同名变量

ES6中,关于作用域引入了块级作用域和let等

* 函数内部变量不会变量提升

* 函数本身的作用域同变量

参数

JavaScript的函数的参数基本没什么特殊的地方,形参实参,引用还是传递

* arguments对象用来在函数内部获取参数,它是一个对象,但是很像数组(无法使用数组的方法),里面存放着传入的参数,可以用来修改传入的参数的值(非严格模式strict下),arguments的length获取函数调用时传入的参数的个数。arguments代有callee属性返回所对应的函数

闭包

闭包是JavaScript的一个重点和难点,它的难点在于作用域的理解;引入闭包的原因是为了获取函数作用域的局部变量。

为了获取函数内部的局部变量,只有在函数内部再定义一个函数,获取父函数的局部变量再返回,这样的子函数就叫闭包。

本质上,闭包就是将函数内部和函数外部连接起来的媒介。


闭包是一个难点,后续要进一步详细学习,写一个闭包的专题!

立即调用的函数表达式(IIFE)

在 Javascript 中,圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。

有时,我们需要在定义函数之后,立即调用该函数。这时,你不能在函数的定义之后加上圆括号,这会产生语法错误。

function(){ /* code */ }();

// SyntaxError: Unexpected token (

产生这个错误的原因是,function这个关键字即可以当作语句,也可以当作表达式。

// 语句

function f() {}

// 表达式

var f = function f() {}

为了避免解析上的歧义,JavaScript 引擎规定,如果function关键字出现在行首,一律解释成语句。因此,JavaScript 引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。

解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。

(function(){ /* code */ }());

// 或者

(function(){ /* code */ })();

上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称 IIFE。

注意,上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个 IIFE,可能就会报错。

// 报错

(function(){ /* code */ }())

(function(){ /* code */ }())

上面代码的两行之间没有分号,JavaScript 会将它们连在一起解释,将第二行解释为第一行的参数。

推而广之,任何让解释器以表达式来处理函数定义的方法,都能产生同样的效果,比如下面三种写法。

var i = function(){ return 10; }();

true && function(){ /* code */ }();

0, function(){ /* code */ }();

甚至像下面这样写,也是可以的。

!function () { /* code */ }();

~function () { /* code */ }();

-function () { /* code */ }();

+function () { /* code */ }();

通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

// 写法一

var tmp = newData;

processData(tmp);

storeData(tmp);

// 写法二

(function () {

var tmp = newData;

processData(tmp);

storeData(tmp);

}());

上面代码中,写法二比写法一更好,因为完全避免了污染全局变量。

eval命令

eval命令接受一个字符串作为参数,并将这个字符串当作语句执行。

eval没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。

为了防止这种风险,JavaScript 规定,如果使用严格模式,eval内部声明的变量,不会影响到外部作用域。

不过,即使在严格模式下,eval依然可以读写当前作用域的变量。


以上是数据类型–函数的学习笔记,关于闭包的部分会进一步详细的学习,IIFE和eval相关部分因为用处不大直接摘抄的下面链接的内容。

参考链接

JavaScript 教程



版权声明:本文为qq_36952874原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。