摘要:父類的原型方法子類調用構造函數子類繼承父類前面說到的,實際上是將第一個參數無論是原型對象還是普通對象的屬性添加到新創建對象的原型中,這也就意味著,通過中定義的屬性和方法是無法通過方法添加到新創建對象中的。
ES5 和 ES6 中 Object 添加了很多新的方法,現在很多開源代碼中有用到了這些,今天來整理一番。
Object.assign()這是ES6新添加的方法,Object.assign()用來復制源對象的所有可枚舉屬性復制到目標對象中,方法返回目標對象。語法如下:
Object.assign(target, ...source);
source對象可以有很多個,比如:
let target = {name: "target"}; let source1 = {age: 23}; let source2 = {email: "zhanglun1410@gmail.com"}; // ... // let sourceN ={.....}; traget = Object.assign(target, source1, source2);
如果源對象和目標對象的屬性的key相同,目標對象的屬性將會被源對象中的屬性覆蓋。對于多個源對象來說,如果有相同的key,右邊的屬性將覆蓋左邊的屬性。這個方法只能將源對象的可枚舉對象和自己的屬性復制給目標對象。
什么是可枚舉對象()?
可枚舉屬性是指那些內部 “可枚舉” 標志設置為true的屬性,對于通過直接的賦值和屬性初始化的屬性,該標識值默認為即為true,對于通過Object.defineProperty等定義的屬性,該標識值默認為false。可枚舉的屬性可以通過 for...in 循環進行遍歷(除非該屬性名是一個 Symbol)。
對于源對象,Object.assign使用[[Get]],而在目標對象上使用[[Set]],也就是說,使用這個方法會源對象的getter和目標對象的setters。所以其本質就是定義或者復制一個新的屬性。如果等待合并的源對象包含了getters,那就不太適合用來將源對象合并到原型中。假如復制的屬性到原型里,包括它們的可枚舉屬性,那么應該使用 Object.getOwnPropertyDescriptor() 和 Object.defineProperty() 。String 和 Symbol 屬性都是會被復制的。
如果遇到了一個錯誤,比如目標對象的某個屬性是不可修改的,會拋出一個TypeError的錯誤嗎,目標對象保持不變
var foo = {} Object.defineProperty(foo, "name", { writable: false, value: "zhanglun" }); Object.assign(foo, {name: "zhangxiaolun"}); // TypeError: Cannot assign to read only property "1" of object "#
如果源對象是null或者undefined,Object.assign()不會拋出錯誤:
var foo = {name: "zhanglun"}; Object.assign(foo, null, undefined); console.log(foo); // foo: {name: "zhanglun"}Object.create()
通過指定的原型對象和屬性,創建一個新的對象。語法如下:
Object.create(proto, [,. propertiesObject]);
第一個參數是一個對象,可以是一個普通的對象,比如:{name: "zhanglun"},也可以是一個新創建的對象的原型(prototype),比如:new Array().prototype。無論是那種,都是 JavaScript 中的 Object,其屬性都被添加到返回的對象原型中;第二個參數是可選的,但是不能是undefined,該對象自身擁有的可枚舉屬性會被添加到新創建的對象上,其原型鏈上的屬性是無效的。如果第一個參數不是null或者一個對象值,將會拋出TypeError異常。
Object.create()最直接的作用是基于一個對象創建新的對象,更多時候用在了原型鏈繼承上,先來看看 JavaScript
中創建對象的幾種方法:
對象字面量
var o = {a: 1}; // o這個對象繼承了Object.prototype上面的所有屬性 // 所以可以這樣使用 o.hasOwnProperty("a"). // hasOwnProperty 是Object.prototype的自身屬性。 // Object.prototype的原型為null。 // 原型鏈如下: // o ---> Object.prototype ---> null var a = ["yo", "whadup", "?"]; // 數組都繼承于Array.prototype // (indexOf, forEach等方法都是從它繼承而來). // 原型鏈如下: // a ---> Array.prototype ---> Object.prototype ---> null function f(){ return 2; } // 函數都繼承于Function.prototype // (call, bind等方法都是從它繼承而來): // f ---> Function.prototype ---> Object.prototype ---> null
構造函數
在 JavaScript 中,構造器其實就是一個普通的函數。當使用 new 操作符 來作用這個函數時,它就可以被稱為構造方法(構造函數)。如果沒有 new 關鍵字而是直接調用的話,相當于是在當前作用域上調用,此時函數中如果有 this 的話,this 指向的是當前作用域。
function Graph() { this.vertexes = []; this.edges = []; } Graph.prototype = { addVertex: function(v){ this.vertexes.push(v); } }; var g = new Graph(); // g是生成的對象,他的自身屬性有"vertices"和"edges". // 在g被實例化時,g.[[Prototype]]指向了Graph.prototype. Graph(); console.log(window.vertexes); // 在全局作用域中調用,意外地增加了全局變量
使用 Object.create()
var a = {a: 1}; // a ---> Object.prototype ---> null var b = Object.create(a); // b ---> a ---> Object.prototype ---> null console.log(b.a); // 1 (繼承而來) var c = Object.create(b); // c ---> b ---> a ---> Object.prototype ---> null var d = Object.create(null); // d ---> null console.log(d.hasOwnProperty); // undefined, 因為d沒有繼承Object.prototype
ES6 中的 Class 關鍵字
ES6 引入了一套新的關鍵字用來實現 class。這是一個語法糖,其本質還是基于原型的。這些新的關鍵字包括 class, constructor, static, extends, 和 super。關于 Class的使用,回頭再開一篇文章深入學習。
"use strict"; class Polygon { constructor(height, width) { this.height = height; this.width = width; } } class Square extends Polygon { constructor(sideLength) { super(sideLength, sideLength); } get area() { return this.height * this.width; } set sideLength(newLength) { this.height = newLength; this.width = newLength; } } var square = new Square(2);使用 Object.create() 實現繼承
下面是一個使用 Object.create()實現類的繼承的例子。
function Shape() { this.x = 0; this.y = 0; } // 父類的原型方法 Shape.prototype.move = function(x, y) { this.x += x; this.y += y; console.info("Shape moved."); }; // 子類 function Rectangle() { Shape.call(this); // 調用構造函數 } // 子類繼承父類 Rectangle.prototype = Object.create(Shape.prototype); Rectangle.prototype.constructor = Rectangle; var rect = new Rectangle(); console.log("Is rect an instance of Rectangle?", rect instanceof Rectangle);// true console.log("Is rect an instance of Shape?", rect instanceof Shape);// true rect.move(1, 1); // Outputs, "Shape moved."Object.create VS new
前面說到的,Object.create實際上是將第一個參數(無論是原型對象還是普通對象)的屬性添加到新創建對象的原型中,這也就意味著,通過new Function()中定義的屬性和方法是無法通過 create()方法添加到新創建對象中的。
Object.create創建一個新的對象,這個對象“繼承”了第一個參數。構造函數新創建的對象“繼承”構造函數的prototype。
let o = new SomeConstructor(); // o 直接繼承自`SomeConstructor.prototype`。
兩者的最明顯的不同之處在于:如果 Object.create()的第一個參數是null,新創建的對象不會“繼承”自任何地方,沒有原型,也沒有往上追溯的原型;在使用構造函數時,如果將其原型設置為 null,SomeConstructor.prototype = null;,新創建的對象將會“繼承”自 Object 的 prototype。
Object.freeze()字面意思就是將一個 object“凍住”:不能添加新的屬性;不能刪除現有的屬性;不能修改現有屬性,包括屬性的enumerability, configurability和 writability。這個方法返回一個不可修改的對象,使用語法:
Object.freeze(obj)
任何嘗試修改該對象的操作都會失敗,可能是靜默失敗,也可能會拋出異常。在嚴格模式中會拋出異常(本地測試下來嚴格模式也不會報錯)。數據屬性的值不可更改,訪問器屬性(有getter和setter)也同樣,但由于是函數調用,給人一種修改了這個屬性的錯覺。如果一個屬性的值是個對象,則這個對象中的屬性是可以修改的,除非它也是個凍結對象。
let foo = { name: "zhanglun", age: 23, } console.log(foo.name); // "zhanglun" console.log(foo.age); // 23 foo.name = "zhanglun1410"; foo.age = 24; console.log(foo.name); // "zhanglun1410" console.log(foo.age); // 24 Object.freeze(foo); foo.name = "zzzz"; // 靜默失敗 console.log(foo.name); // "zhanglun1410" "use strict"; foo.name = "zzzz"; // TypeError 或者 靜默失敗 console.log(foo.name); // "zhanglun1410"Object.freeze VS const
被凍結的對象是不可改變的。但是它不一定是常量。對常量而言,它的所有引用,無論是直接的還是間接的都是引用的不可改變的對象。string,number,和 boolean 總是不可變的(當你把一個變量從字符串 A 修改到字符串 B時,A 和 B 都是不可變的,A 還是 A,B 也還是 B,只不過變量的之前指向的是 A,修改之后指向了 B)。通常來說,一般不會創建一個對象常量,也不存在freezeAll()這樣的方法。
const 用于聲明常量,將變量綁定到一個不可修改的對象,常量最終指向的是一個不可修改的對象,比如一個被凍結的對象,而 Object.freeze 作用在對象的值上,將一個對象變成不可修改的對象。
深度凍結對象前面提到的,Object.freeze作用在對象的屬性上,使對象的屬性不可修改。而如果屬性值也是一個對象的話,依然能夠修改,除非這個對象也被凍結了。因此,可以把 Object.freeze 理解成是“淺凍結”。可以編寫額外的代碼來實現“深凍結”:
obj1 = { internal: {} }; Object.freeze(obj1); obj1.internal.a = "aValue"; obj1.internal.a // "aValue" // 深度凍結 function deepFreeze(obj) { // 獲取到對象的屬性的名字 var propNames = Object.getOwnPropertyNames(obj); // 先凍結內部的對象 propNames.forEach(function(name) { var prop = obj[name]; // Freeze prop if it is an object if (typeof prop == "object" && prop !== null) deepFreeze(prop); }); // 凍結 obj return Object.freeze(obj); } obj2 = { internal: {} }; deepFreeze(obj2); obj2.internal.a = "anotherValue"; obj2.internal.a; // undefinedObject.freeze 的注意事項
在 ES5 中,如果參數不是一個對象,是一個原始數據類型,會拋出 TypeError。在 ES6 中,不是對象的參數的會被當做是一個已經被凍結的普通對象,只是返回這個參數。
Object.defineProperty()Object.defineProperty是ES5新增的一個方法,可以給對象的屬性增加更多的控制。語法如下:
Object.defineProperty(obj, prop, descriptor)
前面兩個參數很簡單,修改的對象和修改或者新增的屬性,著重介紹一下第三個參數:屬性描述符。
ES5 中定義了一個名叫“屬性描述符”的對象,用于描述了的各種特征,它本身是一個 Object。屬性描述符對象有4個屬性:
configurable:可配置性,控制著其描述的屬性的修改,表示能否修改屬性的特性,能否把屬性修改為訪問器屬性,或者能否通過delete刪除屬性從而重新定義屬性。默認值為true。
enumerable:可枚舉性,表示能否通過for-in遍歷得到屬性。默認值為true。
writable:可寫性,表示能否修改屬性的值。默認值為true。
value:數據屬性,表示屬性的值。默認值為undefined。
和兩個存取器屬性,分別是get和set,可以代替value和writable。
get:在讀取屬性時調用的函數。只指定get則表示屬性為只讀屬性。默認值為undefined。
set:在寫入屬性時調用的函數。只指定set則表示屬性為只寫屬性。默認值為undefined。
屬性描述符只能在Object.defineProperty或Object.defineProperties中使用。
var o = {}; // Creates a new object // Example of an object property added with defineProperty with a data property descriptor // 添加屬性 a,值為37,并設置屬性描述符 Object.defineProperty(o, "a", { value: 37, writable: true, enumerable: true, configurable: true }); var bValue = 38; Object.defineProperty(o, "b", { get: function() { return bValue; }, set: function(newValue) { bValue = newValue; }, enumerable: true, configurable: true }); o.b; // 38 // o 對象中存在屬性 b,他的值為38; // 只要 o.b沒有重新定義,它的值永遠都是38 // 訪問器不能和 value或者 writable混在一起用 Object.defineProperty(o, "conflict", { value: 0x9f91102, get: function() { return 0xdeadbeef; } }); // 拋出一個錯誤 Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #Object.defineProperties()
相比 Object.defineProperty, Object.defineProperties可以說是前者的升級版,可以一次同時定義多個屬性,語法略有不同:
let obj = {}; Object.defineProperties(obj, { "property1": { value: true, writable: true }, "property2": { value: "Hello", writable: false }, "property3": { get: function() { return "Hello, Object.defineProperties"; }, set:function() { this.property2 = "xxxxxx"; } } // etc. etc. });
參考資料:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
http://stackoverflow.com/a/17952160
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81365.html
摘要:將這個空對象的原型,指向構造函數的屬性。構造函數內部,指的是一個新生成的空對象,所有針對的操作,都會發生在這個空對象上。構造函數之所以叫構造函數,就是說這個函數的目的,就是操作一個空對象即對象,將其構造為需要的樣子。 toString() 的應用:判斷數據類型為了得到類型字符串,最好直接使用Object.prototype.toString方法。通過函數的call方法,可以在任意值上調...
摘要:函數式對象的一個子類型,中的函數是一等公民內置對象中還有一些對象子類型,通常被稱為內置對象。內容對象的內容是由一些存儲在特定命名位置的任意類型的值組成的,我們稱之為屬性。 語法 對象兩種定義形式 聲明(文字)形式 構造形式 //聲明(文字)形式 var myObj = { key: value // ... } //構造形式 var myObj = new Ob...
摘要:調用構造函數對該對象進行初始化。,并將該字節碼文件封裝成了對象。清單,獲取字節碼文件中的字段。 1、什么是反射技術?動態獲取指定類以及類中的內容(成員),并運行其內容。應用程序已經運行,無法在其中進行new對象的建立,就無法使用對象。這時可以根據配置文件的類全名去找對應的字節碼文件,并加載進內存,并創建該類對象實例。這就需要使用反射技術完成 2、獲取class對象的三種方式獲取Clas...
摘要:知識點變量作用域上方的函數作用域中聲明并賦值了,且在之上,所以遵循就近原則輸出等于。上方的函數作用域中被重新賦值,未被重新聲明,且位于之下,所以輸出全局作用域中的。若執行則會輸出。上方利用方法進行對象的深拷貝可以避免源對象被篡改的可能。 前言 本文是我學習JavaScript過程中收集與整理的一些易錯知識點,將分別從變量作用域,類型比較,this指向,函數參數,閉包問題及對象拷貝與賦值...
摘要:知識點變量作用域上方的函數作用域中聲明并賦值了,且在之上,所以遵循就近原則輸出等于。上方的函數作用域中被重新賦值,未被重新聲明,且位于之下,所以輸出全局作用域中的。上方利用方法進行對象的深拷貝可以避免源對象被篡改的可能。 前言 本文是我學習JavaScript過程中收集與整理的一些易錯知識點,將分別從變量作用域,類型比較,this指向,函數參數,閉包問題及對象拷貝與賦值這6個方面進行由...
閱讀 3097·2023-04-25 16:50
閱讀 911·2021-11-25 09:43
閱讀 3523·2021-09-26 10:11
閱讀 2524·2019-08-26 13:28
閱讀 2535·2019-08-26 13:23
閱讀 2428·2019-08-26 11:53
閱讀 3571·2019-08-23 18:19
閱讀 2993·2019-08-23 16:27