一 js中的物件是什麼?
- 是一個容器,封裝了屬性(property)和方法(method),由若干鍵值對(key-value)組成
- 是對實物的抽象,實物間關係轉換成了物件間關係,模擬現實的情況,轉換為針對物件的程式設計。
– 例如將動物抽象成一類物件,此類物件有著動物中特有的屬性和動作,不同於植物,而動物中又分有豬,狗,雞不同型別的事物物件,這些型別事物對像既有著動物型別的屬性和動作,又有著自身獨特型別的屬性和動作,可以這樣一直細分下去,直到每一種動物的個例,也就是物件的例項了,它不僅具備上層事物共有的屬性和方法,也有著自己獨特個性,是一個獨特的個體。
1-1 js的物件如何建立?
— 物件可通過物件字面量({}),建構函式(如new Object(),或自定義),Object.create等方式建立。
1-1-1 物件字面量,與new Object()建立物件方式的異同?
相同點:建立的物件在使用上是一致的,var a = {} // 是var a= new Object()的語法糖
不同點:初始化的過程有些區別,new Object()是通過建構函式例項化物件,{ }是直接建立JSON物件,且在初始化時可以直接賦值,相比{ }比較高效
1-1-2 訪問及修改物件的屬性
— 物件是由鍵值對組成,物件的鍵名又稱屬性(property),都是字串型別,鍵名符合標識名條件或是數字可省略” “,系統會預設自動轉為對應的字串,鍵值可以是任意型別,若鍵值是函式,則該屬性又可被稱為方法,像函式般呼叫。
- 使用 . 或 [ ] 訪問或修改增添物件的屬性
- delete刪除屬性
- in檢查是否屬於自身的屬性
- Object.keys(obj)檢視所有自身可列舉屬性,Object.getOwnPropertyNames(obj)包括自身不可列舉的屬性如陣列中的”length”,都返回屬性名陣列
- for…in 迴圈遍歷物件本身屬性
- Object.observe(obj,function(changes))觀察物件屬性變化
1-1-2eg1:物件字面量式建立
<script> //物件字面量式建立 var obj = { name: `fermin`, run: function () { alert(`run`); } }; obj.age = 18; //新增屬性 console.log(obj.age); //18 delete obj.age; //刪除屬性,注意:不能刪除繼承的屬性,會返回false,刪除不存在的屬性不會報錯,且返回true alert(obj.age); // undefined,訪問不存在的屬性返回undefined alert("age" in obj); //false, 注意:in對物件擁有的屬性都返回ture,無論是否是繼承的,無法識別繼承屬性 console.log(Object.keys(obj)); //["name","run"] //Object.keys()返回所有本身屬性名 for (let i in obj) { console.log(i); //name run //for..in 遍歷可enumerable的自身和繼承的屬性,可加obj.hasOwnProperty(i),過濾為自身屬性 } obj.run(); //run //用點號訪問屬性和方法 obj[`run`](); //run //用[]訪問屬性和方法 alert("toString" in obj); //true
</script>
建構函式:建構函式建立物件是借用Object.create(原型) 來實現。
1-1-2eg2:new Object() 建立 (訪問和修改物件屬性與上述相同)
<script> //new Object()建立 var obj = new Object(); //new可以省略 obj.name = `fermin`; obj.age = 18; //新增屬性 obj.run = function () { alert(`run`); }; //新增屬性 delete obj.age; //刪除屬性 //delete只能刪除物件的屬性如:var a=1; delete a //false alert(obj.age); // undefined,訪問不存在的屬性返回undefined alert("age" in obj);//false console.log(Object.keys(obj)); //["name","run"] for (let i in obj) { console.log(i); //name run } obj.run(); //用點號訪問屬性和方法 obj[`run`](); //用[]訪問屬性和方法 alert("toString" in obj); //true
</script>
1-1-2eg3:自定義建構函式
<script> //自定義建構函式 function Person(name) { this.name = name; } var obj = new Person("fermin"); obj.age = 18; obj.run = function () { alert("run"); }; //新增屬性 alert(Object.keys(obj)); //name,age,run for (let i in obj) { console.log(i); //name age run } obj.run(); // run obj[`run`](); // run alert("toString" in obj); //true </script>
1-1-2eg4:Object.create(原型) // Object.create(Object.prototype) <==> new Object()
<script> //Object.create(原型); var obj = Object.create(null); //原型為null,不能繼承Object.prototype中的屬性 obj.name = `fermin`; obj.age = 18; //新增屬性 obj.run = function () { alert(`run`); }; //新增屬性 delete obj.age; //刪除屬性 alert(obj.age); // undefined,訪問不存在的屬性返回undefined alert("age" in obj);//false console.log(Object.keys(obj)); //["name","run"] for (let i in obj) { console.log(i); //name run } obj.run(); //用點號訪問屬性和方法 obj[`run`](); //用[]訪問屬性和方法 alert("toString" in obj); //false //沒有繼承Object.prototype中的屬性 </script>
1-1-3 new一個物件的過程
var obj = new Function();
- 建立一個空物件 // var obj = {};
- 將例項的__proto__屬性指向建構函式的prototype原型 // obj__proto__ = Function.prototype
- 將建構函式的指標指向例項 // Function.call(obj)
1-1-3eg1:模擬new過程
<script>
function Person(name) { this.name = name; } var obj = Person("fermin"); //不用new obj = {}; obj.__proto__ = Person.prototype; Person.call(obj); obj.age = 18; obj.run = function () { alert("run"); }; //新增屬性 alert(Object.keys(obj)); //name,age,run for (let i in obj) { console.log(i); //name age run } obj.run(); // run obj[`run`](); // run alert("toString" in obj); //true </script>
1-1-4 函式中this的指向問題 (避免在函式中包含多層this,往往第一層代表呼叫的物件,第二層就表示window了)
— this的指向在函式執行時才能確定,總是指向呼叫該函式的物件
- 由new呼叫,指向新建的物件 //在建構函式執行
- 由call,apply,bind呼叫,指向繫結的物件 //在強制繫結物件執行
- 由上下文呼叫,指向所屬的上下文物件 //在物件屬性執行
- 沒有所屬物件時,嚴格模式指向undefined或指向全域性物件(window或global) //在普通函式執行
1-1-4-eg1:
<script> var obj1= { a:3, b:{ a:10, fn:function(){ console.log(this.a); //undefined console.log(this); //window } } }; var j = obj1.b.fn; j(); //將fn賦值給變數j時,obj呼叫fn沒有執行,所以它最終指向的是window,而不是obj var obj2 = { a : `A`, fn: function () { console.log(this.a); } }; obj2.fn(); //"A" // this ===obj2 obj2.fn.call({a: `AA`}); //"AA" // this === {name: `AA`} var fn1 = obj2.fn; fn1(); //undefined //this === window </script>
二 object物件的方法
2-1 六個例項物件方法,繼承Object.prototype
- valueOf():返回當前物件對應的值,預設返回物件本身 // var o1 = new Object (); o1.valueOf === o1 //true
- toString():將物件轉換為字串形式並返回 // var o2 = {a : 1}; o2.toString() // [object Object] //用Object.prototype.toString.call() 更準確的判斷型別[object,物件型別11種/ Number/ String/ Boolean/ Object/ Array/ Function/ Null/ Undefined/ RegExp/ NaN/ Infinite]
- toLocalString():將對像轉換為本地對應的字串並返回
- hasOwnPropoty():判斷某個屬性是否是自身的非繼承屬性,是的返回true
- isPrototypeOf(): 判斷當前物件是為另一物件原型,是返回true
- propertylsEnumerable():判斷某個屬性是否可列舉
2-2 物件屬性的特徵 attributes 物件用Object.getOwnPropertyDescriptor(obj,字串屬性名)讀取。
- value:表示該屬性的值,預設undefined
- writable:表示該屬性的值是否可改,預設true
- enumerable:表示該屬性名是否可列舉,預設true //改為false時,for..in 及Object.keys(),JSON.stringify()不遍歷該屬性,可成私密屬性
- configurable:表述該對像是否可配置,預設true //var 宣告的變數會為false,沒宣告為false,如a=1或this.a=1 //改為false時,writable可從true改為false,當writable為true可改value值,其他值不可修改,不能再用delete刪除該屬性
- get:表屬性取值函式(getter)預設undefined //定義後,writable不能為true且定義value的值 // get,set為存取器的命令,常使用於某屬性值需依賴物件內部資料場合。 // 利用存取器可以實現資料物件與DOM物件的雙向繫結
- set:表屬性存值函式(setter)預設underfined //定義後,writable不能為true且定義value的值
2-3 定義物件屬性的attributes物件,下面兩種方法定義後,屬性中的writable,enumerable,configurable的值預設值又都會變為false
- Object.defineProperty(obj,字串屬性名,attributesObj)
- Object.defineProperties(obj,{字串屬性名: attributesObj,字串屬性名: attributesObj,…})
2-3-eg1:定義單一attribules屬性
<script> var o = Object.defineProperty({}, "p", { value: 3, writable: false, enumerable: true, configurable: false }); alert(o.p); //3 o.p = 33; alert(o.p); //3 //writable為false,修改不了該屬性的值 </script>
2-3-eg2:定義多個attributes屬性
<script> var o = Object.defineProperties({}, { p1: { value: 3, enumerable: true, writable: true, //configurable值沒設定,預設為false }, p2: { get: function () { return this.p1 + 6; }, //有get,就不能直接定義其value值 enumerable: true, configurable: true } }); alert(o.p1); //3 o.p1 = 4; //writable,值為true後,可直接用.或[]修改該屬性值, alert(o.p1); //4 alert(o.p2); //10 Object.defineProperty(o, "p1",{value:33}); //若再修改p1其它屬性值會報錯,因為一開始configurable為false alert(o.p1); //33 //PS:當writable值為false,configurable值為true,只能在defineProperty()中修改 </script>
2-4 物件的拷貝
–將一物件所有屬性拷貝到另一個物件上,有重複的屬性,將被覆蓋
<script> var extend = function (to, from) { for (var property in from) { var descriptor = Object.getOwnPropertyDescriptor(from, property); if (descriptor && (!descriptor.writable || !descriptor.enumerable || !descriptor.configurable || !descriptor.get || !desciptor.set)) { Object.defineProperty(to, property, descriptor); //都拷貝 } else { to[property] = from[property]; //這個else遇存取器定義屬性只拷貝值,適用於descriptor的5個特徵都具備 } } return to; }; var aa = extend({a:1, b:2}, {a:11, bb:22}); console.log(aa); //{a:11, b:2, bb:22} </script>
2-5 控制物件的狀態
- Object.preventExtensions(obj) //無法再新增新屬性,嚴格模式下新增會拋錯,可用delete刪除現有屬性
- Object.isExtensible(obj) //沒使用Object.preventExtensions(obj)返回false,使用了返回true
- Object.seal(obj) //無法新增新屬性,也無法刪除舊屬性,現有屬性的configurable會變為false
- Object.isSeal(obj) //沒使用Object.seal(obj)返回false,使用了返回true,且isExtensible返回false
- Object.isFrozen(obj) //是否被凍結,指不可擴充套件,所有屬性不可配置,所有資料屬性(即沒有getter或setter元件的訪問器的屬性)都是不可寫的。
2-5-eg:
<script> var o = new Object(); Object.preventExtensions(o); o.p = 1; console.log(o.p); // undefined console.log(Object.isExtensible(o)); //false </script>