函数
定义函数
函数提升仅适用于函数声明,而不适用于函数表达式
函数声明
函数表达式
//例子1
const factorial = function fac(n) {
return n < 2 ? 1 : n * fac(n - 1);
};
console.log(factorial(3)); // 6
//factorial(n)、fac(n)、arguments.callee()
-------------------------------------------
//例子2 该函数接收由函数表达式定义的函数,并对作为第二个参数接收的数组的每个元素执行该函数:
function map(f, a) {
const result = new Array(a.length);
for (let i = 0; i < a.length; i++) {
result[i] = f(a[i]);
}
return result;
}
const cube = function (x) {
return x * x * x;
};
const numbers = [0, 1, 2, 5, 10];
console.log(map(cube, numbers)); // [0, 1, 8, 125, 1000]
递归
function foo(i) {
if (i < 0) {
return;
}
console.log(`开始:${i}`);
foo(i - 1);
console.log(`结束:${i}`);
}
foo(3);
// 打印:
// 开始:3
// 开始:2
// 开始:1
// 开始:0
// 结束:0
// 结束:1
// 结束:2
// 结束:3
用于获取DOM树子节点
function walkTree(node) {
if (node === null) {
return;
}
// 对节点做些什么
for (let i = 0; i < node.childNodes.length; i++) {
walkTree(node.childNodes[i]);
}
}
闭包
闭包在一个函数里面嵌套另外一个函数。嵌套(内部)函数对其容器(外部)函数是私有的。闭包是可以拥有独立变量以及绑定了这些变量的环境(“封闭”了表达式)的表达式(通常是函数)。既然嵌套函数是一个闭包,就意味着一个嵌套函数可以“继承”容器函数的参数和变量。换句话说,内部函数包含外部函数的作用域。
闭包是 JavaScript 中最强大的特性之一。JavaScript 允许函数嵌套,并且内部函数具有定义在外部函数中的所有变量和函数(以及外部函数能访问的所有变量和函数)的完全访问权限。
外部函数却不能访问定义在内部函数中的变量和函数。这给内部函数的变量提供了一种封装。
此外,由于内部函数可以访问外部函数的作用域,因此当内部函数生存周期大于外部函数时,外部函数中定义的变量和函数的生存周期将比内部函数执行的持续时间要长。当内部函数以某一种方式被任何一个外部函数之外的任何作用域访问时,就会创建闭包。
- 内部函数只可以在外部函数中访问。
- 内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量。
function addSquares(a, b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
console.log(addSquares(2, 3)); // 13
console.log(addSquares(3, 4)); // 25
console.log(addSquares(4, 5)); // 41
--------------------------------------
//由于内部函数形成了闭包,因此你可以调用外部函数并为外部函数和内部函数指定参数:
function outside(x) {
function inside(y) {
return x + y;
}
return inside;
}
const fnInside = outside(3); // 可以这样想:给我一个可以将提供的值加上 3 的函数
console.log(fnInside(5)); // 8
console.log(outside(3)(5)); // 8
保存变量:上例中
inside
被返回时x
是怎么被保留下来的。一个闭包必须保存它可见作用域中所有参数和变量。因为每一次调用传入的参数都可能不同,每一次对外部函数的调用实际上重新创建了一遍这个闭包。只有当返回的inside
没有再被引用时,内存才会被释放。
多层嵌套函数
- 函数(
A
)可以包含函数(B
),后者可以再包含函数(C
)。 - 这里的函数
B
和C
都形成了闭包,所以B
可以访问A
,C
可以访问B
。 - 此外,因为
C
可以访问B
(而B
可以访问A
),所以C
也可以访问A
。
闭包可以包含多个作用域;它们递归地包含了所有包含它的函数作用域。这个称之为作用域链
function A(x) {
function B(y) {
function C(z) {
console.log(x + y + z);
}
C(3);
}
B(2);
}
A(1); // 打印 6(即 1 + 2 + 3)
------------------------------------
//实例2
function outside() {
const x = 5;
function inside(x) {
return x * 2;
}
return inside;
}
console.log(outside()(10)); // 20(而不是 10)
//这里的作用链域是 {inside、outside、全局对象}。因此 inside 的 x 优先于 outside 的 x
---------------------------------------------------------------------------------
const createPet = function (name) {
let sex;
const pet = {
// 在这个上下文中:setName(newName) 等价于 setName: function (newName)
setName(newName) {
name = newName;
},
getName() {
return name;
},
getSex() {
return sex;
},
setSex(newSex) {
if (
typeof newSex === "string" &&
(newSex.toLowerCase() === "male" || newSex.toLowerCase() === "female")
) {
sex = newSex;
}
},
};
return pet;
};
const pet = createPet("Vivie");
console.log(pet.getName()); // Vivie
pet.setName("Oliver");
pet.setSex("male");
console.log(pet.getSex()); // male
console.log(pet.getName()); // Oliver
----------------------------------------------------------------------------------------
//内部函数保留“稳定”而又“被封装”的数据参与运行。
const getCode = (function () {
const apiCode = "0]Eal(eh&2"; // 我们不希望外部能够修改的代码......
return function () {
return apiCode;
};
})();
console.log(getCode()); // "0]Eal(eh&2"
arguments对象
函数的实际参数会被保存在一个类似数组的 arguments 对象中
使用 arguments
对象,你可以处理比声明更多的参数来调用函数。这在你事先不知道会需要将多少参数传递给函数时十分有用。你可以用 arguments.length
来获得实际传递给函数的参数的数量,然后用 arguments
对象来访问每个参数。
function myConcat(separator) {
let result = ""; // 初始化列表
// 迭代 arguments
for (let i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
console.log(myConcat("、", "红", "橙", "蓝"));
// "红、橙、蓝、"
console.log(myConcat(";", "大象", "长颈鹿", "狮子", "猎豹"));
// "大象;长颈鹿;狮子;猎豹;"
console.log(myConcat("。", "智者", "罗勒", "牛至", "胡椒", "香菜"));
// "智者。罗勒。牛至。胡椒。香菜。"
函数参数
默认参数
function multiply(a, b) {
b = typeof b !== "undefined" ? b : 1;
return a * b;
}
-----------------------------------------
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5)); // 5
剩余参数
将不确定数量的参数表示为数组
function multiply(multiplier, ...theArgs) {
return theArgs.map((x) => multiplier * x);
}
const arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
无单独的this
在箭头函数出现之前,每一个新函数都定义了自己的 this 值(在构造函数中是一个新的对象;在严格模式下是 undefined;在作为“对象方法”调用的函数中指向这个对象;等等)。事实证明,这对于面向对象的编程风格来说并不理想。
//箭头函数没有自己的 this,而是使用封闭执行上下文的 this 值。
//因此,在以下代码中,传递到 setInterval 中的函数内的 this 与闭包函数中的 this 相同:
function Person() {
this.age = 0;
setInterval(() => {
this.age++; // 这里的 `this` 正确地指向 person 对象
}, 1000);
}
const p = new Person();
预定义函数
eval()
方法执行方法计算以字符串表示的 JavaScript 代码。
parseFloat()
函数解析字符串参数,并返回一个浮点数。
parseInt()
函数解析字符串参数,并返回指定的基数(基础数学中的数制)的整数。
表达式与运算符
解构:一个能从数组或对象对应的数组结构或对象字面量里提取数据的 Javascript 表达式。
var foo = ["one", "two", "three"];
// 不使用解构
var one = foo[0];
var two = foo[1];
var three = foo[2];
// 使用解构
var [one, two, three] = foo;
比较运算符
位运算符与位逻辑运算符
(Java基础知识综合)-CSDN博客
数值推导
[for (i of [ 1, 2, 3 ]) i*i ];
// [ 1, 4, 9 ]
var abc = [ "A", "B", "C" ];
[for (letters of abc) letters.toLowerCase()];
// [ "a", "b", "c" ]
this
假设一个用于验证对象value
属性的validate
函数,传参有对象,最高值和最低值。
function validate(obj, lowval, hival) {
if (obj.value < lowval || obj.value > hival) console.log("Invalid Value!");
}
你可以在任何表单元素的onchange
事件处理中调用validat
函数,用this
来指代当前的表单元素,用例如下:
<p>Enter a number between 18 and 99:</p>
<input type="text" name="age" size="3" onChange="validate(this, 18, 99);" />
instanceof
如果所判别的对象确实是所指定的类型,则返回true
var theDay = new Date(1995, 12, 17);
if (theDay instanceof Date) {
// statements to execute
}
in
如果所指定的属性确实存在于所指定的对象中,则会返回true
// Arrays
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees; // returns true
3 in trees; // returns true
6 in trees; // returns false
"bay" in trees; // returns false (you must specify the index number,
// not the value at that index)
"length" in trees; // returns true (length is an Array property)
// Predefined objects
"PI" in Math; // returns true
var myString = new String("coral");
"length" in myString; // returns true
// Custom objects
var mycar = { make: "Honda", model: "Accord", year: 1998 };
"make" in mycar; // returns true
"model" in mycar; // returns true
void
可以用 void 运算符指明一个超文本链接。该表达式是有效的,但并不会在当前文档中进行加载。
//下面的代码创建了一个超链接,当用户单击它时,提交一个表单。
<a href="javascript:void(document.form.submit())">Click here to submit</a>
typeof
typeof 操作符返回一个表示 operand 类型的字符串值。operand 可为字符串、变量、关键词或对象,其类型将被返回。operand 两侧的括号为可选。
var myFun = new Function("5 + 2");
var shape = "round";
var size = 1;
var today = new Date();
--------------------------------------
typeof myFun; // returns "function"
typeof shape; // returns "string"
typeof size; // returns "number"
typeof today; // returns "object"
typeof dontExist; // returns "undefined"
delete
x = 42;
var y = 43;
myobj = new Number();
myobj.h = 4; // create property h
delete x; // returns true (can delete if declared implicitly)
delete y; // returns false (cannot delete if declared with var)
delete Math.PI; // returns false (cannot delete predefined properties)
delete myobj.h; // returns true (can delete user-defined properties)
delete myobj; // returns true (can delete if declared implicitly)
-------------------------------------------------------------------------
逗号操作符
逗号操作符(
,
)对两个操作数进行求值并返回最终操作数的值。它常常用在for
循环中,在每次循环时对多个变量进行更新。
var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var a = [x, x, x, x, x];
for (var i = 0, j = 9; i <= j; i++, j--)
console.log("a[" + i + "][" + j + "]= " + a[i][j]);
短路求值
false
&& anything // 被短路求值为 falsetrue
|| anything // 被短路求值为 true
字符串运算符
var myString = "alpha";
myString += "bet"; // 返回 "alphabet"