ECMAScript 5.1 对象(Object)与原型

1、对象的定义

JS中的对象(Object)是一个基本数据类型,是一种复合值,它将不少值(原始值或者其余对象)聚合在一块儿,可经过名字访问这些值(属性)的无序集合。每一个对象都有一个原型,原型能够为空。git

Object每一个属性既能够是一个命名的数据属性,也能够是一个命名的访问器属性,或者是一个内部属性:github

  • 命名的数据属性(named data property)有一个名字与一个ECMAScript语言值和一个Boolean属性集合组成
  • 命名的访问器属性(named accessor property)由一个名字与一个或者两个访问器函数,和一个Boolean属性组成。访问器函数用于存取一个与该属性相关联的ECMAScript语言值
  • 内部属性(internal property)没有名字,且不能直接经过ECMAScript语言操做。内部属性的存在纯粹为了规范的目的

    一、Object类型里面数据属性具备特性,以下表1

    image
    image

二、 Object类型里面访问器属性的特性,以下表2

image
image

上面两个列表中属性的默认值为(以下表3)函数

image
image

每个对象数据属性都具备表1的特性,每个对象里面的方法属性都具备表3里面的特性,这些特性能够经过ECMAScript内置对象Object构造器方法Object.defineProperty来修改。如a.x属性的Enumerable被修改成false时,对象在for-in循环里将迭代不出该属性。其余的属性这里不详细讲解ui

三、Object内部属性和方法

全部的Object对象都必须具备下表的属性(表4)this

image
image

[[prototype]]用于实现继承,[[Class]]用于区分对象的种类,也就是对象的名字。如String对象的[[Class]]值为"String"。建立对象时,对象的内部属性的[[Classs]]是除了"Arguments","Date","Array"....等内部对象名以外的值。因此咱们在定义对象的时候不能重名和用内置对象的名字。es5

ECMAScript不一样的对象的行为不一样,对应实现上表这些属性的方式也是略有不一样。可是必须都具有上述属性。不一样的内置对象具备不一样的属性,下表是只有在某些对象里面才具备属性(表5)spa

image
image

image
image

相对于Object内置对象Number,Date对象多实现[[primitiveValue]],Function对象多实现[[Code]]等属性,而经过Function.prototype.bind方法建立的Function对象还多实现[[TargetFunction]]方法,RegExp对象比Object多实现了[[Match]]属性等。而若是是可用于构造的对象则必须实现[[construct]]属性。prototype

2、建立对象的方法

一、对象字面量建立3d

var obj = {
    name: 'lyq',
    age: 18
}
console.log(obj.name) //lyq复制代码

直接复制操做。不详细讲解,由于涉及到JS中的“=、{}”操做符的运行过程,详细讲解的话又是一个话题。code

二、构造函数建立

2.一、JS原始(自带的)对象Object

var obj = new Object();
obj.name = 'lyq';
console.log(obj.name); //lyq复制代码

2.二、经过function 定义的构造函数建立

function Obj (name) {
    this.name = name;
    this.age = 18;
}
var obj = new Obj('lyq');
console.log(obj.name); //lyq
console.log(obj.age); //18复制代码

2.三、建立过程

2.3.1 经过JS原始(自带的)对象Object建立的过程

  • 若是提供了value则
    • 若是Type(value)是Object,则
      • value是原声ECMAScript对象(Array,Date这些),不建立新对象,简单返回value
      • 若是value是宿主对象,则采起动做和放回依赖实现的结果的方式可使依赖于宿主对象(不纠结字面意思,简单的说就是返回宿主对象)
    • 若是Type(value)是String类型,返回ToObject(value).
    • 若是Type(value)是Boolean类型,返回ToObject(value).
    • 若是Type(value)是Number类型,返回ToObject(value).
  • 未提供参数value或者类型是Null或者Undefined
    • 令obj为一个新建立的原声ECMAScript对象
    • 设定obj的[[prototype]]内部属性为标准内置的Object的prototype。
    • 设定obj的[[Class]]内部属性为"Object“
    • 设定obj的[[Extensible]]内部属性为true
    • 设定obj的表1指定的全部内部方法
    • 返回obj。

2.3.二、经过function 定义的构造函数建立过程

根据表5建立Function对象,必须实现多些属性,其中包括Function对象需拥有 FormalParameterList 为可选参数列表,FunctionBody 为函数体,词法环境 Scope ,严格模式标记Strict。经过Function构造器建立Function对象的步骤以下:

  • 令argCount为传给这个函数调用的参数总数
  • 令P为空字符串
  • 若是argCount=0,令body为空字符串
  • 不然若是argCount=1,令body为那个参数
  • 不然argCount>1
    • 令firstArg为第一个参数
      • 令P为ToString(firstArg)
      • 令k为2
      • 只要k<argCount就重复
        • 令nextArg为第k个参数
        • 令P为以前的P值。字符串“,”(逗号),ToString(nextArg)串联结构
        • k递增
      • 令body为第k个参数
      • 令body为ToString(body)
      • 若是P不可解析为一个FormalParameterListopt,则抛出一个SyntaxError异常
      • 若是body不可解析为FunctionBody,则抛出一个SyntaxError异常
      • 建立一个新的 ECMAScript 原生对象,令 F 为此对象。
      • 依照 表1 描述设定 F 的除 [[Get]] 之外的全部内部方法
      • 设定 F 的 [[Class]] 内部属性为 "Function"。
      • 设定 F 的 [[Prototype]] 内部属性为指定的标准内置 Function 对象的 prototype 属性。
      • 设定 F 的 [[Get]] 内部属性。
      • 设定 F 的 [[Call]] 内部属性。
      • 设定 F 的 [[Construct]] 内部属性。
      • 设定 F 的 [[HasInstance]] 内部属性。
      • 设定 F 的 [[Scope]] 全局环境。
      • 设定 F 的 [[FormalParameters]] 内部属性为 P。
      • 设定 F 的 [[Code]] 内部属性为 body 解析后的 FunctionBody。
      • 设定 F 的 [[Extensible]] 内部属性为 true。
      • 令 argCount为 FormalParameterList 指定的形式参数的个数。
      • 以参数 "length",属性描述符 {[[Value]]: len, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false},false 调用 F 的 [[DefineOwnProperty]] 内部方法。
      • 令 proto 为仿佛使用 new Object() 表达式建立新对象的结果,其中 Object 是标准内置构造器名。
      • 以参数 "constructor", 属性描述符 {[[Value]]: F, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}, false 调用 proto 的 [[DefineOwnProperty]] 内部方法。
      • 以参数 "prototype", 属性描述符 {[[Value]]: proto, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}, false 调用 F 的 [[DefineOwnProperty]] 内部方法。
      • 若是body是严格模式代码。则令strict为true,不然令strict为false
      • 若是strict是true,按照严格模式下的代码解析判断是否抛出异常
      • 返回 F。

上述建立内部方法的步骤都没有详细标出,可是不碍理解对象的建立过程。对象建立中的内存分配是由构造函数的内部方法[[Construct]]负责的。该内部方法的行为是ECMAScript定义好的,全部的构造函数都是使用该方法来为新对象分配内存的。而若是被new的对象没有实现[[contruct]]这个属性将会抛出异常。接下来补充上面Function对象的建立过程当中省略的建立[[construct]]属性的过程。

2.3.三、ECMAScript定义的Function对象建立[[construct]]属性的步骤

当以一个可能的空的参数列表调用函数对象 F 的 [[Construct]] 内部方法,采用如下步骤:

  • 令 obj 为新建立的 ECMAScript 原生对象。
  • 依照 8.12 设定 obj 的全部内部方法。
  • 设定 obj 的 [[Class]] 内部方法为 "Object"。
  • 设定 obj 的 [[Extensible]] 内部方法为 true。
  • 令 proto 为以参数 "prototype" 调用 F 的 [[Get]] 内部属性的值。
  • 若是 Type(proto) 是 Object,设定 obj 的 [[Prototype]] 内部属性为 proto。
  • 若是 Type(proto) 不是 Object,设定 obj 的 [[Prototype]] 内部属性为 标准的内置对象
  • 以 obj 为 this 值,调用 [[Construct]] 的参数列表为 args,调用 F 的 [[Call]] 内部属性,令 result 为调用结果。
  • 若是 Type(result) 是 Object,则返回 result。
  • 返回 obj

回到前面实例2,Obj 是一个Function对象,实现了[[contruct]]属性,能够经过 new Obj()来建立一个新的对象。而此时new Obj() 的过程就能new Object()原理同样。

3、原型与原型链

一、对象属性的增、删、查、改

1.1 增,即给对象添加属性,给对象的一个属性赋值,若是该属性原先不存在就会建立一个新属性,并将值赋予该属性

var obj = {};
console.log(obj.name); //undefined
obj.name = 'lyq';
console.log(obj.name); // lyq复制代码

1.二、删,经过delete操做能够删除掉对象的属性

var obj = {
     name : 'lyl'
 };
 console.log(obj.name); //lyl
 delete obj.name; 
 console.log(obj.name); //undefined复制代码

1.三、查,对象为无序组合,访问对象的属性要经过属性名(key)访问,若是对象不存在对应的key值,则返回undefined值。可使用“."操做符来访问,也能够经过"[]"操做符来访问

var obj = {
    name: 'lyq'
};
// 第一种方法
console.log(obj['name']); //lyq
// 第二种方法
console.log(obj.name); // lyq
console.log(obj.age); // undefined复制代码

1.四、改、更改对象属性的值

var obj = {
    name: 'lyq'
};
console.log(obj.name); // lyq
obj.name = 'lee';
console.log(obj.name); // lee复制代码

二、对象在原型上的操做

2.一、什么是原型和原型链

var person = function Person(){};
Person.prototype.x = 1var person = new Person();复制代码

根据上述建立对象的过程,当我经过new建立一个实例对象的时候,会先判断构造函数的prototype是否存在,同时是一个对象,若是是则将这个prototype对象赋值给新建立的对象的prototype。若是不存在和不是一个对象,则赋给新建立出来的对象一个标准内置的prototype。上述Person.prototype就是实例对象的原型。(经过上述函数对象的建立过程当中可以看出,每一个函数都会自动建立一个prototype对象,默认为标准的内置对象,用于知足函数会被当作构造函数的可能性。)

image
image

构造函数建立出来的实例对象共有一个prototype对象,而不是每一个实例都复制一份prototype出来,每一个实例对象都有一个指向原型的对象为proto
构造函数的prototype里面有一个指向构造函数自己的属性为constructor,标识每一个实例是经过哪一个构造函数建立出来的,如:

Person === person.__proto__.constructor //true;复制代码

prototype也是一个对象,也具备prototype属性,上例中Person.prototype的prototype属性是Object对象的内置prototype属性。而Object对象的内置prototype的prototype为null。对象的原型图以下

image
image

当要获取person对象的属性时,就会照图中蓝色的链来查找属性,这条蓝色的链便可理解为原型链(这逼其实就是js中的原型链)。

image
image

js在查找值的时候就是经过表1中的[[get]]方法来获取的,设置值是经过[[put]]方法来设置。这里不深刻讲解这两个方法,有兴趣能够继续深刻了解。ECMAScript中[[get]]会照着原型链获取属性,而[[put]]是直接将属性设置在person对象上,因此即便对象原型上存在的属性,在设置的时候若是对象自己不存在该属性,则直接建立新属性,而不会影响原型,以下图

image
image

因此当对对象进行增删改操做时不会影响到对象的原型和构造函数,当对对象进行查操做时,若是在自身获取不到,则会继续在原型链上查找,直到获取到该对象或到根原型为null为止

4、ECMAScript5 Object对象的方法

一、构造器属性方法

  • Object.prototype —— 对象原型
  • Object.getPrototypeOf(o) —— 返回o的内部属性值
  • Object.getOwnPropertyDescriptor ( O, P ) —— 返回对象O上面P属性的描述符
  • Object.create ( O [, Properties] ) —— 建立一个拥有指定原型和若干属性的对象
  • Object.defineProperty ( O, P, Attributes ) —— 在对象上添加或者修改一个属性,并返回这个对象,其中obj为要修改的对象,p为要修改或者添加的属性,Attributes将被定义或者修改的属性的描述符
  • Object.defineProperties ( O, Properties ) —— 添加或者修改多个属性,操做多个属性不能更改描述符
  • Object.seal ( O ) —— 密封对象
  • Object.freeze ( O ) —— 冻结对象中的某个属性,使该属性没法进行增删改查
  • Object.preventExtensions ( O ) ——禁止对象不能扩展,这样对象就永远不能添加更改属性
  • Object.isSealed ( O ) —— 判断一个对象是否被密封
  • Object.isFrozen ( O ) —— 判断一个对象是否被冻结
  • Object.isExtensible ( O ) —— 判断对象是否能够扩展

二、Object 的prototype上的属性

  • Object.prototype.constructor —— 标准内置的Object构造器
  • Object.prototype.toString ( ) —— 返回对象的字符串表示
  • Object.prototype.toLocaleString ( ) —— 返回对象的本地字符串表示
  • Object.prototype.valueOf ( ) —— 返回指定对象的原始值
  • Object.prototype.hasOwnProperty (V) —— 返回一个布尔值,表示对象是否包含有V这个属性,该属性为对象自己属性,不包括原型链上的属性
  • Object.prototype.isPrototypeOf (V) —— 返回一个布尔值,表示对象是否包含V这个属性,包含自身和原型链上的
  • Object.prototype.propertyIsEnumerable (V) —— 返回一个布尔值,表示V属性是否能够枚举

本文的参考文章

ECMAScript 5.1 中文文档

JavaScript深刻系列

相关文章
相关标签/搜索