JS关于对象的属性和方法整理

对象简介

  • 对象是js的基本数据类型,对象是一种复合值,它将很多值聚合在一起,可通过名字访问这些值。
  • 对象也可看做是属性的无序集合,每个属性都是一个名/值对,属性名是字符串,因此我们也可以把对象看成是从字符串到值的映射
  • 这种基本数据结构还有很多叫法,“散列”、“散列表”、“字典”、“关联数组”。
  • js对象还可以从一个称为原型的对象继承属性(核心特性)。
  • 除了字符串、数字、truefalsenullundefined之外,js的值都是对象。
三类js对象
  • 内置对象(native object) :数组、函数、日期、正则表达式
  • 宿主对象(host object)js所运行的环境提供的对象比如:BOM中的WindowDOM中的document
  • 自定义对象(user-defined object):代码创建的对象。
两类属性
  • 自有属性:直接在对象中定义的属性。
  • 继承属性:是在对象的原型对象中定义的属性。

创建对象

  • 对象直接量(创建对象最简单的方式)
  • 关键字new
  • Object.create()函数

1、对象直接量

// 没有任何属性的对象
var empty = {};

// 复杂对象
var book = {
  x:0,                        // 正常属性
  "main title":"Js",          // 属性名字有空格,必须用字符串表示
  "sub-title":"js js",        // 属性名字有连字符,必须用字符串表示
  "for":"all"                 // for 是关键字,必须用引号,不建议使用关键字作为属性名
}

如上:对象直接量是一个表达式,这个表达式每次运算都创建并且初始化一个新的对象,每次计算对象直接量的时候,也都会计算它的每个属性的值,所以,如果在一个重复调用的函数中的循环体内使用了对象直接量,它将创建很多新对象,并且每次创建的对象的属性值也有可能不同。

2、new一个对象
new运算符创建并初始化一个新对象,关键字new后跟随一个函数调用,这里的函数称做构造函数(constructor),构造函数用以初始化一个新创建的对象。

var o = new Object(); // 创建一个空对象,和{}一样
var a = new Array(); //  创建一个空数组,和 []一样
var d = new Date(); // 创建一个表示当前时间的Date对象
var r = new RegExp('js'); // 创建一个可以进行模式匹配的RegExp对象

3、Object.create()
Object.create()是一个静态函数,而不是提供给某个对象调用的方法,使用它的方法只需传入所需原型对象即可。*·what·? ? ? *

  • Object.create()只能传入对象或者 null,什么都不传报错。
var o1 = Object.create({x:1}); // o1继承了属性x

如上代码所示:o1继承了属性x,注意是继承,此时我们打印o1,输出{},但是打印o1.x却能输出1,再看o1.__proto__输出{x:1}o1的原型对象是{x:1}{x:1}的原型对象是Object,这一系列链接的原型对象就是所谓的“原型链”。

原型链

  • 可以传入参数null来创建一个没有原型的新对象,但是通过这种方法创建的对象不会继承任何东西,甚至不包括基本方法,比如toString()
var o2 = Object.create(null); // o2不继承任何属性和方法。
o2
  • 如果想创建一个普通的空对象,需要传入Object.prototype
var o3 = Object.create(Object.prototype); // o3和{}和 new Object() 一样
o3

继承

js对象具有“自有属性”,也有一些属性是从原型对象继承而来的。

function inherit(p) {
  if(p == null){
    throw TypeError();            // p 是一个对象,但是不能是null
  }
  if(Object.create) {
    return Object.create(p);     // 如果Object.create()存在就直接使用
  }
  var t = typeof p;
  if(t !== "Object" && t !== "function") {
    throw TypeError();
  }
  function f(){};           // 定义一个空构造函数
  f.prototype = p;         // 将其原型属性设置为p
  return new f();         // 使用f()创建p的继承对象
}

var o = {};                 // o 从 Object.prototype 继承对象的方法
o.x = 1;                   // 给o定义一个属性x
var p = inherit(o);       // p 继承 o 和 Object.prototype
p.y = 2;                 // 给p定义一个属性y
var q = inherit(p);      //  q 继承 p、o、Object.prototype
q.z = 3;                // 给 q 定义一个属性 z
var s = q.toString();  // toString 继承自Object.prototype
q.x + q.y;             // => 3     x 和 y 分别继承自 o 和 p

假设要查询对象 o 的属性 x,如果 o 中不存在 x,那么将会继续在 o 原型对重查询属性 x,如果原型对象中也没有 x,但这个原型对象也有原型,那么继续在这个原型对象的原型上执行查询,直到找到x或者查找到一个原型是null的对象为止,可以看到,对象的原型属性构成一个“链”,通过这个“链”可以实现属性的继承。

现在假设给对象o的属性x赋值,如果o中已经有属性x(这个属性不是继承而来的),那么这个赋值操作只改变这个已有属性x的值,如果o中不存在属性x,那么赋值操作给o添加一个新属性x,如果之前o继承自属性x,那么这个继承的属性就被新创建的同名的属性覆盖了。

var unitcircle = {r:1};
var c = inherit(unitcircle);
c.x = 1;
c.y = 1;
c.r = 2;
unitcircle.r;         // => 1 原型对象没有修改

原型
每一个js对象(除了null)都和另一个对象相关联,“另一个”对象就是我们熟知的原型,每一个对象都是从原型继承属性。


属性访问错误

  • 查询一个不存在的属性并不会报错,如果在对象o自身的属性或继承的属性中均未找到属性x,属性访问表达式o.x返回undefined
var o = {y:1};
o.x; // => undefined
  • 如果对象不存在,那么查询这个不存在的对象的属性就会报错,nullundefined值都没有属性,因此查询这些值的属性户报错。
var o = null;
o.x; // => Uncaught TypeError: Cannot read property 'x' of null

var p = {y:1};
o.x.z; // =>  Uncaught TypeError: Cannot read property 'z' of undefined

删除属性

  • delete运算符可以删除对象的属性
  • delete运算符只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响到所有继承自这个原型的对象)

检测属性

js对象可以看做属性的集合,我们经常会检测集合中成员的所属关系,判定某个属性是否存在某个对象中。
js提供四种个方式:

  • in运算符,会去查继承属性
  • hasOwnPreperty() ,只会查自有属性,不会去查继承的属性
  • propertyIsEnumerable() ,只查自有且可枚举的属性,不查继承属性
  • 通过属性查询 ,不能区分值是undefined的属性
    1、in运算符
    in运算符的左侧是属性名(字符串),右侧是对象,如果对象的自有属性或继承属性中包含这个属性则返回true
var o = {x:1};
"x" in o;            // => true 自有属性
"y" in o;            // => false 不存在属性
"toString" in o;     //=> true 继承属性

2、 hasOwnProperty()
hasOwnProperty()用来检测给定的名字是否是对象的自有属性,对于继承的属性它将返回false

var o  = {x:1};
o.hasOwnProperty("x");            // => true 自有属性
o.hasOwnProperty("y");            // => false 不存在属性
o.hasOwnProperty("toString");    // => false ,继承属性

3、propertyIsEnumerable()
propertyIsEnumerable()hasOwnProperty()的增强版,只有检测到是自有属性且这个属性可枚举为true时它才返回true

var o = inherit({y:2});
o.x = 1;
o.propertyIsEnumerable("x");   // => true 自有可枚举属性
o.propertyIsEnumerable("y");  // => false 继承属性
Object.prototype.propertyIsEnumerable("toString"); // => false 不可枚举属性

4、使用!==判定一个属性是否是undefined

var o = {x:1,y:undefined};
o.x !== undefined;              // => true 自有属性
o.y !== undefined;              // => false 不存在属性
o.toString !== undefined;      // => true 继承属性
o.y !== undefined;            // => false 属性存在但是值为undefined

如上代码:当属性存在,但是值是undefined!==不能返回希望的结果。


枚举属性

除了检测对象的属性是否存在,我们还会经常的遍历对象的属性。

  • for/in循环遍历 ,所有可枚举属性
  • Object.keys(),(ES5提供)返回由可枚举的自有属性的名称组成的数组
  • Object.getOwnPropertyNames()ES5提供)只返回自有属性

1、for/in
for/in循环可以在循环体重遍历对象中所有可枚举的属性(自有和继承),把属性名称赋值给循环遍历。

var o = {x:1};
var p = inherit(o);
p.y = 2;
for (var key in p){
  console.log(key);
}
// => y 自有属性
// => x 继承属性
// 没有输出 toString ?
"toString" in p;                     // => true 继承属性
p.propertyIsEnumerable("toString"); // => false 不可枚举属性

如何过滤继承属性?

for (var key in p){
  if(!p.hasOwnProperty(key)) continue; // 跳过继承属性
   console.log(key)
}
// => x 只输出自有属性

2、Object.keys()
Object.keys() 枚举属性名称的函数,它返回一个数组,这个数组由对象中可枚举的自有属性的名称组成。

var o = {x:1};
var p = inherit(o);
p.y = 2;
p.z = 3;
Object.keys(p); // => ["y","z"] 未返回继承属性x

3、Objcet.getOwnPropertyNames()
只返回对象的自有属性名称,而不仅仅是可枚举属性,不可枚举属性也会返回。

var o = {x:1};
var p = inherit(o);
p.y = 2;
p.z = 3;
Object.defineProperty(p,"h",{value:4,enumerable:true}); // 添加可枚举属性
Object.defineProperty(p,"w",{value:5,enumerable:false}); // 添加不可枚举属性
Object.keys(p); // => ["y","z","h"]  未返回不可枚举属性w 和继承 属性 x
Object.getOwnPropertyNames(p); // => ["y","z","h","w"] 只是未返回继承属性 x

属性getter和setter

ES5中属性值可以用一个或两个方法代替,这俩方法就是gettersetter。由gettersetter定义的属性称做“存取器属性”

  • 如果属性同时具有gettersetter方法,那么它是一个读/写属性。
  • 如果它只有一个getter方法,那么它是一个只读属性。
  • 如果它只有一个setter方法,那么它是一个只写属性,读取只写属性总是返回undefined
var o = {
  x:1,
  y:2,
  h:undefined,
  get product(){
    return this.x * this.y;
  },
  set product(value){
    this.x = value;
    this.y = value * 2;
  },
  get sum(){
    return this.x + this.y;
  },
  set z(value){
    this.h = value;
  }
}
// product 是一个读/写属性
o.product;     // => 2 
o.product = 2;
o.product;     // => 8

// sum 是一个只读属性
o.sum;       // => 3
o.sum = 5;
o.sum;       // => 3

// z 是一个只写属性
o.z;      // => undefined
o.z = 4;
this.h; // => 4

属性的特性

除了包含名字和值之外,属性还包含一些标识它们可写、可枚举、可配置的特性。ES3无法设置这些特性。

  • 可以通过这些API给原型对象添加方法,并将它们设置成不可枚举,这让它们看起来更像内置方法。
  • 可以通过这些API给对象定义不能修改或删除的属性,借此“锁定”这个对象。
  • 数据属性4个特性分别是:
    value
    可写性(writable
    可枚举性(enumerable
    可配置性(configurable
  • 存取器属性 不具有 可写性 它们的可写性由setter方法存在与否决定,存取器属性的4个特性是:
    读取(get)
    写入(set)
    可枚举
    可配置

    ES5定义了一个名为“属性描述符”的对象,这个对象代表那4个属性。
{
  value:数据属性,表示属性的值,默认: undefined
  writable: 可写性,表示能否修改属性。默认值:true
  enumerable:可枚举性,表示能否通过 for/in 遍历得到属性,默认true
  configurable:可配置性,如果属性为false则不可在对属性描述符进行修改,默认值为true。
}

通过调用Object.getOwnPropertyDescriptor()可以获得某个对象特定属性的属性描述对象

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

由上边代码可见,这个方法只能得到自有属性的描述符。
那么如何设置属性的特性呢? Object.defineProperty()

  • 第一个参数:要修改的对象
  • 第二个参数:要创建或者修改的属性名称
  • 第三个对象:属性描述符对象

这个方法要么修改已有属性要么新建自有属性,但是不能修改继承属性。

var o = {x:1};
// 设置一个不可枚举的属性,其他属性默认true
Object.defineProperty(o,"x",{value:1,enumerable:false});
Object.keys(o); // => []

Object.defineProperties()
如果要同时修改或者创建多个属性:

  • 第一个参数:要修改的对象
  • 第二个参数:一个映射表,它包含要新建或修改的属性的名称以及它们的属性描述符
var o = {};
// 给对象o添加俩个属性,x 和 y 其中 y 不可枚举不可配置
Object.defineProperties(o,{
  x:{"value":1,"writable":true,"enumerable":true,"configurable":true},
  y:{"value":2,"writable":true,"enumerable":false,"configurable":false}
});
// y不可配置,如果这个时候我们想要修改y的属性描述符,会报出错误异常
Object.defineProperties(o,{
  y:{"value":2,"writable":true,"enumerable":true,"configurable":false}
});
// => Uncaught TypeError: Cannot redefine property: y

对象的三个属性

  • 原型
  • 可扩展性
原型属性
  • 对象的原型属性是用来继承属性的。
  • 原型属性是在实例对象创建之初就设置好的。
  • ES5中,将对象作为参数传入Objcet.getPrototypeOf()可以查询它的原型(一个并不可靠的方法)。
  • 要检测一个对象是否是另一个对象的原型,可以使用isPrototypeOf()
var o = {x:1};
var p = Object.create(o);
o.isPrototypeOf(p);        // => true p继承自o
Object.prototype.isPrototypeOf(o); // true  o 继承自Object.prototype
可扩展性

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


序列化对象

对象序列化是指将对象的状态转换为字符串,也可以将字符串还原为对象。
ES5提供了内置函数JSON.stringify()JSON.parse()用来序列化和还原js对象。

  • NaNInfinity-Infinity序列化的结果是null
  • 函数、RegExpError对象和undefined值不能序列化和还原
  • JSON.stringify() 只能序列化对象可枚举的自有属性
JSON.stringify(NaN); // => "null"
JSON.stringify(Infinity); // => "nulll"
JSON.stringify(undefined); // => undefined

instanceof 运算符

instanceof运算符希望左操作数是一个对象,右操作数标识对象的类,如果左侧对象是右侧类的示例,则表达式返回true,否则返回false
js对象的类是通过初始化它们的构造函数来定义的,这样的话instanceof的右操作数应当是一个函数

  • 所有对象都是Object的实例
  • 如果instanceof左操作数不是对象的话,返回false
  • 如果右操作数不是函数,则抛出一个类型错误的异常
var d = new Date(); // 通过Date()构造函数来创建一个新对象
d instanceof Date; // => true d是由Date创建的
d instanceof Object ; // => true 所有的对象都是Object的实例
d instanceof Number; // => false 

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

推荐阅读更多精彩内容

  • 小时候很天真的以为爸爸妈妈都会一直陪在我们身边永远不会离开 甚至是我们的最亲的亲人都一样不会离开我们 会陪...
    琼瑶简单阅读 105评论 6 2
  • 一个人不孤独,想一个人才孤独,一个人不寂寞,想一个人才寂寞 我骗得了别人,却始终骗不了自己,每天都恍恍惚惚,既喜欢...
    lserdusyg阅读 580评论 6 3
  • 文/萧寒晨夕 做成一件事,没有持之以恒的毅力,几乎是不可能的。 这个世界大多时候是公平的,不付出超乎常人的努力,便...
    萧寒晨夕阅读 1,190评论 2 6