一、JavaScript 函数
将脚本编写为函数,就可以避免页面载入时执行该脚本。
打印 定义的函数名 就相当于打印这整个函数
函数包含着一些代码,这些代码只能被事件激活或者在函数被调用时才会执行。
你可以在页面中的任何位置调用脚本(如果函数嵌入一个外部的 .js 文件,那么甚至可以从其他的页面中调用)。
创建函数的语法:
function 函数名(var1,var2,...,varX){
代码...
}
注意:无参数的函数必须在其函数名后加括号
函数的调用必须是函数名后面跟(),有参给参无参不给
函数名是什么? 函数名 == 整个函数。
函数加载问题? JS加载的时候,只加载函数名,不加载函数体。所以如果想使用内部的成员变量,需要先调用这个函数。
第一种:函数声明(最常用)
function 函数名(){
代码...
}
第二种:函数表达式
var text = function (var1,var2,...,varX) {
代码……
}
第三种:面向对象(构造函数)
var fn = new Function(var1,var2,...,varX);
第四种:匿名函数(即没有名字的函数)
function (){
代码……
}
特殊的函数:
第一种:回调函数(一个函数作为另一个函数的参数进行传递,高阶函数)
function fn(a,b,){
return a()+b();
}
var one = function(){
return 1;
}
var two = function(){
return 2:
}
//函数调用
fn(one,two)
第二种:匿名回调函数(将匿名函数作为参数传递的函数)
第三种:自调函数/立即执行函数(自己定义自己调用,别人不能调用的函数)
(function () {
alert("a");
}) ();
第四种:内部私有函数(函数里面还有函数)
内部私有函数的表现形式:
function fn() {
function fn1() {
console.log("fn1");
}
}
fn();
// fn1();//如果执行fn1()会报错
内部私有函数的解决办法:(通过函数表达式的方式将内部的那个函数给放到外部)
var fn2 ;
function fun() {
fn2=function () {
alert("fn2");
}
}
fun();
fn2();
闭包:
/*
1.如何产生闭包?
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
2.闭包到底是什么?
闭包 = 内层函数 + 外层函数的变量
理解一:闭包是嵌套的内部函数(绝大部分人)
理解二:包含被引用变量(函数)的对象(极少数人)
注意:闭包存在于嵌套的内部函数中
3.产生闭包的条件?
函数嵌套
内部函数引用了外部函数的数据(变量/函数)
4.闭包的作用?
封闭数据,实现数据私有,外部也可以访问函数内部的变量
闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来
5.使用场景
1.创建私有变量
2.延长变量的生命周期
6.闭包可能引起的问题?
内存泄漏
*/
function outer() {
var a = 2;
function fn() { // 执行函数定义就会产生闭包(不用调用内部函数)
console.log(a);
}
}
outer();
// 闭包的常规用法
/**
* 1.将函数作为另一个函数的返回值
* 2.将函数作为实参传递给另一个函数调用
*/
function fn1() {
var a = 2;
function fn2() {
a++;
console.log(a);
}
return fn2;
}
var fn = fn1();
fn();
fn();
// 2.将函数作为实参传递给另一个函数调用
function showDelay(msg,time) {
setTimeout(() => {
alert(msg);
}, time);
}
showDelay("4444",1000);
/**
* 闭包的作用:
* 1.使用函数内部的变量在函数执行后,仍然存活在内存中(延长了局部变量的生命周期)
* 2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)
*
* 问题:
* 1.函数执行完后,函数内部声明的局部变量是否还存在? 一般是不存在,存在于闭包中的变量才可能存在
* 2.在函数外部能直接访问函数内部的局部变量吗? 不能,但我们可以通过闭包让外部操作它
*
*/
/**
* 闭包的生命周期:
* 1.产生:在嵌套内部函数定义执行完成时就产生了(不是在调用)
* 2.死亡:在嵌套的内部函数成为垃圾对象时
*/
// fn = null; // 闭包死亡(包含闭包的函数对象成为垃圾对象---回收闭包)
/**
* 1.闭包的缺点:
* 函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
* 容易造成内存泄漏
* 2.解决:
* 能不用闭包就不用
* 及时释放
*/
//作用域、上下文
function a() {
var i=0;
function b() {
console.log(++i);
}
return b;
}
var c = a();
c();
首先函数c是无法访问函数a中的变量的,此时通过在函数a内构造一个内部函数b,b自然是可以读取a中的变量的,此时c便可以通过b来读取a中的变量,这便是闭包了。
如果之后再执行c(),此时会输出1,这是为什么呢?
这和js的垃圾回收机制有很大的关系,由于闭包c的存在,函数b依赖于a中的变量,使得函数a的i始终存在在内存。
特点:可以重复利用变量,并且这个变量不会污染全局的一种机制;这个变量是一直保存在内存中,被垃圾回收机制回收
使用闭包的注意点:
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除(i = null;)。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
使用场景:
1.防抖
2.节流
3.柯里化函数
4.函数嵌套函数避免全局污染的时候
递归
递归:就是函数内部自己调用自己。 必须有跳出条件 必须在外部执行这个函数才能形成递归
注意:别忘记 JavaScript 中大小写字母的重要性。"function" 这个词必须是小写的,否则 JavaScript 就会出错。另外需要注意的是,必须使用大小写完全相同的函数名来调用函数。
高阶函数
定义:一个函数的 参数/返回值 是一个函数的函数称为高阶函数。常见的像数组的map、reduce、filter这些都是高阶函数
高阶函数:如果一个函数符合下面2个规范中的任何一个,那么该函数就是高阶函数
1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数:Promise、setTimeout、arr.map()、arr.reduce()
// 简单的高阶函数
function add(x,y,fn){
return fn(x) + fn(y);
}
function fn(num){
return Math.abs(num);
}
add(-5,6,fu); // 11
函数柯里化
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
柯里化(Currying)又称部分求值,一个柯里化的函数首先会接收一些参数,接收了这些参数后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要它们求值的时候,之前传入的所有参数都会被一次性用于求值。
柯里化是一种函数的转换,它是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)或者f(a, b)(c)或者f(a)(b, c)。
函数柯里化的主要作用和特点就是参数复用、提前返回和延迟执行。
// 普通的add函数
function add(x,y){
return x + y;
}
// 柯里化后
function curryingAdd(x){
return function(y){
return x + y;
}
}
add(1,2); // 3
curryingAdd(1)(2); // 3
return 语句
return 语句用来规定从函数返回的值。
因此,需要返回某个值的函数必须使用这个 return 语句。
function prod(a,b){
var x=a*b
return x;
}
注意:
1. 如果函数没有显示的使用 return 语句 ,那么函数有默认的返回值:undefined
2. 如果函数使用 return 语句,那么跟再return后面的值,就成了函数的返回值
3. 如果函数使用 return 语句,但是return后面没有任何值,那么函数的返回值 也是:undefined
4. 函数使用 return 语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后面的所有其他代码都不会再执行。
变量和作用域(隐式全局变量和变量声明提升)
变量和作用域(函数中的变量需要函数执行后才能执行)
一、全局变量(成员变量)
哪里都可以访问到的变量。
(进入script立即定义的变量或函数外部和函数没有var的变量)
二、局部变量(只有局部能够访问的变量)
函数内部的变量,只有函数内部可以访问到。
(函数内部用var定义的变量和形参)
手动清除变量: aaa = null
作用域:全局变量一直存在,手动消失;局部变量函数调用完就被回收(特例:闭包不会消失需要手动消失)。
隐式全局变量
隐式全局变量就是隐藏的全局变量不好被发现。(没有被var声明的变量可看成是全局变量)
function fn(){
var a = b = c = 1; // b和c就是隐式全局变量
}
注意:
function fn(){
var a = b = c = 1; // b和c就是隐式全局变量(等号)
var a = 1; b = 2; c = 3; // b和c就是隐式全局变量(分号)
var a = 1 , b = 2 , c = 3; // b和c就不是隐式全局变量(逗号)
}
变量声明提升(出现原因:预解析)
函数中,定义变量在使用变量之后。
var/function 声明只提升变量名/函数体,不提升变量值,容易出现undefined。计算后形成NaN。
function fn(){
// var aaa;
console.log(aaa);
var aaa = 1;
}
//提前看一眼这个习惯叫什么呢? 预解析!
//变量声明提升:在预解析的时候,成员变量和函数,被提升到最高位置,方便其他程序访问。
//变量声明提升特点:成员变量只提升变量名,不提升变量值。但是,函数是所有内容全部提升。(function直接定义的)
//函数范围内照样会出现变量声明提升
//什么情况容易出现变量声明提升:使用变量在定义变量之前。
函数范围内,照样适用。
预解析过程:
-
语法检查,如果有错误,直接停止后续步骤不再运行。
-
把变量和函数的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值和调用。
-
先提升变量后提升函数,如果函数和变量同名,则变量会被函数替换;
代码执行过程:
变量的赋值,函数的调用,循环判断等,根据代码由上往下顺序执行;
小知识:
1.函数不调用不执行
2.function函数名等于整个函数
3.加载函数的时候,只加载函数名,不加载函数体(原因:函数里面的值不加载不执行)
4.就近原则使用变量(同名变量)
5.两个平级的函数变量不会相互影响,但可以使用相同的参数。
请描述JS中的作用域及其作用域链。
JavaScript中的作用域是指变量、函数等在程序中被访问的范围。JS采用的是词法作用域,即代码的作用域在定义时就已经确定了。作用域链是指变量在代码中查找的过程,JavaScript引擎在查找变量时会先在当前作用域内查找,如果没有找到,就会逐级向上查找直到全局作用域。
JavaScript中的作用域有全局作用域和局部作用域。在函数内部定义的变量,只在该函数内部可见,称为局部变量;在函数外定义的变量,称为全局变量,在整个程序中都可以访问。
作用域链是由当前执行环境的变量对象和上一级执行环境的变量对象构成,这些变量对象有序地排列在一个链表结构中。当执行环境需要访问一个变量时,会首先在当前的变量对象中查找,如果没有找到,则会到上一级的变量对象中查找,直到找到该变量或者到达全局作用域。如果在全局作用域中也没有找到该变量,则会抛出“未定义”错误。
作用域和作用域链的概念在理解JavaScript中的变量、函数、闭包等重要概念时非常重要。
如有不足请多多指教!希望给您带来帮助!