JavaScript对象(一)

对象

对象是动态的——可以新增属性也可以删除属性,但是对象经常用来模拟静态对象以及静态类型语言中的“结构体”。对象的属性是可以增加和删除的。
对象特性

  • 对象原型(prototype):指向另外一个对象,本对象的属性继承自它的原型对象。
  • 对象的类(class):是一个表示对象类型的字符串。
    *对象的扩展标记(extensible flag):指明是否可以向该对象添加新属性。

属性特性

  • 可写 (是否可以设置属性值)
  • 可枚举 (是否可以通过for in 循环返回该属性)
  • 可配置 (是否可以删除或者修改属性)

对象的创建

对象直接量
创建对象最简单的方法是通过对象直接量。

var empty = {}; //没有任何属性的对象
 var point = {x:0, y:0}; // 两个属性
 var point2 = {x:point.x, y:point.y+1}; 
 var student = {
     name:"Allen",                //属性名可以不用引号
     age:23,
     school:{                      //这个属性值是一个对象,
       "primary school" :"试验小学",//属性名中有空格,必须字符串
       "middle-school" :"育才中学" ,//属性名中有连接符,必须用字符串
}
}
/*最后注意一点,属性名尽量避免使用空格、连接符、或者关键字,如果必须使用的话,尽量用引号引起来*/

通过new 创建对象

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

通过 Object.create()
在这里,我们先弄清楚一个名词——原型,每一个JS对象(除了null)都和另一个对象相关联。“另一个”对象就是我们所说的原型,每一个对象都从原型继承属性。
所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过JS代码Object.prototype获得对原型对象的引用。通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。因此,同使用{}创建对象一样,通过new Object()创建对象也继承自Object.prototype。

ECMAScript 5 定义了一个名为Object.create()的方法,它创建一个新的对象,其中第一个参数就是对象原型,第二个参数是可选参数,用以对对象的属性进行进一步描述。
Object.create()是静态函数,而不是提供给某一个对象调用的方法。

var obj = Object.create({x:1,y:2}); // obj继承了属性x,y
var obj2 = Object.create(obj); //obj2的原型是obj,继承了属性x,y;
// 如果想创建一个普通的空对象(比如通过{}或者new Object()创建的对象 ),需要传入Object.prototype
var obj3 = Object.create(Object.prototype);//obj3和{}和new Object()一样

现在我们来看看如何自己写一个函数来通过原型来创建对象,看例子

function createObject(obj){
        // 先对obj对象进行判断,看是否为null,如果是抛出异常
        if(obj == null) throw TypeError();
        // Object.create是否可用,可用则返回
        if(Object.create)  return Object.create(obj);
        // 不可用时,检测对象的类型
        var objType = typeof obj;
        // 如果对象类型不是Object 也不是函数的话,抛出异常
        if(objType !=="object" && objType !=="function") 
        throw TypeError();
        // 定义一个空构造函数 并且将函数的原型设置成obj
        function mfunc(){};
        mfunc.prototype = obj;
        return new mfunc();
    }

属性

对象访问属性的方法有两个,通过(.)和([])运算符来获取属性值。这里不做解释。
下来我们详细讲解一下通过([])获取属性值,这种获取属性值的方法很像数组,只是数组的索引是数字,而它的索引是字符串。这种数组就是我们将要说明的关联数组,也成散列、映射或字典。由于JS是弱类型语言,在任何对象中程序都可以创建任意数量的属性。通过([])来访问对象的属性时,属性名是字符串,这样更利于我们在程序运行时进行修改和创建属性。
我们举个例子来说明它的灵活性吧,假设我们去超市购买东西,我们把需要买的东西放入购物车,这辆购物车的作用就是显示我们放入的商品名称,以及商品的单价和数量。你有没有思路呢?学过类似C语言的同学,可以想想如何来实现。简单说一下我的思路吧(<small>用类似C语言的方式的思路,大神勿喷,可以留下你的思路</small>),此处的购物车我们用一个类似字典的对象来表示,字典的Key就是我们的商品名称,Value则是一个对象,对象中的属性包含:商品价格、数量。现在想想用JavaScript如何,思路其实同我上面说的类似,直接看代码吧。

var shoppingCart = {};//这里是购物车
// 添加商品的方法
function addGoods(name,price,count){
    shoppingCart[name] = {m_price:price,m_count:count};
}
(一)继承(属性的查询和设置)

JS对象有自有属性和继承属性,我觉得通过举例更容易说明这两个属性的关系。
<small>查询对象的属性举例:</small>
假设要查询对象obj的属性x,如果obj中不存在x,那么将会继续在obj的的原型对象中查询属性x,如果原型对象也没有x,但是这个原型对象也有原型(原型不为null),那么继续在这个原型对象的原型上执行查询,直到找到x或者查找到一个原型为null的对象为止。
<small>给对象的属性赋值举例:</small>
假设给对象obj的属性x赋值,如果obj中已经有属性x(这个属性不是继承来的),那么这个赋值操作只改变这个已有属性x的值。如果obj中不存在属性x,那么赋值操作给obj添加一个新属性x。如果之前obj继承自属性x,那么这个继承的属性就会被新创建的同名属性覆盖。
<small>小总结:对象的属性赋值,不会修改原型的属性值的,只会创建属性或者对自有属性赋值。但是需要注意的是,属性赋值操作首先是检查原型链,以此判断是否允许赋值,如果继承的属性是只读的,则赋值操作是不允许的</small>
看一个例子:

var obj = {x:1,y:2};
var obj2 = Object.create(obj);
console.log(obj);   // ==> {x:1,y:2}
console.log(obj2);   // ==> {x:1,y:2}
//----看点 1-----
// 现在对obj2 属性x值进行修改,看看obj 和 obj2的变化
obj2.x = 100;
console.log(obj);   // ==> {x:1,y:2}
console.log(obj2);   // ==> {x:100,y:2}
/*可以看出————对象的属性不会修改原型的属性*/
// -------------------------------------------------------
//----看点 2-----
// 这个时候我们对obj 属性x进行修改,看看变化
obj.x = 111;
console.log(obj);   // ==> {x:111,y:2}
console.log(obj2);   // ==> {x:100,y:2}
/*可以看出————对象属性赋值的时候只会创建属性或者对自有属性进行赋值,因为在之间对obj2的x属性进行赋值的时候,obj2的自有属性中没有x属性,所以obj2会创建新的属性x并赋值,同时覆盖掉继承属性x的值*/
// -------------------------------------------------------
//----看点 3-----
// 我们现在对obj 属性y进行修改,看看变化
obj.y = 222;
console.log(obj);   // ==> {x:111,y:222}
console.log(obj2);   // ==> {x:100,y:222}
/*这里就可以体现我们上面说到的对象属性的查询过程,因为obj的y属性修改为222,当我们查询obj2的y属性的时候,obj2的自有属性中没有y值,所以obj2就会到它的原型(obj)中查找,原型中的y属性经过修改变成222,所以查询的obj2属性y的结果就是222*/
// -------------------------------------------------------
//----看点 4-----
// 我们来看看访问一个不存在的属性,
console.log(obj2.m_string); // ==> undefined
console.log(obj2.m_string.length); // ==>  这里会报错
/*当访问的属性不存在的时候,系统会返回一个undefined,而null和undefined值是没有属性的,因此上述第二行代码会报错。*/
// 我们可以通过判断的方法来避免错误的出现
var strLength;
if(obj2 && obj2.m_string)  strLength = obj2.m_string.length;
//还有一种更简练的方法
strLength = obj2 && obj2.m_string && obj2.m_string.length;

经过上面的例子,你是否搞懂继承了?

(二)属性的删除、检测、枚举

删除属性
说先我们来了解一下delete运算符,该运算符可以删除对象的属性。但是delete只是断开属性和宿主对象的关系,而不会去操作属性中的属性。delete表达式删除成功时会返回true。
需要注意的是,delete运算符只能删除自有属性,不能删除继承属性,也不能删除不可配置的属性。

// 删除属性的操作
var mframe = {point:{x:11,y:22},size:{width:100,heght:50}};
// 这里用p对象引用mfram的point属性
var p = mframe.point;
// 当我们执行删除操作之后,来打印p看看结果
delete mframe.point;
console.log(p);  // ==> {x:11,y:22};
/*执行代码之后,我们知道mframe已经没有了point属性,但是由于已经删除的属性的引用依然存在,因此在JS的某些实现中,可能因为这种不严谨的代码而造成内存泄漏,所以在销毁对象的时候,要遍历属性中的属性,依次删除*/

检测属性
检测属性主要是来判断某属性是否存在于某个对象中,我们用代码来介绍一下检测属性。

// in运算符来检测属性,左侧是 属性名(字符串),右侧是对象
var mpoint = {x:1,y:2};
console.log("x" in mpoint);   //==> true
console.log("z" in mpoint);   //==> false
console.log("toString" in mpoint);  //==> true,toString是继承属性;
// -------------------------------------------------------
// hasOwnProperty()方法用来检测给定的名字是否是对象的自有属性,对于继承属性它将返回false。
console.log(mpoint.hasOwnProperty("x")); //==> true
console.log(mpoint.hasOwnProperty("z")); //==> false
console.log(mpoint.hasOwnProperty("toString")); //==> false
// -------------------------------------------------------
// propertyIsEnumerable()是hasOwnProperty()的加强版,只有检测到是自有属性且这个属性是可枚举的才会返回ture.这里就不做举例了

枚举属性
我们经常会遍历对象的属性,通常使用for/in循环来遍历。for/in 循环可以遍历对象中所有可枚举的属性(包括自有属性和继承属性)。
有许多实用工具库给Object.prototype添加了新的方法或属性,这些属性可以被所有对象继承并使用,但是在ECMAScript 5标准之前,这些新添加的方法是不能定义为不可枚举的,因此使用for/in循环进行枚举的时候,这些属性会被枚举出来。为了避免这种情况,我们需要在for/in 中进行过滤,有两种常见的方法:

for(p in obj){
    if(!obj.hasOwnProperty(p)) continue;  // 跳过继承的属性
}
//---------------------------------
for(p in obj){
    if(typeof obj[p] === "function") continue;  // 跳过方法
}

除了for/in循环之外,ECMAScript 5 定义了两个用以枚举属性名称的函数。

  • Object.keys(),它返回一个数组,这个数组由对象中可枚举的自有属性的名称组成。
  • Object.getOwnPropertyNames(),它返回对象的所有自有属性的名称(包括自有属性中的可枚举属性和不可枚举属性).
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容