JavaScript函数表达式——“函数的递归和闭包”的注意要点css
name
属性和函数提高首先,name
属性,经过这个属性能够访问到给函数指定的名字。(非标准的属性)如:html
function People(){}; console.log(People.name); //People
其次,函数声明提高,意味着能够把函数声明放在调用它的语句后面。如:前端
sayHi(); //调用函数 function sayHi(){ //声明函数 console.log("Hi"); } //不会报错
使用函数表达式则不能够:vue
sayHi(); var sayHi = function(){ console.log("Hi"); } //报错
建立函数的两种方式,一个是函数声明(如第一种方式);一个是函数表达式(如第二种方式)。第二种函数建立方式建立的函数叫“匿名函数”或“拉姆达函数”,由于function 关键字后面没有标识符。node
须要注意的是,做为对比,下面的两种代码中,第一个是错误的(会致使各浏览器出现不一样的问题);第二个才使正确的。代码以下:webpack
var condition = true; if (condition){ function sayHI(){ console.log("hi") } //欢迎加入前端全栈开发交流圈一块儿学习交流:1007317281 sayHI(); //"hello" }else{ function sayHI(){ console.log("hello") } sayHI(); }
报错web
var condition = false; var sayHi; if(condition){ sayHi = function(){ console.log("hi") }; //欢迎加入前端全栈开发交流圈一块儿学习交流:1007317281 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 }
这里也不会出现问题。出现上面问题的根源就是函数提高,就是函数声明和函数表达式之间的区别所致使的。浏览器
给你们推荐一个技术交流学习圈,里面归纳移动应用网站开发,css,html,webpack,vue node angular以及面试资源等。获取资料
对web开发技术感兴趣的同窗,能够加入交流圈👉👉👉1007317281,无论你是小白仍是大牛都欢迎,还有大牛整理的一套高效率学习路线和教程与您免费分享,同时天天更新视频资料。闭包
递归函数就是在一个函数经过名字调用自身的状况下构成的。如:
function factorial(num){ if(num <= 1){ return 1; }else{ return num * factorial(num - 1); } //欢迎加入前端全栈开发交流圈一块儿学习交流:1007317281 } 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执行环境和做用域的注意要点》一文)
如下面的代码为例
function compare(value1,value2){ if (value1 > value2){ return 1; }else if (value1 < value2){ return -1; }else { return 0; } } //欢迎加入前端全栈开发交流圈一块儿学习交流:1007317281 var result = compare(5,10);
调用函数compare
时,函数执行环境中的做用域链:
做用域链本质上是一个指向变量对象的指针列表。
另外一个例子:
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; } //欢迎加入前端全栈开发交流圈一块儿学习交流:1007317281 }; 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; } //欢迎加入前端全栈开发交流圈一块儿学习交流:1007317281 }; } var result = compareNames({name: "Nicholas"},{name: "Greg"}); compareNames = null;
调用compareNames()
函数的过程当中产生的做用域链之间的关系图以下:
常见的闭包的模式通常都是这样:
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 + "<br/>"; }else if(value1 instanceof Array){ return value1.toString() + "<br/>" + value2.toString(); }else{ return false; } //欢迎加入前端全栈开发交流圈一块儿学习交流:1007317281 }; } 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; }; } //欢迎加入前端全栈开发交流圈一块儿学习交流:1007317281 }; 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; }; } //欢迎加入前端全栈开发交流圈一块儿学习交流:1007317281 }; 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
来操做。
感谢您的观看,若有不足之处,欢迎批评指正。
本次给你们推荐一个免费的学习群,里面归纳移动应用网站开发,css,html,webpack,vue node angular以及面试资源等。获取资料👈👈👈
对web开发技术感兴趣的同窗,欢迎加入Q群:👉👉👉1007317281👈👈👈,无论你是小白仍是大牛我都欢迎,还有大牛整理的一套高效率学习路线和教程与您免费分享,同时天天更新视频资料。最后,祝你们早日学有所成,拿到满意offer,快速升职加薪,走上人生巅峰。