JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有七种。
数值
、字符串
、布尔值
这三种是原始类型。对象
则称为合成类型。undefined
和null
,为两个特殊值。ES6 添加了Symbol
类型的值。
一.概述
- 函数如何声明? ①使用function命令的方式; ②使用函数赋值给变量的方式; ③使用构造方式(基本不用) 具体见:函数声明
- 函数如何返回? ①使用return语句返回; ②若无返回,则返回
undefined
具体见:return - 函数名提升?①函数是第一等的公民,也和普通变量一样有变量提升的特性具体见:函数名提升
- 函数属性name,length,toString()?①name返回函数名称;②length形参长度;toString返回函数字符串具体见:函数的属性和方法
- JavaScript的作用域?①函数中定义为局部变量,其余地方定义为全局变量;具体见:函数作用域
- 参数省略和传参特性?①调用函数时,可以省略参数,缺省值为
undefined
;②基础数据为值传参,对象数组等为引用传参具体见:参数 - arguments对象?①arguments对象只能在函数内部使用;②用于获取所有实际调用参数具体见:arguments 对象
二.定义函数
- 函数式第一等的公民,
JavaScript 语言将函数看作一种值
,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。 - JavaScript 有三种声明函数的方法
1.函数的声明
Ⅰ.function命令的方式(Function Declaration)
-
function
命令声明函数,结构function 函数名(参数){函数体},如下范例function print(s) { console.log(s); } print('Hi Joey');
Ⅱ.函数表达式:函数赋值给变量的方式
-
将一个匿名函数赋值给变量,此种匿名函数又称函数表达式(Function Expression)
var print = function(s) { console.log(s); }; print('Hi Joey');
-
若函数表达式,添加了函数名,则函数名只能在函数体内使用,其的作用:①可以在函数体内部调用自身;②方便除错(除错工具显示函数调用栈时,将显示函数名,而不再显示这里是一个匿名函数)
var print = function x(){ console.log(typeof x); }; x // ReferenceError: x is not defined print() // function
-
如上:函数表达式赋值并添加函数名,这种定义函数的方式也比较常用
var f = function f() {};
Ⅲ.Function 构造函数
-
使用Function对象的构造函数声明,在形式参数中最后一个为函数体,此种定义方式几乎不会用到。如下:
//1. x,y为参数;return x + y为函数体 var add = new Function( 'x', 'y', 'return x + y' ); // 等同于 function add(x, y) { return x + y; } //2. 若只有一个参数,此参数为函数体 var foo = new Function( 'return "hello world";' ); // 等同于 function foo() { return 'hello world'; }
2.函数的重复声明
-
若函数重复声明,则后声明的函数覆盖先声明的函数(函数也是一个变量,所以符合变量提升规则)
function f() { console.log(1); } f() // 2 function f() { console.log(2); } f() // 2
3.函数调用,return语句,递归
-
函数调用,函数名加传入函数值就可以调用函数
-
return语句:一返回数据,中断函数执行,余下有语句也不执行。 二函数可没有return语句,表示不返回数据,实际返回
undefined
function add(x, y) { return x + y; } add(1, 1) // 2
-
递归(recursion),函数调用自身,满足一定条件返回
//计算斐波那契数列 function fib(num) { if (num === 0) return 0; if (num === 1) return 1; return fib(num - 2) + fib(num - 1); } fib(6) // 8
4.第一等公民和函数名提升
-
JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。
function add(x, y) { return x + y; } // 将函数赋值给一个变量 var operator = add; // 将函数作为参数和返回值 function a(op){ return op; } a(add)(1, 1) // 2
-
函数名提升
//一此种定义方式不会报错 f(); function f() {} //二此种定义方式报错 f(); var f = function (){}; // TypeError: undefined is not a function //上面的代码等同于下面的形式,f()只声明未定义 var f; f(); f = function () {};
三.函数的属性和方法
1.函数的name属性
-
函数的name属性返回函数的名字
//使用function关键字定义的 function f1() {} f1.name // "f1" //使用匿名函数赋值 var f2 = function () {}; f2.name // "f2" //使用命名函数赋值 var f3 = function myName() {}; f3.name // 'myName'
2.函数的length 属性
-
判断形参的个数
function f(a, b) {} f.length // 2
2.toString()函数
-
函数的
toString
方法返回一个字符串,内容是函数的源码 -
JavaScript 引擎提供的原生函数,toString()方法就返回原生代码的提示
//一自定义函数 function f() {} f.toString() // function f() {} //二原生函数 Math.sqrt.toString() // "function sqrt() { [native code] }"
四.函数作用域
1.函数作用域定义
-
JavaScript 只有
两种作用域
:一种是全局
作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在,叫做局部
作用域。ES6 又新增了块级作用域。 -
局部作用域可以引用全局作用域,全局作用域不可引用内部作用域。
-
函数内部定义的变量会在此作用域内覆盖全局变量
-
对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量
//一局部可以引用全局 var v = 1; function f() { console.log(v); } f() // 1 //二全局不可引用局部 function f2(){ var v2 = 1; } v2 // ReferenceError: v is not defined //三函数内部定义的变量覆盖全局变量 var v3 = 1; function f3(){ var v3 = 2; console.log(v3); } f3() // 2 v3 // 1 //四对于var命令来说:除去在方法中为局部变量,其他区块中都为全局变量 if (true) { var x = 5; } console.log(x); // 5
2.函数内部也存在变量提升
-
在函数内部,将所有区块中的变量提升到函数的首部
function foo(x) { if (x > 100) { var tmp = x - 100; } } // 等同于 function foo(x) { var tmp; if (x > 100) { tmp = x - 100; }; }
3.函数本身的作用域
-
函数本身也是基础类型,本身也有作用域,其的作用域就是与其他类型相同,就是声明时所在作用域
//范例一 var a = 1; var x = function () { console.log(a); }; function f() { var a = 2; //运行的是外部,外部函数a引用的变量值是1 x(); } f() // 1 //范例二 var x = function () { console.log(a); }; function y(f) { var a = 2; f(); } y(x) // ReferenceError: a is not defined //范例三:函数内部的定义函数 function foo() { var x = 1; function bar() { console.log(x); } return bar; } var x = 2; var f = foo(); f() // 1
五.参数
1.参数省略
-
函数参数不是必需的,JavaScript 允许省略参数,省略的参数默认传入undefined;参数顺序省略,不可省略前部参数,只传后部参数
function f(a, b) { return a; } f(1, 2, 3) // 1 f(1) // 1 f() // undefined f.length // 2 //省略第一参数会报错 f( , 1) // SyntaxError: Unexpected token ,(…)
2.参数传递方式
-
函数传值的方式有2种,数值传递和引用传递
-
数值传递:原始类型的值(数值、字符串、布尔值)的传递方式是传值传递(passes by value),在函数内修改参数值不会影响到外部数值;
-
引用传递:复合类型的值(数组、对象、其他函数)传递方式是传址传递(pass by reference),在函数内部修改数据,会影响原始值;
-
在引用传递的情况下,函数内部直接替换引用,则不会影响原值
//一数值传递 var p = 2; function f(p) { p = 3; } f(p); p // 2 //二引用传递 var obj = { p: 1 }; function f(o) { o.p = 2; } f(obj); obj.p // 2 //三直接替换引用 var obj = [1, 2, 3]; function f(o) { o = [2, 3, 4]; } f(obj); obj // [1, 2, 3]
3.同名参数
-
函数出现同名参数,则以最后出现的为准
function f(a, a) { console.log(a); } f(1, 2) // 2 //若调用时忽略参数,也取的最后一个的值 function f(a, a) { console.log(a); } f(1) // undefined //可以使用arguments函数获取第一个参数 function f(a, a) { console.log(arguments[0]); } f(1) // 1
4.arguments 对象
-
arguments
对象可以在函数内部获取传递到函数的所有参数,以完善JavaScript 允许函数有不定数目参数的特性 -
arguments
对象只有在函数内部才可以使用 -
不开启严格模式可修改函数的值,开启以后无法修改
//一取值 var f = function (one) { console.log(arguments[0]); console.log(arguments[1]); console.log(arguments[2]); } //二非严格模式可以修改参数 f(1, 2, 3) // 1 // 2 // 3 var f = function(a, b) { arguments[0] = 3; arguments[1] = 2; return a + b; } f(1, 1) // 5 //二严格模式不可修改参数 var f = function(a, b) { 'use strict'; // 开启严格模式 arguments[0] = 3; arguments[1] = 2; return a + b; } f(1, 1) // 2 //三获取真实参数长度 function f() { return arguments.length; } f(1, 2, 3) // 3 f(1) // 1 f() // 0
-
① length属性,获取传入时的长度; ② callee 属性 返回所对应的函数;③ 与数组的关系,arguments是一个对象可以与数组转换,从而获取数组对象操作数据的能力,例如(
forEach
,slice
)//一获取真实参数长度 function f() { return arguments.length; } f(1, 2, 3) // 3 f(1) // 1 f() // 0 //二获取所对应的函数 var f = function () { console.log(arguments.callee === f); } f() // true //三转换为数组对象 var args = Array.prototype.slice.call(arguments); // 或者 var args = []; for (var i = 0; i < arguments.length; i++) { args.push(arguments[i]); }