JavaScript系列之对象

1.创建对象
(1)对象直接量,每次计算对象直接量的时候,也都会计算它的每个属性的值。
(2)通过new创建对象,new运算符创建并初始化一个新对象。JavaScript语言核心中原始类型都包含内置构造函数。

每一个JavaScript对象(null除外)都和另一个对象相关联。每一个对象都从原型继承属性。
多用通过对象直接量创建的对象都具有同一个原型对象,并可以通过JavaScript代码Object.prototype获得对原型对象的引用。
没有原型的对象为数不多,Object.prototype就是其中之一。它不继承任何属性,其他原型对象都是普通对象,普通对象都具有原型。所有的内置构造函数都具有一个继承自Object.prototype的原型。

Object,create()第一个参数是这个对象的原型,第二个可选参数,用以对对象的属性进行进一步描述。
可以通过传入参数null来创建一个没有原型的新对象,通过这种方式创建的对象不会继承任何东西。
如果想创建一个普通的空对象,需要传入Object.prototype

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();
}

new的底层原理

function A(x,y){this.x=x;this.y=y}
var a=new A(1,2);

var a=new Object();
a.__proto__=A.prototype;
var r=A.apply(a,arguments);
if(typeof r == 'object')return r;
return a;

2.属性的查询和设置
当使用方括号是,方括号内的表达式必须返回字符串。更严格地讲,表达式必须返回字符串或返回一个可以转换为字符串的值。
JavaScript对象都是关联数组。
属性赋值操作首先检查原型链,以此判断是否允许赋值操作。
如果允许属性赋值操作,它也总是在原始对象上创建属性或对已有的属性赋值,而不会去修改原型链。
在JavaScript中,只有在查询属性是才会体会到继承的存在,而设置属性则和继承无关。

查询一个不存在的属性并不会报错,但是,如果对象不存在,那么试图查询这个不存在的对象的属性就会报错。null和undefined值都没有属性,因此查询这些值的属性会报错。当然,给null和undefined设置属性也会报类型错误。给其他值设置属性也不总是成功,有一些属性是只读的,不能重新赋值,有一些对象不允许新增属性,这些设置属性的失败操作不会报错。

在这些场景下给对象o设置属性p会失败:
o中的属性p是只读的:不能给只读属性重新赋值(defineProperty()方法中有一个例外,可以对可配置的只读属性重新赋值)。
o中的属性p是继承属性,且它是只读的:不能通过同名自有属性覆盖只读的继承属性。
o中不存在自有属性p:o没有使用setter方法继承属性p,并且o的可扩展性是false。如果o中不存在p,而且没有setter方法可供调用,则p一定会添加至o中。但如果o不是可扩展的,那么在o中不能定义新属性。

3.删除属性
delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性。
delete运算符只能删除自有属性,不能删除继承属性。
当delete表达式删除成功或没有任何副作用(比如删除不存在的属性)时,它返回true。如果delete后不是一个属性访问表达式,delete同样返回true。
delete不能删除那些可配置性为false的属性。某些内置对象的属性是不可配置的,比如通过变量声明和函数声明创建的全局对象的属性。
var x=1;//声明一个全局变量
delete this.x;//不能删除这个属性

this.x=1;//创建一个可配置的全局属性
delete this.x;

4.检测属性
in运算符的左侧是属性名(字符串),右侧是对象。如果对象的自有属性或继承属性中包含这个属性则返回true。
对象的hasOwnProperty()方法用来检测给定的名字是否是对象的自有属性。对于继承属性它将返回false。
propertyIsEnumerable()是hasOwnProperty()的增强版,只有检测到自有属性且这个属性的可枚举性为true时它才返回true。某些内置属性是不可枚举的。通常又JavaScript代码创建的属性都是可枚举的。
!==判断一个属性是否是undefined
in可以区分不存在的属性和存在但值为undefined的属性。

5.枚举属性
for/in循环可以在循环体中遍历对象所有可枚举的属性(包括自有属性和继承的属性),把属性名称赋值给循环变量。对象继承的内置方法不可枚举的,但在代码中给对象添加的属性都是可枚举的。
Object.keys(),它返回一个数组,这个数组由对象中可枚举的自有属性的名称组成。
Object.getOwnPropertyNames(),它返回一个数组,这个数组由对象中所有自有属性的名称组成。

6.属性getter和setter
和数据属性不同,存取器属性不具有可写性。如果属性同时具有getter和setter方法,那么它是一个读/写属性。如果它只有getter方法,那么它是一个只读属性。如果它只有setter方法,那么它是一个只写属性(数据属性中有一些例外),读取只写属性总是返回undefined。
和数据属性一样,存取器属性是可以继承的。

数据属性的4个特性分别是它的值(value)、可写性(writable)、可枚举性(enumerable)和可配置性(configurable)。
存取属性不具有值特性和可写性,它们的可写性是由setter方法存在与否决定的。
存取器属性的4个特性是读取(get)、写入(set)、可枚举性和可配置性。

为了实现属性特性的查询和设置操作,ECMAScript5中定义了一个名为“属性描述符”的对象,这个对象代表那4个特性。描述符对象的属性和它们所描述的属性特性是同名的。
通过调用Object.getOwnPropertyDescriptor()可以获得某个对象特定属性的属性描述符。该方法只能得到自有属性的描述符,要想获得继承属性的特性,需要遍历原型链。获取原型的方法是Object.getPrototypeOf()

要想设置属性的特性,或者想让新建属性具有某种特性,则需要调用Object.defineProperty(),传入要修改的对象、要创建或修改的属性的名称以及属性描述符对象。
传入Object.defineProperty()的属性描述符不必包含所有4个特性。对于新创建的属性来说,默认的特性值是false或undefined。
如果要同时修改或创建多个属性,则需要使用Object.defineProperties()。第一个参数是要修改的对象,第二个参数是一个映射表,它包含要新建或修改的属性的名称,以及它们的属性描述符。

对于那些不允许创建或修改的属性来说,如果用Object.defineProperties()和Object.defineProperty()对其操作(新建或修改)就会抛出类型错误异常。
如果对象是不可扩展的,则可以编辑已有的自身属性,但不能给它添加新属性。
如果属性是不可配置的,则不能修改它的可配置性和可枚举性。
如果存取器属性是不可配置的,则不能修改器getter和setter方法,也不能将它转换为数据属性。
如果数据属性是不可配置的,则不能将它转换为存取器属性。
如果数据属性是不可配置的,则不能将它的可写性从false修改为true,但可以从true修改为false。
如果数据属性是不可配置且不可写的,则不能修改它的值。然而可配置但不可写属性的值是可以修改的。

7.对象的三个属性
对象的原型属性是用来继承属性的。
要检测一个对象是否是另一个对象的原型(或处于原型链中),请使用isPrototypeOf()方法。
在ECMAScript5中,将对象作为参数传入Object.getPrototypeOf()可以查询它的原型。

对象的类属性

function classof(){
    if(o===null)return 'Null';
    if(o===undefined)return 'Undefined'
    return Object.prototype.toString.call(o).slice(8,-1)
}

可扩展性
所有内置对象和自定义对象都是显式可扩展的,宿主对象的可扩展性是由JavaScript引擎定义的。
ECMAScript5定义了用来查询和设置对象可扩展性的函数。通过将对象传入Object.isExtensible(),来判断对象是否可扩展的。如果想将对象转换为不可扩展的,需要调用Object.preventExtensions(),将待转换的对象作为参数传进去。
一旦将对象转换为不可扩展的,就无法再将其转回回可扩展的了。preventExtensions()只影响到对象本身的可扩展性。如果给一个不可扩展的对象的原型添加属性,这个不可扩展的对象同样会继承这些新属性。

可扩展属性的目的是将对象“锁定”,以避免外界的干扰。

Object.seal()能够将对象设置为不可扩展的,还可以将对象的所有属性都设置为不可配置的。对于已经封闭起来的对象是不可解封的,可以使用Object.isSealed()来检测对象是否封闭。
Object.freeze()能够将对象设置为不可扩展的,还可以将对象的所有属性都设置为不可配置的,同时将它自有的所有数据属性设置为只读。(如果对象的存取器属性具有setter方法,存取器属性将不受影响,仍可以通过给属性赋值调用它们)。使用Object.isFrozen()来检测对象是否冻结。

8.JSON语法是JavaScript语法的子集,它并不能表示JavaScript里的所有值。
支持对象、数组、字符串、无穷大数字、true、false和null,并且它们可以序列化和还原。NaN、Infinity和-Infinity序列化的结果是null,日期对象序列化的结果是ISO格式的日期字符串。函数、RegExp、Error对象和undefined值不能序列化和还原。JSON.stringify()只能序列化对象可枚举的自有属性。对于一个不能序列化的属性来说,在序列化后的输出字符串中会将这个属性省略掉。

JSON的全称是javascript Object Notation,JavaScript对象表示法。

相关推荐