5. 1: 闭包的作用域困扰和自执行函数 (1) (1) 作用域解析和闭包。 首先作用域是指对某一属性(变量)或方法(函数)具有访问权限的代码空间。在 js 中 作用域是在函数中进行维护的。闭包是与作用域相关的一个概念,它指的是“内部函数即使在外部函数执行完毕并终止以后,仍然可以访问外部函数的属性“。当引用一个变量或方法时, js 会沿着由对象执行的路径构成的作用域链对作用域进行解析”。 例子 1: function initAnchors(){ for(var i=1;i<=3;i++){ var anchor=document.getElementById(”anchor“+i); anchor.addEventListener('click',function(){ alert("My id is anchor"+i); }); } } 单击该 dom 对象 onclick 事件时, i 都会是 3 而不是期望的分别为 1,2,3,
6. 1: 闭包的作用域困扰和自执行函数 (2) 具体来说,当点击 click 事件侦听器被调用并在它的内部作用域中查找 i 的值时,结果没有找到(因为 I 的值没有在匿名函数中定义) 解决:可以用一个方法包装事件注册,用一个方法来激活一个作用域。 function registerListener(anchor,i){ anchohr.addListener('click',function()){ alert('My id is anchor'+i); } } 这样原来的代码改写为 function initAnchors(w3cEvent){ for(var i=1;i<=3;i++){ var anchor=document.getElementById(”anchor“+i); registerListener(anchor,i); } }
7. 1: 闭包的作用域困扰和自执行函数 (3) 2 自执行函数。 原型: (function(){})(); (function(){})() , (function(){});// 强制其理解为函数,“函数 ()” 表示执行该函数,即声明后立即执行。 function initAnchors(){ for(var i=1;i<=3;i++){ var anchor=document.getElementById('anchor'+i); ,(function(anchor,i){ anchor.addListener('click',function(){ alert('My id is anchor'+i); }) ; })(anchor,i); } }
8. 2:call 和 apply 重定义执行 Context(1) 在函数的执行环境改变后怎么重新定义 Context function doubleCheck(){ this.message="ar you sure you want to leave?"; } docubleCheck.prototype.sayGoodbye=function(){ return confirm(this.message); } initPage(){ var clickLink=new doubleCheck(); var links=document.getElementsByTagName('a'); for(var i=0;i<links.length;i++){ links[i].addListener('click',clickedLink.sayGoodbye); } }
11. 三、面向类继承的 js(2) 并没有类( class )的概念。其他面向对象语言中大多需要实例化具体类实例,但是 js 中不用, js 里对象本身可以用来创建新对象,而对象也可以继承自其他对象。这个概念称之为“原型化继承”。 不过 js 使用何种对象方案,首先还是应该有一种创建新对象的方法。 js 的做法是任何函数都可以被实例化一个对象。 例子: function User(){} var me=new User(); var you=new me.constructor(); alert(me.constructor=you.constructor); constructor 这个属性的使用,它在每个对象中都存在,并一直指向创建它的函数,这样一来可以有效的复制对象了。用同一个基类创建并赋予不同的属性。 2 :创建可重用的代码 例子:
12. 三、面向类继承的 js(3) function Person(name){ this.name=name; } // 给 Person 对象添加一个新方法 Person.prototype.getName=function(){ return this.name; }; // 创建一个新的 User 对象的构造函数 function User(name,password){ this.name=name; this.password=password; }; //User 对象继承所有 Person 对象的方法 User.prototype=new Person(); User.prototype.getPassword=function(){return this.password;};
13. 三、面向类继承的 js(4) 类式继承对于大部分开发者来说都已经熟悉,只要有了带方法的类就可以把它实例化为对象。 先看一段代码 Function.prototype.method = function (name, func) { this.prototype[name] = func; return this; }; 这一段代码扩展了 Function 类,动态绑定新函数到对象的 prototype 上,这段函数还是比较好理解的。它把函数和构造函数的原型关联起来,之所以有效,是因为所有的构造函数本身都是函数, 所以能获得” method“ 这个新方法。 但是下面这个函数就相当复杂,它实现了子类继承父类时,当子类 override 父类方法时 用类似 super 的方式访问父类被覆盖的方法,这是 js 大师 Douglas Crockford 的作品 值得我们再三拜读。 O(∩_∩)O~
14. 三、面向类继承的 js(5) Function.method('inherits', function (parent) { var depth=0; var p = (this.prototype = new parent()); this.method('uber', function uber(name) { var f, r, t = depth, v = parent.prototype; if (t) { for(t=depth;t>0; t -= 1){ v = v.constructor.prototype; } f = v[name]; } else { f = p[name]; if (f == this[name]) { f = v[name]; } } depth += 1; r = f.apply(this, Array.prototype.slice.apply(arguments, [1])); depth -= 1; return r; }); return this; });
15. 三、面向类继承的 js(6) 这个函数定义了一个继承的方法,同时也定义了一个 uber 方法,该方法可以让子类访问到父类的被 override 的方法。 depth 定义对象继承链的深度,如果 uber 函数内还存在 uber 函数的话,就会递归调用 uber 的闭包函数,注意此时 this 上下文是相同的,使用 depth 来定位 prototype 链回溯的深度。 Function.method('swiss', function (parent) { for (var i = 1; i < arguments.length; i += 1) { var name = arguments[i]; this.prototype[name] = parent.prototype[name]; } return this; });
19. 四:动态构建和 eval 函数 (1) var s='function test(){return 1;}'; // 一个函数定义语句 function demo(){ eval(s); } demo(); alert(test()); //->error:test is not defined 这是因为 test 函数在局部空间定义, demo2 函数内可以访问到,外面( alert )就访问不到了。 将 var s='function test(){return 1;}'; 这句改为 var s=' var test=function(){return 1;}'; 这样的变量型的定义 方式 结果是一样的 因为 test 执行环境在函数 demo 中,所以 外面也访问到 我们将 其改为 var s=' test=function(){return 1;}'; 就可以访问了 因为 test 是全局范围内的变量(因为前面没有 var )。
21. 四:动态构建和 eval 函数( 3 ) // Evalulates a script in a global context globalEval: function( data ) { data = jQuery.trim( data ); if ( data ) { // Inspired by code by Andrea Giammarchi // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-dom.html var head = document.getElementsByTagName("head")[0] || document.documentElement, if ( jQuery.browser.msie ) script.text = data; else // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709). head.insertBefore( script, head.firstChild ); } }, head.removeChild( script ); script.appendChild( document.createTextNode( data ) ); script = document.createElement("script"); script.type = "text/javascript";