一、創建一個對象的方法
1.工廠模式
在函數中創建對象,并給這個對象添加屬性,然后在這個函數中返回這個對象。在函數外部調用這個函數來創建對象的實例。
function createPerson(name,age,job){ var o=new Object();//在函數內部創建一個對象 o.name=name; o.age=age; o.job=job; o.sayName=function(){ alert(this.name); }; return o;//在函數內部返回這個對象 } var person1=createPerson("xiaowang","22","workers");//在函數外部創建對象的實例,不用new var person1=createPerson("xiaoliu","22","workers");
問題:沒有解決對象的識別問題(不能知道一個對象的類型)
2.構造函數模式(可以用來創建特定類型的對象)
function Person(name,age,job){//注意構造函數開頭的字母應該大寫 //構造函數中使用this this.name=name; this.age=age; this.job=job; this.sayName=function(){ alert(this.name); } } var person1=new Person("xiao",22,"tech");//使用new創建實例 var person2=new Person("li",32,"sin");
與工廠模式的不同之處:
(1)沒有顯示的創建對象
(2)直接將屬性和方法賦值給this指向的對象
(3)沒有return 語句
這兩個實例都有一個constructor屬性,指向Person。
構造函數可以識別其實例是什么類型的對象,使用instanceof操作符更可靠一些。
問:構造函數和普通函數有什么不同?
答:用new操作符來調用的就是構造函數,不用new來調用的是普通函數。
構造函數的問題:每個方法都要在每個實例上重新創建一遍。
3.原型模式
將對象實例所共享的屬性和方法不放在構造函數中,而是全部放在原型對象之中。
function Person(){ };//構造函數什么也不設置 Person.prototype.name="xiao";//全部都放在原型對象上 Person.prototype.age=22; Person.prototype.job="stu"' Person.prototype.sayName=function(){ alert(this.name); } var person1=new Person(); var person2=new Person(); console.log(person1.sayName==person2.sayName);//true
原型模式的問題:對于包含應用類型值的屬性來說,由于原型模式的共享性,改變一個實例的該引用類型值改變,則其他的實例的該屬性值也被改變了。
function Person={} Person.prototype={ constructor:Person, name:"Nick", age:29, friends:['a','b'];//引用類型的值 sayName:function(){ alert(this.name); } } var person1=new Person(); var person2=new Person(); //想要改變person1實例的friends屬性 person1.friends.push("c); alert(person1.friends);//["a","b","c"] alert(person2.friends);//["a","b","c"]; alert(person1.friends==person2.friends);//true;
4.組合模式(構造函數和原型)
構造函數定義實例的屬性,原型定義方法和共享的屬性。
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; } Person.prototype={ constructor:Person, sayname:function(){ alert(this.name) } }
1.理解
每個構造函數Person都有一個prototype屬性指向它的原型對象,既原型對象為Person.prototype;而每個原型對象中有一個constructor方法,用來指回構造函數Person。另外,調用構造函數創建的實例person1,有一個[[Prototype]]屬性(_proto_),也指向構造函數的原型對象。 注意,連接發生在實例和構造函數的原型對象之間!而實例和構造函數沒有任何關系。
isPrototypeOf()方法:檢測原型對象和實例是否有原型連接的關系
Person.prototype.isPrototypeOf(person1);//true
Object.getPrototypeOf()方法:該方法返回[[prototype]]的值,既返回一個實例的原型對象。
Object.getPrototypeOf(person1);// Person.prototype
注意:一定要先設置構造函數的原型對象,再new實例。(原型的動態性)
實例:
function Person(){ } var friend=new Person(); Person.prototype={ constructor:Person, name:'Nick', age:29, job:'student', sayName:function () { alert(this.name); } }; friend.sayName();//error
這樣的話,Person的原型被重寫了:p.157
2.屬性的訪問
問:原型([[Prototype]])引用有什么作用?
答:當引用對象的屬性的時候,會觸發底層的[[Get]]操作。對于默認的[[Get]]操作來說,第一步是檢查對象本身是否有這個屬性,如果有的話就使用它,如果沒有的話,這時候[[Prototype]]鏈就派上用場了。如果對象本身沒有所要的屬性的時候,就繼續沿著整條原型鏈查找,找到的話就返回該屬性的值,找不到的話就返回undefined。
for...in... 遍歷對象的原理和查找[[Prototype]]鏈類似。使用in操作符來檢查屬性在對象中是否存在時,也會檢查對象的整條原型鏈(不論屬性是否被枚舉)。
[[Prototype]]原型鏈的最頂端設置為Object.prototype對象。
3.屬性的設置與屏蔽
myObject.foo="bar";//設置屬性foo
step1:當myObject對象中有foo這個屬性的時候,則直接將foo修改為“bar”;
step2:當foo屬性既存在于myObject,又存在于原型鏈上,則myObject上foo屬性會屏蔽原型鏈上所有的foo屬性;
step3:當myObject對象中沒有foo這個屬性的時候,則會往上查找而存在于myObject的原型鏈;
3.1如果[[Prototype]]鏈上層存在foo屬性,并且其沒有標記為只讀(writable:false),那么在myObject上直接添加一個foo屬性,它是屏蔽屬性;
var myObject={ }; myObject.prototype={ foo:"c" }; myObject.foo="b"; console.log(myObject.foo);//b
3.2如果foo屬性被標記為只讀,那么無法在myObject上修改已有屬性或創建屏蔽屬性。如果在嚴格模式下會拋出錯誤。
var myObject={ }; myObject.prototype={ foo:"c" }; Object.defineProperty(myObject,"foo",{ writable:false }) myObject.foo="b"; console.log(myObject.foo);//undefined
3.3如果在[[Prototype]]上存在foo并且是一個setter,則一定會調用這個setter。foo不會被添加到myObject,也不會重新定義setter這個屬性。
var myObject={ }; myObject.prototype={ //foo是一個setter set foo(val){ alert(this.val); } } myObject.foo="f"; console.log(myObject.foo)//f foo還是原來的setter函數,沒有被修改
如果在3.2和3.3這兩種情況下,則不能使用=操作符在賦值,而是使用Object.defineProperty(...)方法來添加,
step4:如果myObject對象和原型鏈上都沒有foo屬性的時候,直接添加到myObject上。
var myObject={ }; myObject.prototype={ foo:"c" }; myObject.foo="b"; console.log(myObject.foo);//b
4.屬性的修改
對象實例可以修改對象原型的屬性值嗎?
答:分兩種情況:一:當原型里的屬性是值類型的話,不會被修改;
function ClassA(){}; ClassA.prototype.num=4;//num為值類型 const a=new ClassA(); a.num=3; const b=new ClassA(); console.log(b.num);
二:當原型里的屬性是引用類型的話,則會被修改。
function ClassA(){}; ClassA.prototype.obj={ num:4//num在object中,是引用類型 }; const a=new ClassA(); a.obj.num=3; const b=new ClassA(); console.log(b.obj.num);
相關推薦:
JavaScript 基于原型的對象(創建、調用)_js面向對象
js如何創建對象?js中創建對象的方法(附代碼)
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com