匿名函数
没有函数名的函数
- 单独的匿名函数是无法运行和调用的
- 可以把匿名函数赋值给变量
- 通过表达式自我执行,语法:(匿名函数)();
- 匿名函数传递参数,语法:(匿名函数)(参数);
<script>
// 普通函数
function func1(){
alert("我是普通函数");
}
// func1();
// 匿名函数
// function(){
// alert("我是匿名函数");
// }
// 将匿名函数赋值给变量
// var myfunc = function (){
// alert("我是匿名函数2");
// }
// alert(myfunc);//将函数表达式输出
// myfunc();//运行匿名函数
// 匿名函数通过表达式自我执行
(function(name, age){
alert("匿名函数自我执行")
})();
// 匿名函数传递参数
(function(name, age){
alert(name + "今年" + age + "岁")
})("OliGit", 23);
</script>
闭包
概念
- 闭包的英文单词是closure,是指有权访问另一个函数作用域中变量的函数
- 在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。内层的函数可以使用外层函数的所有变量,即使外层函数执行完毕
- 这是JavaScript中非常重要的一部分知识,因为使用闭包可以大大减少我们的代码量,使我们的代码看起来更加清晰等等,总之功能十分强大
相关知识点
- 常见的方式是在函数内部创建另一个函数
function myfunc(){
return function(){
return("***********");
}
}
// alert(myfunc);//输出整个函数表达式
// alert(myfunc());//输出匿名函数表达式
// 调用闭包方式1
// alert(myfunc()());//输出匿名函数返回值
//调用闭包方式2
var bb = myfunc();
alert(bb());
- 闭包的第一个用途:通过闭包可以访问局部变量
// 通过闭包访问局部变量
function func(){
var jb = "局部变量";
}
// alert(jb);//函数外无法访问函数内的局部变量
function func2(){
var jb2 = "局部变量2";
return function(){
return(jb2);//通过匿名函数返回func2()的局部变量jb2
}
}
// 调用方式1
// alert(func2()());
// 调用方式2
var JB2 = func2();
alert(JB2());
- 闭包的第二个用途:可以让这些变量的值始终保持在内存中
1、忧点:可以把局部变量驻留在内存中,可以避免使用全局变量;(全局变量在复杂程序中会造成许多麻烦(比如命名冲突,垃圾回收等),所以推荐使用私有的封装的局部变量。而闭包可以实现这一点)
2、缺点:由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存;所以过度使用闭包会导致性能下降
var num = 100;//全局变量
function add(){
alert(++num);
}
// add();
// add();
// add();//每执行函数一次,num累加一次
function add2(){
var num2 = 100;//局部变量
alert(++num2);
}
//每调用一次初始化局部变量一次,所以无法实现累加
// add2();add2();add2();
//通过闭包实现局部变量的累加
function add3(){
var num3 = 100;//局部变量
return function(){
alert(++num3);
}
}
// 调用方式1:无法实现累加,因为每次调用num3都会初始化一次,
// 这是因为每一次都是重新调用函数到结束,再重新调用一次,所以num3都会初始化一次
// add3()();add3()();add()();//101
// 调用方式2:可以实现累加,因为外部函数add3只执行一次,即num3只初始化一次
//后面执行三次的都是匿名函数,而匿名函数没有初始化num3,所以可是实现累加
var ADD3 = add3();//这里只初始化一次
ADD3();ADD3();ADD3();//101,102,103
ADD3 = null;//最后赋值为空,及时解除引用,否则会占用更多内存
闭包中的this
匿名函数的执行环境具有全局性,this通常是指向window的
- 可以使用对象冒充强制改变this的指向
- 将this赋值给一个变量,闭包访问这个变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>闭包中的this</title>
</head>
<body>
<script>
var name = "The Window";
var obj1 = new Object();
obj1.name = "The Object";
obj1.getName = function(){
return this.name;
}
// alert(obj1.getName());//返回The Object
var obj2 = {
name : "The Object2",
getName: function(){
return this.name;
}
}
// 属性的函数内
// alert(obj2.getName());//返回The Object2
// 闭包里的this/
var obj3 = {
name : "The Object3",
getName : function(){
return function(){
// 匿名函数的执行环境具有全局性,this通常是指向window的
return this.name;
}
}
}
// alert(obj3.getName()());//返回The Window
// 如果想访问属性name
// 方法一:可以使用对象冒充call()强制改变this的指向
// alert(obj3.getName().call(obj3));//返回The Window
// 方法二:将this赋值给一个变量,闭包访问这个变量
var obj4 = {
name : "The Object4",
getName : function(){
//返回的值object对象,属性方法this指向object,也可以看最上面的2个例子
// alert(this);
// 所以可以在属性方法里将this赋值给一个变量,然后匿名函数访问这个变量即可
var self = this;//this指向Object对象
return function(){
// 闭包函数可以向上访问父函数的属性
return self.name;
}
}
}
alert(obj4.getName()());
</script>
</body>
</html>
循环函数中的匿名函数和闭包
循环函数中的匿名函数
function func1(){
var arr = [];
for (var i = 0; i < 5; i++){
arr[i] = "元素" + i;
}
return(arr);
}
// alert(func1());//元素0,元素1,元素2,元素3,元素4
//循环里包含匿名函数
function func2(){
var arr2 = [];
for (var i = 0; i < 5; i++){
arr2[i] = function(){
return "元素" + i;
};//此时匿名函数没有执行,里面的i为参数
}
return(arr2);//此时arr2数组内5个空间都为匿名函数
}
// alert(func2());//输出一个包含五个匿名函数的数组,证明匿名函数没有执行
//function(){ return "元素" + i; },
//function(){ return "元素" + i; },
//function(){ return "元素" + i; },
//function(){ return "元素" + i; },
//function(){ return "元素" + i; }
var Bb = func2();//循环已经结束,此时的i已经变成5,当调用里面的
// 匿名函数时,将i = 5 传入匿名函数内
// alert(Bb[0]); alert(Bb[1]); //function(){ return "元素" + i; }
// alert(Bb[0]()); alert(Bb[1]()); //元素5, 元素5
// for (var i = 0; i < 5; i++){
// alert(Bb[i]());//全是元素5,没有得到想要的结果
// }
// 让匿名函数立刻执行赋值
function func3(){
var arr3 = [];
for (var i = 0; i < 5; i++){
arr3[i] = (function(){
return "元素" + i;
})()//匿名函数立刻执行
}
return arr3;//此时arr3数组内得到的为匿名函数的返回值
}
var Bb3 = func3();
// alert(Bb3.length);//5
// alert(Bb3);//元素0,元素1,元素2,元素3,元素4
// alert(Bb3[0]);//元素0
循环函数中的闭包
function func4(){
var arr4 = [];
for (var i = 0; i < 5; i++){
arr4[i] = (function(n){
return function(){
return "元素" + n;
}
})(i)
}
return arr4;
}
var Bb4 = func4();
// alert(Bb4.length);//5
for (var i = 0; i < 5; i++){
// alert(Bb4[i]);
alert(Bb4[i]());//成功输出元素0-4
}
模仿块级作用域
块级作用域又叫私有作用域,单数JS没有块级作用域的概念;这意味着在块语句(比如for语句)中定义的变量,不会因为离开了for语句后就失效
- 使用了块级作用域后,匿名函数中定义的任何变量,都会在执行结束时被销毁
- 一般来说,我们都应该尽可能少向全局作用域中添加变量和函数;过多的全局变量和函数很容易导致命名冲突
- 使用块级作用域,每个开发者既可以使用自己的变量,又不担心搞乱全局作用域
- 在全局作用域中使用块级作用域可以减少闭包占用内存问题
function func(){
for (var i = 0; i < 5; i++){
// i不会因为离开了for语句就失效
}
var i;//即使重新声明一个i变量,结果还是5
alert(i);//i输出5
}
// func();
// 模仿块级作用域
function func2(){
(function(){
for (var i = 0; i < 5; i++){
}
})()
alert(i);//此时的i已经不存在,会报错:"i is not defined"
}
func2();
私有变量
JavaScript没有私有属性概念;所有的属性都是公用的
私有变量的概念:在任何函数中定义的变量,都是私有变量,因为不能在函数外部访问这些变量
- 私有变量:包括函数的参数、局部变量和函数内部定义的其他函数
- 特权方法:内部创建一个闭包,闭包可以访问私有变量;因此创建用于访问私有变量的公用方法,称作特权方法
- 可以通过构造方法传参来访问私有变量(这种方法的缺点是会为每一个实例创建一组新的方法,不能实现共享)
<script>
function sum(){
var m = 100;
}
// alert(m);//会报错,私有变量(局部变量),外部无法访问
function people(){
this.name = "OliGit";
this.say = function(){
return "我是" + this.name;
}
}
// var pson = new people();
// alert(pson.name);//OliGit
// alert(pson.say());//我是OliGit
function people2(){
var name = "OliGit";//私有变量
function say(){//私有方法、函数
return "我是" + name;
}
this.getName = function(){
return name;
}
this.getSay = function(){
return say();
}
}
var pson2 = new people2();
// alert(pson2.name);//undefined
// alert(pson2.age);//undefined
// alert(pson2.say());//say is not a function
alert(pson2.getName());//OliGit
alert(pson2.getSay());//我是OliGit
// 通过构造方法传参来访问私有变量
function people3(_name){
var name = _name;
this.getName = function(){
return name;
}
this.setName = function(value){
name = value;
}
}
var pson3 = new people3("OliGit");
alert(pson3.getName());
pson3.setName("奥利给");
alert(pson3.getName());
</script>
静态私有变量
通过块级作用域(私有作用域)中定义私有变量或函数,创建对外公共的特权方法
- 首先创建私有作用域
- 定义私有变量或函数
- 定义构造函数和特权方法
- 这种方式创建的私有变量因为使用原型而实现共享
- 同时由于共享,实例也就没有自己的私有变量
<script>
(function(){
var name = "OliGit";
User = function(){}// 构造函数
User.prototype.getName = function(){
return name;
}
User.prototype.setName = function(value){
name = value;
}
})()
var VIP1 = new User();
// alert(VIP1.getName());
VIP1.setName("Olivia");
alert(VIP1.getName());//Olivia
var VIP2 = new User();
alert(VIP2.getName());//Olivia
</script>