文章目录
使用对象
在js中,对象是一个泛化概念,任何值都可以转换成对象,以对象的方式使用,如数字对象、布尔值对象、字符串对象、类型对象、函数对象、数组对象等,他们都继承Object类型对象,拥有共同的基本属性和方法。此外,js也允许自定义对象。从狭义的概念来分析,对象是最基本的数据类型,是复合型结构、引用型的数据,它是无序数据集合,对象中每个成员被称为属性。
- 【重点】
- 定义对象
- 访问对象
- 使用对象的属性
- 使用内置对象
定义对象
在js中有三种方法:
构造对象
使用new运算符调用构造函数,可以构造一个实例对象。具体用法如下:
var objectName = new functionName(args);
参数说明:
-
objectName:返回的实例对象。
-
functionName:构造函数,与普通函数基本相同,但是不需要return返回值,返回实例对象,在函数内可以使用this预先访问。
-
args:实例对象初始化配置参数列表。
-
【实例】下面实例使用不同类型的构造函数定义各种实例。
var o = new Object();
var a = new Array();
var f = new Function();
对象直接量
使用对象直接量可快速定义对象,也是最高效,最方便的方法。
var objectName = {
属性名1: 属性值1,
属性名2: 属性值2,
属性名3: 属性值3,
...
}
在对象直接量中,属性名和属性值之间通过冒号进行分隔,属性值可以是任意数据类型,属性名可以是js标识符,或者是字符串表达式。属性与属性之间通过逗号进行分隔,最后一个末尾不需要逗号。
- 【实例1】下面代码使用对象直接量定义两个对象。
var o = {
a: 1,
b: true
}
var o1 = {
"a": a,
"b": true
}
- 【实例2】属性值可以是任意类型的值。如果属性值是函数,则该属性也称为方法。
var o = {
a: function() {
return 1;
}
}
- 【实例3】如果属性值是对象,可以设计嵌套结构的对象。
var o = {
a: {
b: 1
}
}
- 【实例4】如果不包含任何属性,则可以定义一个对象。
var o = {
}
使用Object.create
Object.create是RCMAScript5新增的一个静态方法,用来定义定义一个实例对象。该方法可以指定对象的原型和对象特性。
Object.create(prototype, descriptors)
参数说明:
-
prototype:必须参数,指定原型对象,可以为null。
-
descriptors:可选参数,包含一个或多个属性描述的js对象。属性描述包含数据特性和访问器特性,
- 其中数据特性说明如下:
- value:指定属性值。
- writable:默认为false,设置属性是否可写。
- enumerable:默认为false,设置属性是否可枚举(for/in)。
- configurable:默认为false,设置是否可以修改属性特征和删除属性。
- 访问器特性包含两个方法:
- set():设置属性值。
- get():返回属性值。
-
【实例1】下面使用Object.create定义一个对象,继承null,包含两个可枚举的属性size和shape,属性值分别为“large”和“round”。
var newObj = Object.create(null, {
size: {
value: "large",
enumerable: true
},
shape: {
value: "round",
enumerable: true
}
});
console.log(newObj.size); // large
console.log(newObj.shape); // round
console.log(Object.getPrototypeOf(newObj)); // null
- 【实例2】
var obj = Object.create(Object.prototype, {
x: {
value: undefined,
writable: true,
configurable: true,
enumerable: true
}
});
console.log("obj.prototype = " + Object.getPrototypeOf(obj));
// obj.prototype = [object Object]
- 提示:
Object.getPrototypeOf()
函数可以获取原始对象的原型。如果要获取对象的属性描述符,可以使用Object.getOwnPropertyDescriptor()
函数。 - 【实例3】下面实例定义一个对象,使用访问器属性b来读写数据属性a。
var obj = Object.create(Object.prototype, {
a: {
// 数据属性a
writable: true,
value: "a"
},
b: {
// 访问器属性b
get: function() {
return this.a;
},
set: function(value) {
this.a = value;
}
}
});
console.log(obj.a); // a
console.log(obj.b); // a
obj.b = 20;
console.log(obj.b); // 20
操作对象
对象是引用型、复合型数据,因此对象的操作主要包括引用、复制、克隆、销毁等。
引用对象
对象是引用型数据,赋值操作实际上就是赋予地址。
- 【实例】下面实例定义一个对象obj,然后赋值个obj1,obj就全等于obj1,它们都引用同一个对象,也就是说他们的值都是同一个地址。
var obj = {
x: true,
y: false
}
var obj1 = obj;
console.log(obj1 === obj); // true
console.log(obj1.x); // true
console.log(obj.x); // true
复制对象
复制对象就是利用for/in遍历对象,然后把每个对象成员赋值给另一个对象。
- 【实例】在下面实例中,通过复制操作把obj的属性转移给obj1对象。
var obj = {
x: true,
y: false
}
var obj1 = {
};
for (var i in obj) {
obj1[i] = obj[i];
}
console.log(obj1 === obj); // false
console.log(obj1.x); // true
console.log(obj.x); // true
克隆对象
克隆对象也是一种复制操作,不过它执行效率更高一些。
- 【实现方法】
第一步,封装克隆工具。为Function类型扩展一个原型方法。
var clone = function (obj) {
function Temp() {
};
Temp.prototype = obj;
return new Temp();
}
第二步,调用工具函数clone()把obj克隆给obj1。
var obj = {
x: true,
y: false
}
var obj1 = {
};
obj1 = clone(obj);
第三步,检测对象,其拥有对象obj所有属性,但是他们不全等。
console.log(obj1 === obj); // false
console.log(obj1.x); // true
console.log(obj1.x); // true
这里通过直接赋值的方式把一个对象传递给另一个临时构造函数的原型对象,然后实例化类型函数,并返回这个实例对象,它拥有了参数对象的所有成员,但不再与原参数对象保持联系。
摧毁对象
js能够自动回收无用存储单元,当一个对象没有被引用时,该对象就被废除了,会自动销毁所有废除的对象。把所有引用都设置为null,可以强制废除对象。
var obj = {
x: true,
y: false
}
obj = null;
操作属性
属性也称为名值对,包括属性名和属性值。属性名可以是包含空字符串在内的任意字符串,一个对象中不能存在两个同名的属性。属性值可以是任意类型的数据。
定义属性
- 直接量定义
在对象直接量中,属性名与属性值之间通过冒号分隔,冒号左侧是属性名,右侧是属性值,名值对(属性)之间通过逗号分隔。
- 【实例1】下面实例,使用直接量方法定义对象obj,然后添加了两个成员,一个是属性,另一个是方法。
var obj = {
x: 1,
y: function(){
return this.x + this.x;
}
}
- 点语法定义
- 【实例2】通过点语法,可以在构造函数内或对象外添加属性。
var obj = {
}
obj.x = 1;
obj.y = function () {
return this.x + this.x;
}
- 使用Object.defineProperty
使用Object.defineProperty()函数可以为对象添加属性,或者修改现有属性。如果指定的属性名在对象中不存在,则执行添加属性;如果在对象中存在同名属性,则执行修改操作。
具体用法:
Object.defineProperty(Object, propertyname, descriptor)
参数说明:
- object:指定要添加或修改属性的对象,可以是js对象或者DOM对象。
- propertyname:表示属性名的字符串。
- descriptor:定义属性的描述符,包括数据属性或访问器属性。
Object.defineProperty返回值为已修改的对象。
- 【实例3】下面实例先定义一个对象直接量obj,然后使用Object.defineProperty()函数为obj对象添加属性,属性名为x,属性值为1,可写、可枚举、可修改特性。
var obj = {
};
Object.defineProperty(obj, "x", {
value: 1,
writable: true,
enumerable: true,
configurable: true
});
console.log(obj.x); // 1
- 使用Object.defineProperties
使用Object.defineProperties()函数可以一次定义多个属性,具体用法如下:
Object.defineProperties(object, descriptors)
参数说明:
-
object:对其添加或修改属性的对象,可以是本地对象或DOM对象。
-
descriptors:包含一个或多个描述对象,每个描述对象描述一个数据属性或访问器属性。
-
【实例4】在下面实例中,使用Object.defineProperties()函数将数据属性和访问器属性添加到对象obj上。
var obj = {
};
Object.defineProperties(obj, {
x: {
value: 1,
writable: true,
},
y: {
set: function(x) {
this.x = x;
},
get: function() {
return this.x;
},
}
});
obj.y = 10;
console.log(obj.x); // 10
删除属性
使用delete运算符可以删除对象的属性。
- 【实例】下面实例使用delete运算符删除指定属性。
var obj = {
x: 1
}
delete obj.x;
console.log(obj.x); // 返回undefined
- 提示:当删除对象属性之后,不是将该属性值设置为undefined,而是从对象中彻底清除属性。如果使用for/in语句枚举对象属性,只能枚举属性值为undefined的属性,但不会枚举已删除的属性。
使用方法
方法也是函数,当函数被赋值给对象的属性,就被成为方法。方法的使用与函数是相同的,唯一不同的是在方法内常用this引用调用对象,其实在普通函数内也有this,只不过不常用。
使用点语法或中括号语法,可以访问方法,使用小括号可以激活方法。
- 【实例1】与普通函数用法一样,可以在调用方法时传递参数,也可以设置返回值。
var obj = {
}
obj.f = function(n) {
return 10 * n;
}
var n = obj.f(5);
console.log(n); // 50
- 【实例2】在方法内this总是指向当前调用对象。在下面实例中,当在不同运行环境中调用对象obj的方法时,该方法的this指向是不同的。
var obj = {
f: function() {
console.log(this);
}
}
obj.f(); // 此时this指向对象obj
var f1 = obj.f;
f1(); // 此时this指向对象window
对象与数组
对象(Object)与数组(Array)是两种不同的数据集合,其中对象是包含已命名的值的无序集合,而数组则是包含已编码的有序集合。
- 【实例1】下面实例分别使用对象和数组来存储1和true这两个值。
var o = {
x: 1,
y: true
}
var a = [
1,
true
]
对象的存储形式很像数组,因此被称为关联数组,但它不是真正意义上的数组。关联数组就是将值与特定字符串关联在一起。真正的数组与字符串没有联系,但是它将值和非负整数的下标关联在一起。
console.log(o["x"]); // 1,在对象o中,值1与字符串x关联
console.log(a[0]); // 1,在数组a中,值1与数值0关联
使用点语法(.)可以存取对象属性,而数组使用中括号([])来存取属性。针对上面对象属性的读取操作,下面两行代码的意思是相同的。
o.x;
o["x"];
使用点语法存取属性时,属性名是标识符;而中括号存取属性时,属性名是字符串。
- 【实例2】当用点运算符来存取对象属性时,属性名是用标识符表示的;当用中括号来存取对象属性时,属性名是用字符串表示的,因此可以在运行过程中动态生成字符串。
var o = {
p1: 1,
p2: true
}
for (var i = 0; i < 3; i++) {
console.log(o["p" + i]);
}
通过关联数组访问带有字符串表达式的对象属性时非常灵活的。当对象属性非常多时,使用点语法来存取对象属性会比较麻烦。另外,在一些特殊情况下只能使用关联数组形式来存取对象属性。
属性描述对象
属性描述对象是ECMAScript5新增的一个内部对象,用来描述对象的属性的特性。
属性描述对象的结构
在定义对象,定义属性时,曾介绍过属性描述符,属性描述符实际上就是一个对象。属性描述对象包含6个属性,可以选择使用。
-
value:设置属性值,默认值为undefined。
-
writable:设置属性值是否可写,默认值为true。
-
enumerable:设置属性是否可枚举,即是否允许使用for/in语句或Object.keys()函数遍历访问,默认为true。
-
configurable:设置是否可设置属性特性,默认为true。如果为false,将无法删除该属性,不能够修改属性值,也不能修改属性的描述对象。
-
get:取值函数,默认为undefined。
-
set:存值函数,默认为undefined。
-
【实例1】下面实例演示使用value读写属性值的基本方法。
var obj = {
}
Object.defineProperty(obj, "x", {
value: 100
});
console.log(Object.getOwnPropertyDescriptor(obj, "x").value); //100
- 【实例2】下面实例演示了使用writable属性禁止修改属性x。
var obj = {
}
Object.defineProperty(obj, "x", {
value: 1,
writable: false
});
obj.x = 2;
console.log(obj.x); //1
-
提示:在正常模式下,如果writable为false,重写属性值不会报错,但操作失败,而在严格模式下则会抛出异常。
-
【实例3】enumerable可以禁止使用for/in语句、Object.keys()函数,JSON.stringify()方法遍历访问指定属性,这样可以设置隐私属性。
var obj = {
};
Object.defineProperty(obj, 'x', {
value: 1,
enumerable: false
});
console.log(obj.x); //1
for (var i in obj) {
console.log(i);
}
console.log(Object.keys(obj)); //[]
console.log(JSON.stringify(obj)); // {}
- 【实例4】configurable可以禁止修改属性描述对象,当其值为false、时,value、writable、enumerable、configurable禁止修改,同时禁止删除属性。下面实例中,当设置属性x禁止修改配置后,下面操作都是不允许的,其中obj.x=5操作失败,则后面4个操作方法都会抛出异常。
var obj = Object.defineProperty({
}, 'x', {
configurable: false
});
obj.x = 5;
console.log(obj.x);
Object.defineProperty(obj, 'x', {
value: 2
});
Object.defineProperty(obj, 'x', {
writable: true
});
Object.defineProperty(obj, 'x', {
enumerable: true
});
Object.defineProperty(obj, 'x', {
configurable: true
});
- 注意:当configurable为false时,如果把writable=true改为false是允许的。只要writable或configurable有一个为true,则value也允许修改。
访问器
除了使用点语法或中括号语法访问属性的value外,还可以使用访问器,包括set和get两个函数。其中,set()函数可以设置value属性,而get()函数可以读取value属性值。
借助访问器,可以为属性的value设计高级功能,如禁用部分特性、设计访问条件、利用内部变量或属性进行数据处理等。
- 【实例1】下面实例设计对象obj的x属性必须为数字。为属性x定义了get和set特性,obj.x取值时,会调用get,赋值时,会调用set。
var obj = Object.create(Object.prototype, {
_x: {
value: 1,
writable: true
},
x: {
get: function() {
return this._x;
},
set: function(value) {
if (typeof value != "number") {
throw new Error("请输入数字");
}
this._x = value;
}
}
});
console.log(obj.x); // 1
obj.x = "2"; //Uncaught Error: 请输入数字
- 【实例2】js也支持一种简写方法。针对实例1,通过以下方式可以快速定义属性。
var obj = {
_x: 1,
get x() {
return this._x;
},
set x(value) {
if (typeof value != "number") {
throw new Error("请输入数字");
}
this._x = value;
}
};
console.log(obj.x); // 1
obj.x = 2;
console.log(obj.x); // 2
- 注意:取值函数get()不能接受参数,存值函数se()只能接受一个参数,用于设置属性的值。
操作属性描述对象
属性描述对象是一个内部对象,无法直接读写,可以通过下面几个函数进行操作。
- Object.getOwnPropertyDescriptor():可以读出指定对象私有属性的属性描述对象。
- Object.defineProperty():通过定义属性描述对象来定义或修改一个睡醒,然后返回修改后的对象。
- Object.defineProperties():可以同时定义多个属性描述对象。
- Object.getOwnPropertyNames():获取对象的所有私有属性。
- Object.keys():获取对象的所有本地可枚举的属性。
- propertyIsEnumerable():对象实例方法,直接调用,判断指定的属性是否可以枚举。
控制对象状态
js提供了3种方法,用来精确控制一个对象的读写状态,防止对象被改变。
- Object.preventExtensions:阻止为对象添加新的属性。
- Object.seal:阻止对象添加新的属性,同时也无法删除旧属性。等价于把属性描述对象的configurable属性设置为false。注意,该方法不影响修改某个属性的值。
- Object.freeze:阻止为一个对象添加新属性、删除旧属性、修改属性值。
同时提供了3个对应的辅助检查函数,简单说明如下。
-
Object.isExtensible:检查一个对象是否允许添加新属性。
-
Object.isSealed:检查一个对象是否使用了Object.seal方法。
-
Object.isFrozen:检查一个对象是否使用了Objcet.freeze方法。
-
【实例】下面代码分别使用Object.preventExtensions、Object.seal、Object.freeze函数控制对象的状态,然后再使用Object.isExtensible、Object.isSealed和Objcet.isFrozen函数检测对象的状态。
var obj1 = {
};
console.log(Object.isExtensible(obj1)); //true
Object.preventExtensions(obj1);
console.log(Object.isExtensible(obj1)); //false
var obj2 = {
};
console.log(Object.isSealed(obj2)); //false
Object.seal(obj2);
console.log(Object.isSealed(obj2)); //true
var obj3 = {
};
console.log(Object.isFrozen(obj3)); //false
Object.freeze(obj3);
console.log(Object.isFrozen(obj3)); //true
Object原型方法
js原生提供Ojbect类型对象,其他所有对象都继承自Object,都是Object的实例。Object原生方法分类两类:Object静态函数和Object原型方法。
Object原型方法定义在Object.prototype对象上,也称为实例方法,所有Object的实例对象都继承了这些方法。
使用toString()
toString()方法能够返回一个对象的字符串,它返回的字符串比较灵活,可能是一个具体的值,也可能是具体的值,也可能是一个对象的类型标识符。
- 【实例1】下面代码显示实例与对象类型的toString()方法返回值是不同的。
function F(x, y) {
this.x = x;
this.y = y;
}
var f = new F(1, 2);
console.log(F.toString()); //返回函数源代码
console.log(f.toString()); //返回字符串”[object Object]“
toString()方法返回信息简单,为了能够返回更多有用信息,用户可以重写该方法 。例如,针对实例对象返回的字符串都是"[object Object]",可以对其进行扩展,让对象实例能够返回构造函数的源代码。
Object.prototype.toString = function () {
return this.constructor.toString();
}
调用f.toString(),则返回函数的源代码,而不是字符串"[object Object]"。当然,重写方法不会影响js内置对象的toString()返回值,因为它们都是只读的。
console.log(f.toString()); // 返回函数源代码
当把数据转换为字符串时,js一般都会调用toString()方法来实现。由于不同类型的对象在调用该方法时,所转换的字符串都不同,而且都有规律,所以开发人员常用来判断对象的类型,弥补typeof运算符和constructor属性在检测对象数据类型的不足。
- 【实例2】当自定义类型时,用户可以重置toString()方法,自定义对象的数据类型。下面实例为自定义类型Me定义一个标识字符串"[object Me]"。
function Me() {
Me.prototype.toString = function() {
return "[object Me]";
}
}
var me = new Me();
console.log(me.toString()); // [object Me]
console.log(Object.prototype.toString.apply(me)); // [object Object]
- 提示:Object还定义了toLocaleString()方法,该方法主要作用——留出一个接口,允许不同对象返回只本地的字符串标识。在默认情况下,toLocaleString()方法返回值与toString()方法返回值完全相同。
- 目前,主要有3个对象自定义了toLocalString()方法。
- Array.prototype.toLocaleString()
- Number.prototype.toLocaleString()
- Date.prototype.toLocaleString()
- 在Array中重写toString(),让其返回元素值的字符串组合;在Date中重写toString(),让其返回当前日期字符串表示;在Number中重写toString(),让其返回数组的字符串表示;在Date中重写toLocaleString(),让其返回当地格式化日期字符串。
使用valueOf()
valueOf()方法能够返回对象的值。JavaScript自动类型转换时会默认调用这个方法。Object对象默认valueOf()方法返回值与toString方法返回值相同,但是部分类型对象是重写了valueOf()方法。
- 【实例1】Date对象的valueOf()方法返回值是当前日期对象的毫秒数。
var o = new Date();
console.log(o.toString()); //Mon Sep 14 2020 14:56:22 GMT+0800 (中国标准时间)
console.log(o.valueOf()); //1600066582252 (时间戳:距离1970年1月1日午夜之间的毫秒数)
console.log(Object.prototype.valueOf.apply(o)); //Mon Sep 14 2020 14:56:22 GMT+0800 (中国标准时间)
当String、Number、Boolean对象具有明显的原始值时,它们的valueOf()方法会返回合适的原始值。
- 【实例2】在自定义类型时,除了重写toString()方法外,也可以重写valueOf()方法。这样当读取自定义对象的值时,就能避免返回的值总是"[object Object]"。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.valueOf = function() {
return "(" + this.x + "," + this.y + ")";
}
var p = new Point(26, 44);
console.log(p.valueOf()); //(26,44)
console.log(Object.prototype.valueOf.apply(p)); // {x: 26, y: 44}
在特定环境下进行数据转换时(如果把对象转换为字符串),valueOf()方法的优先级比toString()方法的优先级高。因此,如果一个对象的valueOf()方法返回值和toString()方法返回值不同,且希望转换字符串为toString方法的返回值时,就必须明确调用对象的toString()方法。
检测私有属性
根据继承关系不同,对象属性可以分为两类:私有属性和继承属性。
- 【实例1】在下面自定义类型中,this.name就表示对象的私有属性,而原型对象中的name属性就是继承属性。
function F() {
// 自定义数据类型
this.name = "F的私有属性";
}
F.prototype.name = "F的继承属性";
为了方便判定一个对象属性的类型,Object对象预定义了一个hasOwnProperty()方法,该方法可以快速检测属性的类型。
- 【实例2】针对上面的自定义类型,可以实例化对象,然后判定当前对象调用的属性name是什么类型。
var f = new F();
console.log(f.hasOwnProperty("name")); //true
console.log(f.name); //F的私有属性
凡是构造函数的原型属性(原型对象包含的属性),都是继承属性,使用hasOwnProperty()方法检测时,都会返回false。但是,对于原型对象本身来说,这些原型属性又是原型对象的私有属性,所以返回值又是true。
- 【实例3】在下面实例中,演示了toString()方法对于Date对象来说是继承属性,但是对于Date构造函数的原型对象来说,则是它的私有属性。
var d = Date;
console.log(d.hasOwnProperty("toString"));//false
var d = Date.prototype;
console.log(d.hasOwnProperty("toString"));//true
hasOwnProperty()方法只能判断指定对象中是否包含指定名称的属性,无法检测对象原型链中是否包含某个属性,所以能够检测出来的属性必须是对象成员。
- Object对象还定义了isPrototypeOf()方法,该方法可检测一个对象的原型对象。
Object静态函数
Object静态函数就是定义在Object对象上的方法,通过Object直接调用,不需要实例继承。
对象包装函数
Object()也是一个函数,它可以将任意值转为对象。如果参数为空,或者为undefined和null,Object()返回一个空对象。例如:
var obj = Object();
// 等同于
var obj = Object(undefined);
var obj = Object(null)
- 【实例】如果参数为数组、对象、函数、则返回原对象,不进行转换。根据这个特性,可以设计一个类型检测函数,专门检测一个值是否为引用型对象。
function isObject(value) {
return value === Object(value);
}
console.log(isObject([]));//true
console.log(ifObject(true));//false
对象构造函数
Object()不仅可以当做工具函数使用,还可以当做构造函数使用。如果使用new命令调用Object()函数,将创建一个实例对象。例如,下面代码将创建一个新的实例对象。
var obj = new Object();
静态函数
- 遍历对象:
- Object.keys:以数组形式返回参数对象的可枚举私有属性名。
- Object.getOwnPropertyNames:以数组的形式返回参数对象包含的私有属性名。
- 对象属性:
- Object.getOwnPropertyDescriptor():获取某个属性的描述对象。
- Object.defineProperty():通过描述对象,定义某个属性。
- Object.defineProperties():通过描述对象,定义多个属性。
- 对象状态控制:
- Object.preventExtensions():防止对象扩展。
- Object.isExtensibel():判断对象是否可扩展。
- Object.seal():禁止对象配置。
- Object.isSealed():判断一个对象是否可配置。
- Object.freeze():冻结一个对象。
- Object.isFrozen():判断一个对象是否被冻结。
- 对象原型:
- Object.create():返回一个新的对象,并制定原型对象和属性。
- Object.getPrototypeOf():获取对象的Prototype对象。