1. 程式人生 > >JS粗讀筆記(亂七八糟一鍋燉版四)

JS粗讀筆記(亂七八糟一鍋燉版四)

console物件 table() 對於某些複合型別的資料,console.table方法可以將其轉為表格顯示。 var languages = [ { name: "JavaScript", fileExtension: ".js" }, { name: "TypeScript", fileExtension: ".ts" }, { name: "CoffeeScript", fileExtension: ".coffee" }];console.table(languages); 上面程式碼的language變數,轉為表格顯示如下。
(index) name fileExtension
0 “JavaScript” “.js”
1 “TypeScript” “.ts”
2 “CoffeeScript” “.coffee”
複合型資料轉為表格顯示的條件是,必須擁有主鍵。對於陣列來說,主鍵就是數字鍵。對於物件來說,主鍵就是它的最外層鍵。 count() count方法用於計數,輸出它被呼叫了多少次。
function greet(user) {  console.count();  return 'hi ' + user;}greet('bob')//  : 1// "hi bob"greet('alice')//  : 2// "hi alice"greet('bob')//  : 3// "hi bob"

上面程式碼每次呼叫greet函式,內部的console.count方法就輸出執行次數。 該方法可以接受一個字串作為引數,作為標籤,對執行次數進行分類。 dir(),dirxml() dir方法用來對一個物件進行檢查(inspect),並以易於閱讀和列印的格式顯示。
console.log({f1: 'foo', f2: 'bar'})// Object {f1: "foo", f2: "bar"}console.dir({f1: 'foo', f2: 'bar'})// Object//   f1: "foo"//   f2: "bar"//   __proto__: Object

上面程式碼顯示dir方法的輸出結果,比
log方法更易讀,資訊也更豐富。 該方法對於輸出DOM物件非常有用,因為會顯示DOM物件的所有屬性。
console.dir(document.body)

dirxml方法主要用於以目錄樹的形式,顯示DOM節點。
console.dirxml(document.body)

如果引數不是DOM節點,而是普通的JavaScript物件,console.dirxml等同於console.dir assert() assert方法接受兩個引數,第一個引數是表示式,第二個引數是字串。只有當第一個引數為false,才會輸出第二個引數,否則不會有任何結果。
console.assert(true === false, '判斷條件不成立')// Assertion failed: 判斷條件不成立

time(),timeEnd() 這兩個方法用於計時,可以算出一個操作所花費的準確時間。
console.time('Array initialize');<span style="white-space:pre">											</span>       var array= new Array(1000000);<span style="white-space:pre">												</span>          for (var i = array.length - 1; i >= 0; i--) {    <span style="white-space:pre">										</span>array[i] = new Object();};<span style="white-space:pre">												</span>console.timeEnd('Array initialize');// Array initialize: 1914.481ms

time方法表示計時開始,timeEnd方法表示計時結束。它們的引數是計時器的名稱。呼叫timeEnd方法之後,console視窗會顯示“計時器名稱: 所耗費的時間”。 profile(),profileEnd()
console.profile方法用來新建一個性能測試器(profile),它的引數是效能測試器的名字。
console.profile('p')// Profile 'p' started.
console.profileEnd方法用來結束正在執行的效能測試器。
console.profileEnd()// Profile 'p' finished.


開啟瀏覽器的開發者工具,在profile面板中,可以看到這個效能偵錯程式的執行結果 group(),groupend(),groupCollapsed() console.groupconsole.groupend這兩個方法用於將顯示的資訊分組。它只在輸出大量資訊時有用,分在一組的資訊,可以用滑鼠摺疊/展開。
console.group('Group One');<span style="white-space:pre">												</span>       console.group('Group Two');// <span style="white-space:pre">												</span>       some codeconsole.groupEnd(); // Group Two 結束console.groupEnd(); // Group One 結束

console.groupCollapsed方法與console.group方法很類似,唯一的區別是該組的內容,在第一次顯示時是收起的(collapsed),而不是展開的。 屬性描述物件 Object.getOwnPropertyDescriptor() Object.getOwnPropertyDescriptor方法可以讀出物件自身屬性的屬性描述物件。
var o = { p: 'a' };
Object.getOwnPropertyDescriptor(o, 'p')<span style="white-space:pre">												</span>// Object { value: "a",//   writable: true,//   enumerable: true,//   configurable: true// }


上面程式碼表示,使用Object.getOwnPropertyDescriptor方法,讀取o物件的p屬性的屬性描述物件。 Object.defineProperty(),Object.defineProperties() Object.defineProperty方法允許通過定義屬性描述物件,來定義或修改一個屬性,然後返回修改後的物件。它的格式如下。 Object.defineProperty(object, propertyName, attributesObject) 上面程式碼中,Object.defineProperty方法接受三個引數,第一個是屬性所在的物件,第二個是屬性名(它應該是一個字串),第三個是屬性的描述物件。比如,新建一個o物件,並定義它的p屬性,寫法如下。
var o = Object.defineProperty({}, 'p', {  value: 123,  writable: false,  enumerable: true,  configurable: false});<span style="white-space:pre">		</span>o.p// 123o.p = 246;o.p// 123// 因為writable為false,所以無法改變該屬性的值

如果一次性定義或修改多個屬性,可以使用Object.defineProperties方法。
var o = Object.defineProperties({}, {  p1: { value: 123, enumerable: true },  p2: { value: 'abc', enumerable: true },  p3: { get: function () { return this.p1 + this.p2 },    enumerable:true,    configurable:true  }});
o.p1 // 123o.p2 // "abc"o.p3 // "123abc"<span style="color:#ff0000;">
</span>

這時需要注意的是,一旦定義了取值函式
get(或存值函式set),就不能將writable設為true,或者同時定義value屬性,會報錯。 如果一個屬性的enumerablefalse,下面三個操作不會取到該屬性。
  • for..in迴圈
  • Object.keys方法
  • JSON.stringify方法
Object.getOwnPropertyNames() Object.getOwnPropertyNames方法返回直接定義在某個物件上面的全部屬性的名稱,而不管該屬性是否可列舉。
var o = Object.defineProperties({}, {  p1: { value: 1, enumerable: true },  p2: { value: 2, enumerable: false }});Object.getOwnPropertyNames(o)// ["p1", "p2"]


Object.prototype.propertyIsEnumerable() 物件例項的propertyIsEnumerable方法用來判斷一個屬性是否可列舉。 屬性描述物件 概述 JavaScript提供了一個內部資料結構,用來描述一個物件的屬性的行為,控制它的行為。這被稱為“屬性描述物件”(attributes object)。每個屬性都有自己對應的屬性描述物件,儲存該屬性的一些元資訊。 下面是屬性描述物件的一個例項。
{  value: 123,  writable: false,  enumerable: true,  configurable: false,  get: undefined,  set: undefined}

屬性描述物件提供6個元屬性。 (1)value value存放該屬性的屬性值,預設為undefined。 (2)writable writable存放一個布林值,表示屬性值(value)是否可改變,預設為true。 (3)enumerable enumerable存放一個布林值,表示該屬性是否可列舉,預設為true。如果設為false,會使得某些操作(比如for...in迴圈、Object.keys())跳過該屬性。 (4)configurable configurable存放一個布林值,表示“可配置性”,預設為true。如果設為false,將阻止某些操作改寫該屬性,比如,無法刪除該屬性,也不得改變該屬性的屬性描述物件(value屬性除外)。也就是說,configurable屬性控制了屬性描述物件的可寫性。 (5)get get存放一個函式,表示該屬性的取值函式(getter),預設為undefined。 (6)set set存放一個函式,表示該屬性的存值函式(setter),預設為undefinedObject.getOwnPropertyDescriptor() Object.getOwnPropertyDescriptor方法可以讀出物件自身屬性的屬性描述物件。
var o = { p: 'a' };<span style="white-space:pre">													</span>       Object.getOwnPropertyDescriptor(o, 'p')<span style="white-space:pre">												</span>// Object { value: "a",//   writable: true,//   enumerable: true,//   configurable: true// }


上面程式碼表示,使用Object.getOwnPropertyDescriptor方法,讀取o物件的p屬性的屬性描述物件。 Object.defineProperty(),Object.defineProperties() Object.defineProperty方法允許通過定義屬性描述物件,來定義或修改一個屬性,然後返回修改後的物件。它的格式如下。
Object.defineProperty(object, propertyName, attributesObject)

上面程式碼中,Object.defineProperty方法接受三個引數,第一個是屬性所在的物件,第二個是屬性名(它應該是一個字串),第三個是屬性的描述物件。比如,新建一個o物件,並定義它的p屬性,寫法如下。
var o = Object.defineProperty({}, 'p', {  value: 123,  writable: false,  enumerable: true,  configurable: false});o.p// 123o.p = 246;o.p// 123// 因為writable為false,所以無法改變該屬性的值


如果屬性已經存在,Object.defineProperty方法相當於更新該屬性的屬性描述物件。 需要注意的是,Object.defineProperty方法和後面的Object.defineProperties方法,都有效能損耗,會拖慢執行速度,不宜大量使用。 如果一次性定義或修改多個屬性,可以使用Object.defineProperties方法。
var o = Object.defineProperties({}, {  p1: { value: 123, enumerable: true },  p2: { value: 'abc', enumerable: true },  p3: { get: function () { return this.p1 + this.p2 },    enumerable:true,    configurable:true  }});o.p1 // 123o.p2 // "abc"o.p3 // "123abc"


上面程式碼中的p3屬性,定義了取值函式get。這時需要注意的是,一旦定義了取值函式get(或存值函式set),就不能將writable設為true,或者同時定義value屬性,會報錯。
var o = {};Object.defineProperty(o, 'p', {  value: 123,  get: function() { return 456; }});// TypeError: Invalid property.// A property cannot both have accessors and be writable or have a value,


上面程式碼同時定義了get屬性和value屬性,結果就報錯。 Object.defineProperty()Object.defineProperties()的第三個引數,是一個屬性物件。它的writableconfigurableenumerable這三個屬性的預設值都為false
var obj = {};Object.defineProperty(obj, 'foo', { configurable: true });Object.getOwnPropertyDescriptor(obj, 'foo')// {//   value: undefined,//   writable: false,//   enumerable: false,//   configurable: true// }


上面程式碼中,定義obj物件的foo屬性時,只定義了可配置性configurabletrue。結果,其他元屬性都是預設值。 writable屬性為false,表示對應的屬性的值將不得改寫。
var o = {};Object.defineProperty(o, 'p', {  value: "bar"});o.p // baro.p = 'foobar';o.p // barObject.defineProperty(o, 'p', {  value: 'foobar',});// TypeError: Cannot redefine property: p


上面程式碼由於writable屬性預設為false,導致無法對p屬性重新賦值,但是不會報錯(嚴格模式下會報錯)。不過,如果再一次使用Object.defineProperty方法對value屬性賦值,就會報錯。 configurable屬性為false,將無法刪除該屬性,也無法修改attributes物件(value屬性除外)。
var o = {};Object.defineProperty(o, 'p', {  value: 'bar',});delete o.po.p // "bar"

上面程式碼中,由於configurable屬性預設為false,導致無法刪除某個屬性。 enumerable屬性為false,表示對應的屬性不會出現在for...in迴圈和Object.keys方法中。
var o = {  p1: 10,  p2: 13,};<span style="white-space:pre">													</span>Object.defineProperty(o, 'p3', {  value: 3,});for (var i in o) {  console.log(i, o[i]);}// p1 10// p2 13


上面程式碼中,p3屬性是用Object.defineProperty方法定義的,由於enumerable屬性預設為false,所以不出現在for...in迴圈中。 元屬性 屬性描述物件的屬性,被稱為“元屬性”,因為它可以看作是控制屬性的屬性。 可列舉性(enumerable) JavaScript的最初版本,in 運算子和基於它的for...in迴圈,會遍歷物件例項的所有屬性,包括繼承的屬性。 var obj = {};'toString' in obj // true 上面程式碼中,toString不是obj物件自身的屬性,但是in運算子也返回true,導致被for...in迴圈遍歷,這顯然不太合理。後來就引入了“可列舉性”這個概念,只有可列舉的屬性,才會被for...in迴圈遍歷,同時還規定原生繼承的屬性都是不可列舉的,這樣就保證了for...in迴圈的可用性。 可列舉性(enumerable)用來控制所描述的屬性,是否將被包括在for...in迴圈之中。具體來說,如果一個屬性的enumerablefalse,下面三個操作不會取到該屬性。
  • for..in迴圈
  • Object.keys方法
  • JSON.stringify方法
因此,enumerable可以用來設定“祕密”屬性。 var o = {a: 1, b: 2};o.c = 3;Object.defineProperty(o, 'd', { value: 4, enumerable: false});o.d // 4for (var key in o) { console.log(o[key]);}// 1// 2// 3Object.keys(o) // ["a", "b", "c"]JSON.stringify(o) // "{a:1, b:2, c:3}" 上面程式碼中,d屬性的enumerablefalse,所以一般的遍歷操作都無法獲取該屬性,使得它有點像“祕密”屬性,但不是真正的私有屬性,還是可以直接獲取它的值。 基本上,JavaScript原生提供的屬性都是不可列舉的,使用者自定義的屬性都是可列舉的。 與列舉性相關的幾個操作的區別的是,for...in迴圈包括繼承自原型物件的屬性,Object.keys方法只返回物件本身的屬性。如果需要獲取物件自身的所有屬性,不管是否可列舉,可以使用Object.getOwnPropertyNames方法,詳見下文。 考慮到JSON.stringify方法會排除enumerablefalse的值,有時可以利用這一點,為物件添加註釋資訊。
var car = {  id: 123,  color: 'red',  ownerId: 12};<span style="white-space:pre">										</span>var owner = {  id: 12,  name: 'Jack'};<span style="white-space:pre">											</span>Object.defineProperty(car, 'ownerInfo', {  value: owner,  enumerable: false});<span style="white-space:pre">						</span>car.ownerInfo// {id: 12, name: "Jack"}JSON.stringify(car)//  "{"id": 123,"color": "red","ownerId": 12}"


上面程式碼中,owner物件作為註釋部分,加入car物件。由於ownerInfo屬性不可列舉,所以JSON.stringify方法最後輸出car物件時,會忽略ownerInfo屬性。 這提示我們,如果你不願意某些屬性出現在JSON輸出之中,可以把它的enumerable屬性設為false可配置性(configurable) 可配置性(configurable)決定了是否可以修改屬性描述物件。也就是說,當configurablefalse的時候,value、writable、enumerable和configurable都不能被修改了。
var o = Object.defineProperty({}, 'p', {  value: 1,  writable: false,  enumerable: false,  configurable: false});Object.defineProperty(o,'p', {value: 2})// TypeError: Cannot redefine property: pObject.defineProperty(o,'p', {writable: true})// TypeError: Cannot redefine property: pObject.defineProperty(o,'p', {enumerable: true})// TypeError: Cannot redefine property: pObject.defineProperties(o,'p',{configurable: true})// TypeError: Cannot redefine property: p


上面程式碼首先定義物件o,並且定義o的屬性pconfigurablefalse。然後,逐一改動valuewritableenumerableconfigurable,結果都報錯。 需要注意的是,writable只有在從false改為true會報錯,從true改為false則是允許的。
var o = Object.defineProperty({}, 'p', {  writable: true,  configurable: false});Object.defineProperty(o,'p', {writable: false})// 修改成功


至於value,只要writableconfigurable有一個為true,就允許改動。
var o1 = Object.defineProperty({}, 'p', {  value: 1,  writable: true,  configurable: false});Object.defineProperty(o1,'p', {value: 2})// 修改成功var o2 = Object.defineProperty({}, 'p', {  value: 1,  writable: false,  configurable: true});Object.defineProperty(o2,'p', {value: 2})// 修改成功


另外,configurablefalse時,直接對該屬性賦值,不報錯,但不會成功。
var o = Object.defineProperty({}, 'p', {  value: 1,  configurable: false});o.p = 2;o.p // 1

上面程式碼中,o物件的p屬性是不可配置的,對它賦值是不會生效的。 可配置性決定了一個變數是否可以被刪除(delete)。
var o = Object.defineProperties({}, {  p1: { value: 1, configurable: true },  p2: { value: 2, configurable: false }});delete o.p1 // truedelete o.p2 // falseo.p1 // undefinedo.p2 // 2


上面程式碼中的物件o有兩個屬性,p1是可配置的,p2是不可配置的。結果,p2就無法刪除。 需要注意的是,當使用var命令宣告變數時,變數的configurablefalse
var a1 = 1;Object.getOwnPropertyDescriptor(this,'a1')// Object {//  value: 1,//  writable: true,//  enumerable: true,//  configurable: false// }


而不使用var命令宣告變數時(或者使用屬性賦值的方式宣告變數),變數的可配置性為true
a2 = 1;Object.getOwnPropertyDescriptor(this,'a2')// Object {//  value: 1,//  writable: true,//  enumerable: true,//  configurable: true// }// 或者寫成window.a3 = 1;Object.getOwnPropertyDescriptor(window, 'a3')// Object {//  value: 1,//  writable: true,//  enumerable: true,//  configurable: true// }


上面程式碼中的this.a3 = 1a3 = 1是等價的寫法。window指的是瀏覽器的頂層物件。 這種差異意味著,如果一個變數是使用var命令生成的,就無法用delete命令刪除。也就是說,delete只能刪除物件的屬性。
var a1 = 1;a2 = 1;delete a1 // falsedelete a2 // truea1 // 1a2 // ReferenceError: a2 is not defined<strong>
</strong>

可寫性(writable)
可寫性(writable)決定了屬性的值(value)是否可以被改變。
var o = {};Object.defineProperty(o, 'a', {  value: 37,  writable: false});o.a // 37o.a = 25;o.a // 37

上面程式碼將o物件的a屬性可寫性設為false,然後改變這個屬性的值,就不會有任何效果。 注意,正常模式下,對可寫性為false的屬性賦值不會報錯,只會默默失敗。但是,嚴格模式下會報錯,即使是對a屬性重新賦予一個同樣的值。 關於可寫性,還有一種特殊情況。就是如果原型物件的某個屬性的可寫性為false,那麼派生物件將無法自定義這個屬性。
var proto = Object.defineProperty({}, 'foo', {  value: 'a',  writable: false});var o = Object.create(proto);o.foo = 'b';o.foo // 'a'


上面程式碼中,物件protofoo屬性不可寫,結果proto的派生物件o,也不可以再自定義這個屬性了。在嚴格模式下,這樣做還會丟擲一個錯誤。但是,有一個規避方法,就是通過覆蓋屬性描述物件,繞過這個限制,原因是這種情況下,原型鏈會被完全忽視。
Object.defineProperty(o, 'foo', {  value: 'b'});o.foo // 'b'

Object.getOwnPropertyNames() Object.getOwnPropertyNames方法返回直接定義在某個物件上面的全部屬性的名稱,而不管該屬性是否可列舉。
var o = Object.defineProperties({}, {  p1: { value: 1, enumerable: true },  p2: { value: 2, enumerable: false }});Object.getOwnPropertyNames(o)// ["p1", "p2"]
一般來說,系統原生的屬性(即非使用者自定義的屬性)都是不可列舉的。
// 比如,陣列例項自帶length屬性是不可列舉的Object.keys([]) // []Object.getOwnPropertyNames([]) // [ 'length' ]// Object.prototype物件的自帶屬性也都是不可列舉的Object.keys(Object.prototype) // []Object.getOwnPropertyNames(Object.prototype)// ['hasOwnProperty',//  'valueOf',//  'constructor',//  'toLocaleString',//  'isPrototypeOf',//  'propertyIsEnumerable',//  'toString']


上面程式碼可以看到,陣列的例項物件([])沒有可列舉屬性,不可列舉屬性有length;Object.prototype物件也沒有可列舉屬性,但是有不少不可列舉屬性。 Object.prototype.propertyIsEnumerable() 物件例項的propertyIsEnumerable方法用來判斷一個屬性是否可列舉。 var o = {};o.p = 123;o.propertyIsEnumerable('p') // trueo.propertyIsEnumerable('toString') // false 上面程式碼中,使用者自定義的p屬性是可列舉的,而繼承自原型物件的toString屬性是不可列舉的。 存取器(accessor) 除了直接定義以外,屬性還可以用存取器(accessor)定義。其中,存值函式稱為setter,使用set命令;取值函式稱為getter,使用get命令。 存取器提供的是虛擬屬性,即該屬性的值不是實際存在的,而是每次讀取時計算生成的。利用這個功能,可以實現許多高階特性,比如每個屬性禁止賦值。
var o = {  get p() {    return 'getter';  },  set p(value) {    console.log('setter: ' + value);  }};

上面程式碼中,o物件內部的getset命令,分別定義了p屬性的取值函式和存值函式。定義了這兩個函式之後,對p屬性取值時,取值函式會自動呼叫;對p屬性賦值時,存值函式會自動呼叫。
o.p // "getter"o.p = 123 // "setter: 123"

注意,取值函式Getter不能接受引數,存值函式Setter只能接受一個引數(即屬性的值)。另外,物件也不能有與取值函式同名的屬性。比如,上面的物件o設定了取值函式p以後,就不能再另外定義一個p屬性。 存取器往往用於,屬性的值需要依賴物件內部資料的場合。 利用存取器,可以實現資料物件與DOM物件的雙向繫結。 Object.preventExtensions() Object.preventExtensions方法可以使得一個物件無法再新增新的屬性。 不過,對於使用了preventExtensions方法的物件,可以用delete命令刪除它的現有屬性。 Object.isExtensible() Object.isExtensible方法用於檢查一個物件是否使用了Object.preventExtensions方法。也就是說,檢查是否可以為一個物件新增屬性。
var o = new Object();Object.isExtensible(o) // trueObject.preventExtensions(o);Object.isExtensible(o) <strong>
</strong>

Object.seal()
Object.seal方法使得一個物件既無法新增新屬性,也無法刪除舊屬性。 Object.seal實質是把屬性描述物件的configurable屬性設為false,因此屬性描述物件不再能改變了。 同樣是使用了Object.seal方法,如果writable原為false,改變這個設定將報錯;如果原為true,則不會有問題。 Object.isSealed() Object.isSealed方法用於檢查一個物件是否使用了Object.seal方法。 Object.freeze() Object.freeze方法可以使得一個物件無法新增新屬性、無法刪除舊屬性、也無法改變屬性的值,使得這個物件實際上變成了常量。 Object.isFrozen() Object.isFrozen方法用於檢查一個物件是否使用了Object.freeze()方法 prototype 物件 Object.getPrototypeOf() Object.getPrototypeOf方法返回一個物件的原型。這是獲取原型物件的標準方法
// 空物件的原型是Object.prototypeObject.getPrototypeOf({}) === Object.prototype// true// 函式的原型是Function.prototypefunction f() {}Object.getPrototypeOf(f) === Function.prototype// true// f 為 F 的例項物件,則 f 的原型是 F.prototypevar f = new F();Object.getPrototypeOf(f) === F.prototype// true<strong style="font-size: 24px; line-height: 1.5; white-space: pre-wrap;">
</strong>

Object.setPrototypeOf()
Object.setPrototypeOf方法可以為現有物件設定原型,返回一個新物件。 Object.setPrototypeOf方法接受兩個引數,第一個是現有物件,第二個是原型物件 Object.create() Object.create方法用於從原型物件生成新的例項物件,可以替代new命令。 它接受一個物件作為引數,返回一個新物件,後者完全繼承前者的屬性,即原有物件成為新物件的原型
var A = { print: function () { console.log('hello'); }};var B = Object.create(A);B.print() // helloB.print === A.print // true


上面程式碼中,Object.create方法在A的基礎上生成了B。此時,A就成了B的原型,B就繼承了A的所有屬性和方法。 下面三種方式生成的新物件是等價的。
var o1 = Object.create({});var o2 = Object.create(Object.prototype);var o3 = new Object();


除了物件的原型,Object.create方法還可以接受第二個引數。該引數是一個屬性描述物件,它所描述的物件屬性,會新增到新物件。
var o = Object.create({}, { p1: { value: 123, enumerable: true }, p2: { value: 'abc', enumerable: true }});// 等同於var o = Object.create({});o.p1 = 123;o.p2 = 'abc';


Object.create方法生成的物件,繼承了它的原型物件的建構函式
function A() {}var a = new A();var b = Object.create(a);b.constructor === A // trueb instanceof A // true

上面程式碼中,b物件的原型是a物件,因此繼承了a物件的建構函式A Object.prototype.isPrototypeOf() 物件例項的isPrototypeOf方法,用來判斷一個物件是否是另一個物件的原型。
var o1 = {};var o2 = Object.create(o1);var o3 = Object.create(o2);o2.isPrototypeOf(o3) // trueo1.isPrototypeOf(o3) // true

上面程式碼表明,只要某個物件處在原型鏈上,isProtypeOf都返回true Object.prototype.__proto__ __proto__屬性(前後各兩個下劃線)可以改寫某個物件的原型物件。
var obj = {};var p = {};obj.__proto__ = p;Object.getPrototypeOf(obj) === p // true

上面程式碼通過__proto__屬性,將p物件設為obj物件的原型。 根據語言標準,__proto__屬性只有瀏覽器才需要部署,其他環境可以沒有這個屬性,而且前後的兩根下劃線,表示它本質是一個內部屬性,不應該對使用者暴露。因此,應該儘量少用這個屬性,而是用Object.getPrototypeof()(讀取)和Object.setPrototypeOf()(設定),進行原型物件的讀寫操作。 推薦使用第三種Object.getPrototypeOf方法,獲取原型物件。 Object 物件與繼承 物件本身的屬性之中,有的是可以列舉的(enumerable),有的是不可以列舉的,Object.getOwnPropertyNames方法返回所有鍵名。只獲取那些可以列舉的屬性,使用Object.keys方法。 Object.prototype.hasOwnProperty() 物件例項的hasOwnProperty方法返回一個布林值,用於判斷某個屬性定義在物件自身,還是定義在原型鏈上。
Date.hasOwnProperty('length')// trueDate.hasOwnProperty('toString')// false

hasOwnProperty方法是JavaScript之中唯一一個處理物件屬性時,不會遍歷原型鏈的方法。 in 運算子和 for…in 迴圈 in運算子返回一個布林值,表示一個物件是否具有某個屬性。它不區分該屬性是物件自身的屬性,還是繼承的屬性。 'length' in Date // true'toString' in Date // true in運算子常用於檢查一個屬性是否存在。 物件的拷貝 如果要拷貝一個物件,需要做到下面兩件事情。
  • 確保拷貝後的物件,與原物件具有同樣的prototype原型物件。
  • 確保拷貝後的物件,與原物件具有同樣的屬性。
下面就是根據上面兩點,編寫的物件拷貝的函式。
function copyObject(orig) { var copy = Object.create(Object.getPrototypeOf(orig)); copyOwnPropertiesFrom(copy, orig); return copy;}function copyOwnPropertiesFrom(target, source) { Object .getOwnPropertyNames(source) .forEach(function(propKey) { var desc = Object.getOwnPropertyDescriptor(source, propKey); Object.defineProperty(target, propKey, desc); }); return target;}