前言

      继承,代码复用的一种模式。和其它高级程序语言相比,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'

模式特点:

  • C不仅继承了P的所有属性,同时还继承了P的原型(P.prototype)的所有属性,但有时候,我们只想继承P的原型的属性;
  • C的构造函数不能传递参数,也就是说无法通过给C构造方法传递参数,C将参数传递到P的构造方法(个人认为这个问题不大大,实现方式二 可以解决这个问题)。

引用对象继承-借用构造函数

借用构造函数模式,其实就是指采用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 说明:默认继承模式可以修改父对象的属性; 而借用构造函数模式中,对象继承的属性仅仅是一个副本

模式特点:

  • 通过这种方式继承,每个子对象都具有父对象的属性,且该属性是父对象属性的副本(非引用),所以它不会出现子对象通过修改继承的属性而导致父对象属性被覆盖的风险;
  • 通过这种方式实现的继承,一个子对象可以继承多个父对象;
  • 子对象只能继承父对象的属性,但是不能继承父对象的prototype对象的属性。

引用对象继承-借用构造函数和设置原型

该模式其实就是默认继承模式和借用构造函数的组合所实现的一种模式,该模式可以解决先前两个模式存在的一些问题,具体见下例:

   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

【前端学习】javascript面向对象编程(继承和复用)的更多相关文章

  1. 快速学习JavaScript面向对象编程

    到处都是属性.方法,代码极其难懂,天哪,我的程序员,你究竟在做什么?仔细看看这篇指南,让我们一起写出优雅的面向对象的JavaScript代码吧! 作为一个开发者,能否写出优雅的代码对于你的职业生涯至关 ...

  2. Javascript 面向对象编程—继承和封装

      前  言 Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象.但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类) ...

  3. 从Prototype学习JavaScript面向对象编程

    概述 JavaScript是一种基于对象的编程语言.它是灵活的,既有面向过程(也就是面向函数)的编程,也有面向对象的编程.因此我称它是基于对象的编程语言. 对于JavaScript的面向过程的编程特性 ...

  4. 简单学习JavaScript面向对象编程

    JavaScript是一种弱类型语言.有一种原型机制. 1.创建一个空对象:var bill = {}; 给这个对象添加属性和方法: bill.name = "Bill E Goat&quo ...

  5. JavaScript面向对象编程学习笔记

    1  Javascript 面向对象编程 所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量.对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例 ...

  6. JavaScript面向对象编程(一)原型与继承

    原型(prototype) JavaScript是通过原型(prototype)进行对象之间的继承.当一个对象A继承自另外一个对象B后,A就拥有了B中定义的属性,而B就成为了A的原型.JavaScri ...

  7. (三)Javascript面向对象编程:非构造函数的继承

    Javascript面向对象编程:非构造函数的继承   这个系列的第一部分介绍了"封装",第二部分介绍了使用构造函数实现"继承". 今天是最后一个部分,介绍不使 ...

  8. (二)Javascript面向对象编程:构造函数的继承

    Javascript面向对象编程:构造函数的继承   这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生成实例. 今天要介绍的是,对象之间的"继承 ...

  9. Javascript面向对象编程(三):非构造函数的继承(对象的深拷贝与浅拷贝)

    Javascript面向对象编程(三):非构造函数的继承   作者: 阮一峰 日期: 2010年5月24日 这个系列的第一部分介绍了"封装",第二部分介绍了使用构造函数实现&quo ...

随机推荐

  1. B树叶子节点split

    一.B-Tree索引的分裂 1. 创建测试表 SQL> create table split_tab (id number, name varchar2(100)); 表已创建. SQL> ...

  2. SQL之用户自定义函数

    关于SQL Server用户自定义的函数,有标量函数.表值函数(内联表值函数.多语句表值函数)两种. 题外话,可能有部分朋友不知道SQL Serve用户自定义的函数应该是写在哪里,这里简单提示一下,在 ...

  3. 【转】如何删除一个repository(仓库)

    原文网址:http://my.oschina.net/anna153/blog/377758?p=1 如何删除自己创建的一个项目,我浏览了一下github网站,确实不太容易找到删除功能.这里介绍一下啊 ...

  4. iOS 9之New System Fonts(San Francisco 字体)

    金田 此次苹果发布的iOS 9系统测试版目前已经开放下载,新系统将弃用Helvetica字体,改用了San Francisco字体, 包括系统菜单.App名称等各个部分. 最初San Francisc ...

  5. Android AutoCompleteTextView和MultiAutoCompleteTextView使用

    Android AutoCompleteTextView和MultiAutoCompleteTextView的功能类似于百度或者Google在搜索栏输入信息的时候,弹出的与输入信息接近的提示信息: 它 ...

  6. cf437A The Child and Homework

    A. The Child and Homework time limit per test 1 second memory limit per test 256 megabytes input sta ...

  7. [Qt] searchBox 搜索框实现

    [Qt] searchBox 搜索框实现 也就是在lineEdit中加入button.在搜索框的右边会有个小小的搜索图标,输入内容之后,搜索的图标会变成叉叉. 类中的IconHelper见我的另一篇博 ...

  8. 初识前端HTML

    HTML 超文本标记语言 HTML的解析 顾名思义,HTML就是由一个个的标签组成的,组成后,HTML可被浏览器直接识别以及处理成我们想给用户展示的样子. 下面我们就来解析HTML的一个个标签. &l ...

  9. #爬虫必备,解析html文档----beautifulsoup的简单用法

    #出处:http://mp.weixin.qq.com/s?__biz=MjM5NzU0MzU0Nw==&mid=201820961&idx=2&sn=b729466f334d ...

  10. 为MyEclipse加入自己定义凝视

    非常多时候我们默认的MyEclipse的类凝视是这种,例如以下图 能够通过改动MyEclipse的凝视规则来改变,不但能够改动类的.还能够改动字段.方法等凝视规则,操作方法例如以下 1.针对方法的凝视 ...