python函数闭包和递归_JavaScript函数表达式——“函数的递归和闭包”的注意要点...

函数表达式的基本概念

name属性和函数提升

首先,name属性,通过这个属性可以访问到给函数指定的名字。(非标准的属性)如:

function People(){};

console.log(People.name); //People

其次,函数声明提升,意味着可以把函数声明放在调用它的语句后面。如:

sayHi(); //调用函数

function sayHi(){ //声明函数

console.log("Hi");

} //不会报错

使用函数表达式则不可以:

sayHi();

var sayHi = function(){

console.log("Hi");

} //报错

创建函数的两种方式,一个是函数声明(如第一种方式);一个是函数表达式(如第二种方式)。第二种函数创建方式创建的函数叫“匿名函数”或“拉姆达函数”,因为function 关键字后面没有标识符。

函数提升的常见错误

需要注意的是,作为对比,下面的两种代码中,第一个是错误的(会导致各浏览器出现不同的问题);第二个才使正确的。代码如下:

var condition = true;

if (condition){

function sayHI(){

console.log("hi")

}

sayHI(); //"hello"

}else{

function sayHI(){

console.log("hello")

}

sayHI();

}

报错

var condition = false;

var sayHi;

if(condition){

sayHi = function(){

console.log("hi")

};

sayHi();

}else{

sayHi = function(){

console.log("hello")

};

sayHi(); //hello

}

没有错误

var condition = true;

if(condition){

var sayHi = function(){

console.log("hi")

};

sayHi(); //hi

}else{

var sayHi = function(){

console.log("hello")

};

sayHi(); //hello

}

这里也不会出现问题。出现上面问题的根源就是函数提升,就是函数声明和函数表达式之间的区别所导致的。

函数的递归

递归函数就是在一个函数通过名字调用自身的情况下构成的。如:

function factorial(num){

if(num <= 1){

return 1;

}else{

return num * factorial(num - 1);

}

}

console.log(factorial(4)); //24 4*3*2*1

但是,函数里面包含了函数自身所以,在应该使用arguments.callee来解决该问题。如:

function factorial(num){

if(num <= 1){

return 1;

}else{

return num * arguments.callee(num - 1);

}

}

console.log(factorial(4)); //24 4*3*2*1

但如果使用上面这种方法,则在严格模式下行不通。不过可以使用命名函数表达式来达成相同的结果。如:

var factorial = (

function f(num){

if(num <= 1){

return 1;

}else{

return num * f(num - 1);

}

}

);

console.log(factorial(4)); //24 4*3*2*1

即,把它包含在一个变量里面,在遇到其他这种需要使用arguments.callee的时候都可以这样做。

函数的闭包

闭包就是有权访问另一个函数作用域中的变量的函数。常见的创建闭包的方式就是在一个函数内部创建另一个函数。在此之前应该先掌握作用域链的概念(见《Javascript执行环境和作用域的注意要点》一文《Javascript执行环境和作用域的注意要点》)

作用域链

以下面的代码为例

function compare(value1,value2){

if (value1 > value2){

return 1;

}else if (value1 < value2){

return -1;

}else {

return 0;

}

}

var result = compare(5,10);

调用函数compare时,函数执行环境中的作用域链:

addd83cb41d777fa416ba645c1b9edf5.png

作用域链本质上是一个指向变量对象的指针列表。

另一个例子:

function createComparisonFunction(propertyName){

return function(object1,object2){

var value1 = object1[propertyName];

var value2 = object2[propertyName];

if (value1 < value2){

return -1;

}else if(value1 > value2){

return 1;

}else{

return 0;

}

};

}

var compare = createComparisonFunction("name");

var result = compare({name: "Nicholas"},{name: "Greg"});

这个就相当于:

var compare = function(object1,object2){

var value1 = object1["name"];

var value2 = object2["name"];

if (value1 < value2){

return -1;

}else if(value1 > value2){

return 1;

}else{

return 0;

}

};

var result = compare({name: "Nicholas"},{name: "Greg"});

相当于:

var result = function(){

var value1 = {name: "Nicholas"}["name"];

var value2 = {name: "Greg"}["name"];

if (value1 < value2){

return -1;

}else if(value1 > value2){

return 1;

}else{

return 0;

}

};

console.log(result()); //1

所以,完整的代码如下:

var compareNames = createComparisonFunction("name");

function createComparisonFunction(propertyName){

return function(object1,object2){

var value1 = object1[propertyName];

var value2 = object2[propertyName];

if (value1 < value2){

return -1;

}else if(value1 > value2){

return 1;

}else{

return 0;

}

};

}

var result = compareNames({name: "Nicholas"},{name: "Greg"});

compareNames = null;

调用compareNames()函数的过程中产生的作用域链之间的关系图如下:

ca078fd4a2375763cc8381d673e13738.png

常见的闭包的模式一般都是这样:

var X = function A(a){

return function(b1,b2){...a...} //匿名函数

};

var Y = X(b1,b2);

举个例子:

var obj1 = {

name: "co",

color: ["white","black"]

};

var obj2 = {

name: "lor",

color: ["yellow","red"]

};

function displayProperty(propertyName){

return function(obj1,obj2){

var value1 = obj1[propertyName];

var value2 = obj2[propertyName];

if(typeof value1 === "string"){

return value1 + " and " + value2 + "
";

}else if(value1 instanceof Array){

return value1.toString() + "
" + value2.toString();

}else{

return false;

}

};

}

var displayP = displayProperty("name");

var displayStr = displayP(obj1,obj2);

document.write(displayStr);

displayP = null;

var displayP = displayProperty("color");

var displayStr = displayP(obj1,obj2);

document.write(displayStr);

/*

co and lor

white,black

yellow,red

*/

闭包会携带他包含的函数的作用域,因此会更多的占用内存资源,建议只有在绝对必要的时候再考虑使用闭包。V8 优化后的js 引擎会尝试收回被闭包占用的内存。

闭包与变量

闭包的副作用是闭包只能取得包含函数中任何变量的最后一个值。

this对象

全局函数中this = window;当函作为位某个对象的方法调用时this = 那个对象;但是匿名函数的执行环境又全局性,this 通常指向window;但也有例外:

var name = "the window";

var obj = {

name: "the obj",

getNameFunc: function(){

return function(){

return this.name;

};

}

};

console.log(obj.getNameFunc()()); //"the window" 别忘了要写两个小括号

因为每个函数在被调用的时候都会自动取得两个特殊的变量:this 和arguments;内部函数在搜索这两个变量时,只会搜索到其活动对象为止。

var obj = {

name: "Oliver",

age: 18,

friends: ["alice","troy"],

sayName: function(){

return this.name;

},

sayFriends: function(){

return function(){

return this.friends;

};

}

};

console.log(obj.sayFriends()()); //undefined

上面这个代码就是因为闭包的问题,导致错误。又如:

var friends = "window's friends";

var obj = {

name: "Oliver",

age: 18,

friends: ["alice","troy"],

sayName: function(){

return this.name;

},

sayFriends: function(){

return function(){

return this.friends;

};

}

};

console.log(obj.sayFriends()()); //window's friends 匿名函数执行环境的全局性

解决这个问题的方法是:

var friends = "window's friends";

var obj = {

name: "Oliver",

age: 18,

friends: ["alice","troy"],

sayName: function(){

return this.name;

},

sayFriends: function(){

var outer = this;

return function(){

return outer.friends;

};

}

};

console.log(obj.sayFriends()()); //["alice","troy"] 这样就可以正常搜索到了

又如:

var friends = "window's friends";

var obj = {

name: "Oliver",

age: 18,

friends: ["alice","troy"],

sayWindowFriends: function(){

return function(){

return this.friends;

};

},

sayFriends: function(){

var outer = this;

return function(){

return function(){

return function(){

return outer.friends

};

};

};

}

};

console.log(obj.sayWindowFriends()()); //"window's friends"

console.log(obj.sayFriends()()()()); //["alice", "troy"]

再举个例子:

var obj = {

name: "Oliver",

age: 18,

friends: ["alice","troy"],

sayFriends: function(i){

var outer = this;

return function(j){

return outer.friends[i] + outer.friends[j];

};

}

};

console.log(obj.sayFriends(0)(1)); //alicetroy

另外,在几种特殊情况下,this 的值可能会发生改变。如:

var name = "the window";

var obj = {

name: "my object",

getName: function(){

return this.name;

}

};

console.log(obj.getName()); //my object

console.log((obj.getName)()); //my object 与上面的是相同的,多了个括号而已

console.log((obj.getName = obj.getName)()); //the window

只要不用下面两种方式调用函数即可。

内存泄露

经过上面的一番折腾,最明显的就是window.name 一直占用内存,无法清空。必须手动把它清理掉,用window.name = null来操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值