1.递归函数
在JavaScript中,函数直接或间接的调用自己,则该函数便称为递归函数。
function fn(){ //定义函数fn
console.log(1); //在控制台打印输出1
fn(); //调用函数本身
}
fn(); //调用函数
上述代码是一个典型的死递归,当在外部调用函数fn时,会先打印数字1,然后再次调用函数,再打印1…最终陷入永无止境的循环之中,如果电脑配置不好的,甚至可能导致死机。
下面的代码是一段简单的累加递归函数
function add(n) {
// 传递进来的是 5
// 当 n === 1 的时候要结束
if (n === 1) {
return 1;
}
}
add(5)
2.纯函数
纯函数的定义:
1.确定的输入,一定会产生确定的输出;
var names = ["abc", "cba", "nba", "dna"]
// slice只要给它传入一个start/end, 那么对于同一个数组来说, 它会给我们返回确定的值
// slice函数本身它是不会修改原来的数组
// slice -> this
// slice函数本身就是一个纯函数
// var newNames1 = names.slice(0, 3)
// console.log(newNames1)
// console.log(names)
// ["abc", "cba", "nba", "dna"]
// splice在执行时, 有修改掉调用的数组对象本身, 修改的这个操作就是产生的副作用
// splice不是一个纯函数
var newNames2 = names.splice(2)
console.log(newNames2)// ['nba', 'dna']
console.log(names)//(2) ['abc', 'cba']
var fruits = ["Banana", "Orange", "Apple", "Mango"];
var newFruits = fruits.splice(2, 1, "Lemon", "Kiwi");
console.log(newFruits);//['Apple']
console.log(fruits);//['Banana', 'Orange', 'Lemon', 'Kiwi', 'Mango']
//splice() 方法向/从数组添加/删除项目,并返回删除的项目。
2.函数在执行过程中,不能产生副作用;
const counter = { x: 1 }
const foo = (obj, b) => {
obj.x = 2
return obj.x + b
}
foo(counter, 2) // => 4
counter.x // => 2
执行 foo
函数时,有将作为参数传进来的外部变量 counter.x
的值修改为 2
。无论函数在哪里执行,都会修改函数外部变量的值,所以它产生了 副作用
,不是 纯函数
。
3.自执行函数
自执行函数也叫立执行函数,是将函数的声明和调用合并在一起
意义:自执行函数是为了封装,不需要将普通的函数特意换成自执行函数的写法
自执行函数的第一种写法
;(function () {
//代码块
})()
//解析
//第一步
//先写两个()
() ()
//第一个()可以这样想
1 + 1 * 3 //我想先执行 1 + 1
(1 + 1) * 3 //这样就实现了先执行()内的
//把函数放进()去
(function () {})
//第二步
(functon () {}) ()
第一个()//可以看作一个函数 后面这个()则是函数的执行
自执行函数的第二种写法
;(function () {
// 代码块
}())
//解释
第一步 将函数放在()内
(function () {})
第二部 在函数的{} 后面加一个() 表示调用
(function () {} ())
现在是自执行函数的传参
// 形参在function旁边的() 与普通函数一样
;(function (x,y) {
return x + y
//实参在最后的()中
})(1,2)
// 形参在function旁边的() 与普通函数一样
;(function (x,y) {
return x + y
//实参在最后的()中 注意两种写法的括号位置是不一样的
}(1,2))
4.柯里化函数
概念:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
简单点来说就是:每次调用函数时,它只接受一部分参数,并返回一个函数,直到传递所有参数为止。// 等待我们柯里化实现的方法add
function add(a, b, c, d) {
return a + b + c + d;
};
// 最简单地实现函数add的柯里化
// 有点low,有助于理解
function add(a, b, c, d) {
return function(a) {
return function(b) {
return function(c) {
return a + b + c + d;
}
}
}
}
5.闭包
换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
function init() {
var name = "Mozilla"; // name 是一个被 init 创建的局部变量
function displayName() {
// displayName() 是内部函数,一个闭包
alert(name); // 使用了父函数中声明的变量
}
displayName();
}
init();
function makeAdder(x) {
return function (y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
add5
和 add10
都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在 add5
的环境中,x
为 5。而在 add10
中,x
则为 10。
6.this
(1)this永远指向一个对象;(2)this的指向完全取决于函数调用的位置;
const test = {
prop: 42,
func: function () {
return this.prop;
},
};
console.log(test.func());
// Expected output: 42
在上述代码中,在函数内部,this
的值取决于函数如何被调用,以将 this
看作是函数的一个隐藏参数(就像函数定义中声明的参数一样),this
是语言在函数体被执行时为你创建的绑定
对于典型的函数,this
的值是函数被访问的对象。换句话说,如果函数调用的形式是 obj.f()
,那么 this
就指向 obj
。
function getThis() {
return this;
}
const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };
obj1.getThis = getThis;
obj2.getThis = getThis;
console.log(obj1.getThis()); // { name: 'obj1', getThis: [Function: getThis] }
console.log(obj2.getThis()); // { name: 'obj2', getThis: [Function: getThis] }