《JavaScript高级程序设计》——函数表达式

来源:互联网 发布:增值税发票查询平台js 编辑:程序博客网 时间:2024/05/17 02:59
和以前一样,代码都是自己一点一点敲的,对代码的理解都放在了注释中。
//函数表达式://使用函数声明创建函数:function functionName(arg0,arg1,arg2){console.log();}console.log(functionName.name);//函数名.name:返回函数名。functionNamesayHi();//函数声明提升:在执行代码之前会先读取函数声明。所以可以把函数声明放在调用它的语句后面。function sayHi() {console.log("hi!");}//使用函数表达式创建函数://函数表达式不同于声明。函数声明要求有名字,但函数表达式不需要。没有名字的函数表达式也叫匿名函数(本章重点)。sayHi();//Error:函数还不存在。因为用了匿名函数,没有进行函数声明提升。sayHi = function() {console.log("hi!");};if(true) {//不可以使用这样的条件表达式。function sayHi() {console.log("hi!");}} else {function sayHi() {console.log("no!");}}sayHi();//"no!"本质上是错误的,每种浏览器处理方式不一样。if(true) {//但可以使用这样的条件表达式。sayHi = function() {console.log("hi!");}} else {sayHi = function() {console.log("no!")};}sayHi();//"hi!"//这样不会发生意外,不同的函数根据条件被赋给sayHi//匿名函数实例:创建比较器的函数。function createComparisonFunction(propertyName) {return function(object1,object2) {var val1 = object1[propertyName];var val2 = object2[propertyName];if(val1<val2) {return 1;} else if(val1>val2) {return -1;} else {return 0;}};}function functionName () {return arguments.callee.name;}console.log(functionName());//functionName:使用函数的.name属性获取了函数名。//以前写过的例子:递归函数应该始终使用arguments.callee来递归地调用自身,降低耦合性。function factorial(num) {if(num==1)return 1;elsereturn num*arguments.callee(num-1);//降低耦合性。}console.log(factorial(6));//注意:严格模式下不能使用arguments.callee函数,会发生错误。//解决方法:使用命名函数表达式(给匿名函数命名)var factorial1 = function f(num) {//用f表示匿名函数,即使将函数名改变,函数也不会出错。if(num==1)return 1;elsereturn num*f(num-1);}var factorial2 = factorial1;console.log(factorial1(6));//720console.log(factorial2(6));//720,改了名仍然有效。//闭包:有权访问另一个函数作用域中变量的函数(通过外部函数中的变量创建内部函数)。//例:function createComparisonFunction(propertyName) {return function(object1,object2) {var val1 = object1[propertyName];var val2 = object2[propertyName];//这两行代码访问了外部函数中的变量。if(val1<val2) {return 1;} else if(val1>val2) {return -1;} else {return 0;}};}var fun = createComparisonFunction("name");console.log(fun.propertyName);*//* * 对闭包的理解: * 一般函数在被调用时,会创建一个执行环境,执行环境指向了一组指针:作用域链 * 作用域链中优先级为函数活动对象→全局对象。 * 而闭包被调用时,有闭包的作用域链为闭包活动对象→外部函数活动对象→全局变量; * 外部函数的作用域链为外部函数活动对象→全局对象。 * 由于外部函数的活动对象一直存在于闭包的作用域链中。 * 所以即使外部函数执行完毕以后,其活动对象也不会被销毁(因为一直在被闭包的作用域链引用)。 * 而是一直被匿名函数的作用域链所指向从而留在内存中,直到匿名对象被销毁。 * 解决:少用闭包,在绝对必要时才用。用完以后手动解除其和外部函数活动对象之间的关联(理解:《JavaScript高级程序设计》 P180 图7-2) *///手动解除关联:var compareNames = createComparisonFunction("name");//将函数创建的闭包函数记录以调用var result = compareNames({ name: 'Steve'},{ name: 'Erison'});//调用函数//使用完毕后:compareNames = null;//解除对匿名函数的引用,释放内存//以便垃圾收集器释放闭包活动对象//然后其作用域链中的其他活动对象(除了全局作用域)也被安全地销毁。//闭包的副作用:闭包只能取的外部函数中任何变量的最后一个值function createFunctions() {//目的:通过闭包创建十个函数,十个函数的返回值为从0到9。var result = new Array();for(var i=0;i<10;i++) {result[i] = function() {return i;}}return result;}var result = createFunctions();for(var i=0;i<result.length;i++) {console.log(result[i]());//返回了10个10!}//原因:闭包的作用域链中包含外部函数,所以闭包中的i是通过作用域链在外部函数中搜索来的。//在最后return result时,i是10,故10个函数的返回值都是10.//解决:function createFunctions() {var result = new Array();for(var i=0;i<10;i++) {result[i] = function(num) {return function() {return num;}}(i);//用定义匿名函数的方式给数组赋予函数。最后传入一个参数i。由于函数参数是按值传递的,所以i会被赋值给参数num。}return result;}var result = createFunctions();for(var i=0;i<result.length;i++) {console.log(result[i]())//返回了0到9}//闭包与this对象var name = "The Window";var object = {name: "My Object",get: function() {//get:返回一个匿名函数return function() {return this.name;};}};console.log(object.get()());//The Window:返回的匿名函数中的this指向外部作用域。//!*这里我没搞懂。书上的解释是:内部函数在搜索this和argument变量时,//只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。//让闭包访问外部函数对象:var name = "The Window"var object = {name: "My Object",get: function() {var that = this;return function() {return that.name;}}};console.log(object.get()());//My Object:在函数内定义了that指向object,起到过渡作用,使内部函数访问到object。//细微区别导致this改变:var name = "The Window";var object = {name: "My Object",getName: function() {return this.name;}}console.log(object.getName());//My Object:因为this.name就是object.name。console.log((object.getName)());//My Object:将object中的函数当作一个独立的函数运行,但this的值得到了维持。//因为object.getName()和(object.getName)()的定义是相同的。console.log((object.getName = object.getName)());//The Window://!*这里也没太看懂。书上的解释:因为这个赋值表达式的值是函数本身。//所以this的值不能得到维持。//(因为将object中的函数重新赋予,this没有维持,所以改变了作用域?)//IE中的BUG:如果闭包的作用域链中保存着一个HTML元素,该元素无法被销毁。//因为IE中队JS对象和COM对象使用的不同的垃圾收集例程(JS:标记清除/COM:引用计数)function assignHandler() {var element = document.getElementById("someElement");element.onclick = function() {alert(element.id);};}//由于匿名函数一直保存着对assignHandler活动对象的引用,导致活动对象一直存在,element的引用数至少是1,所占用的内存永远不会被回收。//解决:不让闭包直接访问HTML属性,而是创建一个副本让闭包访问。function assignHandler() {var element = document.getElementById("someElement");var id = element.id;//创建闭包要访问的属性的副本。element.onclick = function() {alert(id);//将内部函数解除对element的引用(变为引用所需要的element对象属性的副本)。};element = null;//解除对element的引用。}//由于被闭包访问的整个活动对象会一直存在,所以即使闭包引用的是id,包含函数中还是会存在一个对element的引用。所以要手动解除对element的引用。//模仿块级作用域:js中没有块级作用域,语句块中的变量实际上在函数中,所以可以用匿名函数来模仿块级作用域。function output() {for(var i=0;i<10;i++) {}var i;//js中,后续声明一个已存在的变量会被无视,i还是10。console.log(i);//后面的输出结果说明了,这里可以访问到i。}output();//10:由于没有块级作用域,导致所有变量都在函数中。所以在函数内的for语句之外也可以访问到i。(function(){//块级作用域:创建一个匿名函数并立即执行。for(var i=0;i<10;i++) {}})();console.log(i);//错误:因为i在匿名函数模仿的块级作用域中,所以外部访问不到。//用途:经常会用在全局作用域中的函数外部,为了不向全局作用域中添加过多的数据,增强封装型,满足程序员们的强迫症。//私有变量//构造函数方法function Person(name) {//通过在构造函数内部定义两个特权方法,定义了私有变量和函数this.getName = function() {//由于没有显式地定义name属性,除了通过这两个方法以外,没有其他方法可以访问name属性。return name;};this.setName = function(value) {name = value;};}var person = new Person("Steve");console.log(person.getName());//通过构造函数定义特权变量缺点:像创建对象的构造函数模式一样,无法实现方法的复用。//静态私有变量//通过在私有作用域(通过匿名函数实现)中定义私有变量或函数,创建特权方法。(function {//私有变量和私有函数var privateVariable = 10;function privateFunction() {return false;}//构造函数MyObject = function() {};//公有/特权方法MyObject.prototype.publicMethod = function() {privateVariable++;return privateFunction;};})();//与构造函数中定义特权方法的区别:将方法定义在原型上,原型上的方法是共享的。因为不管是哪个实例中的此方法都包含着对作用域的引用。(function() {var name = "";Person = function(value) {//不使用var关键字:确保Person为全局函数name = value;}Person.prototype.getName = function(){return name;}Person.prototype.setName = function(value) {name = value;}})();var p1 = new Person("Steve");console.log(p1.getName());var p2 = new Person("Erison");console.log(p1.getName());//Erisonconsole.log(p2.getName());//Erison由于变量的共享,都被改成了Erison//模块模式:为单例添加私有变量和特权方法。var application = function(){//创建了一个含有指定方法的实例:在私有域中定义方法和属性,并添加方法,然后立刻返回一个单例。//私有变量和函数。var components = new Array();//初始化component.push(new BaseComponent());//公共方法return {//用字面量定义法返回一个单例getComponentCount: function(){return component.length;},registerComponent: function(component){if(typeof conponent == "object") {components.push(conponent);}}}();//这种单例以Object形式存在(因为由对象字面量方法定义)。//增强的模块模式。//在返回对象之前加入对其增强的代码。适合单例必须是某种类型的实例。//使用构造函数而不是字面量方法来返回对象//修改后的上述代码: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;}();

0 0
原创粉丝点击