參考一篇不錯的文章:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object
1、創建對象:
var person = new Object();
或
var person = {};
2、對象屬性:
2.1、為對象添加屬性:
person.firstname = "Bill"; // 為person對象添加firstname屬性 person.lastname = "Gates"; // 為person對象添加lastname屬性 person.age = 56; // 為person對象添加age屬性 person.eyecolor = "blue"; // 為person對象添加eyecolor屬性
2.2、對象的屬性:
person.length; // 返回person對象的長度
2.3、Object的原型對象:prototype:
(1) Object.prototype
屬性的屬性特性:
writable | false |
enumerable | false |
configurable | false |
(2) 幾乎所有的 JavaScript 對象都是 Object
的實例;一個典型的對象繼承了Object.prototype
的屬性(包括方法),盡管這些屬性可能被遮蔽(也被稱為覆蓋)。改變Object
原型,會通過原型鏈,而改變所有對象;除非這些屬性和方法被其他對原型鏈更里層的改動所覆蓋。這提供了一個非常強大的、但有潛在危險的機制,來覆蓋或擴展對象行為。
(3)Object.prototype.constructor:返回創建實例對象的 Object
構造函數的引用。注意,此屬性的值是對函數本身的引用,而不是一個包含函數名稱的字符串。對原始類型來說,如1
,true
和"test"
,該值只可讀。
var o = {}; o.constructor === Object; // true var o = new Object; o.constructor === Object; // true var a = []; a.constructor === Array; // true var a = new Array; a.constructor === Array // true var n = new Number(3); n.constructor === Number; // true
function Tree(name) { this.name = name; } var theTree = new Tree("Redwood"); console.log( "theTree.constructor is " + theTree.constructor ); // 輸出結果: theTree.constructor is function Tree(name) { this.name = name; }
3、方法:
3.1、Object.assign():通過復制一個或多個對象來創建一個新的對象
const object1 = { a: 1, b: 2, c: 3 }; const object2 = Object.assign({}, object1); console.log(object2.c); // 3 /* * 復制一個對象 */ var obj = { a: 1 }; var copy = Object.assign({}, obj); console.log(copy); // { a: 1 } /* * 深拷貝問題 */ function test() { 'use strict'; let obj1 = { a: 0 , b: { c: 0}}; let obj2 = Object.assign({}, obj1); console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}} obj1.a = 1; console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}} console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}} obj2.a = 2; console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}} console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}} obj2.b.c = 3; console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}} console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}} // Deep Clone obj1 = { a: 0 , b: { c: 0}}; let obj3 = JSON.parse(JSON.stringify(obj1)); obj1.a = 4; obj1.b.c = 4; console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}} } test(); /* * 合並對象 */ var o1 = { a: 1 }; var o2 = { b: 2 }; var o3 = { c: 3 }; var obj = Object.assign(o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 } console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目標對象自身也會改變。 /* * 合並具有相同屬性的對象 */ var o1 = { a: 1, b: 1, c: 1 }; var o2 = { b: 2, c: 2 }; var o3 = { c: 3 }; var obj = Object.assign({}, o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 } /* * 拷貝 symbol 類型的屬性 */ var o1 = { a: 1 }; var o2 = { [Symbol('foo')]: 2 }; var obj = Object.assign({}, o1, o2); console.log(obj); // { a : 1, [Symbol("foo")]: 2 } (cf. bug 1207182 on Firefox) Object.getOwnPropertySymbols(obj); // [Symbol(foo)] /* * 繼承屬性和不可枚舉屬性是不能拷貝的 */ var obj = Object.create({foo: 1}, { // foo 是個繼承屬性。 bar: { value: 2 // bar 是個不可枚舉屬性。 }, baz: { value: 3, enumerable: true // baz 是個自身可枚舉屬性。 } }); var copy = Object.assign({}, obj); console.log(copy); // { baz: 3 } /* * 原始類型會被包裝為對象 */ var v1 = "abc"; var v2 = true; var v3 = 10; var v4 = Symbol("foo") var obj = Object.assign({}, v1, null, v2, undefined, v3, v4); // 原始類型會被包裝,null 和 undefined 會被忽略。 // 注意,只有字符串的包裝對象才可能有自身可枚舉屬性。 console.log(obj); // { "0": "a", "1": "b", "2": "c" } /* * 異常會打斷后續拷貝任務 */ var target = Object.defineProperty({}, "foo", { value: 1, writable: false }); // target 的 foo 屬性是個只讀屬性。 Object.assign(target, {bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4}); // TypeError: "foo" is read-only // 注意這個異常是在拷貝第二個源對象的第二個屬性時發生的。 console.log(target.bar); // 2,說明第一個源對象拷貝成功了。 console.log(target.foo2); // 3,說明第二個源對象的第一個屬性也拷貝成功了。 console.log(target.foo); // 1,只讀屬性不能被覆蓋,所以第二個源對象的第二個屬性拷貝失敗了。 console.log(target.foo3); // undefined,異常之后 assign 方法就退出了,第三個屬性是不會被拷貝到的。 console.log(target.baz); // undefined,第三個源對象更是不會被拷貝到的。 /* * 拷貝訪問器 */ var obj = { foo: 1, get bar() { return 2; } }; var copy = Object.assign({}, obj); // { foo: 1, bar: 2 } // copy.bar的值來自obj.bar的getter函數的返回值 console.log(copy); // 下面這個函數會拷貝所有自有屬性的屬性描述符 function completeAssign(target, ...sources) { sources.forEach(source => { let descriptors = Object.keys(source).reduce((descriptors, key) => { descriptors[key] = Object.getOwnPropertyDescriptor(source, key); return descriptors; }, {}); // Object.assign 默認也會拷貝可枚舉的Symbols Object.getOwnPropertySymbols(source).forEach(sym => { let descriptor = Object.getOwnPropertyDescriptor(source, sym); if (descriptor.enumerable) { descriptors[sym] = descriptor; } }); Object.defineProperties(target, descriptors); }); return target; } var copy = completeAssign({}, obj); console.log(copy); // { foo:1, get bar() { return 2 } }
3.2、Object.create(proto, [propertiesObject]):使用指定的原型對象及其屬性去創建一個新的對象。
例外:如果proto參數不是 null 或一個對象,則拋出一個 TypeError 異常。
/* * 使用Object.create()來實現類式繼承 */ // Shape - 父類(superclass) function Shape() { this.x = 0; this.y = 0; } // 父類的方法 Shape.prototype.move = function(x, y) { this.x += x; this.y += y; console.info('Shape moved.'); }; // Rectangle - 子類(subclass) function Rectangle() { Shape.call(this); // call super constructor. } // 子類續承父類 Rectangle.prototype = Object.create(Shape.prototype); Rectangle.prototype.constructor = Rectangle; // 因為使用“.prototype =...”后,constructor會改變為“=...”的那個 // constructor,所以要重新指定.constructor 為自身。 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.' /* * 如果你希望能繼承到多個對象,則可以使用混入的方式 */ function MyClass() { SuperClass.call(this); OtherSuperClass.call(this); } // 繼承一個類 MyClass.prototype = Object.create(SuperClass.prototype); // 混合其它 Object.assign(MyClass.prototype, OtherSuperClass.prototype); // 重新指定constructor MyClass.prototype.constructor = MyClass; MyClass.prototype.myMethod = function() { // do a thing }; /* * 使用 Object.create 的 propertyObject參數 */ var o; // 創建一個原型為null的空對象 o = Object.create(null); o = {}; // 以字面量方式創建的空對象就相當於: o = Object.create(Object.prototype); o = Object.create(Object.prototype, { // foo會成為所創建對象的數據屬性 foo: { writable:true, configurable:true, value: "hello" }, // bar會成為所創建對象的訪問器屬性 bar: { configurable: false, get: function() { return 10 }, set: function(value) { console.log("Setting `o.bar` to", value); } } }); function Constructor(){} o = new Constructor(); // 上面的一句就相當於: o = Object.create(Constructor.prototype); // 當然,如果在Constructor函數中有一些初始化代碼,Object.create不能執行那些代碼 // 創建一個以另一個空對象為原型,且擁有一個屬性p的對象 o = Object.create({}, { p: { value: 42 } }) // 省略了的屬性特性默認為false,所以屬性p是不可寫,不可枚舉,不可配置的: o.p = 24 o.p //42 o.q = 12 for (var prop in o) { console.log(prop) } //"q" delete o.p //false //創建一個可寫的,可枚舉的,可配置的屬性p o2 = Object.create({}, { p: { value: 42, writable: true, enumerable: true, configurable: true } });
3.3、Object.defineProperty():直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。
如果對象中不存在指定的屬性,Object.defineProperty()
就創建這個屬性。當描述符中省略某些字段時,這些字段將使用它們的默認值。擁有布爾值的字段的默認值都是false
。value
,get
和set
字段的默認值為undefined
。一個沒有get/set/value/writable
定義的屬性被稱為“通用的”,並被“鍵入”為一個數據描述符。
/* * 創建屬性 */ var o = {}; // 創建一個新對象 // 在對象中添加一個屬性與數據描述符的示例 Object.defineProperty(o, "a", { value : 37, writable : true, enumerable : true, configurable : true }); // 對象o擁有了屬性a,值為37 // 在對象中添加一個屬性與存取描述符的示例 var bValue; 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的值現在總是與bValue相同,除非重新定義o.b // 數據描述符和存取描述符不能混合使用 Object.defineProperty(o, "conflict", { value: 0x9f91102, get: function() { return 0xdeadbeef; } }); // throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors /* * 修改屬性 * Writable 屬性:當writable屬性設置為false時,該屬性被稱為“不可寫”。它不能被重新分配。 */ var o = {}; // Creates a new object Object.defineProperty(o, 'a', { value: 37, writable: false }); console.log(o.a); // logs 37 o.a = 25; // No error thrown // (it would throw in strict mode, // even if the value had been the same) console.log(o.a); // logs 37. The assignment didn't work. // strict mode (function() { 'use strict'; var o = {}; Object.defineProperty(o, 'b', { value: 2, writable: false }); o.b = 3; // throws TypeError: "b" is read-only return o.b; // returns 2 without the line above }()); /* * 修改屬性 * Enumerable 特性:enumerable定義了對象的屬性是否可以在 for...in 循環和 Object.keys() 中被枚舉 */ var o = {}; Object.defineProperty(o, "a", { value : 1, enumerable:true }); Object.defineProperty(o, "b", { value : 2, enumerable:false }); Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false o.d = 4; // 如果使用直接賦值的方式創建對象的屬性,則這個屬性的enumerable為true for (var i in o) { console.log(i); } // 打印 'a' 和 'd' (in undefined order) Object.keys(o); // ["a", "d"] o.propertyIsEnumerable('a'); // true o.propertyIsEnumerable('b'); // false o.propertyIsEnumerable('c'); // false /* * 修改屬性 * Configurable 特性:configurable特性表示對象的屬性是否可以被刪除,以及除writable特性外的其他特性是否可以被修改。 */ var o = {}; Object.defineProperty(o, "a", { get : function(){return 1;}, configurable : false } ); // throws a TypeError Object.defineProperty(o, "a", {configurable : true}); // throws a TypeError Object.defineProperty(o, "a", {enumerable : true}); // throws a TypeError (set was undefined previously) Object.defineProperty(o, "a", {set : function(){}}); // throws a TypeError (even though the new get does exactly the same thing) Object.defineProperty(o, "a", {get : function(){return 1;}}); // throws a TypeError Object.defineProperty(o, "a", {value : 12}); console.log(o.a); // logs 1 delete o.a; // Nothing happens console.log(o.a); // logs 1 /* * 添加多個屬性和默認值 */ var o = {}; o.a = 1; // 等同於 : Object.defineProperty(o, "a", { value : 1, writable : true, configurable : true, enumerable : true }); // 另一方面, Object.defineProperty(o, "a", { value : 1 }); // 等同於 : Object.defineProperty(o, "a", { value : 1, writable : false, configurable : false, enumerable : false }); /* * 一般的 Setters 和 Getters */ function Archiver() { var temperature = null; var archive = []; Object.defineProperty(this, 'temperature', { get: function() { console.log('get!'); return temperature; }, set: function(value) { temperature = value; archive.push({ val: temperature }); } }); this.getArchive = function() { return archive; }; } var arc = new Archiver(); arc.temperature; // 'get!' arc.temperature = 11; arc.temperature = 13; arc.getArchive(); // [{ val: 11 }, { val: 13 }] 或者 var pattern = { get: function () { return 'I alway return this string,whatever you have assigned'; }, set: function () { this.myname = 'this is my name string'; } }; function TestDefineSetAndGet() { Object.defineProperty(this, 'myproperty', pattern); } var instance = new TestDefineSetAndGet(); instance.myproperty = 'test'; // 'I alway return this string,whatever you have assigned' console.log(instance.myproperty); // 'this is my name string' console.log(instance.myname);
3.4、Object.entries():返回一個數組,其元素是與直接在object上找到的可枚舉屬性鍵值對相對應的數組。屬性的順序與通過手動循環對象的屬性值所給出的順序相同。
const obj = { foo: 'bar', baz: 42 }; console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ] // array like object const obj = { 0: 'a', 1: 'b', 2: 'c' }; console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ] // array like object with random key ordering const anObj = { 100: 'a', 2: 'b', 7: 'c' }; console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ] // getFoo is property which isn't enumerable const myObj = Object.create({}, { getFoo: { value() { return this.foo; } } }); myObj.foo = 'bar'; console.log(Object.entries(myObj)); // [ ['foo', 'bar'] ] // non-object argument will be coerced to an object console.log(Object.entries('foo')); // [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ] // iterate through key-value gracefully const obj = { a: 5, b: 7, c: 9 }; for (const [key, value] of Object.entries(obj)) { console.log(`${key} ${value}`); // "a 5", "b 7", "c 9" } // Or, using array extras Object.entries(obj).forEach(([key, value]) => { console.log(`${key} ${value}`); // "a 5", "b 7", "c 9" }); /* * 將Object轉換為Map */ var obj = { foo: "bar", baz: 42 }; var map = new Map(Object.entries(obj)); console.log(map); // Map { foo: "bar", baz: 42 }
3.5、Object.freeze() :可以凍結一個對象,凍結指的是不能向這個對象添加新的屬性,不能修改其已有屬性的值,不能刪除已有屬性,以及不能修改該對象已有屬性的可枚舉性、可配置性、可寫性。也就是說,這個對象永遠是不可變的。該方法返回被凍結的對象。
/* * 凍結對象 */ var obj = { prop: function() {}, foo: 'bar' }; // 新的屬性會被添加, 已存在的屬性可能 // 會被修改或移除 obj.foo = 'baz'; obj.lumpy = 'woof'; delete obj.prop; // 作為參數傳遞的對象與返回的對象都被凍結 // 所以不必保存返回的對象(因為兩個對象全等) var o = Object.freeze(obj); o === obj; // true Object.isFrozen(obj); // === true // 現在任何改變都會失效 obj.foo = 'quux'; // 靜默地不做任何事 // 靜默地不添加此屬性 obj.quaxxor = 'the friendly duck'; // 在嚴格模式,如此行為將拋出 TypeErrors function fail(){ 'use strict'; obj.foo = 'sparky'; // throws a TypeError delete obj.quaxxor; // throws a TypeError obj.sparky = 'arf'; // throws a TypeError } fail(); // 試圖通過 Object.defineProperty 更改屬性 // 下面兩個語句都會拋出 TypeError. Object.defineProperty(obj, 'ohai', { value: 17 }); Object.defineProperty(obj, 'foo', { value: 'eit' }); // 也不可能設置屬性 // 下面兩個語句都會拋出 TypeError. Object.setPrototypeOf(obj, { x: 20 }) obj.__proto__ = { x: 20 } /* * 凍結數組 */ // 凍結的數組像一個元組。 // 元組(tuple):與數組類似,但元組一旦初始化就不能修改 let a=[0]; Object.freeze(a); // 數組a現在不能被更改了. a[0]=1; a.push(2); // a=[0] // 凍結的數組可以正常地拆包. let b, c; [b,c]=Object.freeze([1,2]); // b=1, c=2 /* * 不是常量的凍結對象(淺凍結) */ obj1 = { internal: {} }; Object.freeze(obj1); obj1.internal.a = 'aValue'; obj1.internal.a // 'aValue' /* * 深凍結函數. */ function deepFreeze(obj) { // 取回定義在obj上的屬性名 var propNames = Object.getOwnPropertyNames(obj); // 在凍結自身之前凍結屬性 propNames.forEach(function(name) { var prop = obj[name]; // 如果prop是個對象,凍結它 if (typeof prop == 'object' && prop !== null) deepFreeze(prop); }); // 凍結自身(no-op if already frozen) return Object.freeze(obj); } obj2 = { internal: {} }; deepFreeze(obj2); obj2.internal.a = 'anotherValue'; obj2.internal.a; // undefined
3.6、Object.getOwnPropertyDescriptor() :返回指定對象上一個自有屬性對應的屬性描述符。(自有屬性指的是直接賦予該對象的屬性,不需要從原型鏈上進行查找的屬性)
該方法允許對一個屬性的描述進行檢索。在 Javascript 中, 屬性 由一個字符串類型的“名字”(name)和一個“屬性描述符”(property descriptor)對象構成。更多關於屬性描述符類型以及他們屬性的信息可以查看:Object.defineProperty.
var o, d; o = { get foo() { return 17; } }; d = Object.getOwnPropertyDescriptor(o, "foo"); // d { // configurable: true, // enumerable: true, // get: /*the getter function*/, // set: undefined // } o = { bar: 42 }; d = Object.getOwnPropertyDescriptor(o, "bar"); // d { // configurable: true, // enumerable: true, // value: 42, // writable: true // } o = {}; Object.defineProperty(o, "baz", { value: 8675309, writable: false, enumerable: false }); d = Object.getOwnPropertyDescriptor(o, "baz"); // d { // value: 8675309, // writable: false, // enumerable: false, // configurable: false // }
3.7、Object.getOwnPropertyNames():返回一個由指定對象的所有自身屬性的屬性名(包括不可枚舉屬性但不包括Symbol值作為名稱的屬性)組成的數組。
Object.getOwnPropertyNames() 返回一個數組,該數組對元素是 obj自身擁有的枚舉或不可枚舉屬性名稱字符串。 數組中枚舉屬性的順序與通過 for...in 循環(或 Object.keys)迭代該對象屬性時一致。數組中不可枚舉屬性的順序未定義。
/* * 使用 Object.getOwnPropertyNames() */ var arr = ["a", "b", "c"]; console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"] // 類數組對象 var obj = { 0: "a", 1: "b", 2: "c"}; console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"] // 使用Array.forEach輸出屬性名和屬性值 Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) { console.log(val + " -> " + obj[val]); }); // 輸出 // 0 -> a // 1 -> b // 2 -> c //不可枚舉屬性 var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; }, enumerable: false } }); my_obj.foo = 1; console.log(Object.getOwnPropertyNames(my_obj).sort()); // ["foo", "getFoo"] /* * 只獲取不可枚舉的屬性 */ var target = myObject; var enum_and_nonenum = Object.getOwnPropertyNames(target); var enum_only = Object.keys(target); var nonenum_only = enum_and_nonenum.filter(function(key) { var indexInEnum = enum_only.indexOf(key); if (indexInEnum == -1) { // 沒有發現在enum_only健集中意味着這個健是不可枚舉的, // 因此返回true 以便讓它保持在過濾結果中 return true; } else { return false; } }); console.log(nonenum_only);
3.8、Object.getOwnPropertySymbols() :返回一個給定對象自身的所有 Symbol 屬性的數組。
與Object.getOwnPropertyNames()
類似,您可以將給定對象的所有符號屬性作為 Symbol 數組獲取。 請注意,Object.getOwnPropertyNames()
本身不包含對象的 Symbol 屬性,只包含字符串屬性。
因為所有的對象在初始化的時候不會包含任何的 Symbol,除非你在對象上賦值了 Symbol 否則Object.getOwnPropertySymbols()
只會返回一個空的數組。
var obj = {}; var a = Symbol("a"); var b = Symbol.for("b"); obj[a] = "localSymbol"; obj[b] = "globalSymbol"; var objectSymbols = Object.getOwnPropertySymbols(obj); console.log(objectSymbols.length); // 2 console.log(objectSymbols) // [Symbol(a), Symbol(b)] console.log(objectSymbols[0]) // Symbol(a)
3.9、Object.getPrototypeOf() :返回指定對象的原型(內部[[Prototype]]屬性的值)
var proto = {}; var obj = Object.create(proto); Object.getPrototypeOf(obj) === proto; // true var reg = /a/; Object.getPrototypeOf(reg) === RegExp.prototype; // true JavaScript中的 Object 是構造函數(創建對象的包裝器)。 一般用法是: var obj = new Object(); 所以: Object.getPrototypeOf( Object ); // ƒ () { [native code] } Object.getPrototypeOf( Function ); // ƒ () { [native code] } Object.getPrototypeOf( Object ) === Function.prototype; // true Object.getPrototypeOf( Object )是把Object這一構造函數看作對象, 返回的當然是函數對象的原型,也就是 Function.prototype。 正確的方法是,Object.prototype是構造出來的對象的原型。 var obj = new Object(); Object.prototype === Object.getPrototypeOf( obj ); // true Object.prototype === Object.getPrototypeOf( {} ); // true Object.getPrototypeOf('foo'); // TypeError: "foo" is not an object (ES5 code) Object.getPrototypeOf('foo'); // String.prototype (ES2015 code)
3.10、Object.is():判斷兩個值是否是相同的值
Object.is()
判斷兩個值是否相同。如果下列任何一項成立,則兩個值相同:
這種相等性判斷邏輯和傳統的 ==
運算符所用的不同,==
運算符會對它兩邊的操作數做隱式類型轉換(如果它們類型不同),然后才進行相等性比較,(所以才會有類似 "" == false
為 true
的現象),但 Object.is
不會做這種類型轉換。
這與===
運算符也不一樣。===
運算符(和==
運算符)將數字值-0
和+0
視為相等,並認為Number.NaN
不等於NaN
。
Object.is('foo', 'foo'); // true Object.is(window, window); // true Object.is('foo', 'bar'); // false Object.is([], []); // false var test = { a: 1 }; Object.is(test, test); // true Object.is(null, null); // true // 特例 Object.is(0, -0); // false Object.is(-0, -0); // true Object.is(NaN, 0/0); //true
3.11、Object.isExtensible() :判斷一個對象是否是可擴展的(是否可以在它上面添加新的屬性)
默認情況下,對象是可擴展的:即可以為他們添加新的屬性。以及它們的 __proto__
屬性可以被更改。Object.preventExtensions
,Object.seal
或 Object.freeze
方法都可以標記一個對象為不可擴展(non-extensible)。
// 新對象默認是可擴展的. var empty = {}; Object.isExtensible(empty); // === true // ...可以變的不可擴展. Object.preventExtensions(empty); Object.isExtensible(empty); // === false // 密封對象是不可擴展的. var sealed = Object.seal({}); Object.isExtensible(sealed); // === false // 凍結對象也是不可擴展. var frozen = Object.freeze({}); Object.isExtensible(frozen); // === false Object.isExtensible(1); // TypeError: 1 is not an object (ES5 code) Object.isExtensible(1); // false (ES6 code)
3.12、Object.isFrozen():判斷一個對象是否被凍結
一個對象是凍結的是指它不可擴展,所有屬性都是不可配置的,且所有數據屬性(即沒有getter或setter組件的訪問器的屬性)都是不可寫的。
// 一個對象默認是可擴展的,所以它也是非凍結的. Object.isFrozen({}); // === false // 一個不可擴展的空對象同時也是一個凍結對象. var vacuouslyFrozen = Object.preventExtensions({}); Object.isFrozen(vacuouslyFrozen) //=== true; // 一個非空對象默認也是非凍結的. var oneProp = { p: 42 }; Object.isFrozen(oneProp) //=== false // 讓這個對象變的不可擴展,並不意味着這個對象變成了凍結對象, // 因為p屬性仍然是可以配置的(而且可寫的). Object.preventExtensions(oneProp); Object.isFrozen(oneProp) //=== false // ...如果刪除了這個屬性,則它會成為一個凍結對象. delete oneProp.p; Object.isFrozen(oneProp) //=== true // 一個不可擴展的對象,擁有一個不可寫但可配置的屬性,則它仍然是非凍結的. var nonWritable = { e: "plep" }; Object.preventExtensions(nonWritable); Object.defineProperty(nonWritable, "e", { writable: false }); // 變得不可寫 Object.isFrozen(nonWritable) //=== false // 把這個屬性改為不可配置,會讓這個對象成為凍結對象. Object.defineProperty(nonWritable, "e", { configurable: false }); // 變得不可配置 Object.isFrozen(nonWritable) //=== true // 一個不可擴展的對象,擁有一個不可配置但可寫的屬性,則它仍然是非凍結的. var nonConfigurable = { release: "the kraken!" }; Object.preventExtensions(nonConfigurable); Object.defineProperty(nonConfigurable, "release", { configurable: false }); Object.isFrozen(nonConfigurable) //=== false // 把這個屬性改為不可寫,會讓這個對象成為凍結對象. Object.defineProperty(nonConfigurable, "release", { writable: false }); Object.isFrozen(nonConfigurable) //=== true // 一個不可擴展的對象,值擁有一個訪問器屬性,則它仍然是非凍結的. var accessor = { get food() { return "yum"; } }; Object.preventExtensions(accessor); Object.isFrozen(accessor) //=== false // ...但把這個屬性改為不可配置,會讓這個對象成為凍結對象. Object.defineProperty(accessor, "food", { configurable: false }); Object.isFrozen(accessor) //=== true // 使用Object.freeze是凍結一個對象最方便的方法. var frozen = { 1: 81 }; Object.isFrozen(frozen) //=== false Object.freeze(frozen); Object.isFrozen(frozen) //=== true // 一個凍結對象也是一個密封對象. Object.isSealed(frozen) //=== true // 當然,更是一個不可擴展的對象. Object.isExtensible(frozen) //=== false Object.isFrozen(1); // TypeError: 1 is not an object (ES5 code) Object.isFrozen(1); // true (ES2015 code)
3.13、Object.isSealed() :判斷一個對象是否被密封
如果這個對象是密封的,則返回 true,否則返回 false。密封對象是指那些不可 擴展 的,且所有自身屬性都不可配置且因此不可刪除(但不一定是不可寫)的對象。
// 新建的對象默認不是密封的. var empty = {}; Object.isSealed(empty); // === false // 如果你把一個空對象變的不可擴展,則它同時也會變成個密封對象. Object.preventExtensions(empty); Object.isSealed(empty); // === true // 但如果這個對象不是空對象,則它不會變成密封對象,因為密封對象的所有自身屬性必須是不可配置的. var hasProp = { fee: "fie foe fum" }; Object.preventExtensions(hasProp); Object.isSealed(hasProp); // === false // 如果把這個屬性變的不可配置,則這個對象也就成了密封對象. Object.defineProperty(hasProp, "fee", { configurable: false }); Object.isSealed(hasProp); // === true // 最簡單的方法來生成一個密封對象,當然是使用Object.seal. var sealed = {}; Object.seal(sealed); Object.isSealed(sealed); // === true // 一個密封對象同時也是不可擴展的. Object.isExtensible(sealed); // === false // 一個密封對象也可以是一個凍結對象,但不是必須的. Object.isFrozen(sealed); // === true ,所有的屬性都是不可寫的 var s2 = Object.seal({ p: 3 }); Object.isFrozen(s2); // === false, 屬性"p"可寫 var s3 = Object.seal({ get p() { return 0; } }); Object.isFrozen(s3); // === true ,訪問器屬性不考慮可寫不可寫,只考慮是否可配置 Object.isSealed(1); // TypeError: 1 is not an object (ES5 code) Object.isSealed(1); // true (ES2015 code)
3.14、 Object.keys() :返回一個由一個給定對象的自身可枚舉屬性組成的數組,數組中屬性名的排列順序和使用 for...in 循環遍歷該對象時返回的順序一致 (兩者的主要區別是 一個 for-in 循環還會枚舉其原型鏈上的屬性)。
Object.keys 返回一個所有元素為字符串的數組,其元素來自於從給定的對象上面可直接枚舉的屬性。這些屬性的順序與手動遍歷該對象屬性時的一致。
/* Array 對象 */ let arr = ["a", "b", "c"]; console.log(Object.keys(arr)); // ['0', '1', '2'] /* Object 對象 */ let obj = { foo: "bar", baz: 42 }, keys = Object.keys(obj); // CCAC: Chrome Console Auto Copy copy(keys); // ["foo","baz"] /* 類數組 對象 */ let obj = { 0 : "a", 1 : "b", 2 : "c"}; console.log(Object.keys(obj)); // ['0', '1', '2'] 注意: var obj = { 0:"a", 1:"b", 2:{3:"c", 4:"d"}}; console.log(Object.keys(obj)); // (3) ["0", "1", "2"] // 類數組 對象, 隨機 key 排序 let anObj = { 100: 'a', 2: 'b', 7: 'c' }; console.log(Object.keys(anObj)); // ['2', '7', '100'] /* getFoo 是個不可枚舉的屬性 */ var my_obj = Object.create( {}, { getFoo : { value : function () { return this.foo } } } ); my_obj.foo = 1; console.log(Object.keys(my_obj)); // ['foo'] Object.keys("foo"); // TypeError: "foo" is not an object (ES5 code) Object.keys("foo"); // ["0", "1", "2"] (ES2015 code)
3.15、Object.preventExtensions():讓一個對象變的不可擴展,也就是永遠不能再添加新的屬性。
如果一個對象可以添加新的屬性,則這個對象是可擴展的。Object.preventExtensions()將對象標記為不再可擴展,因此它將永遠不會具有超出它被標記為不可擴展的屬性。注意,一般來說,不可擴展對象的屬性可能仍然可被刪除。嘗試將新屬性添加到不可擴展對象將靜默失敗或拋出TypeError(最常見但不排除其他情況,如在strict mode中)。
Object.preventExtensions()僅阻止添加自身的屬性。但屬性仍然可以添加到對象原型。
一旦使其不可擴展,就無法再對象進行擴展。
// Object.preventExtensions將原對象變的不可擴展,並且返回原對象. var obj = {}; var obj2 = Object.preventExtensions(obj); obj === obj2; // true // 字面量方式定義的對象默認是可擴展的. var empty = {}; Object.isExtensible(empty) //=== true // ...但可以改變. Object.preventExtensions(empty); Object.isExtensible(empty) //=== false // 使用Object.defineProperty方法為一個不可擴展的對象添加新屬性會拋出異常. var nonExtensible = { removable: true }; Object.preventExtensions(nonExtensible); Object.defineProperty(nonExtensible, "new", { value: 8675309 }); // 拋出TypeError異常 // 在嚴格模式中,為一個不可擴展對象的新屬性賦值會拋出TypeError異常. function fail() { "use strict"; nonExtensible.newProperty = "FAIL"; // throws a TypeError } fail(); // 一個不可擴展對象的原型是不可更改的,__proto__是個非標准魔法屬性,可以更改一個對象的原型. var fixed = Object.preventExtensions({}); fixed.__proto__ = { oh: "hai" }; // 拋出TypeError異常 Object.preventExtensions(1); // TypeError: 1 is not an object (ES5 code) Object.preventExtensions(1); // 1 (ES2015 code)
3.16、Object.seal() :可以讓一個對象密封,並返回被密封后的對象。密封對象將會阻止向對象添加新的屬性,並且會將所有已有屬性的可配置性(configurable)置為不可配置(false),即不可修改屬性的描述或刪除屬性。但是可寫性描述(writable)為可寫(true)的屬性的值仍然可以被修改。
通常情況下,一個對象是可擴展的(可以添加新的屬性)。密封一個對象會讓這個對象變的不能添加新屬性,且所有已有屬性會變的不可配置。屬性不可配置的效果就是屬性變的不可刪除,以及一個數據屬性不能被重新定義成為訪問器屬性,或者反之。但屬性的值仍然可以修改。嘗試刪除一個密封對象的屬性或者將某個密封對象的屬性從數據屬性轉換成訪問器屬性,結果會靜默失敗或拋出TypeError 異常(嚴格模式)。
不會影響從原型鏈上繼承的屬性。但 __proto__ ( ) 屬性的值也會不能修改。
var obj = { prop: function () {}, foo: "bar" }; // 可以添加新的屬性,已有屬性的值可以修改,可以刪除 obj.foo = "baz"; obj.lumpy = "woof"; delete obj.prop; var o = Object.seal(obj); assert(o === obj); assert(Object.isSealed(obj) === true); // 仍然可以修改密封對象上的屬性的值. obj.foo = "quux"; // 但你不能把一個數據屬性重定義成訪問器屬性. Object.defineProperty(obj, "foo", { get: function() { return "g"; } }); // 拋出TypeError異常 // 現在,任何屬性值以外的修改操作都會失敗. obj.quaxxor = "the friendly duck"; // 靜默失敗,新屬性沒有成功添加 delete obj.foo; // 靜默失敗,屬性沒有刪除成功 // ...在嚴格模式中,會拋出TypeError異常 function fail() { "use strict"; delete obj.foo; // 拋出TypeError異常 obj.sparky = "arf"; // 拋出TypeError異常 } fail(); // 使用Object.defineProperty方法同樣會拋出異常 Object.defineProperty(obj, "ohai", { value: 17 }); // 拋出TypeError異常 Object.defineProperty(obj, "foo", { value: "eit" }); // 成功將原有值改變
3.17、Object.setPrototypeOf() :設置一個指定的對象的原型 ( 即, 內部[[Prototype]]屬性)到另一個對象或 null
如果對象的[[Prototype]]被修改成不可擴展(通過 Object.isExtensible()查看),就會拋出 TypeError異常。如果prototype參數不是一個對象或者null(例如,數字,字符串,boolean,或者 undefined),則什么都不做。否則,該方法將obj的[[Prototype]]修改為新的值。
Object.setPrototypeOf()是ECMAScript 6最新草案中的方法,相對於 Object.prototype.__proto__ ,它被認為是修改對象原型更合適的方法。
var dict = Object.setPrototypeOf({}, null); /* * 向一個原型附加一個鏈 */ function Mammal() { this.isMammal = 'yes'; } function MammalSpecies(sMammalSpecies) { this.species = sMammalSpecies; } MammalSpecies.prototype = new Mammal(); MammalSpecies.prototype.constructor = MammalSpecies; var oCat = new MammalSpecies('Felis'); console.log(oCat.isMammal); // 'yes' function Animal() { this.breathing = 'yes'; } Object.appendChain(oCat, new Animal()); console.log(oCat.breathing); // 'yes' /* * 將一個基本類型轉化為對應的對象類型並添加到原型鏈上 */ function Symbol() { this.isSymbol = 'yes'; } var nPrime = 17; console.log(typeof nPrime); // 'number' var oPrime = Object.appendChain(nPrime, new Symbol()); console.log(oPrime); // '17' console.log(oPrime.isSymbol); // 'yes' console.log(typeof oPrime); // 'object' /* * 給函數類型的對象添加一個鏈,並添加一個新的方法到那個鏈上 */ function Person(sName) { this.identity = sName; } var george = Object.appendChain(new Person('George'), 'console.log("Hello guys!!");'); console.log(george.identity); // 'George' george(); // 'Hello guys!!'
3.18、Object.values():返回一個給定對象自己的所有可枚舉屬性值的數組,值的順序與使用for...in循環的順序相同 ( 區別在於 for-in 循環枚舉原型鏈中的屬性 )
Object.values()返回一個數組,其元素是在對象上找到的可枚舉屬性值。屬性的順序與通過手動循環對象的屬性值所給出的順序相同。
var obj = { foo: "bar", baz: 42 }; console.log(Object.values(obj)); // ['bar', 42] // 類數組對象 var obj = { 0: 'a', 1: 'b', 2: 'c' }; console.log(Object.values(obj)); // ['a', 'b', 'c'] 注意: var obj = { 0: 'a', 1: 'b', 2: { 3: 'c' , 4: 'd' } }; console.log(Object.values(obj)); // ["a", "b", {…}] 內置的對象沒有擴展成數組 // 隨機鍵值的類數組對象 var an_obj = { 100: 'a', 2: 'b', 7: 'c' }; console.log(Object.values(an_obj)); // ['b', 'c', 'a'] // getFoo 是不可枚舉屬性 var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; } } }); my_obj.foo = "bar"; console.log(Object.values(my_obj)); // ['bar'] // 參數是非對象會轉變成對象 console.log(Object.values("foo")); // ['f', 'o', 'o']
4、Object 實例和Object 原型對象:
JavaScript中的所有對象都來自Object;所有對象從Object.prototype繼承方法和屬性,盡管它們可能被覆蓋。例如,其他構造函數的原型將覆蓋constructor屬性並提供自己的toString()方法。Object原型對象的更改將傳播到所有對象,除非受到這些更改的屬性和方法將沿原型鏈進一步覆蓋。
4.1、屬性:Object.prototype.constructor:返回創建實例對象的 Object 構造函數的引用。注意,此屬性的值是對函數本身的引用,而不是一個包含函數名稱的字符串。對原始類型來說,如1,true和"test",該值只可讀。
// 所有對象都會從它的原型上繼承一個 constructor 屬性: var o = {}; o.constructor === Object; // true var o = new Object; o.constructor === Object; // true var a = []; a.constructor === Array; // true var a = new Array; a.constructor === Array // true var n = new Number(3); n.constructor === Number; // true /* * 打印一個對象的構造函數 * 創建一個原型,Tree,以及該類型的對象,即theTree。 然后打印theTree對象的constructor屬性。 */ function Tree(name) { this.name = name; } var theTree = new Tree("Redwood"); console.log( "theTree.constructor is " + theTree.constructor ); // theTree.constructor is function Tree(name) { // this.name = name; // } /* * 改變對象的 constructor * 顯示如何修改泛型對象的構造函數值。 只有true, 1 和"test"不受影響,因為他們有只讀的原生構造函數。 這個例子也表明依靠對象的constructor屬性並不總是安全的。 */ function Type() { }; var types = [ new Array, [], new Boolean, true, // remains unchanged new Date, new Error, new Function, function(){}, Math, new Number, 1, // remains unchanged new Object, {}, new RegExp, /(?:)/, new String, "test" // remains unchanged ]; for(var i = 0; i < types.length; i++) { types[i].constructor = Type; types[i] = [ types[i].constructor, types[i] instanceof Type, types[i].toString() ]; }; console.log( types.join("\n") ); // 輸出結果: /* function Type() {},false, function Type() {},false, function Type() {},false,false function Boolean() { [native code] },false,true function Type() {},false,Mon Sep 01 2014 16:03:49 GMT+0600 function Type() {},false,Error function Type() {},false,function anonymous() { } function Type() {},false,function () {} function Type() {},false,[object Math] function Type() {},false,0 function Number() { [native code] },false,1 function Type() {},false,[object Object] function Type() {},false,[object Object] function Type() {},false,/(?:)/ function Type() {},false,/(?:)/ function Type() {},false, function String() { [native code] },false,test */
4.2、方法:Object.prototype.hasOwnProperty():返回一個布爾值,指示對象自身屬性中是否具有指定的屬性
所有繼承了 Object 的對象都會繼承到 hasOwnProperty 方法。這個方法可以用來檢測一個對象是否含有特定的自身屬性;和 in 運算符不同,該方法會忽略掉那些從原型鏈上繼承到的屬性。
// 使用 hasOwnProperty 方法判斷屬性是否存在 // 檢測了對象 o 是否含有自身屬性 prop: o = new Object(); o.prop = 'exists'; function changeO() { o.newprop = o.prop; delete o.prop; } o.hasOwnProperty('prop'); // 返回 true changeO(); o.hasOwnProperty('prop'); // 返回 false // 自身屬性與繼承屬性 // 下面的例子演示了 hasOwnProperty 方法對待自身屬性和繼承屬性的區別: o = new Object(); o.prop = 'exists'; o.hasOwnProperty('prop'); // 返回 true o.hasOwnProperty('toString'); // 返回 false o.hasOwnProperty('hasOwnProperty'); // 返回 false // 遍歷一個對象的所有自身屬性 // 下面的例子演示了如何在遍歷一個對象的所有屬性時忽略掉繼承屬性,注意這里 for...in 循環只會遍歷可枚舉屬性,
// 所以不應該基於這個循環中沒有不可枚舉的屬性而得出 hasOwnProperty 是嚴格限制於可枚舉項目的(如同 Object.getOwnPropertyNames())。 var buz = { fog: 'stack' }; for (var name in buz) { if (buz.hasOwnProperty(name)) { alert("this is fog (" + name + ") for sure. Value: " + buz[name]); } else { alert(name); // toString or something else } } // 使用 hasOwnProperty 作為屬性名 // JavaScript 並沒有保護 hasOwnProperty 屬性名,因此某個對象是有可能存在使用這個屬性名的屬性,使用外部的 hasOwnProperty 獲得正確的結果是需要的: var foo = { hasOwnProperty: function() { return false; }, bar: 'Here be dragons' }; foo.hasOwnProperty('bar'); // 始終返回 false // 如果擔心這種情況,可以直接使用原型鏈上真正的 hasOwnProperty 方法 ({}).hasOwnProperty.call(foo, 'bar'); // true // 也可以使用 Object 原型上的 hasOwnProperty 屬性 Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
4.3、Object.prototype.isPrototypeOf():用於測試一個對象是否存在於另一個對象的原型鏈上
// 本示例展示了 Baz.prototype, Bar.prototype, Foo.prototype 和 Object.prototype 在 baz 對象的原型鏈上: function Foo() {} function Bar() {} function Baz() {} Bar.prototype = Object.create(Foo.prototype); Baz.prototype = Object.create(Bar.prototype); var baz = new Baz(); console.log(Baz.prototype.isPrototypeOf(baz)); // true console.log(Bar.prototype.isPrototypeOf(baz)); // true console.log(Foo.prototype.isPrototypeOf(baz)); // true console.log(Object.prototype.isPrototypeOf(baz)); // true // 檢查 baz 對象是否繼承自 Foo.prototype: if (Foo.prototype.isPrototypeOf(baz)) { // do something safe }
4.4、Object.prototype.propertyIsEnumerable():返回一個布爾值,表示指定的屬性是否可枚舉
每個對象都有一個propertyIsEnumerable
方法。此方法可以確定對象中指定的屬性是否可以被for...in
循環枚舉,但是通過原型鏈繼承的屬性除外。如果對象沒有指定的屬性,則此方法返回false
。
// propertyIsEnumerable方法的基本用法 // 下面的例子演示了propertyIsEnumerable方法在普通對象和數組上的基本用法: var o = {}; var a = []; o.prop = 'is enumerable'; a[0] = 'is enumerable'; o.propertyIsEnumerable('prop'); // 返回 true a.propertyIsEnumerable(0); // 返回 true // 用戶自定義對象和引擎內置對象 // 下面的例子演示了用戶自定義對象和引擎內置對象上屬性可枚舉性的區別. var a = ['is enumerable']; a.propertyIsEnumerable(0); // 返回 true a.propertyIsEnumerable('length'); // 返回 false Math.propertyIsEnumerable('random'); // 返回 false this.propertyIsEnumerable('Math'); // 返回 false // 自身屬性和繼承屬性 var a = []; a.propertyIsEnumerable('constructor'); // 返回 false function firstConstructor() { this.property = 'is not enumerable'; } firstConstructor.prototype.firstMethod = function() {}; function secondConstructor() { this.method = function method() { return 'is enumerable'; }; } secondConstructor.prototype = new firstConstructor; secondConstructor.prototype.constructor = secondConstructor; var o = new secondConstructor(); o.arbitraryProperty = 'is enumerable'; o.propertyIsEnumerable('arbitraryProperty'); // 返回 true o.propertyIsEnumerable('method'); // 返回 true o.propertyIsEnumerable('property'); // 返回 false o.property = 'is enumerable'; o.propertyIsEnumerable('property'); // 返回 true // 這些返回fasle,是因為,在原型鏈上propertyIsEnumerable不被考慮 // (盡管最后兩個在for-in循環中可以被循環出來)。 o.propertyIsEnumerable('prototype'); // 返回 false (根據 JS 1.8.1/FF3.6) o.propertyIsEnumerable('constructor'); // 返回 false o.propertyIsEnumerable('firstMethod'); // 返回 false
4.5、Object.prototype.toLocaleString():返回一個該對象的字符串表示。此方法被用於派生對象為了特定語言環境的目的(locale-specific purposes)而重載使用
toLocaleString
返回調用 toString()
的結果。
該函數提供給對象一個通用的toLocaleString
方法,即使不是全部都可以使用它。 見下面的列表。
覆蓋 toLocaleString
的對象
Array
:Array.prototype.toLocaleString()
Number
:Number.prototype.toLocaleString()
Date
:Date.prototype.toLocaleString()
4.6、Object.prototype.toString(): 返回一個表示該對象的字符串
每個對象都有一個toString()
方法,當該對象被表示為一個文本值時,或者一個對象以預期的字符串方式引用時自動調用。默認情況下,toString()
方法被每個Object
對象繼承。如果此方法在自定義對象中未被覆蓋,toString()
返回 "[object type]",其中type
是對象的類型。以下代碼說明了這一點:
var o = new Object(); o.toString(); // returns [object Object]
(1)覆蓋默認的toString方法
// 可以自定義一個方法來取代默認的 toString() 方法。該 toString() 方法不能傳入參數並且必須返回一個字符串。
// 自定義的 toString() 方法可以是任何我們需要的值,但如果它附帶有關對象的信息,它將變的非常有用。 // 以下代碼定義了Dog對象類型,並創建了一個Dog類型的theDog對象: function Dog(name,breed,color,sex) { this.name=name; this.breed=breed; this.color=color; this.sex=sex; } var theDog = new Dog("Gabby","Lab","chocolate","female");
如果當前的對象調用了 toString()
方法,它將會返回從Object
繼承下來的 toString()
方法的返回默認值:
theDog.toString(); // returns [object Object]
下面的代碼中定義了一個叫做 dogToString()
的方法來覆蓋默認的 toString()
方法。這個方法生成一個 "property = value;
" 形式的字符串,該字符串包含了當前對象的 name, breed,color 和 sex 的值。
Dog.prototype.toString = function dogToString() { var ret = "Dog " + this.name + " is a " + this.sex + " " + this.color + " " + this.breed; return ret; }
使用上述代碼,任何時候在字符串上下文中使用theDog.toString()
,JavaScript 都會自動調用 dogToString()
方法(dogToString()
可以是一個匿名函數),並且返回以下字符串:
"Dog Gabby is a female chocolate Lab"
(2)使用toString()檢測對象類型
可以通過toString()
來獲取每個對象的類型。為了每個對象都能通過 Object.prototype.toString()
來檢測,需要以 Function.prototype.call()
或者 Function.prototype.apply()
的形式來調用,傳遞要檢查的對象作為第一個參數,稱為thisArg
。
var toString = Object.prototype.toString; toString.call(new Date); // [object Date] toString.call(new String); // [object String] toString.call(Math); // [object Math] //Since JavaScript 1.8.5 toString.call(undefined); // [object Undefined] toString.call(null); // [object Null]
4.7、Object.prototype.valueOf():返回指定對象的原始值
JavaScript調用valueOf
方法將對象轉換為原始值。你很少需要自己調用valueOf
方法;當遇到要預期的原始值的對象時,JavaScript會自動調用它。
默認情況下,valueOf
方法由Object
后面的每個對象繼承。 每個內置的核心對象都會覆蓋此方法以返回適當的值。如果對象沒有原始值,則valueOf
將返回對象本身。
JavaScript的許多內置對象都重寫了該函數,以實現更適合自身的功能需要。因此,不同類型對象的valueOf()方法的返回值和返回值類型均可能不同。
對象 | 返回值 |
---|---|
Array | 返回數組對象本身。 |
Boolean | 布爾值。 |
Date | 存儲的時間是從 1970 年 1 月 1 日午夜開始計的毫秒數 UTC。 |
Function | 函數本身。 |
Number | 數字值。 |
Object | 對象本身。這是默認情況。 |
String | 字符串值。 |
Math 和 Error 對象沒有 valueOf 方法。 |
你可以在自己的代碼中使用valueOf
將內置對象轉換為原始值。 創建自定義對象時,可以覆蓋Object.prototype.valueOf()
來調用自定義方法,而不是默認Object
方法。
/* * 使用 valueOf */ // Array:返回數組對象本身 var array = ["ABC", true, 12, -5]; console.log(array.valueOf() === array); // true // Date:當前時間距1970年1月1日午夜的毫秒數 var date = new Date(2013, 7, 18, 23, 11, 59, 230); console.log(date.valueOf()); // 1376838719230 // Number:返回數字值 var num = 15.26540; console.log(num.valueOf()); // 15.2654 // 布爾:返回布爾值true或false var bool = true; console.log(bool.valueOf() === bool); // true // new一個Boolean對象 var newBool = new Boolean(true); // valueOf()返回的是true,兩者的值相等 console.log(newBool.valueOf() == newBool); // true // 但是不全等,兩者類型不相等,前者是boolean類型,后者是object類型 console.log(newBool.valueOf() === newBool); // false // Function:返回函數本身 function foo(){} console.log( foo.valueOf() === foo ); // true var foo2 = new Function("x", "y", "return x + y;"); console.log( foo2.valueOf() ); /* ƒ anonymous(x,y ) { return x + y; } */ // Object:返回對象本身 var obj = {name: "張三", age: 18}; console.log( obj.valueOf() === obj ); // true // String:返回字符串值 var str = "http://www.xyz.com"; console.log( str.valueOf() === str ); // true // new一個字符串對象 var str2 = new String("http://www.xyz.com"); // 兩者的值相等,但不全等,因為類型不同,前者為string類型,后者為object類型 console.log( str2.valueOf() === str2 ); // false /* * 改寫 .prototype.valueof */ function MyNumberType(n) { this.number = n; } MyNumberType.prototype.valueOf = function() { return this.number; }; var myObj = new MyNumberType(4); myObj + 3; // 7
// 給定 undefined 和 null 類型使用 Object // 將一個空的 Object 對象存到 o 中 var o = new Object(); var o = new Object(undefined); var o = new Object(null); // 使用 Object 生成布爾對象 // 將Boolean 對象存到 o 中 // 等價於 o = new Boolean(true); var o = new Object(true); // 等價於 o = new Boolean(false); var o = new Object(Boolean());