介绍
使用函数切割流程,不仅可以减少重复代码、还可以有效的降低整体复杂度,增加可维护性
函数具有三要素:函数名、参数、返回值
函数包括
参数:表示完成流程所需的必要信息
返回值:表示完成流程后产生的结果
函数体:表示具体的流程
- 定义函数时,只需要考虑这个函数如何实现即可,完全不需要考虑其他无关的东西。
- 调用函数时,只需要考虑向其传递什么参数,如何使用它的返回结果,完全无需考虑函数的具体实现。
函数注释
/\*\*
- 得到某个数的阶乘
- 如果数小于了 1,则得到 0
- @param {number} n 要求阶乘的数
- @return {number} 阶乘结果
\*/
调用的时候这样就可以有提示
函数也是一个对象,每个函数都有一个属性 prototype(原型),往往构造函数才会用他,其他也有,但不使用
function m() {}
m.a = 1;
函数定义方式
//变量保存函数,函数表达式(本质是匿名函数)
var b = function () {};
//函数声明,使用函数声明可以将代码提到顶部
function c() {
}
构造函数
当我们需要通过函数去创建一个对象,通常使用构造函数
function Person(name , age , gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = function(){
alert(this.name);
};
}
//调用Person
person_obj = new Person('张三',18,'男');
person_obj.name
普通函数是直接调用,而构造函数需要使用 new 关键字来调用
构造函数与普通函数相比只是少了定义 obj 和返回 obj,以及把 obj 改为了 this
function Person(firstName, lastName) {
// var this = {};
this.firstName = firstName;
this.lastName = lastName;
this.fullName = firstName + lastName;
this.sayHi = function () {
console.log("我的名字叫做:" + this.fullName);
};
// return this;
}
JS 所有的对象,都是通过构造函数产生的
对象,数组,函数
// 语法糖
// var obj = {
// a: 1,
// b: 2,
// };
// var obj = new Object(); // 创建一个空对象
// obj.a = 1;
// obj.b = 2;
// console.log(obj);
// var arr = [1, 2, 3];
// var arr = new Array(1, 2, 3); // 创建一个数组
// console.log(arr);
function sum(a, b) {
return a + b;
}
// var sum = new Function('a', 'b', 'return a+b');
console.log(sum(1, 2));
回调函数
如果需要获取一个函数中异步操作的结果,则必须通过回调函数来获取
function fn(callback) {
setTimeout(function() {
var data = 'hello'
callback(data)
}, 1000)
}
fn(function(data) {
console.log(data)
})
如果想取到 data 的数据进行处理,就只能进行回调函数,因为定时器是异步的,函数不会等待执行完毕就直接结束了,只有在调用的时候传递一个函数给里面的定时器才行
1). 你定义的
2). 你没有调
3). 但最终它执行了(在某个时刻或某个条件下)
实例
数组过滤函数,通过回调可以使得使用时十分灵活
如果箭头函数函数体只有一句话,那么这个句话可以不带大括号,而且这句话就是返回值(可以不用写 return)
立即执行函数和匿名函数
匿名函数
应用场景
1、事件
var sub = document.querySelector("#sub");
//给按钮增加点击事件。
sub.onclick=function(){
alert("当点击按钮时会执行");
}
2、对象
var obj = {
name:"lynn",
age:18,
fn:function(){
return "我叫"+this.name+"今年"+this.age+"岁了!";
}
};
console.log(obj.fn());//我叫lynn今年18岁了!
3、函数表达式
var fn = function(){
return "2020"
}
立即执行函数
IIFE,其全称为 immediately invoked function expression,即“立即调用的函数表达式”:
立即执行函数就是声明一个匿名函数,并马上调用这个匿名函数
立即执行函数的用途
创建一个独立的作用域,这个作用域里面的变量,外面访问不到(即避免”变量污染”)
立即执行函数往往只会执行一次
//两种写法
(function() {
// ...
})();
(function() {
// ...
}());
(function(a,b){
console.log("a = "+a);
console.log("b = "+b);
})(123,456);
let a = (function() {
// ...
return "ad";
}());
这样可以把立即执行函数里面的数据返回出来一部分你所需要的
闭包函数
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
函数嵌套
内部函数引用了外部函数的数据(变量/函数)
function fn1 () {
var a = 2
var b = 'abc'
function fn2 () { //执行函数定义就会产生闭包(不用调用内部函数)
console.log(a)
}
// fn2()
}
function showDelay(msg, time) {
setTimeout(function () {
alert(msg)
}, time)
}
作用
- 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
- 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
比如希望第一次调用和第二次调用相同函数拥有不一样的返回结果
缺点
- 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
- 容易造成内存泄露
解决
- 能不用闭包就不用
- 及时释放
f = null //让内部函数成为垃圾对象-->回收闭包
闭包是什么就不解释了,直接写一个闭包函数:
var f = function () {
var num = 0;
return function () {
return (num += 1);
};
};
js 中,函数是一等公民,定义一个函数 f,它返回另一个可执行函数function() { return num += 1; };
js 中的作用域,都是一层一层向上找的,在 f 内部函数里面,他的 num 向上找到父函数的作用域。
现在,我们执行一下:
f()()
// 1
f()()
// 1
????? 感觉 不太对?为什么num没有自增呢??
- 内存回收机制
为什么上面执行结果不太对,因为执行f()()
后,f 函数已经执行完毕了,没有其他资源引用 f,ta 会被立即释放,也就是说,f()()
执行完后,立即就释放了。
如何才不释放呢?
// 创建f的引用
var fn = f();
fn()
// 1
fn()
// 2
fn()
// 3
这下就对了,num 成了私有变量,f 拥有了私有作用域。
完了吗?
f 有了 fn 的引用,内存一直得不到释放,咋办呢?这样的函数多了是不是会造成内存溢出?
手动释放一下:
var fn = f();
fn()
// 1
fn()
// 2
fn()
// 3
// 手动释放f的引用
fn = null
// f的引用fn被释放了,现在f的作用域也被释放了。num再次归零了。
var fn = f()
fn()
// 1
箭头函数
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
箭头函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
var f = () => 5;
// 等同于
var f = function (){
return 5
};
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
}
函数方法
我们所创建的每一个函数,解析器都会向函数中添加一个属性 prototype
这个属性对应着一个对象,这个对象就是我们所谓的原型对象
原型对象中有一个属性 constructor, 它指向函数对象
console.log(Date.prototype.constructor === Date)