建立物件
-
物件直接量
-
通過new建立物件
-
原型
- 物件直接量建立的物件具有同一個原型物件——Object.prototype獲得引用
- 通過關鍵字new和建構函式呼叫建立的物件——建構函式的prototype屬性的值
- Object.prototype物件沒有原型
- 內建建構函式的原型繼承自Object.prototype
-
Object.create()
- 第一個引數為原型
var o2 = Object.create(null) //o2不繼承任何屬性和方法
複製程式碼
var o3 = Object.create(Object.prototype) //普通的空物件
複製程式碼
- 通過任意原型建立新物件
function inherit(p){
if(p==null) throw TypeError()
if(Object.create){
return Object.create(p)
}
var t = typeof p
if(t != "object" && t !== "function") throw TypeError()
function f(){}
f.prototype = p
return new f()
}
複製程式碼
- inherit()函式放置庫函式修改物件屬性。只能修改繼承來的屬性,不改變原始物件。
屬性的查詢和設定
ECMAScript 3,點運算子的識別符號不能是保留字,但是可以用方括號訪問,如object["for"].ES 5放寬了限制
- 作為關聯陣列的物件
- js物件都是關聯陣列——通過字串索引而不是數字索引
- for/in迴圈遍歷關聯陣列
function getValue(object){
var key
for(key in object){
let val = object[key]
console.info(`key=${key},val=${val}`)
}
}
複製程式碼
- 繼承
- 原型鏈
var o ={}
o.x = 1
var p = inherit(o)
p.y = 2
var q = inherit(p)
q.z = 4
var s = q.toString() // 4 toString繼承自Object.prototype
q.x + q.y //3
複製程式碼
-
屬性查詢時會檢查原型鏈,設定屬性與繼承無關——總是在物件上建立屬性和賦值,不會修改原型鏈
-
例外:如果o繼承自屬性x,而這個屬性時一個具有setter方法的accessor屬性,那麼此時呼叫setter方法而不是給o建立一個屬性x——setter方法由物件o呼叫,而不是原型物件呼叫
-
屬性訪問錯誤
- 查詢不存在的屬性,返回undefined
book.title //undefined
複製程式碼
- 物件不存在會報錯,null和undefined值沒有屬性
book.title.length //error
//判空
var len
if(book){
if(book.title){
len = book.title.length
}
}
//常用——&&的短路行為
var len = book && book.title && book.title.length
複製程式碼
- 內建建構函式的原型是隻讀的——ES 5已修復該bug,在嚴格模式中,任何失敗的屬性設定會丟擲型別錯誤異常
Object.prototype = o //賦值失敗,不會報錯
複製程式碼
- 以下場景給物件o設定屬性p會失敗
- o中的屬性p是隻讀(defineProperty()方法中有一個例外,可以對可配置的只讀屬性重新賦值)
- o中的屬性p是繼承屬性,且它是隻讀的——不同通過同名自有屬性覆蓋只讀的繼承屬性
- o中不存在自有屬性p:o沒有使用setter方法繼承屬性p,並且o的可擴充套件性是false
刪除屬性
- delete可以刪除物件的屬性
delete object.property
delete object["property"]
複製程式碼
- delete只能刪除自由屬性,不能刪除繼承(影響原型)
- 成功返回true,如果delete後不是屬性訪問表示式,也返回true
//以下都返回true
delete o.x
delete o.x
delete o.toString
delete 1
複製程式碼
- delete不能刪除可配置性為false的屬性(可刪除不可擴充套件物件的可配置屬性)
- 某些內建物件的屬性是不可配置的,嚴格模式下,刪除不可配置屬性會報型別錯誤,非嚴格模式下,返回false
delete Object.prototype //不可配置
var x = 1
delete this.x //不能刪除
function f(){}
delete this.f //不能刪除
複製程式碼
- 非嚴格模式下刪除全域性物件的可配置屬性時,可省略全域性物件的引用;嚴格模式會報錯
檢測屬性
- 通過in,hasOwnProperty()和propertyIsEnumerable()判斷某個屬性是否在某個物件中
var o = {x:1}
"x" in o
"y" in o
"toString" in o
o.hasOwnProperty("x") //自有屬性
o.hasOwnProperty("y")
o.hasOwnProperty("toString")
複製程式碼
- propertyIsEnumerable只有檢測到是自有屬性且這個屬性的可列舉性為true才返回true
- 某些內建屬性是不可列舉的
- 通常由JavaScript程式碼建立的屬性都是可列舉的
- 使用"!=="判斷屬性是否是undefined
列舉屬性
- for/in遍歷物件中所有可列舉的屬性
for(p in 0){
if(!o.hasOwnProperty(p)) continue; //跳過繼承的屬性
}
for(p in o){
if(typeof o[p] === "function") continue; //跳過方法
}
複製程式碼
- extend,merge等方法
function extend(o, p){
for(prop in p){
o[prop] = p[prop]
}
return o
}
function merge(o, p){
for(prop in p){
if(o.hasOwnPropery(prop)){
continue;
}
o[prop] = p[prop]
}
return o
}
function restrict(o,p){
for(prop in o){
if(!(prop in p)){
delete o[prop]
}
}
return o
}
function subtract(o,p){
for(prop in p){
delete o[prop]
}
return o
}
function union(o,p){
return extend(extend({}, o), p)
}
function intersection(o, p){
return restrict(extend({}, o), p)
}
function keys(o){
if(typeof o !== "object") thorw TypeError()
var result = []
for( var prop in o){
if(o.hasOwnProperty(prop)){
result.push(prop)
}
}
return result
}
複製程式碼
- Object.keys()返回物件可列舉的自有屬性
- Object.getOwnPropertyNames()返回物件自有屬性的名稱
屬性getter和setter
- 由getter和setter定義的屬性稱作“存取器屬性”,不同於“資料屬性”
- 定義存取器屬性
var o ={
data_prop:value,
get accessor_prop() {},
set accessor_prop(value){}
}
複製程式碼
- 存取器屬性是可繼承的
- 自增序列號
var serialnum = {
$n:0, //私有屬性
get next() {
return this.$n++
},
set next(n){
if(n >= this.$n){
this.$n = n
} else {
throw "..."
}
}
}
複製程式碼
- 返回隨機數
var random = {
get octet() {
return Math.floor(Math.random() * 256)
}
get uint16(){
return Math.floor(Math.random() * 65536)
}
get int16(){
return Math.floor(Math.random() * 65526 - 32768)
}
}
複製程式碼
屬性的特性
- 一個屬性包含名字和四個特性
- 資料屬性的4個特性分別是值value,可寫性writable,可列舉型enumerable和可配置性configuratble
- 存取器屬性不具有值value和可寫性,它的可寫性由setter方法存在與否決定——讀取get,寫入set,可列舉型和可配置性
- Object.getOwnPropertyDescriptor()可獲得某個物件特定屬性的屬性描述符——自有屬性
// Returns {value: 1, writable:true, enumerable:true, configurable:true}
Object.getOwnPropertyDescriptor({x:1}, "x");
// Now query the octet property of the random object defined above.
// Returns { get: /*func*/, set:undefined, enumerable:true, configurable:true}
Object.getOwnPropertyDescriptor(random, "octet");
// Returns undefined for inherited properties and properties that don't exist.
Object.getOwnPropertyDescriptor({}, "x"); // undefined, no such prop
Object.getOwnPropertyDescriptor({}, "toString"); // undefined, inherited
複製程式碼
- 設定屬性的特性Object.defineProperty——不能修改繼承屬性
var o = {}; // Start with no properties at all
// Add a nonenumerable data property x with value 1.
Object.defineProperty(o, "x", { value : 1,
writable: true,
enumerable: false,
configurable: true});
// Check that the property is there but is nonenumerable
o.x; // => 1
Object.keys(o) // => []
// Now modify the property x so that it is read-only
Object.defineProperty(o, "x", { writable: false });
// Try to change the value of the property
o.x = 2; // Fails silently or throws TypeError in strict mode
o.x // => 1
// The property is still configurable, so we can change its value like this:
Object.defineProperty(o, "x", { value: 2 });
o.x // => 2
// Now change x from a data property to an accessor property
Object.defineProperty(o, "x", { get: function() { return 0; } });
o.x // => 0
複製程式碼
- 修改多個屬性的特性Object.defineProperties()
var p = Object.defineProperties({}, {
x: { value: 1, writable: true, enumerable:true, configurable:true },
y: { value: 1, writable: true, enumerable:true, configurable:true },
r: {
get: function() { return Math.sqrt(this.x*this.x + this.y*this.y) },
enumerable:true,
configurable:true
}
});
複製程式碼
- 規則:
- 如果物件是不可擴充套件的,則可以編輯已有的自有屬性,但不能給他新增新屬性
- 如果屬性是不可配置的,則不能修改他的可配置性和可列舉性
- 如果存取器屬性是不可配置的,則不能修改其getter和setter方法,頁不能將它轉換為資料屬性
- 如果資料屬性是不可配置的,則不能將它轉換為存取器屬性
- 如果資料屬性是不可配置的,則不能將它的可寫性從false修改為true,但可以從true修改為false
- 如果資料屬性是不可配置且不可寫的,則不能修改它的值。然而可配置但不可寫屬性的值是可以修改的
- 上文中的extend()方法沒有複製屬性的特性,改進的extend()
Object.defineProperty(Object.prototype,
"extend", // Define Object.prototype.extend
{
writable: true,
enumerable: false, // Make it nonenumerable
configurable: true,
value: function(o) { // Its value is this function
// Get all own props, even nonenumerable ones
var names = Object.getOwnPropertyNames(o);
// Loop through them
for(var i = 0; i < names.length; i++) {
// Skip props already in this object
if (names[i] in this) continue;
// Get property description from o
var desc = Object.getOwnPropertyDescriptor(o,names[i]);
// Use it to create property on this
Object.defineProperty(this, names[i], desc);
}
}
});
複製程式碼
物件的三個屬性
- 物件具有原型prototype,類class和可擴充套件性extensible attrubute
- 原型屬性——用來繼承屬性
- ES5:使用Object.getPrototypeOf()查詢它的原型
- ES3:使用obj.constructor.prototype檢測物件原型——不可靠
- 使用isPrototypeOf()檢測一個物件是否是另一個物件的原型,或在原型鏈中
var p = {x:1}; // Define a prototype object.
var o = Object.create(p); // Create an object with that prototype.
p.isPrototypeOf(o) // => true: o inherits from p
Object.prototype.isPrototypeOf(o) // => true: p inherits from Object.prototype
複製程式碼
_proto_ 是Mozilla,Safari和Chrome支援,IE和Opera未實現
複製程式碼
- 類屬性
- 類屬性是一個字串,使用toString()來查詢,返回
[object class]
- toString被重寫,故使用以下方法獲取
- 類屬性是一個字串,使用toString()來查詢,返回
function classof(o) {
if (o === null) return "Null";
if (o === undefined) return "Undefined";
return Object.prototype.toString.call(o).slice(8,-1);
}
複製程式碼
-
查詢結果
classof(null) // => "Null
classof(1) // => "Number"
classof("") // => "String"
classof(false) // => "Boolean"
classof({}) // => "Object"
classof([]) // => "Array"
classof(/./) // => "Regexp"
classof(new Date()) // => "Date"
classof(window) // => "Window" (a client-side host object)
function f() {}; // Define a custom constructor
classof(new f()); // => "Object"
複製程式碼
- 可擴充套件性
- 用以表示是否可以給物件新增新屬性,ES5中所有的內建物件和自定義物件都是可擴充套件的
- 使用Object.isExtensible()來判斷是否可擴充套件
- 使用Object.preventExtensions()轉換為不可擴充套件——隻影響物件本身的可可擴充套件性
- Object.seal()除了將物件設定為不可擴充套件的,還可以將物件的自有屬性設定為不可配置的——不可解封
- Object.freeze(),將物件設定為不可擴充套件和屬性設定為不可配置,所有的自有資料屬性設定為只讀——存取器屬性有setter方法,則不受影響
- 以上方法都會返回傳入的物件
// Create a sealed object with a frozen prototype and a nonenumerable property
var o = Object.seal(Object.create(Object.freeze({x:1}),
{y: {value: 2, writable: true}}));
複製程式碼
序列化物件
- JSON.stringify()和JSON.parse()
- 支援物件,陣列,字串,無窮大數字,true,false和null
- NaN,Infinity和-Infinity序列化的結果為null
- 函式,RegExp,Error物件和undefined值不能序列化和還原
- JSON.stringify只能序列化物件可列舉的自有屬性
- 可接受第二個可選引數
物件方法
- toString()
- toLocaleString()
- toJSON()
- valueOf()