递归
递归函数是指一个
函数在自身内部调用本身.
- 当通过
名字调用本身时,又将函数索引赋给另外一个变量,将自身置为null时,调用该函数会报错。
function sum(a){
if(a==1){
return 1;
}else{
return a*sum(a-1);
}
}
console.log("不改变函数索引指向时没问题:"+sum(4));
var test=sum;
sum=null;
console.log("改变了函数索引:"+test(24));
//打印结果
不改变函数索引指向时没问题:24
e:\vue\vue-demo\src\view\test.js:5
return a*sum(a-1);
^
TypeError: sum is not a function
- 在
非严格模式下可以使用arguments.callee()方法避免上述问题
function sum(a){
if(a==1){
return 1;
}else{
return a*arguments.callee(a-1);
}
}
var test=sum;
sum=null;
console.log("改变了函数索引依旧没问题:"+test(4));
//打印结果
改变了函数索引依旧没问题:24
- 以函数表达式
结合函数声明的方式定义,不论是严格模式还是非严格模式
var factorical=(function f(num){
if(num<=1){
return 1;
}else{
return num*f(num-1);
}
})
var cur=factorical;
factorical=null;
f=null;
console.log(cur(4));
//打印结果
24
闭包
- 匿名函数:
function(){},既没有函数名,也没有赋给变量.
function sayName(){
var name="zhangsan";
return function(){
return name;
}
}
console.log(sayName()())
//打印结果
zhangsan
- 闭包:是指
有权访问另一个函数作用域的函数 ---->函数里面定义函数
要想理解
闭包这个概念,首先要清楚执行环境的作用域链和变量对象.
-
后台的
每个执行环境都有一个表示变量的对象---->变量对象。 -
全局环境的变量对象始终存在,而像函数这样的局部环境的变量对象,只在函数执行过程中存在。 -
创建并调用函数的
作用域链的过程图: -
变量会沿着作用域链往前找.具体实例分析和说明:
function createCompareFunction(propName){
return function(object1,object2){
var value1=object1[propName];
var value2=object2[propName];
if(value1>value2){
return 1;
}else if(value2==value1){
return 0;
}else{
return -1;
}
}
}
var ob1={
name:"zhangsan",
age:24
};
var ob2={
name:"lisi",
age:25
};
//创建函数
var compareAge=createCompareFunction("age");
//调用函数
var result=compareAge(ob1,ob2);
console.log(result);
//打印结果
-1
上述示例的执行环境的作用域链图:
闭包和变量
闭包只能取得包含函数中任何变量的最后一个值,这是闭包的副作用----->闭包一般是函数套着函数,包含函数是指包含闭包函数的函数。
function createFunctions(){
var result=new Array();
for(var i=0;i<10;i++){
result[i]=function(){
return i;
}
}
return result;
}
// result是一个函数数组
var results=createFunctions();
console.log("i的最终值是10");
console.log("调用result数组里任意一个函数获得:"+results[0]());
//打印结果
i的最终值是10
调用result数组里任意一个函数获得:10
注意:根据作用域链,如果results[0]函数里找不到i的值,会在createFunctions()查找i的值,发现i=10.
解决方式:再嵌套一个函数
function createFunctions(){
var result=new Array();
for(var i=0;i<10;i++){
result[i]=function(num){
return function(){
return num;
}
}(i);
}
return result;
}
// result是一个函数数组
//每个function()的作用域链上一层都有一个function(num)活动对象,而num的值不同
var results=createFunctions();
console.log("调用results[0]:"+results[0]());
console.log("调用results[1]:"+results[1]());
//打印结果
调用results[0]:0
调用results[1]:1
this 对象
- 当函数作为某个
对象的方法调用时,this等于那个对象. - 匿名对象的执行环境具有
全局性,this指向window对象. - 每个函数在被
调用时都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量.
//内部函数是指function(),外部函数是指getNameFunc()
var object={
name:"my object",
getNameFunc: function(){
return function(){
return this.name;
}
}
}
console.log(object.getNameFunc()());
//打印结果
undefined
注意: function()在getNameFunc()找不到this对象,令that=this即可
var object={
name:"my object",
getNameFunc: function(){
var that=this;
return function(){
return that.name;
}
}
}
console.log(object.getNameFunc()());
//打印结果
my object
内存泄漏
- 如果闭包的
作用域链中保存着一个HTML元素,那么就意味着该元素将无法被销毁.
function assignHandler(){
var element=document.getElementById("someElement");
element.onclick=function(){
console.log(element.id);
}
}
解决方式:
function assignHandler(){
var element=document.getElementById("someElement");
var id=element.id;
element.onclick=function(){
console.log(id);
}
element=null;
}
模仿块级作用域
- 在
JavaScript中块级作用域外可以访问块级作用域内的变量
function assignHandler(){
for(var i=0;i<10;i++){
var j=3;
}
console.log("i的最终值:"+i);
console.log("j的值:"+j);
}
assignHandler();
//打印结果
i的最终值:10
j的值:3
在js中多次声明同一个变量,会对后面的声明视而不见,不过依旧会按照后续的变量声明初始化.(指在同一个函数中)
函数里定义的变量是局部变量,在函数外不能访问,可通过匿名函数模拟块级作用域------->(function(){})(),匿名函数必须用括号括起来再调用。
function assignHandler(){
(function(){
for(var i=0;i<4;i++){
console.log(i);
}
})();
console.log("无法访问,此时i的值为:"+i);
}
assignHandler();
//打印结果
0
1
2
3
ReferenceError: i is not defined
私有变量
- 函数
里面定义的变量是私有变量,可通过特权方法来访问这些私有变量。 特权方法:在函数内部定义一个闭包,而闭包可以通过作用域链来访问函数的私有变量。
function Person(name){
this.getName=function(){
return name;
};
this.setName=function(value){
name=value;
};
}
var person=new Person("zhangsan");
console.log(person.getName());
person.setName("lisi");
console.log(person.getName());
//打印结果
zhangsan
lisi
不直接用this.name=name,因为这样定义它的实例就可以访问了。
静态私有变量
- 在
java中静态变量类变量,所有实例可共享,很容易让我们想起js的对象原型 - 私有变量可通过
闭包的方式定义. - 那么
静态私有变量,可结合两者定义.
(function(){
var privateVariable=10;
function privateFunction(){
return false;
};
//没用 var定义,则是全局变量
MyObject=function(){};
MyObject.prototype.publicMethod=function(){
privateVariable++;
return privateFunction();
};
})();
var ob1=new MyObject();
console.log(ob1.publicMethod());
var ob2=new MyObject();
console.log(ob2.publicMethod());
//打印结果
false
false
对象设置真正的私有或者静态私有变量,不直接把变量与对象绑定,而是闭包(也就是方法)获取或者设置变量。
示例2:
(function(){
var name="";
Person=function(value){
name=value;
};
Person.prototype.getName=function(){
return name;
};
Person.prototype.setName=function(value){
name=value;
}
})();
var person1=new Person("zhangsan");
console.log(person1.getName());
var person2=new Person("zhangdsan");
person2.setName("lisi");
console.log(person2.getName());
//打印结果
zhangsan
lisi
模块模式
- 模块模式是为
单例创建私有变量和特权方法 - 单例在
js中是通过字面量的方式定义的.
var person={name:"zhangsan"}
- 模块模式通过为
单例添加私有变量和特权方法能够使其得到增强.
var singleton=function(){
var privateVariable=10;
function privateFunction(){
return false;
}
//返回一个对象
return {
publicProperty: true,
publicmethod: function(){
privateVariable++;
return privateFunction();
}
}
}();
console.log(singleton.publicmethod());
//打印结果
false
示例2:留出一个方法(接口)注册组件
var application=function(){
var components=new Array();
components.push(new BaseComponent());
//返回对象来操作私有变量 components
return {
getComponentCount:function(){
return components.length;
},
registerComponent:function(component){
if(typeof component =="object"){
components.push(component);
}
}
};
}();
application.registerComponent(component1);
如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式
增强的模块模式
- 与原本返回
任意对象(return {})不同,增强的模块模式是对某种类型的实例进行增强。
var application=function(){
var components=new Array();
components.push(new BaseComponent());
// BaseComponent为一种对象类型
var app=new BaseComponent();
app.getComponentCount=function(){
return components.length;
};
app.registerComponent=function(component){
if(typeof component =="object"){
components.push(component);
}
};
return app;
}();
application.registerComponent(component1);