5.继承 继承可以分成:构造函数的继承和非构造函数的继承。
5.1 构造函数的继承 构造函数的继承,可以用以下五种方法实现。
1)使用call/apply方法 1 2 3 4 5 6 7 8 9 10 11 function People (name,age ) { this .name=name; this .age=age; } function Student (name,age,grade ) { People.apply(this ,arguments ); this .grade=grade; } var ww=new Student("ww" ,"22" ,"100分" );ww.name;
总结:
a. 用call/apply方法能改变this的指向,上面的代码将People对象指向了Student对象。 b. apply使用方法是apply(this,[arg0,arg1,…]); c. call使用方法是call(this,arg0,arg1,…)。
2)使用prototype属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function Animals ( ) { this .category='animals' ; } function People ( ) { this .name='ww' ; this .say=function ( ) { console .log('My name is ' +this .name); } } People.prototype.constructor == People; People.prototype = new Animals(); People.prototype.constructor == People; People.prototype.constructor == Animals.prototype.constructor; People.prototype.constructor == Animals; People.prototype.constructor = People; People.prototype.constructor == People; var p=new People();p.category; p.constructor == People.prototype.constructor;
总结:
a.任何一个prototype对象都有一个constructor属性,指向它的构造函数。 b.每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。
3)直接继承prototype 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function Animals ( ) { }Animals.prototype.category='animals' ; function People ( ) { this .name='ww' ; this .say=function ( ) { console .log('My name is ' +this .name); } } People.prototype=Animals.prototype; Animals.prototype.constructor == Animals; People.prototype.constructor=People; Animals.prototype.constructor == Animals; var p=new People();p.category;
总结:
与方法一相比,这样做的优点:效率比较高(不用执行和建立Animal的实例了),比较省内存;缺点:People.prototype和Animal.prototype现在指向了同一个对象People。
4)利用空对象作为中介 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function Animals ( ) { }Animals.prototype.category='animals' ; function People ( ) { this .name='ww' ; this .say=function ( ) { console .log('My name is ' +this .name); } } function Empty ( ) {}Empty.prototype=Animals.prototype; People.prototype=new Empty(); Animals.prototype.constructor == Animals; People.prototype.constructor=People; Animals.prototype.constructor == Animals; var p=new People();p.category;
将代码进行封装,代码如下:
1 2 3 4 5 6 7 function extend (child, parent ) { var Empty=function ( ) {}; Empty.prototype=parent.prototype; child.prototype=new Empty(); child.prototype.constructor=child; child.uber=parent.prototype; }
总结:
a. 这个extend函数,就是YUI库如何实现继承的方法。 b. 为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。相当于在子对象上打开一条通道,可以直接调用父对象的方法。
5)拷贝继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function Animals ( ) { }Animals.prototype.category='animals' ; function People ( ) { this .name='ww' ; this .say=function ( ) { console .log('My name is ' +this .name); } } function extend (child, parent ) { var p=parent.prototype; var c=child.prototype; for (var i in p){ c[i]=p[i]; } c.uber=p; } extend(People,Animals); var p=new People();p.category;
5.2 非构造函数的继承 非构造函数的继承,可以用以下三种方法实现。
1)使用Object 1 2 3 4 5 6 var chinese={ nation :'中国' }; var doctor=new Object (chinese);doctor.carrer='医生' ; doctor.nation;
总结:
先在父对象的基础上生成子对象,然后再加上子对象本身的属性,这样也能实现继承。
2)浅拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function copy (parent ) { var child={}; for (var i in parent) { child[i]=parent[i]; } child.uber=parent; return child; } var chinese={ nation :'中国' , birth :['成都' ,'南京' ,'上海' ] }; var doctor=copy(chinese);doctor.carrer='医生' ; doctor.nation; doctor.birth.push('北京' ); doctor.birth; chinese.birth;
总结:
a.把父对象的属性,全部拷贝给子对象,也能实现继承。 b.如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正地拷贝,因此存在父对象被篡改的可能。
3)深拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function deepCopy (parent,child ) { var child=child||{}; for (var i in parent) { if (typeof parent[i] === 'object' ) { child[i]=(parent[i].constructor === Array )?[]: {}; deepCopy(parent[i],child[i]); } else { child[i]=parent[i]; } } return child; } var chinese={ nation :'中国' , birth :['成都' ,'南京' ,'上海' ] }; var doctor=deepCopy(chinese);doctor.carrer='医生' ; doctor.nation; doctor.birth.push('北京' ); doctor.birth; chinese.birth;
将代码进行封装,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 function extend (parent,child ) { var child=child||{}; for (var i in parent){ if (typeof parent[i] === 'object' ){ child[i]=(parent[i].constructor === Array )?[]: {}; extend(parent[i],child[i]); }else { child[i]=parent[i]; } } return child; }
总结:
a. 所谓”深拷贝”,就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用”浅拷贝”就行了。 b. jQuery库使用的就是这种继承方法。
大总结:
继承的方式如此多,那究竟该用哪个呢? 简单讲实现继承最好的方式有三个:
1.使用apply/call方法; 2.使用“第三者”将父对象的prototype赋值给子对象的prototype; 3.递归拷贝父对象的属性和方法到子对象。
6.异步 将异步之前,先理解下JavaScript的运行机制。
当JavaScript代码被浏览器解析时,会生成一个主线程,同步任务将从上到下依次执行。可是如果遇到耗时很长的异步任务时(如调接口,定时器等),会把一个个异步任务存放在”任务队列”里,执行完同步任务后,”任务队列”通知主线程某个异步任务可以执行了,该任务才会进入主线程执行。只要主线程空了,主线程就会去读取”任务队列”,”任务队列”反复通知,主线程反复执行,当异步任务都完成后主线程结束,这就是JavaScript的运行机制。
主线程从”任务队列”中读取异步任务,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件轮询)。
所以异步是为了解决JavaScript执行环境是单线程问题。
异步编程有以下几种方法:
1.回调函数 2.事件监听 3.发布/订阅 4.Promises/A规范的对象
1)回调函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function test ( ) { console .log('1' ); setTimeout (function ( ) { console .log('2' ); },10 ); console .log('3' ); } test(); function test2 (callback ) { console .log('1' ); setTimeout (function ( ) { console .log('2' ); callback(); },10 ); } test2(function ( ) { console .log('3' ); });
总结:
回调函数的优点:简单,缺点:高度耦合,每个任务只能指定一个回调函数。
2)事件监听 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 var ww={ events :new Array (), AddNewEvent :function (eventName ) { var newEvent={ eventName :eventName, eventHandles :new Array () }; this .events.push(newEvent); return newEvent; }, GetEventByName :function (eventName ) { for (var i=0 ;i<this .events.length;i++){ if (this .events[i].eventName == eventName){ return this .events[i]; } } return null ; }, AddEventhandler :function (eventName,handler ) { var myevent=this .GetEventByName(eventName); if (myevent == null ){ myevent=this .AddNewEvent(eventName); } myevent.eventHandles.push(handler); }, RaiseEvent :function (eventName,params ) { if (!eventName){ console .log('no eventName' ); return ; } var myevent=this .GetEventByName(eventName); if (myevent != null ){ for (var i=0 ;i<myevent.eventHandles.length;i++){ if (myevent.eventHandles[i] != null ){ if (params != null ){ myevent.eventHandles[i].call(this , params); }else { myevent.eventHandles[i].call(this ); } } } }else { console .log('no event' ); } } } function test2 ( ) { console .log('1' ); setTimeout (function ( ) { console .log('2' ); ww.RaiseEvent('whoNum' ,{num :'3' }); },10 ); ww.AddEventhandler('whoNum' ,function (obj ) {console .log(obj.num)}); } test2();
总结:
事件监听的优点:去耦合,缺点:运行流程会变得很不清晰。
3)发布/订阅 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 var PubSub={};(function (p ) { var topics={},lastUid=-1 ; p.publish=function (topic,data ) { if (!topics.hasOwnProperty(topic)){ return false ; } var subscribers=topics[topic]; for (var i=0 ,j=subscribers.length;i < j;i++){ subscribers[i].func(topic,data); } return true ; }; p.subscribe=function (topic,func ) { if (!topics.hasOwnProperty(topic)){ topics[topic]=[]; } var token=(++lastUid).toString(); topics[topic].push({token :token,func :func}); return token; }; p.unsubscribe=function (token ) { for (var m in topics){ if (topics.hasOwnProperty(m)){ for (var i=0 ,len=topics[m].length;i < len;i++){ if (topics[m][i].token === token){ topics[m].splice(i,1 ); return token; } } } } }; }(PubSub)); function test2 ( ) { console .log('1' ); setTimeout (function ( ) { console .log('2' ); PubSub.publish('whoNum' ); },10 ); PubSub.subscribe('whoNum' ,function ( ) {console .log('3' );}) } test2();
总结:
事件监听的优点:去耦合,缺点:运行流程会变得很不清晰。
4.Promises/A规范的对象 Promises/A是由CommonJS组织制定的异步模式编程规范,有不少库已根据该规范及后来经改进的Promises/A+规范提供了实现,如Q, Bluebird, when, rsvp.js, mmDeferred, jQuery.Deffered()等。 下面重点讲下jQuery.Deffered对象。
总结:
1.$.Deferred()生成deferred对象 ()里可以是匿名函数,默认参数为生成的deferred对象 2.promise() 无法操作resolve()、reject() 3.done()相当于success fail()相当于error 4.resolve() 将未完成变成已完成 然后触发done reject() 将未完成变成已失败 然后触发fail 5.then() 把done和fail一起写 then(successFunc,failFunc) 6.$.when() 为多个函数指定同一个回调函数 使用Deferred为了解决异步问题,具体执行步骤: loadData方法、loadHtml方法执行完成后执行C方法 loadData <– getCategoryList <– ajax返回的数据 设置接口的值:dtd.resolve(data) ajax 获取接口的值:loadData().done(function(data){使用data}) return $.Deferred()层层调用里面的值
7.高级用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var data=0 ;var channelList=data || 'default' ; var ww={ add :function ( ) { console .log('add' ); } }; ww&&ww.add(); function ($ ) {})(jQuery); $.fn.extend({}); $.extend({}); if (new RegExp ('^\\d+$' ).test(2 )){ console .log('type is intager' ); }
总结:
JavaScript高级用法不是说写法多么的高级,而是在项目中能起到的作用有多高。真正学好JavaScript并不是件容易的事情,还有很长的路需要走。
历经近一周的撰写,终于要和《JavaScript高级》说再见了,不是说后期不会再写JavaScript,而是现在自己认为该总结的都差不多了,如果有遗漏的地方,我后期还会继续讲解。
这篇文章和上篇文章不适合初学者学习和使用,但是随着知识的积累,相信你们也能随便看懂。 下一篇讲JavaScript的设计模式,请大家尽情期待。