函数 function
- 函数也是一个对象,普通对象能做的函数都能做,在函数中可以保存可执行的JS代码,并且在需要的时候对这些代码进行调用
- 创建函数的方式一:
函数声明:
function 函数名(形参1,形参2,...形参N)
{
语句...
}
例如:
function fun()
{
console.log("我是fun中封装的代码");
}
- 创建函数的方式二:
函数表达式:
语法:
var 变量 = function(形参1,形参2,...形参N)
{
语句...
}
例如:
var fun2 = function()
{
console.log("fun2中的代码~~~");
}
- 调用函数,将函数中存储的代码执行
- 调用函数:函数对象()
fun(); - 在声明函数的时候,可以在函数的()中指定数量不等的形参,形参(形式参数),声明形参就相当于在函数内部声明了变量但是并不赋值,若没有赋值,默认的就是undefined.
- 调用函数时,可以在()传递实参(实际参数),实参会赋值给对应的形参 (总结:在声明时是形参,在调用时是实参)
- 形参和实参对应的个数问题:
- 调用函数时,JS解析器不会检查实参的个数和类型,可以传递任意数量的实参
- 如果形参和实参数量一样,则对应的实参会赋值给对应形参
- 如果实参的数量大于形参,则多余的实参不会使用
- 如果实参的数量小于形参,则没有对应实参的形参值会是undefined
- 可以传递任意类型的实参,函数可以接收任意类型的实参,也可以是一个对象,函数也可以作为一个函数的参数
调用 fun(sum); 这里sum也是一个函数。
将一个匿名函数作为参数传递fun( function()
{
alert(“hello我是参数!!!”);
});
-返回值return;(只使用在函数当中)
- 形参和实参对应的个数问题:
- 要使用返回的结果,就必须使用一个变量来保存返回值
- 所谓的返回值就是指函数执行完毕以后返回的结果
- 通过return来设置函数的返回值
语法: return 值; - 如果不指定返回值,或return后不跟任何值,则会返回undefined
- 调用函数时,可以通过一个变量来接收函数的返回值。返回值自己不会输出,只是作为值返回来,接收到后再用输出语句就可以输出。
- 语法: var 变量名=函数名();
例如: var result = sum(345,234,312);
console.log(result); - 函数中,return一旦执行,函数立即结束,return后的代码都不会在执行了
- break用于结束当前循环
- continue用于跳过当次循环
- return可以用来退出函数
- 通过return来设置函数的返回值
- 所谓的返回值就是指函数执行完毕以后返回的结果
- console.log(fun); //打印函数体
console.log(fun()); //打印函数的返回值 - 函数的返回值可以是任意的数据类型
- retrun 值 ,若值是数字/字符串/布尔类型/undefined/null,则可以直接加在后面,值会返回来;若是字母需要定义后才能使用。它的值不能是语句。
- 在函数当中嵌套有if语句,这时if语句中,就可以使用return来阻止他后面的语句。这时if后面的语句都会被阻止,跟if同级的语句也会被阻止(在一个函数中,只要见到return的地方,他后面的语句都不会再执行了)!!!
函数和循环语句的作用的区别:
函数:是否重复使用,取决于你调用的地方。
循环体:只用当执行到当前语句时,才能执行多次。
预定义函数:
eavl() :可以执行‘字符串’类型的JS代码,但是他的安全性不高。
例如:var str=“console.log(‘this is string’)”; //字符串
eval(str); //输出结果为 this is string
对中文进行编码和解码:
encodeURI():对字符串进行编码
decodeURI():对字符串进行解码按值传递:就是全局变量和局部变量同名时的值传递。
定义函数时,变量名和函数名同名时的情况:
- 使用字面量定义函数的情况:
第一函数的结构——其实就是使用了变量的声明方式(将一个函数赋给变量)
特点:和变量的特点是一样的
例如: var fn=‘string’;
var fn=function(){ //相当于重新的赋值了
console.log(‘this is string’);
}
console.log(fn()); //this is string - 函数声明的方式:
直接被定义为了一个函数——不是变量
当定义的变量和声明的函数是相同的名字时,变量的名字没有被覆盖,变量还是变量,没有做变化。此时,“变量有效,函数无效”,定义的变量还可以输出,但若输出函数就会报错。 变量的声明提升,会提升到函数定义之前。
例如:var fn=‘string’;
function fn(){
console.log(‘this is string’);
}
console.log(fn); // string
console.log(fn()); //fn is not a function
因为,当变量声明和函数声明同名时,变量有效,函数无效,就是函数不可用。作为值的函数:
为什么要使用特殊函数:来实现代码的优化 ,代码量、性能、可读性等等。
- 匿名函数(不能单独使用,会语法报错)
- 自调函数:自己调用自己,将调用函数过程给省略了
语法结构:第一个小括号 —— 定义函数
第二个小括号 —— 调用函数(一般将其省略,若需要传递实参,则括号里面添加实参)
语法结构:(function(形参){
//函数作用域 – 不占用全局的命名空间
函数体…
})(实参);
其他语法结构: ( function(形参){函数体}(实参) )
- 自调函数:自己调用自己,将调用函数过程给省略了
- 特点:仅仅只能被调用一次,使用的好处是不占用全局的命名空间,优化代码性能。
- 回调函数:将一个函数作为另一个函数的参数,作为参数的函数就叫做回调函数。
作为回调函数:1,回调函数的调用体,在外层函数的定义体内;
2,回调函数的定义体,在外层函数的调用体内。
例如: var one=function(){ one(),two(),就是回调函数
return 1;
}
var two=function(){
return 2;
}
function fn(a,b){
return a() +b(); 回调函数,传的是实参
}
console.log(fn( one,two ));
输出语句也可以改写成:
console.log(function(){return 1;},function()){return 2;} //匿名回调函
分别是a() b()的回调函数的定义体
作用域(scope)
- 作用域指的就是作用范围
- 在JS一共有两种作用域
全局作用域
函数作用域 - 全局作用域
- 所有直接写在script标签中的内容都在全局作用域中
- 生命周期:全局作用域在页面加载时创建,在页面关闭时销毁
- 在全局作用域中有一个全局对象window,该对象由浏览器创建,代表的是整个的浏览器的窗口。在全局作用域中创建的变量都会作为window的属性保存,在全局作用域中创建的函数都会作为window的方法保存
- 全局作用域中创建的变量都是全局变量,可以在页面的任意位置访问
- 声明变量时,如果不写var关键字,就相当于直接向window对象中添加属性
- 函数作用域
- 函数作用域在调用函数时创建,在调用结束时销毁,每调用一次函数就会创建一个新的函数作用域
- 在函数作用域中,可以访问全局作用域的变量;在全局作用域中,无法访问到函数中的变量
- 在函数作用域中如果不使用var关键字,则变量会变成全局变量
- 所有使用var关键字声明的变量,会在函数中的其他代码执行前被声明并没有赋值,所有使用函数声明创建的函数,会在函数中的其他代码执行前被创建。
作用域链:在函数的嵌套中使用一个变量时,它会先在自身的作用域中寻找,如果找到了则直接使用。如果没有找到则去上一级函数作用域中寻找,直到找到全局作用域为止,如果全局作用域中依然没有,则会报错。全局作用域不能访问函数作用域中的变量。
变量的提升(变量的声明提前,提升到最上面一行) 只针对变量声明和函数声明有用。
- 在JS中所有使用var关键字声明的变量,都会在所有的代码执行之前被声明但是不会赋值,但是赋值会等到赋值语句执行时才进行。
- 如果不使用var关键字不会具有声明提前的特性。
- 函数的提升(函数的声明提前,提升到函数体里面的最上面一行)
- 在JS中,使用函数声明创建的函数会在所有的代码执行之前被创建,所以我们可以在函数声明之前就去调用函数
- 使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用
- 在ECMAScript 5 中定义的‘语句’中的变量,是全局变量,在任意位置都可以访问被访问。
函数声明的提升,并不是undefined ,而是函数定义体。
例如: console.log(fn); //得到的是fn函数的定义体
function fn(){
console.log(‘this is fn’);
}- 如果一个变量和一个函数已经声明过了,并且在下面又进行了一次声明,则变量不会做改变(除非是进行声明和赋值才会做改变),但是函数会变成最后声明的那个函数(后面声明的函数会将前面声明的函数覆盖)。在全局作用域中,函数会提升在变量之前。先提升函数再提升变量。
- 变量的提升不会搭理if条件的暗示。
- 最佳实践:在块内部不要去定义函数。
*注意:函数的作用域在其创建时就已经确定了,无论在何处调用都不会改变它的作用域
例如:
函数执行的流程
- 栈 是一种数据结构和数组很类似
- 栈的特点 后进先出
- 创建一个新的执行环境,并且将其放入到执行环境栈的栈顶
- 执行环境是放到一个栈结构中保存的,栈顶保存总是当前的环境,栈底总是全局的环境
- 创建一个新的变量对象,并且将变量对象放入到作用域栈的栈顶
- 变量对象是用来保存变量的对象,函数执行时所有的局部变量和函数
都会保存到变量对象中,变量对象无法访问。除了全局的变量对象window
- 变量对象是用来保存变量的对象,函数执行时所有的局部变量和函数
- 预解析,将使用var声明的变量添加到变量对象中,将函数声明创建函数创建好并添加到变量对象中
- 逐行执行函数中的代码
- 函数执行完毕将变量对象从作用域栈中弹出并销毁,将环境从环境栈中弹出并销毁。
- 创建一个新的执行环境,并且将其放入到执行环境栈的栈顶
闭包(难点)
- 概念:函数可以访问函数之外定义的变量
- 闭包结构必须满足的条件:
1,具有两个关系是平行的函数作用域
2,其中一个函数作用域中具有局部变量
3,在另一个函数作用域中可以访问这个函数作用域的局部变量
我们常见的闭包结构:作为值的函数 和 回调函数 闭包在创建时,每次都是创建一个新的闭包,每个闭包都是新的个体。
图解:
例如: //定义两个平行关系的函数作用域
function fn1(){
//在这个函数作用域中定义一个局部变量
var a=‘a’;
fn2(a); //调用fn2(),将a的值作为实参传递给fn2()函数中的形参b,则fn2可以得到变量a的值。
}
function fn2(b){
console.log(b) //fn2()可以得到a的值
}
fn1();//是有限制的,必须先执行fn1()函数
fn2();作为值的函数:将一个函数作为另一个函数的结果进行返回,作为结果返回的函数称之为作为值的函数。(在一个函数的return 后编写一个函数)
- 作为值的函数的闭包
function fn(){
var a=‘a’;
return function(){
return a; //a
}
}
var fun=fn();
console.log(fun());
解析:定义一个返回值为函数的函数fn,调用fn后,得到值的返回函数function(){return a;},并将其赋值给fun.得到的结果为fun=function(){return a;},在调用fun,就能最后的输出结果为‘a’
作为回调函数的闭包
function fn(n){
var a=‘a’;
return n(a); //调用,实参
}
var fun=fn( function(c){return c;} );
console.log(fun);
“`
解析:调用fn(),它的结果是什么,是由fn()的返回值来决定的。这里它的返回值是调用函数n(),传递的是实参;它的函数定位为function(c){return c;},将‘a’传入后,得出fun=‘a’,他是一个值,所以另一个全局函数是function(c){return c;}闭包的问题:
将局部变量的生命周期无限延长,将导致浏览器内存溢出。
回调函数都是闭包结构,所以有“回调陷阱”问题。将创建的闭包销毁
解决方案:
最后手动释放资源,闭包函数名=null如何写一个闭包:
1.函数嵌套
2.内部函数访问外部函数的变量
3.使用各种手段将内部函数传递出来
4.调用外部函数例子:闭包的应用
- 对数据可以写入和只读
var getvalue,setvalue;
(function(){
//定义函数中的局部变量,之外的环境不能使用
var content=0;
//该方法只能读取当前局部变量的值
getvalue=function(){
return content;
}
//该方法可以修改当前局部变量的值
setvalue=function(x){
content=x;
}
})()
setvalue(100);
console.log(getvlaue());
解析:首先定义了两个变量,()()自调函数从进入JS代码只能使用一次,在自调函数中重新将两个变量定义为函数,所以当自调函数执行完之后,这两个变量成为全局函数,可以进行多次的调用。