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深刻系列

相關文章
相關標籤/搜索