0.在学习之前先思考
- 什么是对象?
- 对象类型有哪一些?
- 对象有哪些特性?
- 原型是什么?
- 原型链是什么?
- 包装对象是什么?
1. 参考资料
《JavaScriptq权威指南》
2.核心内容
一、 什么是对象?
对象是一种复合值,它将很多值(原始值或其他对象)聚合在一起,可通过属性名访问这些值。而属性名可以是包含空字符串在内的任意字符串。
-
什么是数组?
相同类型数据的集合
-
什么是字典?
字符串到值的映射
-
什么是对象?
对象及特殊的字典,简单理解为:字典的展示形式 + 对象的特性
二、 对象有哪些?
JavaScript 数据类型分为两种:
-
原始类型:
数字、字符串和布尔值特殊null undefined (根本区别原始值不可改变)
-
对象类型:
JavaScript中除了数字、字符串、布尔值、null、undefined之外的就是对象。
数组、函数、日期、正则为特殊的对象
三、 创建对象的方式
1、 对象直接量
对象直接量由若干名/值对组成的映射表,名/值对中间用冒号分隔,名/值对之间用逗号分隔,整个映射表用花括号括起来。
var point = {x: 1, y: 2};
var empty ={};
var a= [];
2、 通过new创建对象
new运算符后跟随一个函数调用,即构造函数,创建并初始化一个新对象。例如:
var o =new Object();
var a =new Array();
3、 Object.create()
Object.create()是一个静态方法,它创建一个新对象,其中第一个参数是这个对象的原型对象,Object.create()提供第二个可选参数,用以对对象的属性进行进一步描述:
var o1 = Object.create({x:1,y:2});
JavaScript中对象可以分为三类,对象的属性属性分为两类:
- 内置对象(native object),例如数组、函数、日期和正则表达式;
- 宿主对象(host object),即JavaScript解释器所嵌入的宿主环境定义的;
- 自定义对象(user-defined object),是由运行中的JavaScript代码创建的对象;
对象的属性可以分为两类:
- 自有属性(own property):直接在对象中定义的属性;
- 继承属性(inherited property):在对象的原型对象中定义的属性;
四、 对象有哪些特性?
每个对象都有与之相关的原型(prototype)、类(class)和可扩展性(extensible attribute)
1. 原型特性
基本上每一个JavaScript对象都和另一个对象相关联,“另一个”对象就是所谓的原型对象。每一个对象都从原型对象继承属性,并且一个对象的prototype属性的值就是这个对象的原型对象。
JavaScript语言规定了:
- 所有通过对象直接量创建的对象的原型对象就是Object.prototype对象;
- 通过关键字new和构造函数创建的对象的原型对象就是构造函数prototype属性的值,所以通过构造函数Object创建的对象的原型就是Object.prototype了;
Object.create(null);
那么,如何查询一个对象的原型属性呢?将对象作为参数传入Object.getPrototypeOf()可以查询它的原型
要检测一个对象是否是另一个对象的原型(或处于原型链中),可以使用isPrototypeOf()方法。
Object.getPrototypeOf(o);
a.isPrototypeOf(b);
还有一个非标准但众多浏览器都已实现的对象的属性__proto__(同样是两个下划线开始和结束,以表明其为非标准),用以直接查询/设置对象的原型。
2. 类属性
对象的类属性(class attribute)是一个字符串,用以表示对象的类型信息。
function classof(o){
if(o === null){
return 'Null';
}
if(o === undefined){
return 'Undefined';
}
return Object.prototype.toString.call(o).slice(8, -1);
}
var a1= new Array();
var b1= new Date();
var c1= new Date();
var a = classof(a1);
var b = classof(b1);
var c = classof(c1);
3. 可扩展性
对象的可扩展性用以表示是否可以给对象添加新属性。所有内置对象和自定义对象都是显示可扩展的,宿主对象的可扩展性是由JavaScript引擎定义的。相关函数:
- 1 通过将对象传入Object.isExtensible(),来判断该对象是否是可扩展的。
- 2 如果想将对象转换为不可扩展,需要调用Object.preventExtensions(),将待转换的对象作为参数传进去。
注意:
a.一旦将对象转换为不可扩展的,就无法再将其转换回可扩展的了;
b.preventExtensions()只影响到对象自身的可扩展性,如果给一个不可扩展的对象的原型添加属性,这个不可扩展的对象同样会继承这些新属性;
Object.seal()和Object.preventExtensions()类似,除了能将对象设置为不可扩展的,还可以将对象的所有自有属性都设置为不可配置的。(对于那些已经封闭(sealed)起来的对象是不能解封的);可以使用Object.isSealed()来检测对象是否封闭。
更进一步,Object.freeze()将更严格地锁定对象——“冻结”(frozen)。除了将对象设置为不可扩展和将其属性设置为不可配置之外,还可以将它自有的所有数据属性设置为只读(若对象的存取器属性有setter方法,存取器属性将不受影响,仍可通过给属性赋值调用它们)。使用Object.isFrozen()来检测对象是否总结。
Object.isExtensible();
Object.isSealed();
Object.isFrozen();
//将对象设置为不可扩展
Object.preventExtensions();
//将对象设置为不可扩展,并将对象自有属性设置为不可配置
Object.seal();
//将对象设置为不可扩展,并将对象自有属性设置为不可配置、不可写
Object.freeze();
五、 属性的特性
1.存取器属性
上面我们所说的都是很普通的对象属性,这种属性称做“数据属性”(data property),数据属性只有一个简单的值。然而在ECMAScript 5中,属性值可以用一个或两个方法替代,这两个方法就是getter和setter,有getter和setter定义的属性称做“存取器属性”(accessor property)。
当程序查询存取器属性的值时,JavaScript调用getter方法(无参数)。这个方法的返回值就是属性存取表达式的值。当程序设置一个存取器属性的值时,JavaScript调用setter方法,将赋值表达式右侧的值当做参数传入setter。如果属性同时具有getter和setter方法,那么它就是一个读/写属性;如果它只有getter方法,那么它就是一个只读属性,给只读属性赋值不会报错,但是并不能成功;如果它只有setter方法,那么它是一个只写属性,读取只写属性总是返回undefined。看个实际的例子:
var p = {
x: 1.0,
y: 2.0,
get r(){ return Math.sqrt(this.x*this.x + this.y*this.y); };
set r(newvalue){
var oldvalue = Math.sqrt(this.x*this.x + this.y*this.y);
var ratio = newvalue/oldvalue;
this.x *= ratio;
this.y *= ratio;
},
get theta(){ return Math.atan2(this.y, this.x); },
print: function(){ console.log('x:'+this.x+', y:'+this.y); }
};
正如例子所写,存取器属性定义一个或两个和属性同名的函数,这个函数定义并没有使用function关键字,而是使用get和set,也没有使用冒号将属性名和函数体分隔开。对比一下,下面的print属性是一个函数方法。注意:这里的getter和setter里this关键字的用法,JavaScript把这些函数当做对象的方法来调用,也就是说,在函数体内的this指向这个对象
正如控制台的输出,r、theta同x,y一样只是一个值属性,print是一个方法属性。ECMAScript 5增加的这种存取器,虽然比普通属性更为复杂了,但是也使得操作对象属性键值对更加严谨了。
2.属性的特性
对象属性是由名字和值跟一组属性特性构成的,属性值可以用一个或两个方法进行替代,这两个方法就是getter和setter定义的属性特性。由getter和setter定义的属性叫做“存取器属性”。
在这里我们将存取器属性的getter和setter方法看成是属性的特性。按照这个逻辑,我们也可以把属性的值同样看做属性的特性。因此,可以认为属性包含一个名字和4个特性。数据属性的4个特性分别是它的值(value)、可写性(writable)、可枚举性(enumerable)和可配置性(configurable)。存取器属性不具有值特性和可写性它们的可写性是由setter方法是否存在与否决定的。因此存取器属性的4个特性是读取(get)、写入(set)、可枚举性和可配置性。
为了实现属性特性的查询和设置操作,ECMAScript 5中定义了一个名为“属性描述符”(property descriptor)的对象,这个对象代表那4个特性。描述符对象的属性和它们所描述的属性特性是同名的。因此,数据属性的描述符对象的属性有value、writable、enumerable和configurable。存取器属性的描述符对象则用get属性和set属性代替value和writable。其中writable、enumerable和configurable都是布尔值,当然,get属性和set属性是函数值。通过调用Object.getOwnPropertyDescriptor()可以获得某个对象特定属性的属性描述符:
Object.getOwnPropertyDescriptor({x:1},"x");
Object.getOwnPropertyDescriptor({},"x");
Object.getOwnPropertyDescriptor({},"toString");
从函数名字就可以看出,Object.getOwnPropertyDescriptor()只能得到自有属性的描述符,对于继承属性和不存在的属性它都返回undefined。要想获得继承属性的特性,需要遍历原型链(Object.getPortotypeOf())。
要想设置属性的特性,或者想让新建属性具有某种特性,则需要调用Object.definePeoperty(),传入需要修改的对象、要创建或修改的属性的名称以及属性描述符对象:
Object.defineProperty(o,"x",{value:1,writable:true,enumerable:false,configurable:true})
Object.x;
Object.keys(o); // => [];
Object.defineProperty(o,'x',{writable:false});
o.x = 2;
o.x;
//test 可配置与可写
// object.defineProperty();
六、 属性的查询和设置
可以通过点(.)或方括号([])运算符来获取和设置属性的值。
原型链
概述中说过,JavaScript对象具有”自有属性“,也有“继承属性”。当查询对象obj的属性x时,首先会查找对象obj自有属性中是否有x,如果没有,就会查找对象obj的原型对象obj.prototype是否有属性x,如果没有,就会进而查找对象obj.prototype的原型对象obj.prototype.prototype是否有属性x,就这样直到找到x或者查找到的原型对象是undefined的对象为止。可以看到,一个对象上面继承了很多原型对象,这些原型对象就构成了一个”链“,这也就是我们平时所说的“原型链”,这种继承也就是JavaScript中“原型式继承”(prototypal inheritance)。
对象o查询某一属性时正如上面所说会沿着原型链一步步查找,但是其设置某一属性的值时,只会修改或新增自有属性,并不会修改原型链上其他对象的属性。
七、 删除属性
delete运算符可以删除对象的属性,它的操作数应该是一个属性访问表达式。但是,delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性(内存泄露):
var a = {p:{x:1}};
var b = a.p;
delete a.p;
// test
delete只能删除自有属性,
1 var obj = {x: 1}
delete obj.x; //删除对象obj的属性x,返回true
delete obj.x; //删除不存在的属性,返回true
delete obj.r; //删除对象obj的属性r,返回true
delete obj.toString; //没有任何副作用(toString是继承来的,并不能删除),返回true
delete 1; //数字1不是属性访问表达式,返回true
delete表达式返回true的情况:
- 删除成功或没有任何副作用(比如删除不存在的属性)时;
- 如果delete后不是一个属性访问表达式;
delete表达式返回false的情况:
- 删除可配置性(可配置性是属性的一种特性,下面会谈到)为false的属性时;
delete Object.prototype; //返回false,prototype属性是不可配置的
//通过var声明的变量或function声明的函数是全局对象的不可配置属性
var x = 1;
delete this.x; //返回false
function f() {}
delete this.f; //返回false
八、 检测属性
检测一个对象的属性也有三种方法,下面就来详细说说它们的作用及区别!
1.in运算符
in运算符左侧是属性名(字符串),右侧是对象。如果对象的自有属性或继承属性中包含这个属性则返回true,否则返回false。
2.hasOwnProperty()
对象的hasOwnProperty()方法用来检测给定的名字是否是对象的自有属性(不论这些属性是否可枚举),对于继承属性它将返回false。
3.propertyIsEnumerable()
propertyIsEnumerable()是hasOwnProperty()的增强版,只有检测到是自有属性且这个属性可枚举性为true时它才返回true。
var o = {
value: 1,
b: 2,
c: 3
};
var x = 'b' in o;
var x1 = o.hasOwnProperty('b');
var x2 = o.propertyIsEnumerable('b');
九、 枚举属性
相对于检测属性,我们更常用的是枚举属性。枚举属性我们通常使用for/in循环,它可以在循环体中遍历对象中所有可枚举的自有属性和继承属性,把属性名称赋值给循环变量。
Object.getOwnpropertyNames();//它返回对象的所有自有属性的名称,不论是否可枚举;
Object.keys(); //它返回对象对象中可枚举的自有属性的名称;
for(var c in o){
console.log(o[c]);
}
var m = o.propertyIsEnumerable('toString');
Object.getOwnpropertyNames(o);
Object.keys(o);
3.总结
开头说的问题在本文中大部分都能找到答案,唯独最后一个问题“什么是包装对象?”
接下来先看一段代码
var s ="hello world!";
var word = s.substring(s.indexOf(" ")+1,s.length);
这段代码看起来似乎很正常,但是疑问点就在于,上文说到字符串类型是原始类型怎么会有方法呢?原来:只要引用了字符串s的属性,JavaScript就会将字符串值通过调用new String(s)的方式转换成对象,这个对象继承了字符串的方法,并被用来处理属性的引用。一旦属性引用结束,这个新创建的对象就会销毁...
存取字符串、数字或布尔值的属性时创建的临时对象称做包装对象