这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
在本章中,我们将分析 John Resig 关于 JavaScript 继承的一个实现 - Simple JavaScript Inheritance, 需要的朋友可以参考下
在本章中,我们将分析 John Resig 关于 JavaScript 继承的一个实现 - Simple JavaScript Inheritance。 John Resig 作为 jQuery 的创始人而声名在外。是《Pro JavaScript Techniques》的作者,而且 Resig 将会在今年秋天推出一本书《JavaScript Secrets》,非常期待。
调用方式 调用方式非常优雅: 注意:代码中的 Class、extend、_super 都是自定义的对象,我们会在后面的代码分析中详解。
- var Person = Class.extend({
- // init是构造函数
- init: function(name) {
- this.name = name;
- },
- getName: function() {
- return this.name;
- }
- });
- // Employee类从Person类继承
- var Employee = Person.extend({
- // init是构造函数
- init: function(name, employeeID) {
- // 在构造函数中调用父类的构造函数
- this._super(name);
- this.employeeID = employeeID;
- },
- getEmployeeID: function() {
- return this.employeeID;
- },
- getName: function() {
- // 调用父类的方法
- return "Employee name: " + this._super();
- }
- });
- var zhang = new Employee("ZhangSan", "1234");
- console.log(zhang.getName()); // "Employee name: ZhangSan"
说实话,对于完成本系列文章的目标 - 继承 - 而言,真找不到什么缺点。方法一如 jQuery 一样简洁明了。
代码分析 为了一个漂亮的调用方式,内部实现的确复杂了很多,不过这些也是值得的 - 一个人的思考带给了无数程序员快乐的微笑 - 嘿嘿,有点肉麻。 不过其中的一段代码的确迷惑我一段时间:
- fnTest = /xyz/.test(function() {
- xyz;
- }) ? /\b_super\b/: /.*/;
我曾在几天前的博客中写过一篇文章专门阐述这个问题,有兴趣可以向前翻一翻。
- // 自执行的匿名函数创建一个上下文,避免引入全局变量
- (function() {
- // initializing变量用来标示当前是否处于类的创建阶段,
- // - 在类的创建阶段是不能调用原型方法init的
- // - 我们曾在本系列的第三篇文章中详细阐述了这个问题
- // fnTest是一个正则表达式,可能的取值为(/\b_super\b/ 或 /.*/)
- // - 对 /xyz/.test(function() { xyz; }) 的测试是为了检测浏览器是否支持test参数为函数的情况
- // - 不过我对IE7.0,Chrome2.0,FF3.5进行了测试,此测试都返回true。
- // - 所以我想这样对fnTest赋值大部分情况下也是对的:fnTest = /\b_super\b/;
- var initializing = false, fnTest = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/;
- // 基类构造函数
- // 这里的this是window,所以这整段代码就向外界开辟了一扇窗户 - window.Class
- this.Class = function() { };
- // 继承方法定义
- Class.extend = function(prop) {
- // 这个地方很是迷惑人,还记得我在本系列的第二篇文章中提到的么
- // - this具体指向什么不是定义时能决定的,而是要看此函数是怎么被调用的
- // - 我们已经知道extend肯定是作为方法调用的,而不是作为构造函数
- // - 所以这里this指向的不是Object,而是Function(即是Class),那么this.prototype就是父类的原型对象
- // - 注意:_super指向父类的原型对象,我们会在后面的代码中多次碰见这个变量
- var _super = this.prototype;
- // 通过将子类的原型指向父类的一个实例对象来完成继承
- // - 注意:this是基类构造函数(即是Class)
- initializing = true;
- var prototype = new this();
- initializing = false;
- // 我觉得这段代码是经过作者优化过的,所以读起来非常生硬,我会在后面详解
- for (var name in prop) {
- prototype[name] = typeof prop[name] == "function" &&
- typeof _super[name] == "function" && fnTest.test(prop[name]) ?
- (function(name, fn) {
- return function() {
- var tmp = this._super;
- this._super = _super[name];
- var ret = fn.apply(this, arguments);
- this._super = tmp;
- return ret;
- };
- })(name, prop[name]) :
- prop[name];
- }
- // 这个地方可以看出,Resig很会伪装哦
- // - 使用一个同名的局部变量来覆盖全局变量,很是迷惑人
- // - 如果你觉得拗口的话,完全可以使用另外一个名字,比如function F()来代替function Class()
- // - 注意:这里的Class不是在最外层定义的那个基类构造函数
- function Class() {
- // 在类的实例化时,调用原型方法init
- if (!initializing && this.init)
- this.init.apply(this, arguments);
- }
- // 子类的prototype指向父类的实例(完成继承的关键)
- Class.prototype = prototype;
- // 修正constructor指向错误
- Class.constructor = Class;
- // 子类自动获取extend方法,arguments.callee指向当前正在执行的函数
- Class.extend = arguments.callee;
- return Class;
- };
- })();
下面我会对其中的 for-in 循环进行解读,把自执行的匿名方法用一个局部函数来替换, 这样有利于我们看清真相:
- (function() {
- var initializing = false,
- fnTest = /xyz/.test(function() {
- xyz;
- }) ? /\b_super\b/: /.*/;
- this.Class = function() {};
- Class.extend = function(prop) {
- var _super = this.prototype;
- initializing = true;
- var prototype = new this();
- initializing = false;
- // 如果父类和子类有同名方法,并且子类中此方法(name)通过_super调用了父类方法
- // - 则重新定义此方法
- function fn(name, fn) {
- return function() {
- // 将实例方法_super保护起来。
- // 个人觉得这个地方没有必要,因为每次调用这样的函数时都会对this._super重新定义。
- var tmp = this._super;
- // 在执行子类的实例方法name时,添加另外一个实例方法_super,此方法指向父类的同名方法
- this._super = _super[name];
- // 执行子类的方法name,注意在方法体内this._super可以调用父类的同名方法
- var ret = fn.apply(this, arguments);
- this._super = tmp;
- // 返回执行结果
- return ret;
- };
- }
- // 拷贝prop中的所有属性到子类原型中
- for (var name in prop) {
- // 如果prop和父类中存在同名的函数,并且此函数中使用了_super方法,则对此方法进行特殊处理 - fn
- // 否则将此方法prop[name]直接赋值给子类的原型
- if (typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name])) {
- prototype[name] = fn(name, prop[name]);
- } else {
- prototype[name] = prop[name];
- }
- }
- function Class() {
- if (!initializing && this.init) {
- this.init.apply(this, arguments);
- }
- }
- Class.prototype = prototype;
- Class.constructor = Class;
- Class.extend = arguments.callee;
- return Class;
- };
- })();
写到这里,大家是否觉得 Resig 的实现和我们在第三章一步一步实现的 jClass 很类似。 其实在写这一系列的文章之前,我已经对 prototype、mootools、extjs、 jQuery-Simple-Inheritance、Crockford-Classical-Inheritance 这些实现有一定的了解,并且大部分都在实际项目中使用过。 在第三章中实现 jClass 也参考了 Resig 的实现,在此向 Resig 表示感谢。 下来我们就把 jClass 改造成和这里的 Class 具有相同的行为。
我们的实现 将我们在第三章实现的 jClass 改造成目前 John Resig 所写的形式相当简单,只需要修改其中的两三行就行了:
- (function() {
- // 当前是否处于创建类的阶段
- var initializing = false;
- jClass = function() { };
- jClass.extend = function(prop) {
- // 如果调用当前函数的对象(这里是函数)不是Class,则是父类
- var baseClass = null;
- if (this !== jClass) {
- baseClass = this;
- }
- // 本次调用所创建的类(构造函数)
- function F() {
- // 如果当前处于实例化类的阶段,则调用init原型函数
- if (!initializing) {
- // 如果父类存在,则实例对象的baseprototype指向父类的原型
- // 这就提供了在实例对象中调用父类方法的途径
- if (baseClass) {
- this._superprototype = baseClass.prototype;
- }
- this.init.apply(this, arguments);
- }
- }
- // 如果此类需要从其它类扩展
- if (baseClass) {
- initializing = true;
- F.prototype = new baseClass();
- F.prototype.constructor = F;
- initializing = false;
- }
- // 新创建的类自动附加extend函数
- F.extend = arguments.callee;
- // 覆盖父类的同名函数
- for (var name in prop) {
- if (prop.hasOwnProperty(name)) {
- // 如果此类继承自父类baseClass并且父类原型中存在同名函数name
- if (baseClass &&
- typeof (prop[name]) === "function" &&
- typeof (F.prototype[name]) === "function" &&
- /\b_super\b/.test(prop[name])) {
- // 重定义函数name -
- // 首先在函数上下文设置this._super指向父类原型中的同名函数
- // 然后调用函数prop[name],返回函数结果
- // 注意:这里的自执行函数创建了一个上下文,这个上下文返回另一个函数,
- // 此函数中可以应用此上下文中的变量,这就是闭包(Closure)。
- // 这是JavaScript框架开发中常用的技巧。
- F.prototype[name] = (function(name, fn) {
- return function() {
- this._super = baseClass.prototype[name];
- return fn.apply(this, arguments);
- };
- })(name, prop[name]);
- } else {
- F.prototype[name] = prop[name];
- }
- }
- }
- return F;
- };
- })();
- // 经过改造的jClass
- var Person = jClass.extend({
- init: function(name) {
- this.name = name;
- },
- getName: function(prefix) {
- return prefix + this.name;
- }
- });
- var Employee = Person.extend({
- init: function(name, employeeID) {
- // 调用父类的方法
- this._super(name);
- this.employeeID = employeeID;
- },
- getEmployeeIDName: function() {
- // 注意:我们还可以通过这种方式调用父类中的其他函数
- var name = this._superprototype.getName.call(this, "Employee name: ");
- return name + ", Employee ID: " + this.employeeID;
- },
- getName: function() {
- // 调用父类的方法
- return this._super("Employee name: ");
- }
- });
- var zhang = new Employee("ZhangSan", "1234");
- console.log(zhang.getName()); // "Employee name: ZhangSan"
- console.log(zhang.getEmployeeIDName()); // "Employee name: ZhangSan, Employee ID: 1234"
这篇文章就接受到这了,下面还有一个系列的文章,大家都可以看下
来源: http://www.phperz.com/article/17/0515/331274.html