文章

JS函数

JS函数

JS 函数

JS 函数

函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数有唯一对应的返回值。

函数的声明 Function Declaration

JavaScript 有三种声明函数的方法:

function 命令

function 命令声明的代码区块,就是一个函数。function 命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 无参函数
function functionname() {
	// 执行代码
}
// 可以发送任意多的参数,由逗号 (,) 分隔:
function myFunction(var1,var2) {
	// 代码
}
// 带有返回值的函数
function myFunction() {
    var x=5;
    return x;
}

函数表达式 Function Expression

JavaScript 函数可以通过一个表达式定义。这个匿名函数又称函数表达式(Function Expression)

1
2
3
var print = function(s) {
  console.log(s);
};

采用函数表达式声明函数时,function 命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。

1
2
3
4
5
6
7
8
9
var print = function x(){
  console.log(typeof x);
};

x
// ReferenceError: x is not defined

print()
// function

加入了函数名 x。这个 x 只在函数体内部可用,指代函数表达式本身,其他地方都不可用。这种写法的用处有两个:

  • 一是可以在函数体内部调用自身
  • 二是方便除错(除错工具显示函数调用栈时,将显示函数名,而不再显示这里是一个匿名函数)

下面的形式声明函数也非常常见:

1
var f = function f() {};

需要注意的是,函数的表达式需要在语句的结尾加上分号,表示语句结束。而函数的声明在结尾的大括号后面不用加分号

Function() 构造函数(不推荐使用)

函数同样可以通过内置的 JavaScript 函数构造器(Function())定义。

1
2
3
4
5
6
var myFunction = new Function("a", "b", "return a * 
b");
var x = myFunction(4, 3);
// 也等同于
var myFunction = function (a, b) {return a * b};
var x = myFunction(4, 3);

在 JavaScript 中,很多时候,你需要避免使用 new 关键字。

可以传递任意数量的参数给 Function 构造函数,只有最后一个参数会被当做函数体,如果只有一个参数,该参数就是函数体。

1
2
3
4
5
6
7
8
var foo = new Function(
  'return "hello world";'
);

// 等同于
function foo() {
  return 'hello world';
}

Function 构造函数可以不使用 new 命令,返回结果完全一样。
总的来说,这种声明函数的方式非常不直观,几乎无人使用。

函数的重复声明

如果同一个函数被多次声明,后面的声明就会覆盖前面的声明

1
2
3
4
5
6
7
8
9
10
function f() {
  console.log(1);
}
f() // 2

function f() {
  console.log(2);
}
f() // 2
// 后一次的函数声明覆盖了前面一次。而且,由于函数名的提升,前一次声明在任何时候都是无效的,这一点要特别注意。

函数参数

JavaScript 函数对参数的值没有进行任何的检查。

默认参数

ES6 支持函数带有默认参数,就判断 undefined|| 的操作:

1
2
3
4
5
6
7
function myFunction(x, y = 10) {
    // y is 10 if not passed or undefined
    return x + y;
}
 
myFunction(0, 2) // 输出 2
myFunction(5); // 输出 15, y 参数的默认值

arguments 对象

JavaScript 函数有个内置的对象 arguments 对象。
arguments 对象包含了函数调用的参数数组。
通过这种方式你可以很方便的找到最大的一个参数的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
x = findMax(1, 123, 500, 115, 44, 88);
 
function findMax() {
    var i, max = arguments[0];
    
    if(arguments.length < 2) return max;
 
    for (i = 0; i < arguments.length; i++) {
        if (arguments[i] > max) {
            max = arguments[i];
        }
    }
    return max;
}

函数名的提升(Hoisting)

JavaScript 引擎将函数名视同变量名,所以采用 function 命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。所以,下面的代码不会报错。

1
2
3
f();

function f() {}

上面代码好像在声明之前就调用了函数 f。但是实际上,由于 “ 变量提升 “,函数 f 被提升到了代码头部,也就是在调用之前已经声明了。但是,如果采用函数表达式语句定义函数,JavaScript 就会报错。

1
2
3
4
5
6
7
8
9
f();
var f = function (){};
// TypeError: undefined is not a function

// 上面的代码等同于下面的形式。
var f;
f();
f = function () {};
// 代码第二行,调用f的时候,f只是被声明了,还没有被赋值,等于undefined,所以会报错。

采用 function 命令和 var 赋值语句声明同一个函数,由于存在函数提升,最后会采用 var 赋值语句的定义。

1
2
3
4
5
6
7
8
9
var f = function () {
  console.log('1');
}

function f() {
  console.log('2');
}

f() // 1

表面上后面声明的函数 f,应该覆盖前面的 var 赋值语句,但是由于存在函数提升,实际上正好反过来。

自调用函数

函数表达式可以 “ 自调用 “。
自调用表达式会自动调用。
如果表达式后面紧跟 () ,则会自动调用。
不能自调用声明的函数。
通过添加括号,来说明它是一个函数表达式:

1
2
3
(function () {
    var x = "Hello!!";      // 我将调用自己
})();

函数可作为一个值使用

JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。
由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为第一等公民。
函数作为一个值使用:

1
2
3
4
function myFunction(a, b) {
	return a * b;
}
var x = myFunction(4, 3);

函数可作为表达式使用:

1
2
3
4
function myFunction(a, b) {
	return a * b;
}
var x = myFunction(4, 3) * 2;

函数是对象

在 JavaScript 中使用 typeof 操作符判断函数类型将返回 “function” 。
但是 JavaScript 函数描述为一个对象更加准确。
JavaScript 函数有 属性方法

name 属性

函数的 name 属性返回函数的名字

1
2
function f1() {}
f1.name // "f1"

如果是通过变量赋值定义的函数,那么 name 属性返回变量名。

1
2
var f2 = function () {};
f2.name // "f2"

上面这种情况,只有在变量的值是一个匿名函数时才是如此。如果变量的值是一个具名函数,那么 name 属性返回 function 关键字之后的那个函数名。

1
2
var f3 = function myName() {};
f3.name // 'myName'

上面代码中,f3.name 返回函数表达式的名字。注意,真正的函数名还是 f3,而 myName 这个名字只在函数体内部可用。

name 属性的一个用处,就是获取参数函数的名字。

1
2
3
4
5
6
7
var myFunc = function () {};

function test(f) {
  console.log(f.name);
}

test(myFunc) // myFunc

函数 test 内部通过 name 属性,就可以知道传入的参数是什么函数

length 属性

函数的 length 属性返回函数预期传入的参数个数,即函数定义之中的参数个数。

1
2
3
function f(a, b) {}
f.length // 2
// 它的length属性就是定义时的参数个数。不管调用时输入了多少个参数,length属性始终等于2

arguments.length 属性返回函数调用过程接收到的参数个数:

1
2
3
4
5
6
7
function myFunction(a, b) {
  return arguments.length;
}
function myFunction(a, b) {
  return a * b;
}
var txt = myFunction.toString();

toString()

函数的 toString() 方法返回一个字符串,内容是函数的源码

箭头函数

ES6 新增了箭头函数。
箭头函数表达式的语法比普通函数表达式更简洁。

1
2
3
4
(参数1, 参数2, , 参数N) => { 函数声明 }

(参数1, 参数2, , 参数N) => 表达式(单一)
// 相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }

当只有一个参数时,圆括号是可选的:

1
2
3
(单一参数) => {函数声明}
// 或
单一参数 => {函数声明}

没有参数的函数应该写成一对圆括号:

1
() => {函数声明}

示例:

1
2
3
4
5
6
7
// ES5
var x = function(x, y) {
     return x * y;
}
 
// ES6
const x = (x, y) => x * y;

有的箭头函数都没有自己的 this。 不适合定义一个 对象的方法
当我们使用箭头函数的时候,箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的。
箭头函数是不能提升的,所以需要在使用之前定义。
使用 const 比使用 var 更安全,因为函数表达式始终是一个常量。
如果函数部分只是一个语句,则可以省略 return 关键字和大括号 {},这样做是一个比较好的习惯:

1
const x = (x, y) => { return x * y };

函数调用

JavaScript 函数有 4 种调用方式。每种方式的不同在于 this 的初始化。

this 关键字

一般而言,在 Javascript 中,this 指向函数执行时的当前对象。
this 含义见下面的定义

JS 闭包

闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。
直观的说就是形成一个不销毁的栈环境。

1
2
3
4
5
6
7
8
9
10
var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
 
add();
add();
add();
 
// 计数器为 3

eval

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

1
2
eval('var a = 1;');
a // 1

如果参数字符串无法当作语句运行,那么就会报错。

1
eval('3x') // Uncaught SyntaxError: Invalid or unexpected token

如果 eval 的参数不是字符串,那么会原样返回。

1
eval(123) // 123

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

1
2
3
4
5
6
7
(function f() {
  'use strict';
  var foo = 1;
  eval('foo = 2');
  console.log(foo);  // 2
})()
// 上面代码中,严格模式下,eval内部还是改写了外部变量,可见安全风险依然存在。

总之,eval 的本质是在当前作用域之中,注入代码。由于安全风险和不利于 JavaScript 引擎优化执行速度,一般不推荐使用。通常情况下,eval 最常见的场合是解析 JSON 数据的字符串,不过正确的做法应该是使用原生的 JSON.parse 方法。

javascript:void(0)

javascript:void(0) 中最关键的是 void 关键字, void 是 JavaScript 中非常重要的关键字,该操作符指定要计算一个表达式但是不返回值。
语法格式如下:

1
2
3
4
5
void func()
javascript:void func()
// 或者
void(func())
javascript:void(func())

示例:创建了一个超级链接,当用户点击以后不会发生任何事。

1
<a href="javascript:void(0)">单击此处什么也不会发生</a>

当用户链接时,void(0) 计算为 0,但 Javascript 上没有任何效果。
以下实例中,在用户点击链接后显示警告信息:

1
2
<p>点击以下链接查看结果:</p>
<a href="javascript:void(alert('Warning!!!'))">点我!</a>

以下实例中参数 a 将返回 undefined :

1
2
3
4
5
function getValue(){
   var a,b,c;
   a = void ( b = 5, c = 7 );
   document.write('a = ' + a + ' b = ' + b +' c = ' + c );
}

href="#"href="javascript:void(0)"的区别:

  • 包含了一个位置信息,默认的锚是#top 也就是网页的上端。

  • javascript:void(0), 仅仅表示一个死链接。
  • 在页面很长的时候会使用 # 来定位页面的具体位置,格式为:# + id
  • 如果你要定义一个死链接请使用 javascript:void(0)
1
如果你要定义一个死链接请使用 javascript:void(0) 
本文由作者按照 CC BY 4.0 进行授权