从Prototype学习JavaScript面向对象编程
概述
JavaScript是一种基于对象的编程语言。它是灵活的,既有面向过程(也就是面向函数)的编程,也有面向对象的编程。因此我称它是基于对象的编程语言。
对于JavaScript的面向过程的编程特性,就不用多说了,学过C语言的,就很容易理解什么是面向函数的编程。我看Prototype源码主要就是来理解在JavaScript中的面向对象编程的。
我在学习Prototype源码时,将结合C、C#、Java来学习和比较JavaScript中的面向对象编程。我阅读的Prototype的版本是1.7.1 。
学习Prototype需要的基础知识
1、什么是JavaScript对象?
在JavaScript中,对象很简单,就是一对花括号 { } 。
2、了解JavaScript编程的语法和句法
了解JavaScript编程的语法和句法,说白了就是了解JavaScript中编程中常用的符合、关键字、变量定义、控制语句、循环语句、数组等等。这些学过C语言的人都知道。
阅读Prototype源码需要的辅助工具
在学校宿舍,用的是路由器上网,联通老是拦截导致打不开网页。这样对学习是个很大的障碍,只能从官方网站上下载离线的API和Doc 。通过离线文档学习了。如果可以上网,就不必下载离线API和Doc了,并且还可以直接看官方提供的Demo的。
面向对象编程
从官方的文档上来看,将Prototype分为3大块,分别是Ajax Section、DOM Section、Language Section。我对这三块的理解是:Language是讲语言的,是基础。DOM Section是对HTML文档的操作。Ajax Section是使用JavaScript和后台通信。
先学习语言部分。
Language
Class对象
|
var Class = (function() { var IS_DONTENUM_BUGGY = (function(){ for (var p in { toString: 1 }) { if (p === 'toString') return false; } return true; })(); function subclass() {}; function create() { var parent = null, properties = $A(arguments); if (Object.isFunction(properties[0])) parent = properties.shift(); function klass() { this.initialize.apply(this, arguments); } Object.extend(klass, Class.Methods); klass.superclass = parent; klass.subclasses = []; if (parent) { subclass.prototype = parent.prototype; klass.prototype = new subclass; parent.subclasses.push(klass); } for (var i = 0, length = properties.length; i < length; i++) klass.addMethods(properties[i]); if (!klass.prototype.initialize) klass.prototype.initialize = Prototype.emptyFunction; klass.prototype.constructor = klass; return klass; } function addMethods(source) { var ancestor = this.superclass && this.superclass.prototype, properties = Object.keys(source); if (IS_DONTENUM_BUGGY) { if (source.toString != Object.prototype.toString) properties.push("toString"); if (source.valueOf != Object.prototype.valueOf) properties.push("valueOf"); } for (var i = 0, length = properties.length; i < length; i++) { var property = properties[i], value = source[property]; if (ancestor && Object.isFunction(value) && value.argumentNames()[0] == "$super") { var method = value; value = (function(m) { return function() { return ancestor[m].apply(this, arguments); }; })(property).wrap(method); value.valueOf = (function(method) { return function() { return method.valueOf.call(method); }; })(method); value.toString = (function(method) { return function() { return method.toString.call(method); }; })(method); } this.prototype[property] = value; } return this; } return { create: create, Methods: { addMethods: addMethods } }; })(); |
上面是Class对象的源码。
为什么说是Class对象呢?
从上面的代码的结构是var Class=(function(){})();
这个结构在JavaScript中,意思是:先声明一个匿名函数,然后就执行这个匿名函数。
在这个匿名函数的内部,有个return 语句,就是函数执行后返回的内容。返回的内容是一个对象:
|
{ create: create, Methods: { addMethods: addMethods } } |
也就是说,最后代码的样子是:
|
var Calss={ create: create, Methods: { addMethods: addMethods } } |
所以我说Class是个对象。
学习过程中,要按照浏览器处理的思维来学习Prototype框架。
浏览器预处理Class对象的过程是:
1)先知道了产生Class对象的匿名函数的整体结构:
然后执行该匿名函数。执行后返回一个对象,将这个返回的对象赋值给Class。
自定义定义工具类的2种方式
从这个匿名函数(也可以称之为匿名类)中,知道在这个函数中声明了3个函数。但是返回的对象中,却只有2个。
create方法的访问方式是Class.create,
addMethods方法的访问方式是Class.Methods.addMethods。但是继续往下看,addMethods并不这样去用。这个就先不管了。
Class.create 就是执行create方法。从这个写法上来看,让我们有种感觉,感觉这种写法和Java中的访问类的静态方法一样。
所以我可以暂且这样认为:在JavaScript中,函数也是类,不过是实例类。对象也可以是类,对象中的函数。因此在自己写工具类的时候,就可以这么做。
例如下面这个例子:将一个字符串倒序。
写了一个js文件StringUtil.js,写了这么一段代码
|
在下面的HTML文件中引用: |
这个小程序,证明了,我的想法的可行性。但是如果写的工具类中,方法比较多的话,其中某一个或者一些方法只是在这个工具类中的其他方法中使用,也就是说不会开发使用,这样该如何做呢?
在java中,这样的方法,我们一般用private关键字来修饰的,但是JavaScript中没有private、public这样的关键字,怎么做呢?
其实我们可以像Class对象那样去定义自己的工具类。在Class对象中有个方法function subclass(){}
但是return中并没有这个方法。也就是说subclass()是不能直接访问的。这样无形中就相当于给subclass(){}加了个private关键字一样。
两种方式比较,个人认为第二种方式更好一些。
UI框架中组件初始化的原理
看了Prototype中Clss的源码后,我理解了很多UI框架进行初始化的原理了。
譬如ligerUI、zTree、easyUI等。
|
var Class = (function() { var IS_DONTENUM_BUGGY = (function(){ for (var p in { toString: 1 }) { if (p === 'toString') return false; } return true; })(); function subclass() {}; function create() { // 如果在使用Class.create()时,传递的参数中,第一个是个方法(也就是类),那么就是要继承这个类 var parent = null, properties = $A(arguments); if (Object.isFunction(properties[0])) parent = properties.shift(); // klass代表你要创建的类 function klass() { this.initialize.apply(this, arguments); } // 将Class.Methods给了klass,Class.Methods是{addMethods},如此就将addMethods方法给了要创建的类:klass // 也就是说,这句话是让你要创建的类的对象klass也具备了addMethods方法。 // 之所以让klass有addMethods方法,是想让klass对象也具备你在调用Class.create()中传递的参数作为属性。 Object.extend(klass, Class.Methods); klass.superclass = parent; klass.subclasses = []; if (parent) { subclass.prototype = parent.prototype; klass.prototype = new subclass; parent.subclasses.push(klass); } // 将传递的参数都变为klass对象的属性 for (var i = 0, length = properties.length; i < length; i++) klass.addMethods(properties[i]); // 判断你要创建的类klass有没有initialize方法,如果没有,就指定一个空方法给它 // 如此一来,你要创建的类中必定有initialize方法。如果你在使用Class.create来创建你的类的时候,传递了 // initialze方法就是表示你自定义了一个initialize方法 if (!klass.prototype.initialize) klass.prototype.initialize = Prototype.emptyFunction; klass.prototype.constructor = klass; return klass; } // 在调用Class.create方法时,你可能传递多个参数,每个参数又是一个自定义比较复杂的对象,这种情况是很有可能的。 function addMethods(source) { var ancestor = this.superclass && this.superclass.prototype, // 将每一个参数中所有的属性名取出赋值给properties properties = Object.keys(source); if (IS_DONTENUM_BUGGY) { // 如果你重写了toString、valueOf方法,那么就使用你自定义的toString、valueOf方法 if (source.toString != Object.prototype.toString) properties.push("toString"); if (source.valueOf != Object.prototype.valueOf) properties.push("valueOf"); } // 对Class.create中每一个参数对象,都要取得他们所有属性,然后再对每个属性进行遍历 for (var i = 0, length = properties.length; i < length; i++) { var property = properties[i], value = source[property]; // 如果属性是个函数,并且函数的第一个参数名是$super,那么就代表要调用父类的对应方法 // 例如子类继承父类,传递的参数中,有个参数中有一个方法和父类中的方法是同名的,并且 // 这个方法的第一个参数是$super,那么就是代表这个方式是使用父类中的这个方法。 if (ancestor && Object.isFunction(value) && value.argumentNames()[0] == "$super") { var method = value; value = (function(m) { return function() { return ancestor[m].apply(this, arguments); }; })(property).wrap(method); value.valueOf = (function(method) { return function() { return method.valueOf.call(method); }; })(method); value.toString = (function(method) { return function() { return method.toString.call(method); }; })(method); } this.prototype[property] = value; } return this; } return { create: create, Methods: { addMethods: addMethods } }; })(); |
官方的demo可以验证理解的正确性,也可用通过官方的demo来辅助自己理解:
从Prototype学习JavaScript面向对象编程的更多相关文章
- 快速学习JavaScript面向对象编程
到处都是属性.方法,代码极其难懂,天哪,我的程序员,你究竟在做什么?仔细看看这篇指南,让我们一起写出优雅的面向对象的JavaScript代码吧! 作为一个开发者,能否写出优雅的代码对于你的职业生涯至关 ...
- 简单学习JavaScript面向对象编程
JavaScript是一种弱类型语言.有一种原型机制. 1.创建一个空对象:var bill = {}; 给这个对象添加属性和方法: bill.name = "Bill E Goat&quo ...
- JavaScript面向对象编程学习笔记
1 Javascript 面向对象编程 所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量.对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例 ...
- 转:javascript面向对象编程
作者: 阮一峰 日期: 2010年5月17日 学习Javascript,最难的地方是什么? 我觉得,Object(对象)最难.因为Javascript的Object模型很独特,和其他语言都不一样,初学 ...
- 再谈javascript面向对象编程
前言:虽有陈皓<Javascript 面向对象编程>珠玉在前,但是我还是忍不住再画蛇添足的补上一篇文章,主要是因为javascript这门语言魅力.另外这篇文章是一篇入门文章,我也是才开始 ...
- Javascript 面向对象编程(一):封装 作者:yuan一峰
学习Javascript,最难的地方是什么? 我觉得,Object(对象)最难.因为Javascript的Object模型很独特,和其他语言都不一样,初学者不容易掌握. 下面就是我的学习笔记,希望对大 ...
- JavaScript面向对象编程(2)-- 类的定义
最近这一段时间事情太多了,没有时间再继续写,幸好这两天有点小闲,先小写一下JavaScript中面向对象一中推荐的方法.本文承接上一篇JavaScript面向对象编程(1) -- 基础. 上篇说过,J ...
- 深入理解Javascript面向对象编程
深入理解Javascript面向对象编程 阅读目录 一:理解构造函数原型(prototype)机制 二:理解原型域链的概念 三:理解原型继承机制 四:理解使用类继承(继承的更好的方案) 五:建议使用封 ...
- 【转】Javascript 面向对象编程(一):封装
原文链接:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html Javascript ...
随机推荐
- Java基础复习笔记系列 四 数组
Java基础复习笔记系列之 数组 1.数组初步介绍? Java中的数组是引用类型,不可以直接分配在栈上.不同于C(在Java中,除了基础数据类型外,所有的类型都是引用类型.) Java中的数组在申明时 ...
- Nginx负载均衡配置实例详解
负载均衡是我们大流量网站要做的一个东西,下面我来给大家介绍在Nginx服务器上进行负载均衡配置方法,希望对有需要的同学有所帮助哦. 负载均衡 先来简单了解一下什么是负载均衡,单从字面上的意思来理解就可 ...
- 商业银行在CNAPS体系中对各种交易的处理
简单来讲,商业银行在CNAPS体系中, 一)行内的交易 由各个银行的行内业务系统来自行解决信息流和资金流问题: 二)跨行的交易分渠道处理 柜台和网银等渠道,商业银行直接直连央行的大小额以及超级网银来解 ...
- Java经典实例:实现一个简单堆栈
堆栈(Stack)是一种常见的数据结构,符合后进先出(First In Last Out)原则,通常用于实现对象存放顺序的逆序.栈的基本操作有push(添加到堆栈),pop(从堆栈删除),peek(检 ...
- 设计3D标签
java自带的Label太枯燥了,真是拿不出手啊. 所以,我们要设计3D标签!! 看看下面这张图 原理 看看这图,可以看到哈哈有三种颜色:白色.黑色和灰色 实现的时候并不像PS那样,按几个按钮就O了 ...
- 第二章 部署war包到tomcat
以turbine为例. 一.部署 1.下载或者生成war包(从maven上下载war包,并改名字为turbine.war) 2.将turbine.war拷贝到$TOMCAT_HOME/webapps中 ...
- java多线程功力
一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式.多线程编程可以使程序具有两条或两条以上的并发执行线索. 进程是指一个内存中运行的应用程序,每个进程都有自己 ...
- [AngularJS] 使用AngularAMD动态加载Service
[AngularJS] 使用AngularAMD动态加载Service 前言 「使用AngularAMD动态加载Controller」:这篇文章里介绍如何使用AngularAMD来动态加载Contro ...
- JSON.NET 使用技巧
1. 序列化相关技巧 通过特性忽略某些属性 有时候我们会有这样的需求,我们只需要序列化实体类中的一部分属性,这时候我们可以通过声明忽略掉一些我们不需要序列化的属性,有两种方式可以使用么达到这个目标: ...
- js的一些属性
js attribute(): setAttribute():element.setAttribute(name,balue) getAttribute():element.getAttribute( ...