- A+
对象是Js核心概念之一,也是最常用的数据类型,即引用型数据。对象可以包含多个属性。属性以名值对的形式存在,它是一种复合值,
它将很多值(原始值或者其他对象)聚合在一起。可通过名字访问这些值,构成一个无序集合。除了字符串、数字、true、false、null
和undefined之外, Js中其他的值都是对象。
1.1创建对象
创建对象有三种方法,具体如下:
1.1.1使用构造函数创建对象
使用new运算符调用函数,可以构造一个实例对象。
用法如下:
1 var objectName=new functionName(args);
简单说明如下:
objectName |
表 示 构 造 的 实 例 对 象 |
functionName |
是一个构造函数 , 一般情况下,它不需要返回值 , 构造函数体内可以使用this指代objectName实例对象 |
args |
表 示 参 数 列 表 |
用法1:使用构造函数创建对象
1 var o=new Object(); 2 var o=new Array(); 3 var o=new MyClass();
如length属性可以获得该数组的元素个数,而push()方法将会为该数组对象添加新元素。
1 var o = new Array(); 2 document.write(o.length + '<br>'); 3 var l = o.push(1, 2, 3); 4 document.write(l);
使用new运算符可以调用它们,并初始化为一个个对象实例。
在Js中构造函数具有如下特性:
-
使用new运算符进行调用,也可以使用小括号调用,但返回值不同。
-
构造函数内部通过this关键字指代实例化对象,或者指向调用对象。
-
在构造函数内可以通过点运算符生命本地成员。当然构造函数结构体内也可以包含私有变量或函数,以及任意执行语句。
用法2:使用构造函数定义一个Book类,定义两个参数。
1.1.2new关键字
javascript中的new是一个语法糖,对于学过别的语言等的人来说,以为js里面是有类和对象的区别的,实现上js并没有类,一切皆对象。
new的过程实际上是创建一个新对象,把新对象的原型设置为构造器函数的原型,在使用new的过程中,一共有3个对象参与了协作,
构造器函数是第一个对象,
原型对象是二个,新生成了一个空对象是第三个对象,最终返回的是一个空对象,但这个空对象不是真空的,而是已经含有原型的
引用(__proto__)。
通过new创建对象经历4个步骤:
创建一个新对象:
[var o = {};]
-
将构造函数的作用域赋给新对象(因此this指向了这个新对象);[Person.apply(o)] [Person原来的this指向的是[window]
-
执行构造函数中的代码(为这个新对象添加属性);
-
返回新对象。
1.1.3使用对象直接量创建对象
对象直接量提供了另一种创建新对象的方式。对象直接量允许将对象描述文字嵌入到JavaScript代码中,就比如将文本数据嵌入在
JavaScript代码中,也就像将文本数据嵌入在JavaScript代码中作为引用的字符串一样。对象直接量是由属性说明列表构成的。
这个列表包含在大括号之中,属性与属性之间通过逗号进行分隔,最后一个属性末尾不需要逗号。
对象直接量中的每个属性说明列表都由:
-
一个属性名可以是Js的标识符
-
及跟在其后的冒号和属性值构成
-
或任意形式的字符串
-
属性值可以是任意类型的数据。
使用对象直接量创建对象的一般格式如下:
var objectName = {属性名1:属性值1,属性2:属性值,...,属性名n:属性值n}
具体例子如下:
var WangLiang={ name:王亮, age:24 }
从上面的定义中可以看出,这种定义方式实际上是声明一种类型的变量,并同时进行了赋值。因此,声明后的对象直接量可以在代码中直接使用,而不必使用
new关键字来创建对象。
1.1.4使用create()方法创建对象
Object.create()方法是ECMAScript 5中新增的方法,直接调用该方法可以快速创建一个新对象。它能够创建一个具有指定原型且可选择地包
含指定属性的对象。
具体用法如下:
Object.create(prototype,descriptors)
参数说明如下:
prototype |
必须参数,要用作原型的对象,可以为null, |
descriptors |
可选参数,包含一个或多个属性描述符的Js对象。 |
提示:
在descriptors中,数据属性描述符包含value特性、enumerable(是否可枚举属性)和configurable(是否可修改特性和删除属性)特性。如果未指定最后3个特性,则它们的值默认为false。
下面用法:
var firstLine = { x: undefined, y: undefined }; var secondLine = Object.create(Object.create(Object.prototype, { x: { value: undefined, writable: true, configurable: true, enumerable: true }, y: { value: undefined, writable: true, configurable: true, enumerable: true } })) document.write('firstline prototype =' + Object.getPrototypeOf(firstLine)); document.write('<br>') document.write('second line prototype =' + Object.getPrototypeOf(secondLine));
2.1操作对象
对象除了包含属性之外,每个对象还拥有了3个相关的对象特性:
对象的原型(prototype) |
指向另外一个对象,本对象的属性继承自它的原型对象 |
对象的类(class) |
是一个标识对象类型的字符串 |
对象的扩展标记(extensible flag) |
指明了是否可以向该对象添加新属性 |
2.1.1引用对象
在创建对象之后,可以把对象的地址赋值给变量,实现变量对对象的引用。当把变量赋值给其他变量时,则实现多个变量
引用同一个对象。
下面用法:
WangLiang= { name: '王亮', age: 24 } wangliang= wangliang; document.write(delete wangliang); document.write(wangliang.x);
2.1.2复制对象
复制对象的设计思路是:利用for/in语句遍历对象成员,然后逐一复制给另一对象。
用法1:
function F(x, y) { this.x = x; this.y = y; this.add = function () { return this.x + this.y; } this.min = function () { return this.x - this.y; } } F.prototype.mul = function () { return this.x * this.y; } F.prototype.except = function () { return this.x / this.y; } var f = new F(24, 12); var o = {}; for (var i in f) { o[i] = f[i]; } document.write(o.x + '<br>'); document.write(o.y + '<br>'); document.write(o.add() + '<br>'); document.write(f.min() + '<br>'); document.write(f.mul() + '<br>'); document.write(o.except() + '<br>');
对于该复制法,还可以进行封装,具有灵活性:
Function.prototype.extend=function(o){ for(var i in o){ this.constructor.prototype[i]=o[i]; } }
-
上面的封装函数通过原型为函数function类型对象扩展一个方法,该方法能把指定的参数对象完全复制给当前对象的构造函数的原型对象。
-
this关键字指向是当前实例对象,而不是构造函数本身,所以要为其扩展原型成员,就必须使用constructor属性来指向他的构造器。
-
然后,先新建一个空的构造函数,并为其调用extend()方法把传递进来的F类的实例对象完全复制为原型对象成员。
-
extend()方法只能为构造函数结构复制对象。
2.1.3销毁对象
Js提供了一套垃圾回收机制。当对象没有被任何变量引用时,Js会自动侦测,并运行垃圾回收程序把这些对象注销,以释放内存。
每当函数对象被执行完毕,垃圾回收程序就会自动被运行。
比如下面该用法:
var o = { x: 1, y: 24 } o = null;//删除 document.write(o.x);
在前端职场设计中,对于不用的对象,应该把其所有引用变量都设置为null,将对象废除,就好比如把手机里的不必要的垃圾和缓存清除干净,释放内存空间。
这是一种好的习惯,既节省系统开支,又可以预防错误。
3.1操作属性
属性包括名和值。属性名可以是包含空字符串在内的任意字符串,但对象中不能存在两个同名的属性。值可以是任意的Js值。每个属性还
有一些相关的值。
名称为属性中的特性:
可写(writable attribute) |
表明是否可以设置该属性的值。 |
可枚举(enumerable attribute) |
表明是否可以删除或修改该属性。 |
可配置(configurable attribute) |
表名是否可以删除或修改该属性。 |
3.1.1定义属性
在ECMAScript5中则可以对这些特性加以配置。
使用冒号可以为对象定义属性,冒号左侧是属性名,右侧是属性值。属性和属性之间通过逗号运算符进行分隔。
下列用法:
var o = function () { this.x = 1; this.y = { x: 1, y: true } }
在声明变量时使用var语句,但是在声明对象属性时不能使用var语句。
Js5增加了静态函数,用来为指定对象定义属性。下面进行说明:
Object.defineProperty()
Object.defineProperty()方法可以将属性添加到对象,或者修改现有属性的特性,并返回这个对象。
语法:
Object.defineProperty(obj, prop, descriptor)
参数:
obj |
要在其上定义属性的对象 |
prop |
要定义或修改的属性的名称 |
descriptor |
将被定义或修改的属性描述符 |
返回值:
被传递给函数的对象。
该方法允许精确添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,能够在属性枚举期间呈现出来(for...in 或 Object.keys 方法),这些属性的值可以被改变,也可以被删除。这个方法允许修改默认的额外选项(或配置)。默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改的。
创建属性:
如果对象中不存在指定的属性,Object.defineProperty()就创建这个属性。当描述符中省略某些字段时,这些字段将使用它们的
默认值。拥有布尔值的字段的默认值都是false。value,get和set字段的默认值为undefined。
下面用法:
var obj = {}; Object.defineProperty(obj, 'newDataProperty', { value: 101, writable: true, enumerable: true, configurable: true }); obj.newDataProperty = 102; document.write(obj.newDataProperty);
3.1.2访问属性
通过点运算符可以访问对象属性,点运算符左侧是对象引用的变量,右侧是属性名,属性名必须是一个标识符,而不是一个字符串。
用法1:
alert(o['y']['y']['y'])
以数组形式读取对象属性值时,应以字符串形式指定属性名,而不能使用标识符。
用法2:可以使用for/in语句来遍历对象属性
o = { x: 2, y: 3, z: 4 } for (var i in o) { document.write(o[i]); }
使用for/in语句遍历对象属性时,也只能枚举自定义属性。
3.1.3删除属性
使用delete运算符可以删除对象属性,这与变量操作相同。
用法:
var o = { x: 1 } delete o.x; document.write(o.x);
3.1.4使用方法
在Js中,方法就是对象属性的一种特殊形式,即值为函数的属性。从功能角度分析,方法是对象执行特定行为的逻辑块,是与外界现行为交互的
动作。
用法1:
var o = {}; o.x = function () { document.write('method'); } o.x()
3.2使用方法
在Js中,Object对象默认定义了多个原型方法,由于继承关系,所有对象都将拥有这些方法。
3.2.1使用toString()
toString()方法能够返回一个对象的字符串表示,它返回的字符串比较灵活,可能是一个具体的值,也可能是一个对象的类型标识。
用法1:
function F(x, y) { this.x = x; this.y = y; } var f = new F(1, 2); document.write(f.toString());
当把数据转换为字符串时,Js一般都会调用toString()方法来实现。由于不同的类型的对象在调用该方法时,所以开发人员常用它来判断对象的类型,
弥补typeof运算符和constructor属性在检测对象数据类型的不足。
用法2:
function Me() { } Me.prototype.toString = function () { return '[Object//大类 Me//小类]'; } var me = new Me(); document.write(me.toString()); document.write(Object.prototype.toString.apply(me));
Js在部分子类型中重写了toString()和toLocaleString()方法。
比如:在Array中重写toString(),让其返回数组元素值的字符串组合;在Data中重写了toString(),让其返回当前日期字符串表示。
3.2.2使用valueOf()
valueOf()方法能返回对象的值。Object对象默认valueOf方法返回值与toString()方法返回值相同,但是部分类型对象重写了valueOf()方法。
下面用法:Date对象的valueOf()方法返回值是当前日期对象的毫秒数。
var o = new Date(); document.write(o.toString() + '<br>'); document.write(o.valueOf() + '<br>'); document.write(Object.prototype.valueOf.apply(o));
3.2.3检测私有属性
对象属性可以分为两类:
私 有 属 性 |
继 承 属 性 |
用法1:
function F() { this.name = '私有属性'; } F.prototype.name = '继承属性';
凡是构造函数的原型属性(原型对象包含的属性),都是继承属性。
3.2.4检测枚举属性
在大多数情况下,in运算符是探测对象中属性是否存在的最好途径。
用法1:for/in语句可用来遍历一个对象中的所有属性名。
for (var name in person) { if (typeof person[name] != 'function') { document.write(name + ':' + person[name] + '<br>') } }
该枚举过程将会列出所有的属性。
3.3使用原型
在Js中,构造函数拥有原型,实例对象通过prototype关键字可以访问原型,实现Js原型继承机制。
3.3.1定义原型
原型事件上就是一个数据集合,即普通对象,继承Object类,由Js自动创建并依附于每个构造函数。
使用点语法,用户可以通过function.prototype方式定义原型,所有实例对象可以共享属性和原型方法(所有对象共享)。
下面用法:
function p(x) { this.x = x; } p.prototype.x = 1 var p1 = new p(10); p.prototype.x = p1.x document.write(p.prototype.x);
3.3.2比较原型属性和本地属性
构造函数定义了与原型属性同名的本地属性,则本地属性会覆盖原型属性值。如果使用delete运算符删除本地属性,则原型属性会被访问。
用法1:本地属性可以在实例对象中被修改,但是不同实例对象之间不会相互干扰。
function f() { this.a = 1; } var e = new f(); var g = new f(); document.write(e.a); document.write(g.a); e.a = 2; document.write(e.a); document.write(g.a);
如果希望统一修改实例对象中包含的本地属性值,就需要一个个修改了,工作量会很大。
用法2:原型属性将会影响所有实例对象,修改任何原型属性值,则该构造函数的所有实例都会看到这种变化,这样就避免了本地属性修改的麻烦。
function f() { f.prototype.a = 1; } var e = new f(); var g = new f(); document.write(e.a); document.write(g.a); f.prototype.a= 2; document.write(e.a); document.write(g.a);
prototype属性属于构造函数,所以必须使用构造函数通过点语法来调用prototype属性,再通过prototype属性来访问原型对象。
用法3:
function p(x, y, z) { this.x = x; this.y = y; this.z = z; } p.prototype.del = function () { for (const i in this) { delete this[i]; } } p.prototype//原型对象 = new p(1, 2, 3); var p1 = new p(10, 20, 30); document.write(p1.x); document.write(p1.y); document.write(p1.z); p1.del(); document.write(p1.x); document.write(p1.y); document.write(p1.z);
3.3.3应用原型
下面通过几个实例介绍原型在代码中的应用技巧。
用法1:利用原型间接实现本地数据备份。
function p(x) { this.x = x; } p.prototype.backup = function () { for (const i in this) { p.prototype[i] = this[i] } } var p1 = new p(1); p1.backup(); p1.x = 10; document.write(p1.x); p1 = p.prototype; document.write(p1.x);
用法2:利用院校还可以为对象属性设置‘只读’特性。
function p(x, y) { if (x) this.x = x; if (y) this.y = y; p.prototype.x = 0; p.prototype.y = 0; } function l(a, b) { var a = a; var b = b; var w = function () { return Math.abs(a.x - b.x); } var h = function () { return Math.abs(a.y - b.y); } this.length = function () { return Math.sqrt(w() * w() + h() * h()); } this.b = function () { return a; } this.e = function () { return b; } } var p1 = new p(1, 2); var p2 = new p(10, 20); var l1 = new l(p1, p2); document.write(l1.length() + '<br>'); l1.b().x = 50; document.write(l1.length());
3.3.4原型继承
原型继承是一种简化的继承机制。原型继承不再需要使用类来定义对象的结构,其中引用对象被称为原型对象。
下面用法:
function A(x) { this.x1 = x; this.get1 = function () { return this.x1; } } function B(x) { this.x2 = x; this.get2 = function () { return this.x2 + this.x2; }; } B.prototype = new A(1); function C(x) { this.x3 = x; this.get3 = function () { return this.x2 * this.x2; } } C.prototype = new B(2); var b = new B(2); var c = new C(3); document.write(b.x1); document.write(c.x1); document.write(c.get3()); document.write(c.get2());
prototype的最大特点就是能允许对象实例共享原型对象的成员。不需要声明静态类,而是通过复制已经存在的原型对象来实现继承关系的。
原型继承显得非常简单,其优点也是结构简练,不需要每次构造都调用父类的构造函数,且不需要通过复制属性的方式就能快速实现继承。
但是也存在一下几个缺点:
-
每个类型只有一个原型,所以它不直接支持多重继承。
-
它不能很好地支持多参数或者动态参数的父类。也许在原型继承阶段,用户还不能决定以什么参数来实例化构造函数。
-
使用不够灵活。用户需要在原型声明阶段实例化父类对象,并把它作为当前类型的原型,
-
这限制了父类实例化的灵活性,很多时候无法确定父类对象实例化的时机和场所。
-
prototype属性固有的副作用。
3.3.4扩展原型方法
Js允许为基本数据类型定义方法。通过为Object.prototype添加原型方法,可以使得该方法对所有的对象可用。
用法1:通过给Function.prototype增加方法,是该方法对所有函数可以。
Function.prototype.method = function (name, func) { this.prototype[name] = func; return this; };
为Function.protype增加一个method方法后,不必在使用prototype这个属性了,然后调用method方法就可以直接为各种基本类型添加方法。
用法2:Js并没有单独的整数类型,下面通过为Number.prototype添加一个integer方法来改善它。
Number.method('integer', function () { return Math[this < 0 ? 'ceil' : 'floor'](this); }); document.write((-10 / 3).integer());
3.4实战案例
Js是基于(不是面向)对象的语言,它是以对象为基础,以函数为模型,以原型为继承机制的开发模式。通过多个示例介绍js对象的灵活运用。
3.4.1设计工厂模式
工厂模式是一种创建类型的模式,目的是为了简化创建对象的流程,它把对象实例化简单封装在一个函数中,然后通过函数调用,实现快速、批量生产对象。
下面用法:
function createBook(title, price) { var oTempBook = new Object; oTempBook.title = title; oTempBook.price = price; oTempBook.showTitle = function () { document.write(this.title); }; oTempBook.showPrice = function () { document.write(this.price); }; return oTempBook; } var oBook1 = createBook('JavaScript从入门到精通(标准版)', ' ' + 89.80 + '元'); var oBook2 = createBook('<br>' + ' ' + '人人学音标', ' ' + 29.80 + '元'); oBook1.showTitle() + oBook1.showPrice() + oBook2.showTitle() + oBook2.showPrice();
3.4.2设计类继承
JS中其实是没有类的概念的,所谓的类也是模拟出来的。特别是当我们是用new关键字的时候,在Js中实现类的继承,而类式继承是在函数对象内调用父类的
构造函数,
下面用法:
function extend(Sub, Sup) { var F = function () { }; F.prototype = Sup.prototype; Sup.prototype = new F(); Sup.prototype.constructor = Sub; Sub.sup = Sup.prototype; if (Sup.prototype.constructor == Object.prototype.constructor) { Sup.prototype.constructor = Sup; } }
function A(x) { this.x = x; this.get = function () { return this.x; } } A.prototype.add = function () { return this.x + this.x; } A.prototype.mins = function () { return Math.sqrt(this.x - this.x); } A.prototype.mul = function () { returnthis.x * this.x; } A.prototype.division = function () { return Math.floor(this.x / this.x); } A.prototype.delivery = function () { return Math.floor(this.x % this.x); } function B(x) { A.call(this, x); } extend(B, A); var f = new B(20); document.write(f.get()) document.write(f.add()) document.write(f.mins()) + document.write(f.mul()) + document.write(f.division()) + document.write(f.delivery());