Javascript基础——Javascript语言核心(3):对象

对象

6.1 创建对象

创建对象的方法:对象直接量、关键字new和Object.create()

6.1.1 对象直接量

属性名在不是保留字、没有空格和连接符等情况下可以没有引号。

var empty={}         //没有属性的对象
var point={x:0, y:1} //两个属性的普通对象

var book={
    "main title" : "JS",    //属性名有空格,必须用字符串
    "sub-title" : "Guide",  //属性名有连接符,必须用字符串
    "for" : "all",          //属性名是保留字,必须用字符串
    author:{                //属性值是一个对象
        firstname: "Joe",    //属性名可以没有引号
        surname: "Flanagan"
    }
}   

6.1.2 通过new创建对象

new运算符创建并初始化一个新对象。关键字new后跟随一个函数调用,这个函数成为构造函数(construcor),用来初始化一个新建的对象。

Javascript语言核心中的原始类型都包含内置构造函数:

var o = new Object(); // 创建一个空对象,和{}一样
var a = new Array();  // 创建一个空数组,和[]一样
var d = new Date();   // 创建一个表示当前时间的Date对象

用自定义构造函数来初始化新对象也很常见。

6.1.3 原型

每一个Javascript对象(null除外)都和另一个对象相关联。“另一个”对象就是原型,每个对象都从原型继承属性。

对象创建方式 原型 备注
对象直接量 Object.prototype
关键字new和构造函数 构造函数的prototype 通过new Object()创建,原型为Object.prototype
通过new Array()创建,原型为Array.prototype
通过new Date()创建,原型为Date.prototype

Object.prototype没有原型,像Date.prototype继承自Object.prototype。所以具有以下关系:

new Date()创建的对象 继承自 -> Date.prototype 继承自 -> Object.prototype

这一系列链接的原型对象就是所谓的“原型链”(prototype chain)

6.1.4 Object.create()

方法 用途 参数1 参数2(可选)
Object.create() 创建一个新对象 这个对象的原型 对对象的属性进行进一步描述
var obj1=Object.create({x:1, y:2}); //obj1继承了属性x和y

//创建一个空对象
var obj2=Object.create(Object.prototype); //和{}及new Object()一样

6.2 属性的查询和设置

通过.[]可以获取和设置属性值。

var obj={};

obj.x=1;    //设置属性
obj["y"]=2; //设置属性

obj.y         //=> 2: 查询属性
obj["x"]    //=> 1: 查询属性

注意,.后面不能是保留字,obj.for非法的。只能用[],例如obj["for"]

6.2.2 继承

Javascript对象具有“自有属性”(own property),也有一些属性是从原型对象继承而来的。属性的查找沿着原型链一直向上。

6.2.3 属性访问错误

如果在对象自身的属性和继承的属性中都没有找到某个属性,则返回undefined。

var obj = {};
obj.x;       //=> undefined
obj.x.length //报错

对象为null,或者查询null和undefined的属性,都会报错。

var obj=null;
obj.x;       //报错

常用下面的方式来避免报错:

var len= obj && obj.x && obj.x.length;

内置构造函数的原型是只读的。

Object.prototype = 0; //赋值失败,但不报错

6.3 删除属性

delete运算符可以删除属性对象。需要注意,delete只是断开属性和宿主对象的联系,而不会操作属性中的属性。

var obj={ p:{x:1} };
var b = obj.p;

delete obj.p;
obj.p;        //=> undefined
b.x;          //=> 1

所以在销毁对象时,要遍历属性中的属性,依次删除,否则会造成内存泄露

成功删除或没有任何副作用(比如删除不存在的属性)返回true。不能删除可配置性为false的属性。

delete 1;                //=> true
delete Object.prototype; //=> false

6.4 检测属性

检测某个属性是否在某个对象中,有几种方式:

检测方式 说明 备注
!==运算符(不推荐) 检测自有和继承属性 判断属性是否是undefined
in运算符 检测自有和继承属性 4.9.3中介绍过,比起!==可以区分是不存在的属性,还是存在但值为undefined
hasOwnProperty() 仅检测自有属性
propertyIsENumerable() 仅检测自有属性且该属性可枚举 某些内置属性不可枚举,通常由Javascript代码创建的属性都是可枚举的。
var obj = {x:1};

obj.x !== undefined            //=> true
obj.toString !== undefined     //=> true: 继承属性

"x" in obj;                    //=> true
"toString" in obj              //=> true: 继承属性

obj.hasOwnProperty("x")        //=> true
obj.hasOwnProperty("toString") //=> false: 继承属性

Object.prototype.propertyIsEnumerable("toString") //=> false: 不可枚举

6.5 枚举属性

遍历对象的属性,有以下几种方式:

方式 说明
for/in循环遍历
Object.keys() 返回一个数组,由对象中可枚举的自有属性的名称组成。
Object.getOwnPropertyNames() 返回一个数组,由对象中所有自有属性,不仅仅是可枚举的属性的名称组成。
var obj={x:2,y:3};

for(att in obj){
    console.log(att);                         //=> x y
}

Object.keys(obj)                             //=> ["x","y"]
Object.keys(Object.prototype)                //=> []

Object.getOwnPropertyNames(obj)              //=> ["x","y"]
Object.getOwnPropertyNames(Object.prototype) //=> ["constructor", "__defineGetter__", "__defineSetter__", "hasOwnProperty", "__lookupGetter__", "__lookupSetter__", "isPrototypeOf", "propertyIsEnumerable", "toString", "valueOf", "__proto__", "toLocaleString"]

6.6 属性getter和setter

属性值可以用一个或两个方法(getter和setter)代替。由getter和setter定义的属性称作“存取器属性”(accessories property),它不同于“数据属性”(data property)——只有一个简单的值。

var p={
    x:1,
    y:2,
    get sum(){
        return this.x+this.y;
    },
    set sum(base){
        this.x += base;
        this.y += base;
    }
}

p.x       //=> 1
p.sum     //=> 3

p.sum = 5
p.x       //=> 6
p.sum     //=> 13

和数据属性一样,存取器属性是可以继承的。

6.7 属性的特性

一个属性包含1个名称和4个特性

属性分类 特有特性 共有特性
数据属性 值(value)、可写性(writable) 可枚举性(enumerable)、可配置性(configurable)
存取器属性 读取(get)、写入(set) 同上

为实现属性特性的查询和设置。ECMAScript 5定义了一个名为“属性描述符”(property descriptor)的对象来代表那4个特性。

6.7.1 查询属性描述符

调用Objet.getOwnPropertyDescriptor()获得某个对象特定属性的自有属性描述符。

configurable: true}
Object.getOwnPropertyDescriptor({x:1},"x") //=> {value: 1, writable: true, enumerable: true, 

//6.6中的p对象
Object.getOwnPropertyDescriptor(p,"sum")   //=> {get: ƒ, set: ƒ, enumerable: true, configurable: true}

//继承属性和不存在的属性,返回undefined
Object.getOwnPropertyDescriptor({},"x")           //=> undefined
Object.getOwnPropertyDescriptor({x:1},"toString") //=> undefined

6.7.2 设置单个属性描述符

设置属性特性或想让新建属性具有某种特性,需要调用Object.defineProperty()。注意,不能修改继承属性。

参数: 要修改的对象;创建或修改的属性名称;属性描述符对象。

var obj={};

//添加一个不可枚举的数据属性x,并赋值为1
Object.defineProperty(obj,"x",{ value: 1,
                                writable: true,
                                enumerable: false,
                                configurable: true});

//属性存在,但不可枚举
obj.x            //=> 1
Object.keys(obj) //=> []

//修改x属性为只读
Object.defineProperty(obj,"x",{writable: false});
obj.x = 2; //操作失败但不会报错
obj.x      //=> 1

//可以通过配置修改值
Object.defineProperty(obj,"x",{value: 2});
obj.x      //=> 2

//将x从数据属性修改为存取器属性
Object.defineProperty(obj,"x",{get: function(){return 0;}});
obj.x //=> 0

6.7.3 设置多个属性描述符

如果需要同时修改或创建多个属性,使用Object.defineProperties()

参数:要修改的对象;参数映射表

var p = Object.defineProperties({},{
    x:{value:1, writable:true, enumerable:true, configurable:true},
    y:{value:2, writable:true, enumerable:true, configurable:true},
    sum:{
        get: function(){ return this.x+this.y},
        enumerable:true,
        configurable:true
    }
})

6.8 对象的三个属性

每个对象都有与之相关的原型(prototype)、类(class)和可扩展性(extensible attribute)。

6.8.1 原型属性

对象的原型属性是用来继承属性的,经常把“o的原型属性”直接叫做“o的原型”。

要检测一个对象是否是另一个对象的原型(或处于原型链中),可以使用isPrototypeOf()方法,看下面两个例子:

var p = {x:1};
var obj = Object.create(p);
p.isPrototypeOf(obj); //=> true
var d = new Date();
Date.prototype.isPrototypeOf(d);  //=>true
Object.prototype.isPrototypeOf(d) //=>true: 原型链中

6.8.2 类属性

对象的类属性(class attribute)是一个字符串,用以表示对象的类型信息。

调用对象的toString()方法,提取返回字符串的第8到倒数第2个位置之间的字符就可以获得对象的类,下面这个函数实现了这个功能:

//获取类属性的函数
function classof(o){
    if(o===null) return "Null";
    if(o===undefined) return "Undefined";
    return Object.prototype.toString.call(o).slice(8,-1);
}

//---测试这个函数,进行类属性的获取---
classof(null)       //=> "Null"
classof(false)      //=> "Boolean"
classof(new Date()) //=> "Date"

function f(){}      //定义一个自定义构造函数
classof(f)          //=> "Function"
classof(new f())    //=> "Object"

6.8.3 可扩展性

对象的可扩展性用以表示是否可以给对象添加新属性。

方法 作用 方法返回 备注
Object.preventExtensions() 将对象转换为不可扩展 传入对象 一旦将对象转换为不可扩展,将无法将其转换回可扩展
Object.seal() 将对象转换为不可扩展,且所有自有属性不可配置 同上 已经封闭(sealed)的对象不能解封。不能给对象添加新属性,已有属性不能删除和配置,除了已有的可写属性可以设置。
Object.freeze() 冻结对象,对象不可以扩展,自有属性不可配置且为只读 同上 如果对象存取器属性具有setter方法,则该属性赋值依然可用
Object.isExtensible() 检测对象是否可扩展 布尔型 在ECMAScript5中,所有内置对象和自定义对象都是可以扩展的。
Object.isSealed() 检测对象是否封闭 同上 -
Object.isFrozen() 检测对象是否冻结 同上 -
var obj={x:1};
Object.isExtensible(obj);      //=> true

Object.preventExtensions(obj); //=> {x: 1}
Object.isExtensible(obj);      //=> false
obj.y=2;
obj.y                          //=> undefined: 说明不可扩展 

delete obj.x;
obj.x                          //=> undefined: 属性可以被删除
var obj = Object.defineProperties({},{
    x:{value:1, writable:true, configurable: true},
    y:{value:2, writable:false, configurable: true},
});


//6.7.2中讲到,不可读的属性可以通过配置进行修改,而封锁之后不可修改了
Object.defineProperty(obj,"y",{value: 200}); //=> {x: 1, y: 200}
Object.seal(obj);                            //=> {x: 1, y: 200}: 封锁
Object.defineProperty(obj,"y",{value: 300}); //=> Uncaught TypeError

Object.defineProperty(obj,"x",{value: 100}); //=> {x: 100, y: 200}: 由于x是可写属性,依旧可以修改

delete obj.x                                 //=> false: 不可删除

Object.isSealed(obj);                        //=> true: 已经封锁
var obj = Object.defineProperties({},{
    x:{value:1, writable:true, configurable: true},
    y:{value:2, writable:false, configurable: true},
});

Object.freeze(obj);                          //=> {x: 1, y: 2}: 冻结
Object.defineProperty(obj,"x",{value: 100}); //=> Uncaught TypeError: 所有属性只读

Object.isFrozen(obj);                        //=> true: 已经冻结

6.9 序列化对象

对象序列化(serialization)是指对象字符串间相互转换。

6.9.1 认识JSON

JSON: **J**ava**S**cript **O**bject **N**otation(JavaScript对象表示法),是轻量级的文本数据交换格式。

如果一段字符串符合JSON的语法,就可以将其简称为“一段JSON”。

注意,Javascript中的对象的可以加单引号、双引号或者不加引号,而JSON必须使用双引号

关于JSON的详情,请参考:http://www.json.org.cn/

JSON在线编辑(校验、格式化):
http://www.json.org.cn/tools/JSONEditorOnline2.0/index.htm

6.9.2 JSON和JS对象的转换

函数 作用
JSON.stringify() 对象 –> JSON
JSON.parse() JSON –> 对象
var obj={x:1,y:2}

var myJSON = JSON.stringify(obj); 
myJSON                            //=> "{"x":1,"y":2}"

var myObj = JSON.parse(myJSON); 
myObj                             //=> {x: 1, y: 2}

猜你喜欢

转载自blog.csdn.net/joyce_lcy/article/details/80830572