|
楼主 |
发表于 2012-10-25 08:02
|
显示全部楼层
本帖最后由 yiyiyicz 于 2012-11-9 12:28 编辑
JavaScript继承详解 <6>
在本章中,我们将分析Prototypejs中关于JavaScript继承的实现。
Prototypejs是最早的JavaScript类库,可以说是JavaScript类库的鼻祖。 我在几年前接触的第一个JavaScript类库就是这位,因此Prototypejs有着广泛的群众基础。
不过当年Prototypejs中的关于继承的实现相当的简单,源代码就寥寥几行,我们来看下。
早期Prototypejs中继承的实现 - var Class = {
- // Class.create仅仅返回另外一个函数,此函数执行时将调用原型方法initialize
- create: function() {
- return function() {
- this.initialize.apply(this, arguments);
- }
- }
- };
-
- // 对象的扩展
- Object.extend = function(destination, source) {
- for (var property in source) {
- destination[property] = source[property];
- }
- return destination;
- };
-
复制代码 调用方式:
- var Person = Class.create();
- Person.prototype = {
- initialize: function(name) {
- this.name = name;
- },
- getName: function(prefix) {
- return prefix + this.name;
- }
- };
- var Employee = Class.create();
- Employee.prototype = Object.extend(new Person(), {
- initialize: function(name, employeeID) {
- this.name = name;
- this.employeeID = employeeID;
- },
- getName: function() {
- return "Employee name: " + this.name;
- }
- });
- var zhang = new Employee("ZhangSan", "1234");
- console.log(zhang.getName()); // "Employee name: ZhangSan"
复制代码 很原始的感觉对吧,在子类函数中没有提供调用父类函数的途径。
Prototypejs 1.6以后的继承实现 首先来看下调用方式:
- // 通过Class.create创建一个新类
- var Person = Class.create({
- // initialize是构造函数
- initialize: function(name) {
- this.name = name;
- },
- getName: function(prefix) {
- return prefix + this.name;
- }
- });
-
- // Class.create的第一个参数是要继承的父类
- var Employee = Class.create(Person, {
- // 通过将子类函数的第一个参数设为$super来引用父类的同名函数
- // 比较有创意,不过内部实现应该比较复杂,至少要用一个闭包来设置$super的上下文this指向当前对象
- initialize: function($super, name, employeeID) {
- $super(name);
- this.employeeID = employeeID;
- },
- getName: function($super) {
- return $super("Employee name: ");
- }
- });
- var zhang = new Employee("ZhangSan", "1234");
- console.log(zhang.getName()); // "Employee name: ZhangSan"
复制代码 这里我们将Prototypejs 1.6.0.3中继承实现单独取出来, 那些不想引用整个prototype库而只想使用prototype式继承的朋友, 可以直接把下面代码拷贝出来保存为JS文件就行了。
- var Prototype = {
- emptyFunction: function() { }
- };
- var Class = {
- create: function() {
- 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) {
- var subclass = function() { };
- subclass.prototype = parent.prototype;
- klass.prototype = new subclass;
- parent.subclasses.push(klass);
- }
- for (var i = 0; i < properties.length; i++)
- klass.addMethods(properties[i]);
- if (!klass.prototype.initialize)
- klass.prototype.initialize = Prototype.emptyFunction;
- klass.prototype.constructor = klass;
- return klass;
- }
- };
- Class.Methods = {
- addMethods: function(source) {
- var ancestor = this.superclass && this.superclass.prototype;
- var properties = Object.keys(source);
- if (!Object.keys({ toString: true }).length)
- properties.push("toString", "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().first() == "$super") {
- var method = value;
- value = (function(m) {
- return function() { return ancestor[m].apply(this, arguments) };
- })(property).wrap(method);
- value.valueOf = method.valueOf.bind(method);
- value.toString = method.toString.bind(method);
- }
- this.prototype[property] = value;
- }
- return this;
- }
- };
- Object.extend = function(destination, source) {
- for (var property in source)
- destination[property] = source[property];
- return destination;
- };
- function $A(iterable) {
- if (!iterable) return [];
- if (iterable.toArray) return iterable.toArray();
- var length = iterable.length || 0, results = new Array(length);
- while (length--) results[length] = iterable[length];
- return results;
- }
- Object.extend(Object, {
- keys: function(object) {
- var keys = [];
- for (var property in object)
- keys.push(property);
- return keys;
- },
- isFunction: function(object) {
- return typeof object == "function";
- },
- isUndefined: function(object) {
- return typeof object == "undefined";
- }
- });
- Object.extend(Function.prototype, {
- argumentNames: function() {
- var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g, '').split(',');
- return names.length == 1 && !names[0] ? [] : names;
- },
- bind: function() {
- if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
- var __method = this, args = $A(arguments), object = args.shift();
- return function() {
- return __method.apply(object, args.concat($A(arguments)));
- }
- },
- wrap: function(wrapper) {
- var __method = this;
- return function() {
- return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
- }
- }
- });
- Object.extend(Array.prototype, {
- first: function() {
- return this[0];
- }
- });
-
复制代码 首先,我们需要先解释下Prototypejs中一些方法的定义。
✪ argumentNames: 获取函数的参数数组
- function init($super, name, employeeID) {
- }
- console.log(init.argumentNames().join(",")); // "$super,name,employeeID"
复制代码 ✪ bind: 绑定函数的上下文this到一个新的对象(一般是函数的第一个参数)
- var name = "window";
- var p = {
- name: "Lisi",
- getName: function() {
- return this.name;
- }
- };
- console.log(p.getName()); // "Lisi"
- console.log(p.getName.bind(window)()); // "window"
复制代码 ✪ wrap: 把当前调用函数作为包裹器wrapper函数的第一个参数
- var name = "window";
- var p = {
- name: "Lisi",
- getName: function() {
- return this.name;
- }
- };
- function wrapper(originalFn) {
- return "Hello: " + originalFn();
- }
- console.log(p.getName()); // "Lisi"
- console.log(p.getName.bind(window)()); // "window"
- console.log(p.getName.wrap(wrapper)()); // "Hello: window"
- console.log(p.getName.wrap(wrapper).bind(p)()); // "Hello: Lisi"
复制代码 有一点绕口,对吧。这里要注意的是wrap和bind调用返回的都是函数,把握住这个原则,就很容易看清本质了。
后接18楼
http://www.cnblogs.com/sanshi/archive/2009/07/08/1519036.html
|
评分
-
1
查看全部评分
-
|