JavaScript是一门Web编程语言,用来实现网页的交互功能,它和HTML、CSS共同组成了个Web开发的基础工具集合,也是前端开发者必备的技能;学习JavaScript教程可以了解它在网页开发中的所有特性和相关概念,让我们能够更加快速的去开发Web应用。
在编程中,函数是程序中执行特定功能的一个独立模块。从这个意义上说,函数属于一类过程或例程。在某些编程语言中,过程与函数存在明显差异:函数会返回一个值,而过程仅执行操作但不返回值。在大多数编程语言和脚本语言中,存在预置于库中的内置函数。此外,您还可以编写自定义函数(称为"用户自定义函数"或UDF)来实现特定任务。
Contents:
JavaScript 函数
与其他编程和脚本语言类似,函数是JavaScript中的基本构建单元。函数在程序中重复使用,能够有效节省网页开发时间。要使用函数,必须在其所调用作用域内的某处进行定义。在JavaScript中定义自定义函数是一项简单的任务。
语法:JavaScript 函数
function functionName(argument1,argument2..argumentN)
{
// block of code in JavaScript
}
执行一下函数声明始终以关键字function开头,后接
声明JavaScript函数
例如,要声明一个计算所传入参数立方的函数,可通过以下两种方式之一实现:
注:在算术与代数中,数n的立方即其三次幂——该数自乘两次的结果:n3 = n × n × n。
示例 - 1:
function cube(n)
{
return n*n*n;
}
执行一下示例 - 2:
var cube = function(n)
{
return n*n*n;
};
执行一下函数"cube"接受一个名为n的参数。函数体内仅包含一条JavaScript语句,该语句指示将函数的参数(n)自乘两次后返回计算结果。此处返回语句用于返回运算后的数值。
风格指南:函数声明
优先使用函数声明而非函数表达式,因为函数声明具有名称标识,在调用堆栈中更易追溯。此外,函数声明的整体会被提升(hoisted),而函数表达式仅引用被提升,这可能导致与typeof检查相关的错误。当需要条件式声明时仍应选择函数表达式。箭头函数适用于需要词法this绑定的场景,但需注意其没有prototype属性且无法通过new实例化。
切勿在非函数块(如 if、while 等)中声明函数,而应将其赋值给一个变量。
ECMA-262 将块定义为语句列表。函数声明并非语句。
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}
执行一下调用JavaScript函数
我们已了解如何定义函数。定义函数仅为函数命名并指定其被调用时执行的操作。
调用函数将使用指定参数实际执行函数内部编写的指令。
函数的参数不仅限于字符串和数值,您还可以向函数传递完整的对象。
例如,名为abc()的函数可按如下方式调用。
abc();
执行一下无论是之前的示例一还是示例二,你均可通过如下所示的类似代码行来调用该函数:
cube(2);
执行一下示例 - 1:
函数的作用域为其所声明的上级函数(也就是在声明它的函数内部有效),若声明于顶层则为整个程序。您可以在调用位置之后声明函数,但仅在严格遵循函数声明语法(而非函数表达式)的情况下生效。函数 funcName(){}格式。以下函数有效:
以下代码无法运行。
函数作用域
函数可被访问的源位置称为函数作用域。当您在函数内部定义变量时,这些变量仅存在于该函数作用域内,因此无法在函数外部访问这些变量。在全局作用域中定义的函数能够访问全局作用域内定义的所有变量。在另一个函数内部定义的函数还能访问其父函数中定义的所有变量,以及父函数能够访问的任何其他变量。参考以下示例:
// The following variables are defined in the global scope
var x = 12, y = 25;
// This function is defined in the global scope
function add(){
return (x + y);
}
add(); // Returns 37
执行一下在上述示例中,x、y 和 add() 均在全局作用域中声明。因此 add() 函数可访问 x 和 y 的值,并返回 (x+y) 的计算结果。
// The following variables are defined in the global scope
var x = 12, y = 25;
// This function is defined in the global scope
function add(){
return (x + y);
}
add(); // Returns 37
执行一下在第二个示例中,存在名为calculation()的函数,其内部嵌套了add()函数。因此add()能够访问其父函数中定义的所有变量,并返回(x+y)的计算结果。
重新审视参数
传递给函数的参数可分为两类:原始数据类型参数和非原始数据类型参数。原始数据类型是预定义的数据类型,例如整数、字符和字符串均属于原始数据类型。非原始数据类型由用户创建,有时被称为"引用变量"或"对象引用",因为它们指向存储数据的内存地址。例如用户自定义对象、数组都属于非原始数据类型。
如果将基本类型参数按值传递给函数,且函数内部修改了该参数的值,这种变更不会在全局作用域或调用函数中体现。请参考以下示例:
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Primitive Parameters</title>
</head>
<body>
<script type="text/javascript">
function primitive_parameter(a)
{
a = 100;
console.log("Inside the function a is: " + a);
}
var a = 20;
console.log("Before calling the function a is: " + a);
primitive_parameter(a);
console.log("After calling the function a is: " + a);
</script>
</body>
若将对象作为非原始参数传入函数,且该函数修改了该对象的属性,则这些变更将在全局范围内生效,即这些变更在函数外部可见。参考以下示例:
// 定义对象
let car = { color: '红色', model: 'ABC' };
// 定义修改对象属性的函数
function changeColor(carObj) {
carObj.color = '蓝色';
}
// 调用函数
changeColor(car);
// 输出 'car' 颜色
console.log(car.color); // 输出: "蓝色"
function student(theObject)
{
theObject.name = "Sara";
}
var student1= {name: "Scott", standard: "V", roll_no: 1};
var x,y;
x = student1.name; // x gets the value "Scott"
student(student1);
y = student1.name; // y gets the value "Sara"
// (the name property was changed by the function)
执行一下若向参数重新赋值一个新对象,在函数外部不会产生任何影响,因为这更改的是参数本身的值而非对象属性的值。参考以下示例:
function student(theObject)
{
theObject = {name: "Sara", standard: "VI", roll_no: 1};
}
var student1= {name: "Scott", standard: "V", roll_no: 1};
var x, y;
x = student1.name; // x gets the value "Scott"
student(student1);
y = student1.name; // y still gets the value "Scott"
执行一下第一种情况下,对象 student1 被传递给 student 函数进行处理。该函数修改了其 name 属性,这个变更在全局作用域可见。第二种情况下,函数未修改对象本身的任何属性,而是创建了一个同名的新局部变量,尽管接收的参数与全局对象同名,但实际上不会对传入的全局对象产生任何影响。
风格指南:函数形参
切勿将参数命名为arguments。这会覆盖赋予每个函数作用域的arguments对象。
// bad
function nope(name, options, arguments) {
// ...stuff...
}
// good
function yup(name, options, args) {
// ...stuff...
}
执行一下始终将默认参数置于末尾:
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}
执行一下禁止使用Function构造函数创建新函数。通过此方式创建函数会以类似eval()的方式解析字符串,从而引发安全漏洞。
const riskyFunc = new Function('x', 'return x * 2;'); // 等价于 (x) => x * 2
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}
执行一下函数签名中的间距:
// bad
const f = function(){};
const g = function (){};
const h = function() {};
// good
const x = function () {};
const y = function a() {};
执行一下Closures(闭包)
闭包是JavaScript的重要特性之一。JavaScript允许函数的嵌套,即可以在一个函数内部声明另一个函数。JavaScript赋予内部函数完全访问外部函数内部定义的所有变量和函数,以及外部函数所能访问的其他变量和函数的权限。但外部函数无法访问内部函数内部定义的变量和函数,这为内部函数的变量提供了某种安全性。当内部函数以某种方式对外部函数以外的任何作用域可用时,就会创建一个闭包。示例如下:
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Closure Example</title>
</head>
<body>
<script>
function w3r_outer() {
var str;
str = "w3resource";
function w3r_inner() {
console.log(str);
}
w3r_inner();
}
w3r_outer();
</script>
</body>
使用arguments对象
通过arguments对象,您可以用多于形参数量的实参调用函数,这在提前未知传入参数数量时非常实用。arguments对象中包含的各个参数可通过访问数组元素的相同方式被检索。您可通过以下形式访问每个参数:
arguments[i]
执行一下其中i表示参数的位置或序号,从零开始计数。因此传递给函数的参数依次为arguments[0]、arguments[1]、arguments[2]...以此类推。arguments对象的length属性包含传递给函数的参数总数。
以下示例演示了arguments属性的用法。传入任意数量的数值作为参数,该函数将累加所有数字并返回结果。
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JavaScript arguments Example</title>
</head>
<body>
<script>
function adding_numbers(numbers) {
var result = 0, i;
// iterate through arguments
for (i = 0; i < arguments.length; i++) {
result += arguments[i];
}
return result;
}
console.log(adding_numbers(1,2,3,-4));
</script>
<body>
JavaScript:递归函数
JavaScript 函数甚至可以递归,即函数能调用自身。以下网页文档接收数字并计算阶乘:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JavaScript: Recursive function.</title>
<script type="text/javascript">
function factorial(n)
{
if ((n==0) || (n==1))
return 1;
else
facn = (n * factorial(n-1))
return facn;
}
</script>
</head>
<body>
<h1 style="color: red">JavaScript Recursive function example</h1>
<hr />
<script type="text/javascript">
function factcal()
{
n=document.form1.text1.value;
result = factorial(n);
alert(" Factorial of "+n+" = "+result);
}
</script>
<form name="form1" action="#">
Input a number :
<input type="text" name="text1" size="5" />
<br />
<input type="button" value="Result" onclick="factcal()" />
</form>
</body>
</html>
执行一下完整示例:求数字的立方值
以下网页文档接收用户输入的数字,并通过值传递方式将其传递给函数cube(),返回该数字的立方。函数内部通过正则表达式验证输入值是否为数字:若为数字则计算其立方并通过alert()显示结果;若非数字则通过alert()显示错误信息。
HTML代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Cube of a Number</title>
<link rel='stylesheet' href='form-style.css' type='text/css' />
</head>
<body onload='document.form1.text1.focus()'>
<div class="mail">
<h2>Input a number and get it's Cube</h2>
<form name="form1" action="#">
<ul>
<li><input type='text' name='text1'/></li>
<li> </li>
<li><input type="submit" name="submit" value="Submit" onclick="cube(document.form1.text1)" /></li>
<li> </li>
</ul>
</form>
</div>
<script src="cube-number.js"></script>
</body>
</html>
执行一下CSS代码
li {list-style-type: none;
font-size: 16pt;
}
.mail {
margin: auto;
padding-top: 10px;
padding-bottom: 10px;
width: 400px;
background : #D8F1F8;
border: 1px soild silver;
}
.mail h2 {
margin-left: 38px;
}
input {
font-size: 20pt;
}
input:focus, textarea:focus{
background-color: lightyellow;
}
input submit {
font-size: 12pt;
}
.rq {
color: #FF0000;
font-size: 10pt;
}
执行一下JavaScript代码
function cube(inputtxt)
{
var numbers = /^[-+]?[0-9]+$/;
var x;
if(inputtxt.value.match(numbers))
{
alert('Cube of '+inputtxt.value+' is '+inputtxt.value*inputtxt.value*inputtxt.value);
document.form1.text1.focus();
return true;
}
else
{
alert('Please input numeric characters only');
document.form1.text1.focus();
return false;
}
}
执行一下