`
wangjie2013
  • 浏览: 175948 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

几种JavaScript定义类和对象的方法

阅读更多

 

几种JavaScript定义类和对象的方法

  转自:http://www.nowamagic.net/javascript/js_MethodsToCreateClassObject.php

 

最近偶然碰到有朋友问我"hoisting"的问题。即在js里所有变量的声明都是置顶的,而赋值则是在之后发生的。可以看看这个例子:

	var a = 'global';
	(function () {
	    alert(a);
	    var a = 'local';
	})();
 

大家第一眼看到这个例子觉得输出结果是什么?'global'?还是'local'?其实都不是,输出的是undefined,不用迷惑,我的题外话就是为了讲这个东西的。

其实很简单,看一看JavaScript运行机制就会明白。我们可以把这种现象看做"预声明"。但是如果稍微深究一下,会明白得更透彻。

这里其实涉及到对象属性绑定机制。因为所有JavaScript函数都是一个对象。在函数里声明的变量可以看做这个对象的"类似属性"。对象属性的绑定在语言里是有分"早绑定"和"晚绑定"之分的。

【早绑定】是指在实例化对象之前定义其属性和方法。解析程序时可以提前转换为机器代码。通常的强类型语言如C++,java等,都是早绑定机制的。而JavaScript不是强类型语言。它使用的是"晚绑定"机制。

【晚绑定】是指在程序运行前,无需检查对象类型,只要检查对象是否支持特性和方法即可。可以在绑定前对对象执行大量操作而不受任何惩罚。

上面代码出现的"预声明"现象,我们大可用"晚绑定"机制来解释。在函数的作用域中,所有变量都是"晚绑定"的。 即声明是顶级的。所以上面的代码和下面的一致:

	var a = 'global';
	(function () {
	    var a;
	    alert(a);
	    a = 'local';
	})();
 
在alert(a)之前只对a作了声明而没有赋值。所以结果可想而知。

在JavaScript里,我所知道的几种定义类和对象的方式:

直接量方式

使用直接量构建对象是最基础的方式,但也有很多弊端。

	var Obj = new Object;
	Obj.name = 'sun';
	Obj.showName = function() {
	    alert('this.name');
	}
 

我们构建了一个对象Obj,它有一个属性name,一个方法showName。但是如果我们要再构建一个类似的对象呢?难道还要再重复一遍?NO!,我们可以用一个返回特定类型对象的工厂函数来实现。就像工厂一样,流水线的输出我们要的特定类型结果。

工厂方式

	function createObj(name) {
	    var tempObj = new Object;
	    tempObj.name = name;
	    tempObj.showName = function () {
	        alert(this.name);
	    };
	    return tempObj;
	}
	var obj1 = createObj('obj_one');
	var obj2 = createObj('obj_two');
 

这种工厂函数很多人是不把他当做构建对象的一种形式的。一部分原因是语义:即它并不像使用了运算符new来构建的那么正规。还有一个更大的原 因,是因为这个工厂每次产出一个对象都会创建一个新函数showName(),即每个对象拥有不同的版本,但实际上他们共享的是同一个函数。

有些人把showName在工厂函数外定义,然后通过属性指向该方法,可以避开这个问题:

	function showName () {
	    alert(this.name);
	}    
	function createObj(name) {
	    var tempObj = new Object;
	    tempObj.name = name;
	    tempObj.showName = showName;
	    return tempObj;
	}
	var obj1 = createObj('obj_one');
	var obj2 = createObj('obj_two');
 

可惜的是,这种方式让showName()这个函数看起来不像对象的一个方法。

构造函数方式

这种方式是为了解决上面工厂函数的第一个问题,即没有new运算符的问题。可是第二个问题它依然不能解决。我们来看看。

	function Obj(name) {
	    this.name = name;
	    this.showName = function () {
	        alert(this.name);
	    }
	}
	var obj1 = new Obj('obj_one');
	var obj2 = new Obj('obj_two');
 

它的好处是不用在构造函数内新建一个对象了,因为new运算符执行的时候会自动创建一个对象,并且只有通过this才能访问这个对象。所以 我们可以直接通过this来对这个对象进行赋值。而且不用再return,因为this指向默认为构造函数的返回值。同时,用了new关键字来创建我们想 要的对象是不是感觉更"正式"了。可惜,它仍然不能解决会重复生成方法函数的问题,这个情况和工厂函数一样。

原型方式

这种方式对比以上方式,有个很大的优势,就是它解决了方法函数会被生成多次的问题。它利用了对象的prototype属性。我们依赖原型可以重写对象实例。

	var Obj = function () {}
	Obj.prototype.name = 'me';
	Obj.prototype.showName = function () {
	    alert(this.name);
	}
	var obj1 = new Obj();
	var obj2 = new Obj();
 

我们依赖原型对构造函数进行重写,无论是属性还是方法都是通过原型引用的方式给新建的对象,因此都只会被创建一次。可惜的是,这种方式存在两个致命的问题:

  1. 没办法在构建对象的时候就写入想要的属性,因为原型在构造函数作用域外边,没办法通过传递参数的方式在对象创建的时候就写入属性值。只能在对象创建完毕后对值进行重写。
  2. 致命问题在于当属性指向对象时,这个对象会被多个实例所共享。考虑下面的代码:
	var Obj = function () {}
	Obj.prototype.name = 'me';
	Obj.prototype.flag = new Array('A', 'B');
	Obj.prototype.showName = function () {
	    alert(this.name);
	}
	var obj1 = new Obj();
	var obj2 = new Obj();
	 
	obj1.flag.push('C');
	 
	alert(obj1.flag); // A,B,C
	alert(obj2.flag); //A,B,C
 

是的,当flag属性指向对象时,那么实例obj1和obj2都共享它,哪怕我们仅仅改变了obj1的flag属性,但是它的改变在实例obj2中任然可见。面对这个问题,让我们不得不想是否应该把【构造函数方式】和【原型方式】结合起来,让他们互补。。。

构造函数和原型混合方式

我们让属性用构造函数方式创建,方法用原型方式创建即可:

	var Obj = function (name) {
	    this.name = name;
	    this.flag = new Array('A', 'B');
	}
	Obj.prototype = {
	    showName : function () {
	        alert(this.name);
	    }
	}
	var obj1 = new Obj();
	var obj2 = new Obj();
	 
	obj1.flag.push('C');
	 
	alert(obj1.flag); // A,B,C
	alert(obj2.flag); //A,B
 

这种方式有效地结合了原型和构造函数的优势,是目前用的最多,也是副作用最少的方式。

不过,有些追求完美的家伙还不满足,因为在视觉上还没达到他们的要求,因为通过原型来创建方法的过程在视觉上还是会让人觉得它不太像实例的 方法(尤其对于传统OOP语言的开发者来说。)所以,我们可以让原型活动起来,让他也加入到构造函数里面去,好让这个构造函数在视觉上更为统一。而这一系 列的过程只需用一个判断即可完成。

	var Obj = function (name) {
	    this.name = name;
	    this.flag = new Array('A', 'B');
	    if (typeof Obj._init == 'undefined') {
	        Obj.prototype = {
	            showName : function () {
	                alert(this.name);
	            }
	        };
	        Obj._init = true;
	    }
	}
 

如上,用_init作为一个标志来判断是否已经给原型创建了方法。如果是那么就不再执行。这样其实在本质上是没有任何变化的,方法仍是通过原型创建,唯一的区别在于这个构造函数看起来"江山统一"了。

但是这种动态原型的方式是有问题的,《JavaScript高级程序设计》里并没有深究。创建第一个对象的时候会因为prototype在对象实例化之前没来的及建起来,是根本无法访问的。所以第一个对象是无法访问原型方法的。同时这种方式在子类继承中也会有问题。

分享到:
评论

相关推荐

    晋城市-晋城市-街道行政区划_140500_Shp数据-wgs84坐标系.rar

    晋城市-晋城市-街道行政区划_140500_Shp数据-wgs84坐标系.rar

    【Linux系统管理】经典Linux面试题汇总:涵盖路径操作、文件管理、权限设置及磁盘配额查询等核心知识点

    内容概要:本文档汇总了46个经典的Linux面试题及其答案,涵盖了Linux系统操作的基本命令和概念。内容涉及路径表示与目录切换、进程管理、文件和目录操作、权限设置、文件内容查看等多个方面。每个问题都给出了明确的答案,旨在帮助面试者全面掌握Linux命令行操作技能,同时加深对Linux系统原理的理解。 适合人群:准备Linux相关职位面试的求职者,尤其是有一定Linux基础但缺乏实战经验的技术人员。 使用场景及目标:①用于个人自学或面试前复习,巩固Linux基础知识;②作为企业内部培训资料,帮助员工提升Linux操作水平;③为初学者提供系统化的学习指南,快速入门Linux命令行操作。 其他说明:文档内容侧重于实际操作命令的讲解,对于每个命令不仅提供了基本语法,还解释了具体应用场景,有助于读者更好地理解和记忆。建议读者在学习过程中多加练习,将理论知识转化为实际操作能力。

    唐山市-路南区--街道行政区划_130202_Shp-wgs84坐标系.rar

    街道级行政区划shp数据,wgs84坐标系,直接下载使用。

    【C++编程竞赛】华中杯C++真题解析:涵盖函数参数传递、宏定义、数组操作等10个经典算法题型及源码实现

    内容概要:本文提供了10道华中杯C++竞赛真题的详细解析,涵盖多种基础编程技能与高级特性。每道题目不仅包含详细的解题思路和代码实现,还附带了完整的运行结果。具体包括:函数参数传递(指针实现)、宏定义比较、数组元素打印、几何图形面积计算、字符串拼接、素数判断、多态的实现、文件操作、简单计算器和学生信息管理。这些题目帮助读者深入理解C++语言的核心概念和技术应用。 适合人群:对C++有一定了解的编程初学者和中级开发者,尤其是准备参加编程竞赛的学生或程序员。 使用场景及目标:①作为编程练习和竞赛备考资料,帮助读者掌握C++的基本语法和常用算法;②通过实际代码示例加深对C++特性的理解,如指针、宏定义、面向对象编程等;③提供完整的源码供读者参考和调试,增强动手能力和问题解决能力。 阅读建议:建议读者按照题目难度逐步学习,先理解题目背景和解题思路,再仔细研读代码实现,并尝试独立编写和调试代码。同时,鼓励读者扩展思考,探索更多可能的解决方案,以提高编程水平。

    邯郸市-曲周县--街道行政区划_130435_Shp-wgs84坐标系.rar

    街道级行政区划shp数据,wgs84坐标系,直接使用。

    沧州市-孟村回族自治县--街道行政区划_130930_Shp-wgs84坐标系.rar

    街道级行政区划shp数据,wgs84坐标系,直接使用。

    通用计算器的设计FPGA.doc

    通用计算器的设计FPGA.doc

    晋城市-沁水县-街道行政区划_140521_Shp数据-wgs84坐标系.rar

    晋城市-沁水县-街道行政区划_140521_Shp数据-wgs84坐标系.rar

    赤峰市-松山区-街道行政区划_150404_Shp数据-wgs84坐标系.rar

    赤峰市-松山区-街道行政区划_150404_Shp数据-wgs84坐标系.rar

    JAVA中Stream编程常见的方法分类

    JAVA中Stream编程常见的方法分类

    呼和浩特市_和林格尔县_街道级--街道行政区划_150123_Shp_wgs84坐标系.rar

    街道级行政区划shp数据,wgs84坐标系,直接使用。

    【K02】基于51单片机的秒表计时器设计(二).zip

    大同市-浑源县-街道行政区划_140225_Shp数据-wgs84坐标系.rar

    大同市-浑源县-街道行政区划_140225_Shp数据-wgs84坐标系.rar

    包头市-昆都仑区-街道行政区划_150203_Shp数据-wgs84坐标系.rar

    包头市-昆都仑区-街道行政区划_150203_Shp数据-wgs84坐标系.rar

    临汾市-翼城县-街道行政区划_141022_Shp数据-wgs84坐标系.rar

    街道级行政区划shp矢量数据,wgs84坐标系,下载直接使用

    张家口市-阳原县--街道行政区划_130727_Shp-wgs84坐标系.rar

    街道级行政区划shp数据,wgs84坐标系,直接下载使用。

    汽车电子车载网络拓扑开发概述:涵盖总线类型、设计原则及流程优化

    内容概要:本文详细介绍了车载电子电器架构中的网络拓扑开发,涵盖开发概述、车载网络总线、网络设计原则、开发流程及小结。网络拓扑开发是汽车电气架构中的重要环节,旨在设计合理的网络结构以确保各电子控制单元(ECU)之间的高效通信。文中阐述了通信协议选择、网络节点布局、通信介质选择、拓扑结构设计及安全性考虑等关键要素,并强调了仿真与验证的重要性。此外,还讨论了网络设计的原则,如前瞻性、兼容性、拓展性、实时性、可靠性和安全性,以及网络负载的优化措施。最后,总结了网络拓扑开发的流程,包括需求分析、设计、仿真验证、优化迭代及文档记录。 适合人群:汽车电子工程师、各域功能工程师、子系统及零部件开发者、测试工程师等从事汽车电气架构开发的相关人员。 使用场景及目标:①帮助工程师理解汽车网络拓扑开发的关键步骤和技术要点;②指导工程师在设计过程中遵循科学合理的设计原则,确保网络拓扑的高性能和可靠性;③提供网络负载优化的措施,确保数据传输的实时性和效率。 其他说明:网络拓扑开发不仅需要考虑技术层面的因素,还需兼顾成本效益,以适应不断变化的市场需求和技术趋势。本文建议读者在实践中不断积累经验,关注新技术的应用和发展,以应对未来的挑战和机遇。

    金融行业智能分析AI Agent的应用实践与技术创新:提升企业数字化决策能力

    内容概要:本文探讨了智能分析AI Agent在金融行业的先进实践与展望,指出金融行业在经营分析领域面临的现状和痛点,包括管理团队无法快速获得深度结论,业务团队面对BI产品学习门槛高、依赖人工等问题。文中介绍了智能分析AI Agent相较于传统解决方案的技术创新,如数据建模右移、基于虚拟层的数据编织、指标平台与大模型组合方案等,强调其在降低使用门槛、提高效率和增强交互性方面的优势。同时,文章展示了智能分析AI Agent在交互式指标问询、自动分析报告生成等应用场景中的价值,并对未来的发展进行了展望。 适合人群:金融行业的管理层、业务分析师、数据科学家以及对金融科技感兴趣的从业者。 使用场景及目标:①帮助管理层快速获取数据背后的深层次原因和结论;②降低业务团队使用数据分析工具的门槛,提高工作效率;③实现数据的自动化处理和分析,减少人工干预;④推动企业内部的数据民主化,使更多员工能够参与数据分析和决策。 阅读建议:本文不仅提供了智能分析AI Agent的技术细节,还结合实际案例展示了其应用效果,因此在阅读过程中应重点关注技术创新点及其对企业管理和业务流程的具体影响。

    邢台市-南宫市--街道行政区划_130581_Shp-wgs84坐标系.rar

    街道级行政区划shp数据,wgs84坐标系,直接下载使用。

    脑机接口基于贝叶斯优化的FBCCA参数自动调优系统设计与实现:EEG数据处理及优化流程详解

    内容概要:本文档详细介绍了将贝叶斯优化应用于FBCCA(滤波器组公共空间模式)参数调整的完整解决方案,包括代码实现和优化流程。首先,通过MNE库加载并预处理EEG数据,进行7-30Hz的预滤波处理,提取相关事件片段。接着,定义了FBCABayesianOptimizer类,该类包含创建动态滤波器组、获取模型参数以及定义优化目标函数的方法。其中,参数空间由离散和连续参数组成,涵盖了滤波器数量、CSP组件数、起始频率、带宽、交叠率等,并通过Optuna库进行多维搜索。优化过程中采用5折交叉验证机制,同时引入智能早停策略以提高效率。最后,提供了优化结果的可视化工具,如优化轨迹图、参数重要性图和滤波器组配置图,帮助用户更好地理解和分析优化过程。 适合人群:具有一定编程基础,尤其是对机器学习、脑电数据分析及贝叶斯优化感兴趣的科研人员和技术开发者。 使用场景及目标:①通过动态滤波器组生成算法,捕捉频段间的过渡特征;②利用混合参数空间设计,探索不同参数组合的效果;③借助高效交叉验证机制和智能早停策略,提高优化效率;④通过可视化工具,直观展示优化过程和结果。 阅读建议:此资源不仅展示了完整的代码实现,还深入探讨了FBCCA参数调整的理论基础和实际应用。建议读者在学习过程中结合理论知识与代码实践,逐步理解每个步骤的原理,并尝试调整参数以观察不同设置对优化效果的影响。同时,可根据自身硬件条件,考虑扩展建议中的GPU加速、分布式优化和在线学习等高级特性。

Global site tag (gtag.js) - Google Analytics