文章目录
7.1 什么是对象
对象是现实事物在程序中的抽象表示,根据从生活中的经验,对象有行为和属性。
在JavaScript中,对象是通过函数由new运算符生成的。生成对象的函数被称为类或者构造函数,生成的对象被称为类的对象实体,简称对象。
另外一种叫法:
类:对象实体
把类生成的对象:对象实例
7.2 对象的属性和方法
7.2.1 对象的内置属性
在JavaScript中,几乎所有的对象都是同源对象,它们都继承自Object对象。对象的内置属性指的是它们作为Object实例所具有的属性,这些属性通常反映对象本身的基本信息,和数据无关,因此又称它们为元属性。这些属性通常都是不可枚举的,因此无法用反射机制查看它们。
在JavaScript中,Object是所有对象的父类,ECMAScript标准规定所有的JavaScript核心对象必须拥有Object类的原型属性和方法。
constructor
JavaScript规定,实例的constructor的值总是对构造函数即对象类本身的引用。constructor从概念上而言就是对象实例所属的对象类,在具有继承关系的对象中,它总是指向当前类本身,因此常用它来进行准确的运行时类型识别。
在JavaScript中,对象的constructor的意义并不亚于Java中object的class属性,它们都为对象本身提供了极其重要的元素据——类型本身。
hasOwnProperty()
这是一个对象方法,用来检查对象是否有局部定义的(非继承的)、具有特定名字的属性。
isPrototypeOf()
这个方法用来检查对象是否是指定对象的原型。
<html> <head> <title>Example-7.1</title> </head> <body> <script> <!-- function dwn(s){ document.write(s + "<br/>"); } //定义一个名为ClassA的对象 function ClassA(){ this.a = 1; } //再定义一个名为ClassB的对象 function ClassB(){ this.b = 5; this.func = function(){ } } //再定义一个名为ClassC的对象 function ClassC(){ this.c = 5; this.func = function(){ } } //ClassB原型继承ClassA var classA = new ClassA() ClassB.prototype = classA; var objB = new ClassB(); ClassC.prototype = new ClassAisPrototypeOf(); var objC = new ClassC(); dwn(classA.isPrototypeOf(objB));//true dwn(classA.isPrototypeOf(objC));//false --> </script> </body> </html>
propertyIsEmuerable()
这个方法用来检查对象是否拥有对象指定属性且这个属性可被for/in循环枚举。只有当对象拥有某个属性并且该属性可被枚举时,该方法的返回值才是true。属性是否可枚举是由JavaScript内部机制决定的。一般情况下用户自己的对象属性都是可枚举的。
但要注意,ECMAScript标准规定,propertyIsEnumerable()方法不检测原型链,这意味着它只适用于对象的局部属性,不能检测继承属性的可枚举性。
toLocaleString()
返回对象本地化的字符串表示,该方法的默认实现只调用toString()方法,但子类可以覆盖它,提供本地化。
toString()
返回对象的字符串表示。Object提供的该方法的实现相当简单,并且没有提供更多有用的信息。Object的子类通过定义自己的toString()方法覆盖了这一方法。
valueOf()
返回对象的原始值(如果存在)。对于类型为Object的对象,该方法只返回自身,Object的子类覆盖了该方法,返回的是与对象相关的原始值。valueOf()可以返回对象"拆箱"后的基本值类型。
7.2.2 为对象添加和删除属性
1.给对象属性赋值
2.通过构造函数添加属性
3.为某一类对象的所有实例添加某个方法或者某种属性(prototype)
delete操作符删除对象的属性
通常情况下我们很少直接从对象中删除属性,因为根据习惯,我们认为对象中的属性数目和类型是相对固定的,要消除某个属性的值,应该将它的值设为null,而不是将属性本身删除。对象属性的删除操作一般仅用于将对象当作集合来使用时。
在静态语言中,一般不允许在运行时删除或添加对象的属性,或者改变它们的类型。
7.2.3反射机制——枚举对象属性
如果一个对象存在某些属性,并且这些属性是可枚举的(propertyEmuerable返回true),那么就可以用for/in循环获得它们的属性名称和当前值。
7.3 对象的构造
任何合法的函数都能当作类型来构造对象。对象的构造是通过new操作符和函数调用来完成的。
7.3.1 构造函数——一个双精度浮点数封装类的例子
一旦函数被作为构造函数执行,它内部的this属性将引用对象本身。
构造函数通常没有返回值,它们只是初始化由this值传递进来的对象,并且什么也不返回。如果函数有返回值,被返回的对象就成了new表达式的值。
从形式上来看,一个函数被作为构造函数和普通函数执行的唯一区别是,是否用new 运算符。
如果一个函数的返回值是一个引用类型(数组、对象或者函数)的数据,那么将这个函数作为构造函数用new运算符执行构造时,运行的结果将被它的返回值取代,这时候,构造函数体内的thi值丢失了,取而代之的是被返回的对象。
但是如果函数的返回值是一个值类型,那么这个函数作为构造函数用new运算符执行构造时,它的返回值将被舍弃。new表达式的结果仍然是this所引用的对象。
<html> <head> <title>Example-7.2</title> </head> <body> <script> <!-- function dwn(s){ document.write(s + "<br/>"); } //双精度浮点数包装类 function Double(value){ value = parseFloat(value); if(!isNaN(value) && this && this.constructor == arguments.callee){ //如果是作为构造函数执行,那么满足this.constructor == arguments.callee //这个时候构造一个Double对象,给出doubleValue()和valueOf()方法 this.doubleValue = function(){ return value } this.valueOf = function(){ return value; } this.toString = function(){ return value.toString(); } }else{//否则直接返回数值 return value; } } var d_boxing = new Double(123.34); //d_boxing是一个包装对象 var d_unboxing = Double("123.34");//d_unboxing是一个普通数值 dwn(d_boxing.doubleValue()); dwn(typeof d_boxing); //得到object dwn(d_unboxing); dwn(typeof d_unboxing); //得到number --> </script> </body> </html>
在JavaScript中几乎所有的函数作为构造函数使用,返回的都是以Object为父类的对象实例,对它们使用typeof运算符,得到的结果都是object,除了Function外。对Function使用构造函数创建对象得到的类型是function。因此,从面向对象的角度可以将Function看作为“创建类的类”,所以它们才又被称为“元类”或者“类模版”。
7.3.2 缺省构造和拷贝构造
这里的缺省构造并不是指缺省构造函数本身,而指的是构造函数的参数为空的时候,JavaScript的语法允许缺省构造函数名称后面的括号。
对象的拷贝构造:
<html> <head> <title>Example-7.1</title> </head> <body> <script> <!-- function dwn(s){ document.write(s + "<br/>"); } function dwn(s){ document.write(s + "<br/>"); } var p1 = {x:1,y:2}; var p2 = p1;//p2复制的是p1的引用 p2.x++; dwn(p1.x); function Point(x,y){ if(arguments[0] instanceof Point){//如果是同类型对象,允许拷贝 this.x = arguments[0].x; this.y = arguments[0].y; }else{ this.x = x; this.y = y; } } var p3 = new Point(1,2); //现在要让p2复制p1,只需要 var p2 = new Point(p1); --> </script> </body> </html>
违背了同一种模式解决同一类问题的原则
<html> <head> <title>Example-7.1</title> </head> <body> <script> <!-- function dwn(s){ document.write(s + "<br/>"); } function dwn(s){ document.write(s + "<br/>"); } function Point(x,y){ this.x = x; this.y = y; this.clone = function(){ return new Point(x,y); } } var p3 = new Point(1,2); p3.x = 5; //现在要让p2复制p1,只需要 var p2 = p3.clone(); dwn(p2.x); --> </script> </body> </html>
7.3.3 对象常量
对象常量的构造函数是Object(),它们的类型都是基类Object.
7.4对象的销毁和存储单元的回收
JavaScript中,要销毁一个对象,必须要消除一个对象所有的外部引用。JavaScript的存储单元回收机制采用的是引用计数法,具体来说就是当一个对象被创建,并且它的引用被存储在变量中,引用计数就为1,当它的引用被复制,并且存储在另一个变量中,引用计数就增加1,当保存这个引用的其中一个变量被某个新值覆盖了,引用计数就减少1,以此类推。当一个对象的引用计数被减少为0时,它才会被销毁。
var p1 = new Point(1,2);//构造对象,引用计数为1 var p2 = p1;//复制了该对象,引用计数为2 p1 = null; p2 = null; CollectGarbage();//IE调用这个函数立即回收无用的对象 //实际上不用调用任何函数浏览器也会按照自己的规律执行存储单元回收
JavaScript的闭包打破了一个规律,局部变量在函数调用结束之后未必会被销毁,除非确定它的调用对象没有间接地被外部引用。
function step(a){ return function(){ return a++; //局部变量a被闭包引用,而闭包被随函数调用返回 //因此既是step()函数调用结束后,a也不会被销毁 } } var x = step(10); var y = step(20);//x,y被赋值的同时产生了两个调用对象 x = null; y = null;//使用完毕后必须要消除它们的引用才能够释放生成的调用对象
最后再一次强调的是,JavaScript的delete运算符和对象的销毁并没有直接的关系,delete也不是new运算符的反操作。
7.5 JavaScript的内置对象
7.5.1 Math对象
Math对象是一个静态对象,这意味着不能用它来构造实例。
注意:调用Math静态方法如果出现数值计算错误,返回值NaN,其他情况下,返回值为数值类型的计算结果。
7.5.2 Date对象——创建一个简单的日历
1.缺省参数构造
var now = new Date;
2.依次表示“年”、“月”、“日”、“时”、“分”、“秒”、“毫秒”的数值,并且这些数值除了“年”和“月”之外,其他的都可以缺省。
var d = new Date(1999,1,2);
JavaScript中的月份是从0开始计算的
3.通过一个表示日期的字符串
var d = new Date("1999/01/02 12:00:01");//这一次表示的是1月份了
4.通过一个整数参数来构造日期,这个整数代表的是距离1970/01/01 08:00:00的毫秒数。(valueOf getTime这两个方法返回该值)
5.Date()可以作为普通函数来调用,这时候它忽略所有的参数,简单返回一个表示当前日期的字符串。
在表达式中,JavaScript会根据Date对象的valueOf方法对它们进行类型转换,看起来像是对Date对象的运算符进行了重载。
var date = new Date(); var now = new Date(); date.setMinutes(0); alert((now-date)/1000/60); alert(now>date);
简单的日历:
<html> <head> <title>Example-7.3</title> </head> <body> <script> <!-- var todayDate = new Date(); //得到当月的日期 var date = todayDate.getDate(); //得到月份 //这里需要注意,JavaScript的月份是从0开始的 var month = todayDate.getMonth() +1; //得到年份 var year = todayDate.getFullYear(); //表示星期的中文 var weeks = [ "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", ]; //输出结果 document.write("今天是"); document.write("<br>"); document.write(year); document.write("年"); document.write(month); document.write("月"); document.write(date); document.write("日"); document.write("<br>"); document.write(weeks[todayDate.getDay()]); setInterval(function(){ with(new Date()){ a.innerHTML = toLocaleString()+" 星期"+"日一二三四五六".charAt(getDay()) } },500) --> </script> <p id="a"></p> </body> </html>
7.5.3 Error对象
JavaScript的Error对象是用来在异常处理中保存异常信息。Error对象包括Error极其派生类的实例,Error的派生类是EvalError、RangeError、TypeError和SyntaxError.
Error对象的构造函数只有一个参数,它是一个可以缺省的字符串,用来提供异常的错误信息。如果传递了字符串参数,该Error对象就将它作为message属性的值,否则它将使用默认的字符串作为该属性的值,具体情况视具体实现而定。如果把Error构造函数当作普通函数来调用,它的行为也和使用new运算符进行对象构造时一样。