这章主要讨论闭包和原型,以及面向对象和继承。
闭包
闭包充分利用了JS里面作用域的概念,作用域的好处是内部函数可以访问定义它们的外部函数的参数和变量。使用闭包主要是为了读取函数内部的变量或者将函数内部的变量始终保持在内存中,而不让JS垃圾回收机制回收,所以使用闭包会降低系统性能的,应该避免尽量少用。
具体看下面三个例子:
var elems = document.getElementsByTagName('a'); for(var i = 0; i<elems.length; i++){ elems[i].addEventListener('click', function(e){ e.preventDefault(); console.log('i am click' +i); }, false); } for(var i =0; i< elems.length;i++){ elems[i].addEventListener('click', (function(i){ return function(e) { e.preventDefault(); console.log('i am click' + i); } }(i)), false); } for(var i = 0; i < elems.length; i++){ (function(i){ elems[i].addEventListener('click', function(e){ console.log("i am click" + i); },false) }(i)); }
第一个例子肯定有问题,输出的都是i am click11(总共有11个超链接),关键在于触发监听的时候,获取到的i是当前循环之后的变量i,所以值是11.
第二个例子和第三个例子都是实现闭包,第三个例子比较直观易懂。这里还涉及到一个立即执行的函数概念。
立即执行函数
有两种写法,推荐第一种,如下代码所示,立即执行匿名函数,帮助封装一些方法,并且不留下任何全局变量,保护命名空间的干净,另外在闭包中也经常使用。
(function () { alert('hello world!'); }()); (function(){ alert('hello world!'); })();
还可以像匿名函数中传递参数,如下代码所示:当然如果你把一些插件的全局变量,例如JQuery,YUI,Global传进去,那就可以实现在他们插件的基础上扩展,而且不用担心变量污染的问题。
(function(name){ alert('hello world my name is '+name); }('tonylp'));
具体可以参考文章JavaScript学习笔记(十四) 立即执行函数
原型
原型在很多场合都会用到,尤其是面向对象方向。
对于原型(prototype)记住它既是所有function类型的对象的一个属性,这个属性本身又是一个Object对象。(片面的说又是属性又是对象)。所以我们可以给这个prototype对象添加任意的属性和方法,既然prototype是对象的“原型”,那么有该函数构造出来的对象应该都会具有这个“原型”的特性。也可以这么说,prototype提供了一群类对象共享属性和方法的机制。从这里可以看出可以使用prototype在创建新对象的同时避免复制太多的东西占用内存。我可以只赋值新的变量,而使用公共的方法。还可以通过原型继承,实现原型链。
如下代码通过原型实现基础的类,可以看到SayHello这个公用方法。
function Person(name) { this.name = name; } Person.prototype.SayHello = function(){ alert("Hello i am" +this.name); } var BillGates = new Person("Bill Gates"); var SteveJobs = new Person("Steve Jobs"); BillGates.SayHello(); SteveJobs.SayHello(); aler(BillGates.SayHello() === SteveJobs.SayHello()); // true
接下来展示一段原型链代码,object->person->employee,在SteveJobs调用SayHello的时候,首先遍历employee对象,里面没有此方法,然后通过Person.call(this,name),进入Person对象,找到该方法,然后输出,注意传入参数this和name。 其实这也是基于原型的继承,不是么
function Person(name) { this.name = name; } Person.prototype.SayHello = function(){ alert("Hello i am" +this.name); } function Employee(name, salary){ Person.call(this,name); this.salary = salary; } Employee.prototype.showMeTheMoney = function(){ alert(this.name + "$" + this.salary); }; var BillGates = new Person("Bill Gates"); var SteveJobs = new Employee("Steve Jobs", 2345); BillGates.SayHello(); SteveJobs.SayHello(); SteveJobs.showMeTheMoney(); aler(BillGates.SayHello() === SteveJobs.SayHello()); // true
都到这里了,必然能想到如果我在JS内置的对象上使用prototype,就可以扩展内置对象的方法或者属性了,是的,这条路是可行的。
昨天说到一半的创建对象,接着继续,利用原型来创建对象。
基于原型创建对象
方法1,只使用原型,但是这个方法不好,因为把所有对象的属性也挂载到原型上了,太累赘,而且没法改。
var lev=function(){ return "啊打"; }; function Parent(){ }; Parent.prototype.name="李小龙"; Parent.prototype.age="30"; Parent.prototype.lev=lev; var x =new Parent(); alert(x.name);
方法二,和函数结合,混合模型,这个比较好,属性和方法分离
function Parent(){ this.name="李小龙"; this.age=32; }; Parent.prototype.lev=function(){ return this.name; };; var x =new Parent(); alert(x.lev());
方法三,动态加载,其实就是混合模型,多加了一些判断,防止重复创建原型方法。
function Parent(){ this.name="李小龙"; this.age=32; ; if(typeof Parent._lev=="undefined"){ Parent.prototype.lev=function(){ return this.name; } Parent._lev=true; } };
说到原型,肯定说到遍历,然后说到hasOwnProperty()方法,用来判断是否是函数本身的方法和属性,而不是原型链上的方法和属性,很有用。看段简单的代码就可以了。
Object.prototype.bar = 1; var foo={moo : 1} for (var i in foo) { if(foo.hasOwnProperty(i)) { alert(console.log(i)) } }//此时只会输出moo属性
JS中主要大的概念都已经说完了,还剩下一个this指针,通过this还是讲讲继承,还有AMD和CMD的一些简单介绍,JS里面的一些深坑,还有如果想继续往上深入的一些方向,在下一篇文章中结束吧。之后打算看看一些插件的源码,主要的知识点概念就那些,其实这门语言也不难的,被各大浏览器厂商坑了,还有开发这门语言的人,留下了太多的深坑,总的来说还是很有意思的,完全不同于C++,JAVA那套严谨的面向对象知识。
话说你们用webstorm卡不卡,为什么我用着这么卡呢。
以上全部都属个人思想总结,请大家转载的时候附上原创链接: http://www.cnblogs.com/tonylp