- 浏览: 228191 次
- 性别:
- 来自: 上海
最新评论
-
uchaoxi:
谢谢作者分享的经验,非常有用!
点击<a>标签,禁止页面自动跳到顶部的解决办法 -
独爱cyjs:
试下用rs.getObject()
有关ResultSet类中getLong方法自动将null转为0的解决方式 -
muyeandmuye:
呃,这里被抄袭了。http://www.myexception ...
CSS3实现红心动态变化效果 -
univasity:
非常不错,很全面。学习了。
javascript,php文件上传详解 -
lord_is_layuping:
...
有关ResultSet类中getLong方法自动将null转为0的解决方式
类 Class
类的概念应该是面向对象语言的一个特色,但是JavaScript并不像Java,C++等高级语言那样拥有正式的类,而是多数通过构造器以及原型方式来仿造实现。在讨论构造器和原型方法前,我可以看看一种叫做“工厂方式”的仿造方法。
function start() {
alert("Bang!!");
}
function createCar(color, title) {
var car = {};
car.color = color;
car.title = title;
car.start = start;
return car;
}
var car1 = createCar("red", "BMW");
var car2 = createCar("yellow", "VOIS");
这种方式显然可以实现class的功能,但是外形上怎么也无法说它是个class以及class实例的创建过程。因此,出现了“构造函数方式”,它的关键在于构造器(Constructor)概念的引入。
构造器 Constructor
我们先来看“构造函数方式”的具体做法:
function start(){
alert("Bang!!!");
}
//constructor
function Car(color, title){
this.color = color;
this.title = title;
this.start = start;
}
var car1 = new Car("red", "BMW");
var car2 = new Car("yellow", "VOIS");
这个看起来有点类的样子了吧(先不提那个难看的外置function)?我们发现,那个constructor其实就是一个简单的function,它与“工厂方式”中的createCar()区别就在于:1、方法名大写 2、没有了空对象的创建和返回 3、使用this做引用。那原来的那个空对象的创建以及返回的步骤去哪了呢?这两个步骤,现在都由创建实例时的“new”实现了。“new”这个操作符负责创建一个空对象,然后将那个叫做构造器的function添加到实例对象中并触发它,这样这个function实际上就是这个对象的一个method,function中的this指向的便是这个对象,最后将这个对象返回。根据如上分析,我们可以把这个过程简单分解为如下代码:
var obj = {};
obj.constructor = Car;
obj.constructor("red", "BMW"); //”this“ refers to obj
return obj;
“构造函数方式”方式虽然与高级面向对象语言中的类创建方式已经很接近(使用”new“创建),但是貌似那个游离在类之外的function start()其实却是个相当有碍观瞻的瑕疵。我们应该想一种办法让这个方法与类挂钩,让它成为类的一个属性,不是全局的。于是,这就产生了“构造函数+原型法”的类构造方法。
原型 prototype
在”构造函数+原型法“中,我们对于类的method期待得到的效果是:1、仅是类的method而不是全局的 2、只在类被定义时创建一个method实例,然后被所有类的实例共用。由这两个目标,我们很容易想到高级面向对象语言Java的private static变量的特点。JavaScript没有为我们提供这么简单的符号来实现这个复杂功能,但是却有一个属性可以帮我们仿造出这种效果:prototype。我们先来看几段prototype的使用代码。
- function Car(){
- }
- Car.prototype.material = "steel";
- var car1 = new Car();
- var car2 = new Car();
- document.write(car1.material); //prints "steel"
- document.write(car2.material); //prints "steel"
- //car1.prototype.material = "iron" //compile error:car1.prototype is undefined
- car1.material = "iron";
- document.write(car1.material); //prints "iron"
- document.write(car2.material); //prints "steel"
- document.write(Car.prototype.material); //prints "steel"
- Car.prototype.material = "wood";
- var car3 = new Car();
- document.write(car1.material); //prints "iron"
- document.write(car2.material ); //prints "wood"
- document.write(car3.material ); //prints "wood"
- document.write(Car.prototype.material); //prints "wood"
分析该段代码前,需要明确两个概念:对象的直属属性和继承属性。直接在构造函数中通过this.someproperty = xxx这种形式定义的someproperty属性叫做对象的直属属性,而通过如上第4行代码那样Car.prototype.material = "steel";这种形式定义的material属性叫做继承属性。由上面这段代码,我们可以总结出prototype属性的如下特点:
- prototype是function下的属性(其实任意object都拥有该属性,function是对象的一种)
- prototype属性的值是一个对象,因此可任意添加子属性(line 4)
- 类的实例可以直接通过"."来直接获取prototype下的任意子属性(line 9)
- 所有以此function作为构造函数创建的类实例共用prototype中的属性及值(ling 9,10)
- 类的实例没有prototype属性(line 12)
- 可以直接通过 "实例.属性 = xxx" 的方式修改继承属性,修改后的值将覆盖继承自prototype的属性,但此修改不影响prototype本身,也不影响其它类实例(line 15,16,17)
- 继承属性修改后,该属性就成为类实例的直属属性
- 可以直接修改prototype的属性值,此改变将作用于此类下的所有实例,但无法改变直属属性值(极晚绑定line 21-24)
在起初总结出prototype的这些特点时,我最不能理解的是第三条,为什么类的实例可以像获取自身属性那样的获取function prototype中的属性。有些书上说是因为这些属性被附加到了对象实例中(sigh,我刚开始这样以为),但是,这种说法无法是第8条特性成立,后来看到一位网友的解释是:对象实例在读取某属性时,如果在本身的直属属性中没有查找到该属性,那么就会去查找function下的prototype的属性。这个观点能够很好的解释prototype的第8个特点,有查看了一些书,发现JavaScript—The Definitive Guide这本书里也是如此解释的:)
Tip:我们可以通过hasOwnProperty方法来判断某属性是直属于对象还是继承自它的prototype属性
- car1.hasOwnProperty("material"); // true: material is a direct property of r
- car2.hasOwnProperty("material"); // false: material is an inherited property of r
- "material " in car2;// true: "material " is a property of r
好好理解下prototype的这些特点,我们不难看出,在prototype中定义的属性与Java类中的static属性特点极为相近,适合定义那些所有类实例都可共用的一些属性的值,比如汽车的构造材料这一类。
了解了prototype的这些特点,我们可以言归正传,把它应用到类方法的仿造和实现上了。其实做法很简单,就是把value换成function,先看下面的代码:
- function Car(){
- }
- Car.prototype.start = function(){
alert("Bang!!!");
};
- var car1 = new Car();
- var car2 = new Car();
- car1.start(); //bang!!!
- car2.start(); //bang!!!
得益于prototype的强大特性,我们知道这个method确实是可以这么被绑定到类上的。但是,最有用的是,那个method只会在执行Car.prototype.start =这个定义的时候创建一个实例,之后通过new创建的对象实例里面使用的这个method都是它的引用,因此不会有内存浪费。
好了,method的问题终于解决了,那么整理一下,下面写出使用“构造函数+原型方式”构造一个类的完整代码:
- function Car(color, title){
- this.color = color;
- this.title = title;
- }
- Car.prototype.material = "steel";
- Car.prototype.start = function(){
- alert("Bang!!!");
- };
- Car.prototype.repaint = function(color){
- this.color = color;
- }
- var car1 = new Car("red", "BMW");
- var car2 = new Car("yellow", "VW");
- car1.repaint("pink");
- document.write(car1.color); //prints pink
- document.write(car2.color); //prints yellow
怎么样,这个跟高级面向对象语言中的class的样子更~加类似了吧?Perfect ?NO!!!上述写法只是在“语义”上达到了对类属性和方法的封装,很多面向对象思想的完美主义者希望在“视觉”上也达到封装,因此就产生了“动态原型法”,请看下面的代码:
- function Car(color, title){
- this.color = color;
- this.title = title;
- if (typeof Car._initialized == "undefined") {
- Car.prototype.start = function(){
- alert("Bang!!!");
- };
- Car.prototype.material = "steel";
- Car._initialized = true;
- }
- }
我们看,其实Car.prototype的属性定义是可以被放进Car function的定义之中的,这样就达到了“视觉”封装。但是我们没有单纯的move,我们需要加一个条件,让这些赋值操作只执行一次,而不是每次创建对象实例的时候都执行,造成内存空间的浪费。添加_initialized 属性的目的就在于此。
注意:
对于类的属性值,我们上面只考虑了primitive type和function的类型情况,如果属性值是对象,比如Array,那情况就又复杂了,思考下面的一段代码。
- function Car(){
- }
- Car.prototype.drivers = ["Kitten", "Bear"];
- var car1 = new Car();
- var car2 = new Car();
- car1.drivers.push("Point");
- document.write(car1.drivers); //prints Kitten,Bear,Point
- document.write(car2.drivers); //prints Kitten,Bear,Point
- function Car(){
- this.drivers = ["Kitten", "Bear"];
- }
- Car.prototype
- var car1 = new Car();
- var car2 = new Car();
- car1.drivers.push("Point");
- document.write(car1.drivers); //prints Kitten,Bear,Point
- document.write(car2.drivers); //prints Kitten,Bear
- function Car(){
- this.drivers = ["Kitten", "Bear"];
- if (typeof Car._initialized == "undefined")
- Car.prototype.getDiversNumber = function(){
- alert(this.drivers.length)
- };
- Car._initialized = true;
- }
- }
- var car1 = new Car();
- var car2 = new Car();
- car1.drivers.push("Point");
- document.write(car1.drivers); //prints Kitten,Bear,Point
- document.write(car2.drivers); //prints Kitten,Bear
- car1.getDiversNumber(); //3
- car2.getDiversNumber(); //2
总结出来一句话就是:用构造函数方式定义对象的所有非函数属性,用原型方式定义对象的函数属性。
至此,我们应该说已经比较透彻的了解了JavaScript创建类的方法。目前,“构造函数+原型”方式使用的比较广泛,“动态原型法”也很流行。我个人比较喜欢后者。
发表评论
-
一个让以各种类型元素为容器的“按钮”都文字居中的方法
2013-06-29 13:05 1291最近在做一个Button控件,这个控件不仅限于使用 ... -
JS类中能模拟出依赖对象本身的可变的私有属性吗?
2012-11-22 16:11 760JS是轻量级语言,在类的概念上没有如Java那样严格意义上私有 ... -
LESS —— 动态样式语言 学习纪要
2012-08-30 18:54 8910学习LESS源于一次分享的要求。之前一直没有使用,是因为使用这 ... -
CSS学习笔记
2012-07-09 22:35 1529使用CSS有些年头了,但是很惭愧从来没有系统的看过一本书,基本 ... -
阶段性总结。。。
2012-05-16 17:29 873自己的空间,不说废话 ... -
页面中包含大比率压缩图片会损耗内存
2011-12-25 23:58 1065最近做了一个类似美丽说的图片分享网站,测试时发现打开某个网页时 ... -
上传图片一直不变的问题 | IE的file无法手动置空
2011-11-19 04:15 1680今天在开发一个图片上传控件的时候,发现在IE8下上传一张 ... -
做一个资源加载进度条
2011-11-14 15:51 5031最近在开发一个工具类的网站,网站的背景和各模块的构建都需要加载 ... -
【Frontend Knowlodge Chart】 学习之安全相关
2011-10-20 19:54 1225安全相关 考查前端及 ... -
【Frontend Knowlodge Chart】 学习之HTTP协议
2011-10-20 19:53 1259HTTP协议 考查HTTP协议的认知程度和基本知识 ... -
【转】Frontend Knowlodge Chart
2011-10-17 16:21 1094转一个牛人总结的前端知识框架图 http://www ... -
我最近新学会的XXX
2011-09-01 18:28 1088最近忙上,忙下,忙里,忙外,活儿做了不少。这里有重复劳动 ... -
【转】Firefox下iframe的onload事件有可能无法触发 作者:zhanglili
2011-05-31 14:29 5610Firefox下,已经load的document,重新open ... -
避免function的循环引用
2011-05-26 16:44 840循环引用 window.onload = functi ... -
如何获取一个dom元素的绝对位置
2011-05-18 12:05 5994应用场景:鼠标滑过某元素时,需要弹出一个信息标签,标签的 ... -
关于CSS Selector的优先级
2011-05-16 15:27 1553用CSS这么些年,从来没从算法级别考虑过它的优先级原理,只是凭 ... -
javascript,php文件上传详解
2011-04-29 19:20 3914今儿说点儿基础但是蛮重要的前端技术——使用file ... -
IE6下left绝对定位诡异错误
2011-04-29 01:44 1738今天开发自己的一个小网站时出现一个诡异的错误:目标 ... -
推荐个学WEBGL的网站
2011-04-12 17:59 802http://learningwebgl.com/blog/? ... -
【转】浅谈跨域WEB攻击
2011-04-08 17:54 951转自http://www.80sec.com/cros ...
相关推荐
对象的constructor属性用于返回创建该对象的函数,也就是我们常说的构造函数,除了创建对象,构造函数(constructor) 还做了另一件有用的事情—自动为创建的新对象设置了原型对象(prototype object)
主要介绍了js构造函数constructor和原型prototype原理与用法,结合实例形式分析js构造函数constructor和原型prototype基本原理、功能、使用方法及操作注意事项,需要的朋友可以参考下
本文实例讲述了js核心基础之构造函数constructor用法。分享给大家供大家参考,具体如下: 在js中,可以利用构造函数来创建特定类型的对象,其中,有一些原生的构造函数,Object、Array、等等,所以,当type of ...
真正的原因是:一个对象的constructor是它的构造函数的prototype.constructor,而每一个函数都有一个prototype,默认情况下,这个prototype有一个constructor属性,指向的是它自己。 我觉得Javascript的设计本意是让...
在Javascript中不存在class的概念,它的class概念是通过构造函数(constructor)与原型链(prototype)来实现。 1.构造函数(constructor):创建对象时的初始化对象,总是与new 关键是一同出现。 构造函数存在以下...
使用no-constructor-bind消除构造函数中的绑定函数。 使用no-constructor-state可以消除no-constructor-state初始状态设置。 这样做,很可能您将能够使用ESLint的内置no-useless-constructor来删除应用程序中的许多...
Java面向对象(基础)-- 类的成员之三:构造器(Constructor)
构造方法 马克-to-win Java视频 构造函数 constructor
constructor指向对象的构造函数,prototype指向使用构造函数创建的对象实例的原型对象。 function Person(){ } var person = new Person(); Person.prototype = { constructor : Person, name : 'zxs', age :...
通常情况下, JavaScript中建立一个对象用"new"加上constructor function来实现. 如new Date(), new Object()等. var book = new Object(); book.name = "JavaScript is Cool"; book.author = "tom"; book.pages = ...
客户端原型污染 介绍 如果您不熟悉原型污染攻击,则应首先阅读以下内容: Olivier Arteau的NodeJS...constructor.prototype.test=test ?__proto__.test=test ?__proto__[test]=test ?__proto__[test]={"json":"value"}
分析javascript中 prototype __proto__ constructor之间的关系
获取原型链 返回带有传入对象的原型链(按升序)的数组。安装npm install --save get-prototype-chain用法const getPrototypeChain = require ( 'get-prototype-chain' ) ;class A {}class B extends A {}class C ...
构造函数 PHP 5 允行开发者在一个类中定义一个方法作为构造函数。...使用新标准的构造函数 class BaseClass { function __construct() { print “In BaseClass constructor\n”; } } class SubCla
1、任何一个函数内都有prototype属性,这个prototype属性指向另一个对象 ,这个对象就是就是原型对象 ,简称原型。注意这个prototype是函数本身所自带的属性 2、原型的作用就是实现方法共享,将多个对象会调用的相同...
一直没弄清楚JavaScript中的prototype和constructor属性,今天看了看书,总算有点眉目了
JS:typeof instanceof constructor prototype区别