1. 程式人生 > 實用技巧 >js的建立物件的方法

js的建立物件的方法

第一種:工廠模式

根據接收引數返回,包含引數的物件

優點:解決建立多個物件的問題

缺點:沒法判斷物件的型別

            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; }

第二種:建構函式模式

            function Person(name,age,job){
                this.name = name;
                this.age = age;
                this.job = job;
                this.sayName = function(){
                    alert(this.name);
                }
            }
            let p1 
= new Person('bzw',20,'stu'); console.log(p1 instanceof Person);//true console.log(p1.hasOwnProperty('name'));//true

建構函式模式與工廠模式區別:

1.沒有顯式的建立函式

2.直接將屬性和方法賦值給this物件

3.沒有return語句

建構函式定義:任何函式,只要可以通過new操作符來呼叫,那它就可以作為建構函式

以這種方式呼叫建構函式會經理以下幾個步驟:

1.建立新物件

2.將作用域賦值給新物件

3.執行建構函式的程式碼

4.返回新物件

缺點:每個方法都要在例項化的物件上面重新建立一遍

第三種:原型模式(重點)

  想要知道原型模式,必須得知道什麼叫做原型,每個函式都有一個prototype(原型)屬性,這個屬性是一個指標,指向一個物件,這個物件包括所有例項可以共享的屬性和方法。

此外還需要知道什麼叫做原型物件,還是那句話,只要建立一個函式,就會有一個prototype(原型)屬性,而這個prototype恰恰指向這個函式的原型物件,這個原型物件包括所有例項可以共享的屬性和方法。

每建立一個例項,這個例項會有一個[[prototype]]指標,專門指向建構函式的原型物件,每個原型物件又會有一個constuctor屬性,它是一個指向prototype屬性所在函式的指標

       function Person1(){
                
            }
            Person1.prototype.name = 'bzw';
            Person1.prototype.age = 20;
            Person1.prototype.job = 'stud';
            console.log(Person.prototype);//{constructor: ƒ}
            console.log(Person.prototype.constructor);//指向Person函式
            let p2 = new Person1();
            let p3 = new Person1();
            console.log(Person1.prototype.isPrototypeOf(p2));//true
            console.log(Person1.prototype.isPrototypeOf(p3));//true
            
            console.log(Object.getPrototypeOf(p2) === Person1.prototype);//true
            console.log(p2.hasOwnProperty('name'));//false

  in與hasOwnProperty的區別:
in會在例項化物件裡面找有沒有這個屬性,如果沒有就去它的原型裡面去找,hasOwnProperty只會在例項化物件裡去找有沒有這個屬性

            console.log('name' in p2);//true
            console.log(p2.hasOwnProperty('name'));//false                            

  我們可以寫個函式專門用來判斷某個物件只有原型裡面有某個屬性

            function hasPrototypeProperty(obj,key){
                return !obj.hasOwnProperty(key) && (key in obj);
            }
            console.log(hasPrototypeProperty(p2,'name'));//true
            p2.name = 10;
            console.log(hasPrototypeProperty(p2,'name'));//false

  給原型新增方法每次都需要寫一個Person.prototype這樣寫太麻煩,我們可以下面這樣寫

            function Person2(){
                
            }
            Person2.prototype = {
                name:'bzw',
                age:20,
                job:'stu'
            }

  這樣寫挺方便但是,有個缺點就是原型物件的constructor會指向Object,這不是我們想看到的

             console.log(Person2.prototype.constructor);//Object()
        console.log(Object.keys(Person2.prototype))//["name", "age", "job"]

  我們可以指定它的constructor,這樣就更改過來了,

            Person2.prototype = {
                constructor:Person2,//constructor的enumerable預設為false不可遍歷,但是這種方法會讓它變為true,即可以遍歷
                name:'bzw',
                age:20,
                job:'stu'
            }

  這裡可能大家有個問題,為什麼這樣要指定constructor了,因為我們前面說過,一個建構函式的constructor需要指向prototype屬性的所在的函式

  我們可以列印一下的Person2.prototype.constructor

 console.log(Person2.prototype.constructor);//person2()

  結果如下:

  或者可以用下圖來表達上述的關係

  

  當然這樣寫還是有點弊端

            //Object.keys()會獲取物件中所有可以遍歷的屬性
            console.log(Object.keys(Person2.prototype))//["constructor", "name", "age", "job"]

  constructor竟然可以被遍歷出來,這是不對的,我們發現這個constructor可以遍歷,那麼上面的方法就不是特別好,我們可以用下下面的方法,即指定了Person2的constructor指向問題,又解決constructor可以遍歷的問題,可謂一舉兩得

            Object.defineProperty(Person2.prototype,'constructor',{
                enumerable:false,
                value:Person2
            });
            console.log(Object.keys(Person2.prototype))//["name", "age", "job"]
            console.log(Person2.prototype.constructor);//Person2();

  原型的動態性

  我們可以利用原型的動態性給原型新增屬性和方法

        function Person3(){
                
            }
            let friend = new Person3();
            Person3.prototype.a = 1;
            console.log(friend.a);//1 即使例項已經建立好了, 但是當我給原型新增屬性的時候,例項還是原型後面新增的方法
            Person3.prototype = {
                constructor:Person3,
                name:'bzw'
            }
            //例項化物件裡面會有一個指標指向原型,而不是指向建構函式,這裡把原型修改為另一個物件,就等於切斷了最初原型與建構函式之間的關係
            console.log(friend instanceof Person3)//false
            console.log(friend.name)//undefined

下圖可以反映上述的情況

  原型模式的缺點

            function Person4(){
                
            }
            Person4.prototype={
                constructor:Person4,
                name:[4],
            }
            let p4 = new Person4();
            console.log(p4.name);//[4]
            p4.name.push(5);
            let p5 = new Person4();
            console.log(p4.name,p5.name);//[4, 5],[4, 5]

  我們發現例項竟然可以對更改原型的值,這個不太好,於是就有其他的物件建立方法

第四種:組合建構函式和原型模式

       function Person5(name,age,job){
                this.name = name;
                this.age = age;
                this.job = job;
                this.friend = ['tom','jerry'];
               
            }
            Person5.prototype={
                constructor:Person5,
                sayName:function(){
                    console.log(this.name);
                }
            }
            let p6 = new Person5('bzw',20,'stu');
            let p7 = new Person5('bzw1',19,'stu1');
            p6.friend.push('bob');
            console.log(p6.friend);//["tom", "jerry", "bob"]
            console.log(p7.friend);//["tom", "jerry"]

第五種:動態原型

            function Person6(name,age,job){
                this.name = name;
                this.age = age;
                this.job = job;
                this.friend = [];
                if(typeof this.sayName != 'function'){
                    Person6.prototype.sayName = function(){
                        return this.name;
                    }
                }
            }
            let p8 = new Person6('bzw2',20,'stu');
            p8.friend.push('v');
            console.log(p8.sayName())//bzw2
            let p9 = new Person6('bzw3',21,'stu');
            console.log(p8.friend,p9.friend);//["v"] []

  使用動態原型不能使用物件字面量來重寫原型,否則會切斷建構函式與原先原型的關係,也會切斷例項與新原型的關係

第六種:寄生建構函式模式(和工廠模式很像)

特點:返回的物件與建構函式或者建構函式的原型屬性之間沒有關係,也就是說,建構函式返回的物件與在建構函式在外部建立的物件沒有什麼不同

            function Person7(name,age,job){
                let o = new Object();
                o.name = name;
                o.age = age;
                o.job = job;
                o.sayName = function(){
                    return this.name;
                }
                return o;
            }
            let p10 = new Person7('bzw1',20,'stu');
            console.log(p10.sayName());//bzw1

第七種:穩妥建構函式模式(適合於某些安全執行環境下,這些安全環境會禁用this和new)

          function Person8(name,age,job){
                let o = new Object();
                //定義私有變數和函式
                let sur = '姓名:';
                let sum = function(){
                    return sur+name;
                }
                o.sayName = function(){
                    return sum();
                }
                return o;
            }
            let p11 = Person8('bzw');
            console.log(p11.sayName(),sur);//姓名:bzw,sur is not defined