使用函数声明或者函数表达式创建一个函数
foo(); //foo
bar(); //Uncaught ReferenceError: Cannot access 'bar' before initialization
//函数声明
function foo(){
console.log(foo);
};
//函数表达式
const bar = function(){
console.log('bar');
};
setTimeOut(function(){
//匿名函数
});
2.1 函数的作用域
const age = 21;
function foo(){
const name = 'ian';
console.log(age); //21
return name;
};
console.log(foo); //ian
console.log(name); //undefined
2.2 闭包 closure
形成闭包的三个条件
function outer(){
const name = 'ian';
function inner(){ console.log(name) };
return inner;
}
const bar = outer();
bar(); //ian
outer()函数执行的时候得到inner函数,inner函数中使用了outer作用域中的name导致outer函数执行完无法正常释放,变量bar又对inner有了引用,形成了闭包。闭包实际上提供了一种有外部访问函数内部变量的方法。
2.3 闭包的使用场景
函数柯理化
function add(x){
return function(y){
return x + y;
};
};
const add5 = add(5);
const add3 = add(3);
console.log(add5(1)); //6
console.log(add3(1)); //4
缓存
使用闭包缓存列表进行求和
function sum() {
const list = [];
return function (num) {
if (num) {
list.push(num);
}
else {
//沒有传递参数时返回求和结果
let res = 0;
for (var i = 0; i < list.length; i++) {
res += list[i];
};
return res;
}
};
};
const add = sum();
add(1);
add(2);
add(3);
console.log(add()); //6
使用闭包实现防抖函数
function debounce(fn, await) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, await);
}
}
通过闭包实现变量/方法的私有化
function user(){
const user ={
name: 'ian',
age: 21,
};
return function(){
return name;
}
};
2.4 闭包的优缺点
闭包的优点:
闭包的缺点:
闭包延长了变量的生命周期,增加了变量的引用,大量使用闭包有内存泄露的风险
2.5 闭包一定会造成内存泄露吗?
下面的代码会造成内存泄露
window.onload=function(){
const btn = document.getElementById('btn');
btn.onclick=function(){
console.log(btn.id);
}
};
避免闭包中内存泄漏的两种方法:
window.onload = function () {
const btn = document.getElementById('btn');
const id = btn.id;
btn.onclick = function () {
console.log(id);
};
btn = null;
};
window.onload = function () {
const btn = document.getElementById('btn');
document.getElementById('btn').onclick = function () {
console.log(document.getElementById('btn').id);
};
};
arguments是一个伪数组,用来获取函数的全部参数
function add(x, y) {
return x + y;
};
function add1() {
let res = 0;
for (let i = 0; i < arguments.length; i++) {
res += arguments[i]
};
return res;
}
arguments.callee指向参数所属的当前执行的函数
function foo() {
console.log(arguments.callee === foo);
};
foo();
将Argumnets转为数组
//使用数组的slice方法将伪数组转为数组
function add() {
const nums = [].slice.call(arguments);
console.log(nums);
}
//使用拓展运算符将伪数组转为数组
function add(x, y) {
const nums = [...arguments];
};
add(1, 2); //[1, 2]
4.1 构造函数的使用
function Person(name, age){
this.name = name;
this.age = age;
};
const jack = new Person('jack',21); //{name:'jack', age:21}
const tom = new Person('tom',22); //{name:'tom', age:22}
4.2 构造函数执行的过程
4.3 构造函数的返回值
构造函数默认返回一个新的对象,如果有手动的return情况如下:
function Person(name,age){
this.name = name;
this.age =age;
return 'person';
};
const jack = new Person('jack',23); // //{ name: "jack", age: 23 }
function Person1(name,age){
this.name = name;
this.age =age;
return ['name','age'];
};
const tom = new Person1('tom',23); //Array [ "name", "age" ]
立即执行函数就是声明一个匿名函数然后立即执行它
5.1 立即执行函数的语法
function foo(){} ();
这个代码会报错,因为function关键字既可作为函数声明也可作为函数表达式,当以function开头的时候会被认为是函数声明,直接使用()去执行会抛出异常,我们在函数外面包上一个括号使之成为函数表达式。正确的写法如下:
(function(){
console.log('立即执行函数');
}());
(function(){
console.log('立即执行函数');
})();
甚至你可以这样写:
+ function(){}(console.log('这是一个函数表达式'));
- function(){}(console.log('这是一个函数表达式'));
! function(){}(console.log('这是一个函数表达式'));
~ function(){}(console.log('这是一个函数表达式'));
5.2 立即执行函数的作用
5.3 立即执行函数的经典使用场景
//点击按钮输出都是5
const btns = document.getElementsByClassName('btn');
for(var i = 0;i
使用立即执行函数后,每次for循环都会得到一个新的函数,i从1~5分别被保存到5个不同的函数里,因此点击按钮的时候能依次输出1到5。
递归就是函数调用自身。递归的两个必要条件:
利用递归实现斐波那契数列
function fibonacci(n) {
if (n <= 2) {
return 1
} else {
return fibonacci(n - 1) + fibonacci(n - 2)
}
};
fibonacci(1); //1
fibonacci(2); //1
fibonacci(3); //2
fibonacci(4); //3
fibonacci(5); //5
高阶函数的定义: 函数接收函数作为入参
const nums = [1,2,3];
nums.filter(function(n){ returen n>2} );
高阶函数的好处是将具体的计算以函数参数的形式抽离到函数外部,能更灵活的增强函数的功能
eval接收一个字符串,将这个字符串当作JS语句执行
const a = 1;
eval('console.log(a)'); //1
非严格模式下eval能声明和复写变量的值
let a = 1;
eval('a=2; b = 3;')
console.log(a,b); // 2,3
使用别名调用eval的时候,eval使用全局作用域
const exec = eval;
const a = 1;
funtion foo(){
const a = 2;
exec('console.log(a)'); //1
};
函数的基础使用
使用函数表达式或者函数声明去创建一个函数
函数声明整体提升,函数表达式只提升变量
匿名函数
构造函数
闭包
函数的arguments和arguments.callee
立即执行函数(IIFE)
递归
eval