JavaScript——对象学习笔记

一、对象的概念

  • 对象是JavaScript的基本数据类型。它是一种复合值,它将很多值(原始值或者其它对象)聚合在一起,可以通过属性名访问这些值。对象也可看做是属性的无序列集合,每个属性都是一个名/值对。属性名是字符串,因此我们可以把对象看成是从字符串到值的映射。然而对象不仅是字符串到值的映射,对象除了可以保持自有的属性,JavaScript对象还可以原型对象继承属性。对象的方法通常是继承的属性。
  • JavaScript对象是动态的,既可以新增又可以删除。
  • 对象最常用的方法是创建(create)、设置(set)、查找(query)、删除(delete)、检测(test)、枚举(enumerate)它的属性。
  • 属性包括名字和值。属性名可以是包含空字符串在内的任意字符串,但对象中不能存在两个相同的名的属性。值可以是任意的JavaScript值,或者在(ECMAScript 5中)可以是一个getter或setter函数(或者两者皆有)。
  • 属性特性:
    (1)可写(writable attribute):表明是否可以设置该属性的值。
    (2)可枚举(enumerable attribute):表明是否可以通过for/in循环返回该属性。
    (3)可配置(configurable attribute):表明是否可以删除或修改该属性。
  • 对象的三个相关的对象特性:
    (1)对象的原型指向另一个对象,本对象的属性继承自它的原型对象。
    (2)对象的类(class)是一个标识对象类型的字符串。
    (3)对象的扩展标记(extensible flag)指明了(在ECMA Script 5中)是否可以向该对象添加新属性。
  • 三类JavaScript对象和两类属性
    (1)内置对象(native object)是由ECMAScript规范定义的对象或类。例如,数组、函数、日期和正则表达式都是内置对象。
    (2)宿主对象(host object)是由JavaScript解释器所嵌入的宿主环境(比如Web浏览器)定义的。客户端JavaScript中表示网页结构的HTMLElement对象均是宿主对象。既然宿主环境定以的方法可以当成普通的JavaScript函数对象,那么宿主对象也可以当成内置对象。
    (3)自定义对象(user-defined object)是由运行中的JavaScript代码创建的对象。
    (4)自有属性(own property)是直接在对象中定以的属性。
    (5)继承属性(inherit property)是在对象的原型对象中定以的属性。

二、对象的创建

1、对象直接量

对象直接量是由若干名/值对组成的映射表,名/值对中间用冒号分隔,名/值对之间用逗号分隔,整个映射表用花括号括起来。
需要我们注意的是:对象直接量是一个表达式,这个表达式的每次运算都创建并初始化一个新的对象。每次计算对象直接量的时候,也都会计算它的每个属性的值。也就是说,在一个重复调用的函数中的循环体内使用了对象直接量,它将创建很多新对象,并且每次创建的对象的属性值也有可能不同。
eg:

  • 没有任何属性的对象
var empty = {};
  • 若干属性
var point = {
	x:1,
	y:2
};
  • 属性名字有空格或者有连字符或者保留字,则属性名必须用字符串表示。需要注意的是在ECMAScript 5(以及ECMAScript 3的一些实现中),保留字可以作为不带引号的属性名。
var obj = {
	"main title" : "JavaScript" ,
	"sub-title" : "cc" ,
	"for" : "all audiences"
};
  • 属性值为对象或更复杂的值
var obj = {
	x : point.x + 1,
	author : {
		firstname : "David",
		surname : "Flanagn"
	}
};

2、通过new创建对象

new运算符创建并初始化一个对象。

  • 创建一个空对象和 { }一样
var obj = new Object ( ); 

3、Object.create ( )

ECMAScript 5 定义了Object.create( )方法,用以创建一个新对象。第一个参数是创建的新对象的原型,第二个可选参数用以对新对象的属性进行加以说明。
Object.create ( )是一个静态的函数,不是提供给某个对象调用的方法。使用它的方法很简单,传入所需的原型对象即可。

  • obj1 继承了属性x 和 y
var obj1 = Object.create ( { x: 1, y: 2});
  • 传入参数null 创建的对象没有原型
var obj1 = Object.create( null );
  • 通过传入参数Object.prototype创建一个空对象,和通过 { }以及new Object ( )一样
var obj1 = Object.create ( Object.prototype );
  • 可以通过任意原型创建新对象(换句话说,可以使任意对象可继承),这是一个强大的特性。

三、属性的查询和设置

var book = {
	"main title" : "JavaScript" ,
	"sub-title" : "cc" ,
	"for" : "all audiences",
	author : {
		firstname : "David",
		surname : "Flanagn"
	}
};

1、属性的查询

可以通过(.)或方括号([ ])运算符来获取属性的值。
运算符左侧应当是一个表达式,它返回一个对象。
对于点(.)来说,左侧必须是一个以属性名称命名的简单的标识符。
对于方括号([ ])来说,方括号内必须是一个计算结果为字符串的表达式。更严格的将,方括号内的表达式必须返回字符串或返回一个可以转换为字符串的值,这个字符串就是属性的名字。

  • 得到book的"author"属性
var author = book.author;
  • 得到book 的sub-title属性
var title = book["sub-title"];

2、属性的设置

和查询属性值得写法一样,通过点和方括号也可以创建属性或给属性赋值,但需要将它们放在属性表达式的左侧

  • 创建属性
book.edition = 6;
  • 给已有属性重新赋值
book["sub-title"] = "dd";

3、点运算符与方括号运算符的区别

object.prototype
object["prototype"]
  • object.prototype 语法使用点运算符和一个标识符,标识符是直接出现在JavaScript程序中的,并不是数据类型,因此程序无法动态的修改标识符。
  • object[“prototype”] 语法使用了方括号和一个字符串,是一个通过字符串索引来搜索数组元素的数组,即关联数组,也称作散列、映射或字典。JavaScript对象都是关联数组。而且,字符串值是数据类型是可以动态更改的。
var cutomer = {
	address1:"address1",
	address2:"address2",
	address3:"address3"
};
var add = "";
for (let i = 0; i<4;i++){
	add += cutomer ["address" + i] + '\n';
}

4、继承属性的查询与设置

// inherit 函数时通过原型链创建一个新对象。inherit 函数返回了一个继承自原型对象p的属性的新对象。
function inherit (p){
    if(p === null) throw TypeError();
    if(Object.create)
        return Object.create(p);
    var t = typeof p;
    if(t !== "Object" && t !== "function") throw TypeError();
    function f() {};
    f.prototype = p;
    return new f();
}
  • 首先,对象的属性有自有属性和继承属性。
  • 赋值操作首先检查原型链,以判断是否允赋值。如果是只读属性,则不允许赋值操作。如果允许赋值操作,也只是在原始对象上创建属性或对已有的属性赋值,而不会去修改原型链(原型对象上的属性值)。在JavaScript中,只有在查询属性时才体会到继承的存在,而设置属性则和继承无关。这是JavaScript一个重要特性,该特性让我们可以有选择的覆盖继承的属性。
  • 如若是继承属性,则赋值操作时会被新创建的同名属性覆盖了。
var o = {};
o.x = 1;
var p inherit(o);
p.y = 2;

在这里插入图片描述

p.x  // 1
p.x = 3;

在这里插入图片描述

5、属性访问错误

属性访问并不总是返回或设置一个值,下面总结一些查询或设置属性时的一些错误情况。

  • 查询一个并不存在的属性并不会报错,只会返回undefined。
  • 查询不存在的对象的属性就会报错,会抛出一个类型错误。
// 假设book 不存在
var len = book.subtitle.length;

在这里插入图片描述
如何避免呢?

var len = book && book.subtitle && book.subtitle.length;
  • 给null、undefined设置属性也会报错;
    在这里插入图片描述
    在这里插入图片描述

6、属性设置错误

给只读属性重新赋值虽无法重新赋值,但是并不会报错。但在ECMAScript 5 的严格模式下,任何失败的属性设置操作都会抛出一个类型错误。

Object.prototype = 0;

在这里插入图片描述

  • 如果一个属性时只读的:则不能给只读属性重新赋值,但在defineProperty ( ) 方法中,可以对可配置的只读属性重新赋值。
var n = {};
Object.defineProperty( n, "x", { value : 1,
    writable : false,
    enumerable : true,
    configurable :true
});

在这里插入图片描述

Object.defineProperty(n, "x", {value: 2});

在这里插入图片描述

Object.defineProperty(n, "x", {value:3, configurable: false});

在这里插入图片描述

Object.defineProperty(n, "x", {value:4});

在这里插入图片描述

  • 通过继承得到的只读属性,不能通过同名自有属性覆盖只读的继承属性。
var n = Object.defineProperties({},{
    x:{value:1,writable: true, enumerable: true, configurable: true},
    y:{value:2,writable: false, enumerable: true, configurable: true}
});
var m = Object.create(n);

在这里插入图片描述

m.x = 3;

在这里插入图片描述

m.y = 3;

在这里插入图片描述

  • 如果一个对象中(假设为o)不在某个自有属性(假设为p):o没有使用setter方法继承属性p,并且o的可扩展性是false。如果o中不存在p,而且没有setter方法可供调用,则p一定会添加至o中。但是如果o不是可扩展的,那么o中不能定义新属性。

四、删除属性

  • 删除对象的属性我们需要用到delete运算符,操作数是一个属性访问表达式。
var o = {x:1,y:2};
delete o.x;

在这里插入图片描述

  • 需要注意的是delete只是断开了属性和宿主对象的关系,而不会去操作属性中的属性。
var a = {x:1};
b=a.x;
delete a.x;

在这里插入图片描述
所以说,已经删除的属性的引用依然存在,因此在JavaScript的某些实现中,可能因为这种不严谨的代码而造成内存泄漏,所以在销毁对象的时候,要遍历属性中的属性,依次删除。

  • delete运算符只能删除自有属性,不能删除继承属性。若想删除继承属性则必须从定义属性的原型对象上删除它,且会影响到所有继承自这个原型的对象。
  • delete表达式删除成功或失败没有任何副作用(比如删除不存在的属性)时,它返回true。如果delete后不是一个属性访问表达式,同样返回true。
var a = {x:1};
delete a.x;

在这里插入图片描述
再次删除a.x,此时a.x已经不再存在了。

delete a.x;

在这里插入图片描述
删除继承而来的toString属性。

delete a.toString;

在这里插入图片描述
删除无意以的表达式,也返回true。

delete 1;

在这里插入图片描述

  • 需要注意的是,delete不能删除那些可配置性为false的属性。js中有些内置属性是不可配置的,比如通过变量声明和函数声明创建的全局对象属性。且在严格模式下,删除一个不可配置属性会报一个类型错误。
  • 在删除全局对象的可配置属性时:如果处在非严格模式下,可以省略全局对象的引用,直接在delete操作符后跟随要删除的属性名即可:
this.x = 1;
delete x;

在这里插入图片描述
然而在严格模式中,delete后跟随一个非法的操作数(比如x),则会报一个语法错误,因此必须显示指定对象及其属性:

this.m = 1;
delete this.m;

在这里插入图片描述

五、检测属性

var o = Object.create({x:1});
o.y = 2;
  • in运算符,in运算符的左侧是属性名(字符串),右侧是对象,若对象的自有属性或继承属性中包含这个属性则返回true。需要我们特别注意的是,只有in运算符可以区分不存在的属性和存在但值为undefined的属性。
"x" in o;
"y" in o;

在这里插入图片描述
在这里插入图片描述

  • 对象的hasOwnProperty( )方法,用来检测给定的名字是否是对象的自有属性,对于继承属性将返回false。
o.hasOwnProperty("x");

在这里插入图片描述

o.hasOwnProperty("y");

在这里插入图片描述

  • 对象的properIsEnumerable( )方法:只有检测到是对象的自由属性且这个属性可枚举时才返回true。
o.propertyIsEnumerable("y");

在这里插入图片描述
将o.y的可枚举性设为false:

Object.defineProperty(o,"y",{enumerable:false});
o.propertyIsEnumerable("y");

在这里插入图片描述

六、枚举属性

  • for / in:可以循环体重遍历对象中的所有可枚举的属性,包括自有属性和枚举属性。
  • Object.getOwnPropertyNames( )方法,返回一个包含这个对象的所有的自有属性的名称的数组,不论属性是否可枚举。
  • Object.keys( )方法:返回一个包含这个对象的所有的可枚举的自有属性名称的数组。

七、属性getter和setter

  • 概念:在ECMAScript 5中,属性值可以用一个或两个方法替代,这两个方法就是getter和setter。由getter和setter定义的属性称做“存取器属性”,它不同于“数据属性”,数据属性只有一个简单的值。
  • 定义:存取器属性定义为一个或两个和属性同名的函数,这个函数不使用function关键字,而是使用get和set。需要我们注意的是,get和set方法并没有使用冒号将属性名和函数体分割开,但在函数体结束和下一个方法或数据属性之间有逗号分隔。
  • 定义直接量定义存取器属性:
var o = {
    x:1,
    get y () { return this.x; },
    set y (newValue) { this.x = newValue; }
};

在这里插入图片描述

o.y = 2;

在这里插入图片描述
这里需要我们注意的是:getter和setter里this关键字的用法。JavaScript把这些函数当做对象的方法来调用,也就是说,在函数体内的this指向表示这个点的对象。

  • 读写属性:存取器属性不具有可写性。如果一个存取器属性同时具有getter和setter方法,那么它是一个读 / 写属性。如果只有一个getter方法,那么它是一个只读属性。如果只有setter方法,那么它是一个只写属性,读取只写属性总是返回undefined。
  • 继承属性:可继承。
  • getter方法:当程序查询存取器属性的值时,JavaScript调用getter方法(无参数),getter方法的返回值就是属性存取表达式的值。
  • setter方法:当程序设置一个存取器属性的值时,JavaScript调用setter方法,将赋值表达式右侧的值当做参数传入setter。

八、属性的特性

1、定义

属性除了包含名字和值之外,属性还包含一些标识它们可写、可枚举和可配置的特性。
数据属性可以看做:一个属性包含一个名字和四个特性。数据属性的四个特性分别是它的值(value)、可写性(writable)、可枚举性(enumerable)和可配置性(configurable)。
存取器属性可以看做读取(get)、写入(set)、可枚举性和可配置性。

2、查询对象属性的特性

Object.getOwnPropertyDescriptor()方法:可以获得某个对象特定属性的属性描述符,但是只能得到自有属性的描述符。对于继承属性和不存在的属性,则返回undefined。要想得到继承属性的特性,需要遍历原型链。

var o = {
    x:1,
    get y () { return this.x; },
    set y (newValue) { this.x = newValue; }
};
Object.getOwnPropertyDescriptor(o,"x");

在这里插入图片描述

Object.getOwnPropertyDescriptor(o,"y");

在这里插入图片描述

3、设置属性的特性

  • Object.defineProperty( )方法:需要传入要修改的对象、要创建或修改的属性名称以及属性描述符对象,但是只能修改或新建自有属性,不能修改继承来的属性。
    修改o.x为不可枚举属性:
Object.defineProperty(o, "x", {enumerable:false});

在这里插入图片描述
需要注意的一点是,传入Object.defineProperty()的属性描述符对象不必包含所有的四个特性。对于新创建的属性来说,默认的特性值是false或undefined。对于修改的已有属性来说,默认的特性值没有做任何的修改。
创建一个新属性:

Object.defineProperty(o,"z",{value:3,writable:true,enumerable:false});

在这里插入图片描述

  • Object.defineProperties( ):可以同时新建或修改多个属性。第一个参数是要修改的对象,第二个参数是一个对象,包含要新建或修改的属性的名称,以及他们的属性描述符。
Object.defineProperties(o, {
    x:{value:2,enumerable:true},
    y:{
        get:function(){return this.x+3;},
    }
});

在这里插入图片描述

  • Object.defineProperty( )方法 和Object.defineProperties( )方法会抛出异常的情况:
    (1)如果对象时不可扩展的,则可以编辑已有的自有属性,但不能给它添加新属性。
    (2)如果属性是不可配置的,则不能修改它的可配置性和可枚举性。
    (3)如果存取器属性是不可配置的,则不能修改其getter和setter方法,也不能将它转换为数据属性。
    (4)如果数据属性是不可配置的,则不能将它转换为存取器属性。
    (5)如果数据属性是不可配置的,则不能将它的可写性从false转为true,但是可以从true转为false。
    (6)如果数据属性是不可配置且不可写的,则不能修改它的值。然而可配置但不可写属性的值是可以修改的(实际上是先将它标记为可写的,然后修改它的值,最后转换为不可写的)。

九、对象的三个类属性

1、原型属性

  • 对象的原型属性是用来继承属性的,原型属性是在实例创建之初就设置好的。通过对象直接量创建的对象的原型对象是Object.prototype,通过new创建的对象的原型对象时构造函数的prototype属性,通过Object.create()创建的对象使用第一个参数(也可以是null)作为它们的原型。
  • 查询对象的原型对象:
    (1)ECMAScript 5 中,将对象作为参数传入Object.getPrototypeOf()可以查询它的原型。
    (2)ECMAScript 3 中,使用表达式o.constructor.prototype来检测一个对象的原型。
  • 检测一个对象是否是另一个对象的原型(或处于原型链中),请使用isPrototypeOf()方法。

2、类属性

  • 对象的类属性是一个字符串,用来表示对象的类型信息。
  • 获取方式:要想获得对象的类,可以调用对象的toString( )方法,然后提取已返回字符串的第八个到倒数第二个位置之间的字符。但是由于很多对象继承的toString()方法重写了,为了调用正确的toString()版本,必须间接的调用Function.call()方法。
function classof(o){
    if(o === null) return "NULL";
    if(o === undefined) return "undefined";
    return Object.prototype.toString.call(o).slice(8,-1);
}

3、可扩展性

  • 对象的可扩展性用以表示是否可以对象添加新属性。
  • 所有的内置对象和自定义对象都是显示可扩展的,宿主对象的可扩展性是由JavaScript引擎定义的。
  • ECMAScript 5定义了用来查询和设置可扩展对象的函数:将对象传入Object.esExtensible( )来半段对象时否可以扩展。
  • Object.preventExtensions( ),将对象转换为不可扩展的。需要注意的是,一旦对象转换为不可扩展的,就无法将其转换为可扩展的了。
  • Object.seal():该方法不仅将对象的设置为不可扩展的,还可以将对象的所有自有属性都设置为不配置的。对应的检测方法是Object.isSealed()。
  • Object.freeze( ):不仅将对象设置为不可扩展的和将其属性设置为不可配置,还将自有的所有数据属性设置为只读。可以用Object.isFrozen()来检测。

十、序列化对象

  • 定义:序列化对象是指将对象的转态转换为字符串,也可以将字符串还原为对象;
  • JSON.stringify( ):序列化对象。
  • JSON.parse( ):还原对象。
var o = {
    x:1,
    get y () { return this.x; },
    set y (newValue) { this.x = newValue; }
};
var p = JSON.parse(JSON.stringify(o));

需要注意的是:p是o的深拷贝。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值