1.函数的申明:三种方法:

  1. function命令
  2. 函数表达式:变量赋值
  3. Function构造函数
 //method 1: function命令
function test(){
console.log('hello function');
} //method 2:函数表达式,赋值给变量
var test1=function(){//这是个匿名函数
console.log('hello function1');
};//注意这里有分号 //method 3:Function构造函数
var test2=new Function(
'return "hello function2"'
); test();
test1();
console.log(test2());

运行结果:

第二种方法:函数表达式,如果函数不是匿名函数,而是加上相应的函数名,则只在函数内部有效。

 var test3=function x(){
return typeof x;//此处有效
};
console.log(test3());
console.log(x());//报错 is not defined;外部访问无效

所以引申可以这样写:

 //这样:内部和外部均能访问test4
var test4=function test4(){
return typeof test4;
};
console.log(test4());

好处:可以在函数体内部调用自身;方便排错(除错工具显示函数调用栈时,一层一层往上抛,将显示函数名,而不是匿名函数)

第三种方法:Function构造函数:可以不加new,返回结果一样;可以传递多个参数,只有最后的这个参数被当做函数体。

建议:不要使用该Function构造函数来申明函数!不直观。

 var test5=new Function(
'return "hello Function construct"'
);
//效果相当于 不加new
// var test5=Function(
// 'return "hello Function construct"'
// );
var test6=new Function(
'a',//函数参数
'b',//函数参数
'return a<b ? a: b'//函数体
);
console.log(test5());//hello Function construct
console.log(test6(10,100));//

函数可以重复申明,但是后申明的函数会覆盖前面申明的函数。而且由于函数名的提升,前面的函数在任何时候均无效。

第一等公民:因为函数与其它数据类型的地位平等,因此称作第一等公民!

JavasScript将函数看作一种值,与数值、字符串、布尔值等等其它值地位相等。凡是可以使用值的地方,均能使用函数。如:将函数赋值给变量;将函数赋值给对象的属性(键);将函数作为参数传入其它函数;将函数作为另一个函数的返回结果!

 console.log('函数是第一等公民');
function print(s){
console.log(s);
}
//将函数赋值给变量
var test7=function (){
console.log('将函数赋值给变量');
};
//将函数赋值给对象的属性
var obj={
x:1,
y:print
};
//将函数作为参数传入另一个函数;将函数作为另一个函数结果返回
function test8(print){
return print;
}
print('haha---');
test7();
obj.y('xixi---');
test8(print)('nicai---');

运行结果:

函数名的提升:JavaScript引擎将函数名等同视为变量名,所以采用function命令申明函数时,函数会像变量提升一样,提升至代码头部

 test9();//相当于函数申明之后,然后调用
function test9(){
console.log('test 9');
}

运行结果:

上面相当于:

 function test9(){
console.log('test 9');
}
test9();

但是如果采用函数表达式:

 test10();//报错
var test10=function (){
console.log('test 10');
};

运行结果:

其实上面代码相当于:

 var test10;
test10();
test10=function(){
console.log('test 10');
};

不要在条件语句中使用申明函数:(ECMAScript规范);虽然浏览器中可能不报错!

 var a=null;
if(a){
function test11(){console.log('test')}
}
test11();

运行结果:

但是有些浏览器可能由于函数名提升,而导致运行结果正确。所以要达到这种效果,可以用函数表达式替代function命令申明函数。

 var a=null;
if(a){
var test11=function (){console.log('test')}
}
test11();

函数属性和方法:

name,length,toString()

name:返回function后面的函数名称

 console.log('---function property name');
function test12(){}
var test13=function (){}
var test14=function x(){}
console.log(test12.name,test13.name,test14.name);

运行结果:

length:返回函数预期传入的参数个数(注意:无论实际传入的参数个数是多少,length返回的是预期的参数个数)

 // var test15=function (a,b,c){};//test15.length=3
function test15(a,b,c){}
console.log(test15.length);
test15(1);
console.log(test15.length);
test15();
console.log(test15.length);

运行结果:

toString():返回函数源码;以字符串形式输出

 function test16(){console.log('test 16')}
console.log(test16.toString());//将函数完整的以字符串形式输出
console.log(typeof test16.toString());//string

运行结果:

函数的参数:JavaScript参数不是必须的,允许参数省略;但是不能只传入靠后的参数,不传入靠前的参数(如果要达到这种效果,传入undefined)

 console.log('---paras');
function test17(a,b){
console.log(a<b?a:b);
}
test17(1,10);//正常传入参数
test17(1);//只传入一个参数
test17(1,2,3);//传入多个参数
// test17(,1);//报错
test17(undefined,100);//替代上面这行代码
console.log(test17.length);//获取预期参数个数:2

运行结果:

如果参数传入的是原始类型的值(数值,字符,布尔值),传递方式是传值传递。即函数内部修改变量并不会影响外部。

如果函数参数传入的是复合类型的值(数组,对象,函数),传递方式是传址传递。即函数内部修改变量将会影响外部。

 var op=10;
function test18(op){//传值传递,不影响外部
op=100;
return op;
}
console.log(test18(op));//
console.log(op);//
var op={x:1,y:2};
function test19(op){//修改复合类型里面的单个属性,会影响外部
return op.x=10;//传址引用
}
console.log(test19(op));//
console.log(op.x);// function test20(op){
return op={x:100,y:1000};//完全替换复合属性,不会影响外部
}
console.log(test20(op));//{x:100,y:1000}
console.log(op);//{x:10,y:2}

arguments对象:函数允许不定数目参数,所以arguments对象包含函数运行时所有参数。arguments[0]表示第一个参数,依此类题。同时需要注意:1.arguments只在函数内部使用有效。2.尽管arguments看起来很像一个数组,但它是对象。函数特有的slice,forEach方法,arguments都没有。

 function test21(a,b){
console.log(arguments[0],arguments[1]);
console.log(arguments.length);
}
test21(10,100);
test21(10,100,100);

运行结果:

所以我们可以通过arguments.length达到查看函数实际运行中带有的参数个数。这是test21.length只能查看预期参数的个数所不具备的!

函数作用域(scope)全局变量(global variable);局部变量(local variable)

 var a1=1000;
function test22(){
console.log(a1);//函数内部能够读取函数外部的变量
}
console.log('---scope');
test22();//
function test2_2(a2){
var a2=100;//var申明,其实是局部变量,不能delete掉
return a2;
}
// console.log(a2);//报错;因为函数外部不能读取函数内部的变量(局部变量) function test23(){
a3=1;//不加var 申明,其实是一个全局变量,可以用delete删除掉
}
test23();
console.log(a3);//因为a3是全局变量,所以能够访问 var a4=4;
function test24(a4){
a4=100;
console.log(a4);//函数内部的局部变量可以覆盖函数外部全局变量
}
test24(a4);

即:函数内部可以访问全局变量和局部变量;局部变量会覆盖全局变量;函数外部通常不能访问函数内部局部变量。

注意:var命令申明的局部变量,在函数内部也存在变量提升的现象。

同时注意:函数执行时所在的作用域,是定义时的作用域,不是调用时的作用域!

 var x=1;
function f1(){
console.log(x);
}
function f2(){
var x=10;
f1();
}
f2();

f1()调用时使用的是定义时的作用域。返回结果是1。

注意:

如果“箭头”前面这里没有var,那么其实是定义了全局变量。返回结果得到是10。所有建议所有变量申明的地方均使用var命令!

注意下面这种情况:

 var f3=function(){
console.log(a);
};
function f4(f3){
var a=10;
f3();
}
//报错,a is not defined
f4(f3);//f3()调用时使用的定义时作用域,所以无法访问a.

函数本身作用域:函数作为第一等公民,是一个值,也有自己的作用域。它的作用域与变量一样,就是其申明时所在的定义域。与其运行时所在作用域无关!

由此也就产生了闭包!

闭包(closure):可以简单理解为“定义在函数内部的函数”。本质上闭包就是将函数内部与函数外部相连接的一个桥梁!

 console.log('---closure');
function g(){
var value=10;
function g1(){
console.log(value);
}
return g1;
}
//正常来说我们不能直接在外面访问函数内部局部变量value,但是借助闭包函数g1,我们可以访问value.
var v=g();
v();//获取到了value

闭包的用处:1.读取函数内部变量;2.让这些变量始终保存在内存中!

故:外层函数每次运行,都会产生一个新的闭包。而这个闭包又会保存外层函数的内部变量,内存消耗很大。所以不能随意滥用闭包,否则容易影响网页性能!

 function clickNum(i){
return function () { return i++};
}
var cli=clickNum(10);//cli始终保存在内存中
console.log(cli());//
console.log(cli());//
console.log(cli());//

闭包的另外一个作用是可以封装对象的私有属性和方法

 function Bird(){
var _weight=10;//私有属性加_表示,类似python
function getWeight(){
return _weight;
}
function setWeight(weight){
_weight=weight;
}
return {//返回对象
getWeight:getWeight,
setWeight:setWeight
} } var b=Bird();
console.log(b.getWeight());//
b.setWeight(100);
console.log(b.getWeight());//

立即调用的函数表达式(IIFE,Immediately-Invoked Function Expression):

分清语句与表达式:(程序由语句组成,语句由表达式组成!)

 //这是语句
function fn1(){}
//这是表达式:函数表达式
var f2=function (){};

将function fn1(){}放入()中,那么就变成了表达式,可以立即进行调用!这就是“立即调用的函数表达式”。

 (function (){console.log('hi!')})();

也可以这样写:

 (function(){console.log('orange')}());

所以扩展开来,JavaScript以表达式来处理的函数定义的方法,均能产生这样的效果。

var g=function (){console.log('white')}();
true&&function(){console.log('blue')}();
!function(){console.log('gold')}();
+function(){console.log('silver')}();
new function(){console.log('red')}();

通常:只对匿名函数使用这种“立即执行的函数表达式”。目的:1.不需为函数命名,2.IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量

eval函数将字符串当做语句执行

 console.log(eval('3+5'));//
console.log(eval('var a=3+5'));//undefined var a=10;
eval('a=100');//如果这里是外部人员输入的,那么内部数据a被修改,产生安全问题
console.log(a);//100,

为了规避上面eval函数所带来的风险,严格模式规定,eval内部申明的变量,不会对外部变量造成影响!

但是严格模式下依然可以修改外部变量,安全问题依然存在!

在代码第一行加上'use strict';

 var a=10;
eval('a=100');//如果这里是外部人员输入的,那么内部数据a被修改,产生安全问题
console.log(a);//100,
eval('var ab=1000;');
console.log(ab);// ab is not defined

运行结果:

此外,eval函数中的字符串不会得到JavaScript引擎的优化,运行速度较慢!所有,建议尽量不要使用eval.

经常可以见到eval解析JSON数据字符串,不过正确的写法是使用JSON.parse方法。

eval还有“直接调用”“间接调用”之分。

直接调用”的作用域是当前作用域;“间接调用”的作用域是全局作用域

 //直接调用
var b1=10;
function testb(){
var b1=1;
eval('console.log(b1)');//当前作用域
}
testb();//
console.log(b1);// //间接调用
var b2=10;
function testc(){
var e=eval;
var b2=1;
e('console.log(b2)')//e是eval的引用,作用域是全局作用域
}
testc();//
console.log(b2);//

参考:阮一峰JavaScript标准参考教程

JavaScript(五):函数(闭包,eval)的更多相关文章

  1. JavaScript碎片—函数闭包(模拟面向对象)

    经过这几天的博客浏览,让我见识大涨,其中有一篇让我感触犹深,JavaScript语言本身是没有面向对象的,但是那些大神们却深深的模拟出来了面向对象,让我震撼不已.本篇博客就是在此基础上加上自己的认知, ...

  2. JavaScript碎片———函数闭包(模拟面向对象)

    经过这几天的博客浏览,让我见识大涨,其中有一篇让我感触犹深,JavaScript语言本身是没有面向对象的,但是那些大神们却深深的模拟出来了面向对象,让我震撼不已.本篇博客就是在此基础上加上自己的认知, ...

  3. JavaScript的函数闭包详细解释

    闭包是指有权访问另一个函数作用域中的变量的函数 一.创建闭包的常见的方式: 就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量. //通过闭包可以返回局部变量 function b ...

  4. JavaScript常用函数之Eval()使用

    eval() 功能:首先解释Javascript代码  然后执行它 用法:Eval(codeString) codeString是包含有javascript语句的字符串,在eval之后使用Javasc ...

  5. javascript匿名函数 闭包

    匿名函数 (function(){                console.info("111111111");            })(); var my = (fun ...

  6. 了解Javascript中函数作为对象的魅力

    前言 Javascript赋予了函数非常多的特性,其中最重要的特性之一就是将函数作为第一型的对象.那就意味着在javascript中函数可以有属性,可以有方法, 可以享有所有对象所拥有的特性.并且最重 ...

  7. 深入理解javascript原型和闭包(2)——函数和对象的关系

    上文(理解javascript原型和作用域系列(1)——一切都是对象)已经提到,函数就是对象的一种,因为通过instanceof函数可以判断. var fn = function () { }; co ...

  8. javascript 中函数eval()

    eval()函数可以把一个字符串当作一个JavaScript表达式一样去执行它. 我们常常在Javascript中间到Eval这个函数, 有些人觉得这个函数很奇怪,可以把一些字符串变的功能很强大 在我 ...

  9. 第一百一十节,JavaScript匿名函数和闭包

    JavaScript匿名函数和闭包 学习要点: 1.匿名函数 2.闭包 匿名函数就是没有名字的函数,闭包是可访问一个函数作用域里变量的函数.声明:本节内容需要有面向对象和少量设计模式基础,否则无法听懂 ...

  10. JavaScript进阶系列01,函数的声明,函数参数,函数闭包

    本篇主要体验JavaScript函数的声明.函数参数以及函数闭包. □ 函数的声明 ※ 声明全局函数 通常这样声明函数: function doSth() { alert("可以在任何时候调 ...

随机推荐

  1. ubuntu 下修改文件访问权限chmod 777 -R *血的教训!没事别乱开权限!用谁开谁的就行。。。最后不要用这个命令,文件操作全部改用终端

    本文转自: 个人建议 Ubuntu下修改目录权限命令如下:chmod 600 name (只有所有者有读和写的权限)chmod 644 name (所有者有读和写的权限,组用户只有读的权限)chmod ...

  2. 【ASP.NET MVC 学习笔记】- 08 URL Routing

    本文参考:http://www.cnblogs.com/willick/p/3343105.html 1.URL Routing告诉MVC如何正确的定位Controller和Action. 2.URL ...

  3. LeetCode 283. Move Zeroes (移动零)

    Given an array nums, write a function to move all 0's to the end of it while maintaining the relativ ...

  4. ABAP开发实用快捷键

    在程序中注释代码往往受输入法影响,看了别人的一篇博客,结合自己的测试发现用如下方法可以直接注释源代码不受输入法影响 添加注释:ctrl + space + < 去掉注释:ctrl + space ...

  5. [C#]使用ILMerge将源DLL合并到目标EXE(.NET4.6.2)

    本文为原创文章,如转载,请在网页明显位置标明原文名称.作者及网址,谢谢! 本文主要是使用微软的ILMerge工具将源DLL合并到目标EXE,因此,需要下载以下工具: https://www.micro ...

  6. 数据结构中,几种树的结构表示方法(C语言实现)

    //***************************************** //树的多种结构定义 //***************************************** # ...

  7. Python函数篇:dict函数和列表生成式

    1.dict函数语法:dict()dict(**kwarg) dict(mapping, **kwarg) dict(iterable, **kwarg) 第一种:dict()构造一个空字典 h=di ...

  8. 利用PowerShell 得到 进程总共占用的内存

    $task = tasklist /nh /fo csv $total = 0 for($i=0; $i -lt $task.count; $i++) { $one = $task[ $i ].Spl ...

  9. aria-label

    元素中的 aria-label用来命名一个元素     它的值可以是任何字符   读屏软件就会读出aria-label里的内容 <div role=”form” aria-labelledby= ...

  10. grid 布局

    display:grid 是一种新的布局方式,旧的布局方式通常有副作用,例如float(需要额外修复浮动)或者inline-block(两个元素之间的空格问题)   把父元素定义为grid,就像表格一 ...