del 2015-10-16
CSDN博客:javascript面向对象编程(继承和复用)
继承,代码复用的一种模式。和其它高级程序语言相比,javascript有点点不一样,它是一门纯面向对象的语言,在JS中,没有类的概念,但也可以通过原型(prototype)来模拟对象的继承和多态。根据javascript对象的特点,JS中的继承又可以分类为引用对象继承和实例对象继承。
引用对象继承,子引用类型继承父引用类型,然后通过子引用类型生成的实例对象,具有父引用类型的特性。 而实例对象继承,继承得到的对象都具有父实例对象的所有属性和方法,其实就是指对象的复制和克隆。
实例对象继承也有以下几种实现模式:
默认继承模式很简单,如果要C继承P,实现代码:
function Parent(name) { this.name = name || 'Adam'; } Parent.prototype.say = function() { return this.name;}; function Child(name) { }; function inherit(C , P) { C.prototype = new P(); } var c = new C ('xiaoxin' ); alert(c.say()); // 'Adam'
模式特点:
借用构造函数模式,其实就是指采用Apply或Call来让子对象继承父对象的属性,具体看下面例子:
function Article(name) { this.name = name; this.tags =['js','css']; } var article = new Article('parent'); function BlogPost() {} BlogPost.prototype = article; var blog = new BlogPost(); function Parent2 (name) { //父对象2 this.name2 = name; } function StaticPage(name) { Article.apply(this,arguments); // 子对象可以继承多个父对象 Parent2(this,arguments); // 子对象可以继承多个父对象 } var page = new StaticPage("page"); alert(article.hasOwnProperty('tags')); //true alert(blog.hasOwnProperty('tags')); // false 说明:子对象不能继承父对象的prototype中的属性 alert(page.hasOwnProperty('tags')); //true 说明:子对象继承了父对象的属性 blog.tags.push('html'); page.tags.push('php'); alert(article.tags.join(',')); // js,css,html 说明:默认继承模式可以修改父对象的属性; 而借用构造函数模式中,对象继承的属性仅仅是一个副本
模式特点:
该模式其实就是默认继承模式和借用构造函数的组合所实现的一种模式,该模式可以解决先前两个模式存在的一些问题,具体见下例:
function StaticPage(name) { Article.apply(this,arguments); // 子对象可以继承多个父对象 // Parent2(this,arguments); // 此时 子对象可以继承多个父对象 } StaticPage.prototype = new Article(); //设置原型
模式特点:
共享原型模式的原理就是:子类对象和父类对象共享一个原型,即父类对象的原型,具体见下面例子:
StaticPage.prototype = Article.prototype; //共享原型
模式特点:
前面说了,默认继承模式可能会继承父类对象一些你不想继承的属性,而共享原型模式有存在父类对象属性被修改的风险,怎么办呢?临时构造函数模式则很好的解决了以上存在的问题。临时构造函数模式的原理是: 只继承父类对象的原型对象; 不将父类对象原型直接赋值给子类对象的prototype属性,而是引用一个第三方函数对象,将父类函数对象的prototype赋值给这个第三方函数对象的prototype,然后将这个第三方函数对象的实例对象作为子对象的prototype。
说得有点拗口,具体看下面例子吧:
function inherit(C,P) { //可通过Object.create()方法简化下面代码(ES5) var F = function() {}; //构造一个第三方临时函数 F.prototype = P.prototype // 原型赋值 (要继承的东西都放到protype对象里) C.prototype = new F(); //如此,则解决了默认继承和原型共享继承所带来的问题 C.uber = P.prototype ; //可加 可不加 加上后,可以让在对象可以访问父类(超类) C.prototype.constructor = C; // 重置constructor属性
这种方法,就是为了解决借用构造函数和设置原型存在的父对象的属性被继承两次导致效率低下的问题,它是怎么实现的呢?看下面代码:
function Child() {//继承Parent对象里的属性 Parent.call(this); } //inherit可参照上面章节代码 inherit(Child, Parent);
这种方法是最合适,也是用得最广的继承模式,在nodejs中,推荐采用这种方式来实现继承。比如,我想继承EventEmitter引用类型,代码如下:
function MyEvent() { events.EventEmitter.call(this); } util.inherits(MyEvent, events.EventEmitter);//使这个类继承EventEmitter
现代继承和传统继承不同,它指得不是类的继承,而是指对象的继承,其实质就是:创造出一个对象,和父对象具有相同的属性 。 示例:
function object(parent) { //对象继承的公共方法 var F = function() {}; //创建一个临时函数对象 F.prototype = parent; return new F(); //返回一个新的实例对象 } function Person() { this.name = 'xiaoxin'; } Person.prototype.getName = function() { return this.name; } var pa = new Person(); var kid = object(pa); alert(pa.name); // xiaoxin 继承了父对象属性 alert(kid.getName()); //xiaoxin 继承了父对象的prototype属性
对象的复制也是一种对象继承的方式(注意JS中现代继承的意义)。下面是一个简单的浅克隆方式,浅克隆相对简单,如果需要进行精确的对象复制,建议采用深克隆。Jquery中就应用了大量的深度克隆来实现 jquery 功能和插件的扩展。
function extend(parent,child) { //浅克隆的简单实现 var att ; child = child || {}; for(i in parent) { if(parent.hasOwnProperty(i)) { child[i] = parent[i]; } } return child; }
前面说了对象复制这种模式,它对父对象所有属性进行克隆复制,生成一个子对象,从而实现了父对象中属性代码的复用。但是,有时,我们并不需要复用父对象的所有属性,而是仅仅只需要使用某个属性而已,此时我们可以考虑采用借用模式来实现。借用模式的原理:采用apply或call来实现对象中方法的复用。
具体看下面一个例子:
//假如 我们需要复用数组对象的slice()方法,用来截取类数组arguments中的一段 function f() { var args = [].slice.call(arguments,1,3);// 只复用Array中的slice方法。 // var args = Array.prototype.slice.call(arguments,1,3); //需要输入更长的字符,但是节省了创建一个数组的开销 return args; } f(1,2,3,4,5,6); //返回 [2,3]
面上面的模式用来普通的方法(方法类不含this关键字)是没有问题的,但是如果复用的方法内包含有this关键字,且该方法又作为全局方法或回调函数传递,则会出现一点点小问题,具体看下面示例:
var one = { name : "object", say: function(greet) { return greet+","+this.name; } //被复用的方法 里面包含this关键字 } one.say('hi'); // hi,object var two = { name: 'another object' }; one.say.apply(two, ['hello']) ; // hello,another object //这是没问题的,相当于已经绑定对象到 two了 var say = one.say; say('hello'); //hello,underfined 未绑定对象,导致 this为全局对象,所以取不到name值 var yetanother = { name: 'yet another object', method: function(callback) { return callback('Hola'); } } yetanother.method(one.say); // Hola,underfined 未绑定对象,导致 this为全局对象,所以取不到name值
面以上例子表明:如果要复用方法,一定要将复用的对象和被复用的方法绑定,而全局对象或回调函数的形式是未绑定的,如果使用前要绑定呢?要和two对象进行绑定呢?
function bind(method,obj) { //注意这个红色的return 放回的是一个function方法 return function() { //绑定过程 return method.apply(obj,[].slice.call(arguments)); }; } //相当于 var say = function() { return one.say.apply(two,[].slice.call(arguments));} var say = bind(one.say,two); say('hello'); //hello,another object
版权声明:本文为博主原创文章,未经博主允许不得转载。