一個人就需要物件之js中八種建立物件方式

C樂發表於2019-04-19

前言:說,點進來是不是喜歡我的笑容!!!大家好,我叫C樂,(恩,很直不gay)一名退役的大學生,喜歡摩旅。從上課的一次css接觸後,便喜歡上了前端,以下是我的一些學習筆記,站在前輩們的肩膀上,分享一些我的理解,不足之處還請大家多多指教。性別男(哈哈哈哈哈哈,打不到我吧)

什麼是JavaScript中物件?

js高階程式設計書中是說ECMA—262把物件定義為:“無序屬性的集合,其屬性可以包含基本值、物件或者函式”。物件的每個屬性或方法都有一個名字,而每個名字都能對映一個值,即名值對。內心感覺,這是啥啊(此處省略一萬個字)

我第一次看到這句話的時候也是雲裡霧裡。其實我們可以把物件當做一輛車來看待。一把車鑰匙(函式或物件)匹配一輛車(基本值),建立這樣一個基本概念後,我們繼續理解資料屬性和訪問器屬性就沒那麼難了。資料屬性好比汽油,訪問器屬性好比發動機(放心,這輛車的發動10000公里內絕對不會漏機油,買了絕對不虧,好了言歸正傳),給車子加了汽油(資料型別),發動機(訪問器屬性有函式決定如何處理資料)就開了工作了,然後你就可以陪著你的另一半去浪漫的土耳其了,聽說前端都是帥氣漂亮工資又高的小哥哥小姐姐們,向前端大佬們學習。

有物件時,放假的時候可以有一個人陪著自己逛街,去吃想吃很久的美食,可以一起去看電影等等事情。

有物件時,遇到什麼事情可以和物件撒嬌,訴說委屈,即使事情已經解決了,還是可以得到對方的安慰。可以示弱,不必事事逞強的去做。

有物件時,即使兩個人宅在家裡也是很幸福的,可以做很多的事情,不管做什麼事情都有另一個人可以分享,有時還會得到對方愛的誇獎。

有物件時,當外面下雨了,可以讓物件來接自己,給自己送傘。或者在外面遇到事情都可以給對當打電話。

所以說有個物件是多麼的重要。讓我一起學習JavaScript中建立物件的方法(大佬您來請坐)

鑑於篇幅有點長,感興趣的小夥伴可以收藏後慢慢看,路過的帥哥美女大神們請給個小贊,編寫不易,小的給你打套擒敵拳給您助助興。

八種建立物件的方式

Object建構函式模式(賓士) 物件字面量模式(寶馬) 工廠模式(奧迪) 建構函式模式(路虎) 原型模式(大眾) 建構函式模式和原型結合(布加迪威龍) 動態原型模式 (勞斯萊斯) 寄生建構函式模式(邁凱倫)

  • 也許你會問為什麼後面都加車品牌,因為我在每學會一種建立物件的方式後,彷彿我就擁有了這輛車,哈哈哈,原諒我幻想了下。

Object建構函式模式

 var person = new Object();
person.name = "Jhonha";
person.age = '27';
person.job = 'dancer';
person1.sayName = function () {
  console.log(this.name);
}
person1.sayName(); // Jhonha
console.log(person1.age); // 27
複製程式碼

建構函式在不返回值得情況下,預設會返回新物件例項。而通過建構函式末尾新增return語句,可以重寫呼叫建構函式時候的返回值。

物件字面量模式

var person = {
    name: 'Jhonha',
    age: 22,
    job: 'dancer',
    sayName: function () {
      console.log(this.name);
    }
}
person2.sayName(); // dancer
複製程式碼

雖然Object建構函式和物件字面量都可以用來建立單個物件,但這些方法有個明顯的缺點,就是一個介面建立很多物件,會產生大量的重複程式碼

工廠模式

function createPerson(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
      console.log(this.name);
    };
    return o;
}
var person1 = createPerson('Jhonha', 22, 'dancer');
var person2 = createPerson('Ciris', 22, 'Software engineer');
console.log(person1.name); // Jhonha
console.log(person2.name); // Ciris

優點:解決了建立多個相似物件的問題,函式封裝建立物件,
無需寫重複建立物件的程式碼
缺點:沒有解決物件識別的問題(怎麼樣知道一個物件型別) 那要怎麼辦,
讓我們繼續往下學習吧。
複製程式碼

建構函式模式

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function () {
        console.log(this.name);
    }
}
var person1 = createPerson('Jhonha', 22, 'dancer');
var person2 = createPerson('Ciris', 22, 'Software engineer');
console.log(person1.name); // Jhonha
console.log(person2.name); // Ciris
複製程式碼

在這個例子中,Person()函式取代了createPerson()函式。我們注意到,Person()中的程式碼除了與createPerson()中相同的部分外,還存在以下不同之處

* 沒有明顯的建立物件
*  直接將屬性和方法賦給了 this 物件
*  沒有return語句
複製程式碼

要建立person的新例項,必須使用new操作符,經歷一下四個步驟

* 建立一個新物件
* 將建構函式的作用域付給了新物件(因此this指向了這個新物件)
* 執行建構函式的程式碼(為這個新物件新增屬性)
* 返回新物件
複製程式碼

在前面的例子的最後,person1 和 person2 分別儲存著Person的一個不同的例項.這兩個物件都有一個constructor(建構函式)屬性,該屬性指向Person。

console.log(person1.constructor === Person); // true
console.log(person2.constructor === Person); // true
複製程式碼

我們在這這個例子中建立的所有物件既是Object的例項,同時也是Person的例項,這一點可以通過instanceof操作符得到驗證。

console.log(person1 instanceof Object); // true
console.log(person1 instanceof Person); // true
console.log(person2 instanceof Object); // true
console.log(person2 instanceof Person); // true
複製程式碼

建構函式與其他函式唯一的區別,就在於呼叫他們的方式不同。不過,建構函式畢竟也是函式,不存在定義建構函式的特殊語法。任何函式,只要通過new操作符來呼叫,那他就可以作為建構函式。

// 當作建構函式使用
var person = new Person("Jhonha", 27, "dancer");
person.sayName(); //"Jhonha"

// 作為普通函式呼叫
Person("Ciris", 22, "software engineer"); // 新增到window
window.sayName(); //"Ciris"

// 在另一個物件的作用域中呼叫
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName(); //"Kristen"
複製程式碼

建構函式模式優化

function Person(name) {
    this.name = name;
    this.sayName = sayName;
}

function sayName() {
    console.log(this.name);
}

var person1 = new Person('Jhonha');
console.log(person1.name);// Jhonha
var person2 = new Person('Ciris');
console.log(person2.name);// Ciris
複製程式碼

優點:解決了每個方法都要被重新建立的問題

缺點:person1和person2物件共用了在全域性作用域的同一個sayName()函式,但是全域性作用域中定義的函式實際上只能被某個物件呼叫,這讓全域性作用域優點名不副實,如果要物件要定義很多方法,就需要定義很多個全域性函式,於是我們這個自定義的引用型別就絲毫沒有封裝性可言了。好在,這些問題可以通過使用原型模式來解決。

謝謝您看到這裡,看久了您就休息吧

一個人就需要物件之js中八種建立物件方式

原型模式

function Person(){}
Person.prototype = {
    constructor : Person,
    name: "Jhonha",
    age : 29 ,
    job: "Software Engineer",
    sayName: function(){
        return this.name;
    }
};
var person1 = new Person();
console.log(person1.name); // Jhonha
console.log(person1.age); // 29
console.log(person1.job); // Software Engineer
console.log(person1.sayName()); // Jhonha
複製程式碼

優點:我們建立的每個函式都有一個prototype(原型)屬性,這個屬性是一個指標,指向一個物件,而這個物件的用途是包含可以由特定型別的所有例項共享的屬性和方法。如果按照字面意思來理解,那麼prototype就是通過呼叫建構函式而建立的那個物件例項的原型物件。使用原型物件的好處是可以讓所有物件例項共享它所包含的屬性和方法。

缺點:省略了為建構函式傳遞初始化引數,所有例項在預設情況下都將取得相同的值,當使用原型屬性時會只要在一個例項上修改都會影響到所有的例項,例如在一個例項上修改陣列。

附上一張圖,希望能幫到大家加強理解建構函式,原型,例項。

一個人就需要物件之js中八種建立物件方式
圖片作者

組合使用建構函式模式和原型模式

建立自定義型別是最常見方式,就是組合使用建構函式模式與原型模式。建構函式模式用於定義例項屬性,而原型模式用於定義方法和共享的屬性。結果每個例項都會有自己的一份例項屬性的副本,但同時又共享著對方法的引用,最大限度的節省了記憶體

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Jhonha","Nick"]
}

Person.prototype = {
    constructor: Person,
    sayName: function () {
        console.log(this.name);
    }
}
var person1 = new Person('bob', 22, 'frontend');
var person2 = new Person('lynn', 22, 'doctor');

person1.friends.push("Van");
console.log(person1.friends); // Jhonha Nick Van
console.log(person2.friends); // Jhonha Nick
console.log(person1.name); // bob
console.log(person2.name); // lynn
console.log(person1.sayName === person2.sayName); // true
複製程式碼

在這個例子中,例項屬性都是在建構函式中定義的,而由所有例項共享的屬性constructor和方法sayName()則是在原型中定義的。而修改了person1.friends(向其中新增了一個新的字串),並不影響到person2.friends,因為他們分別來自不同的陣列。

這種建構函式和原型混成的模式,是目前在ECMAScript中使用最廣泛、認同度最高的一種建立定義型別的方法。

謝謝您還在這裡,感恩有你,給個贊可好,謝謝您

一個人就需要物件之js中八種建立物件方式

動態原型模式

function Person(name, age, obj) {
    this.name = name;
    this.age = age;
    this.obj = obj;

    console.log('typeof this.sayName: ', typeof this.sayName);
    // 檢測sayName 是不是一個函式
    // 實際上只在當前第一次時候沒有建立的時候在原型上新增sayName方法
    if (typeof this.sayName != 'function') {
        Person.prototype.sayName = function () {
            return this.name;
        }
    }
}

//因為建構函式執行時,裡面的程式碼都會執行一遍,而原型有一個就行,不用每次都重複,所以僅在第一執行時生成一個原型,後面執行就不必在生成,所以就不會執行if包裹的函式,
//其次為什麼不能再使用字面量的寫法,我們都知道,使用建構函式其實是把new出來的物件作用域繫結在建構函式上,而字面量的寫法,會重新生成一個新物件,就切斷了兩者的聯絡!

var person1 = new Person('bob', 22, 'frontend');
var person2 = new Person('lynn', 22, 'doctor');

console.log(person1.name); // bob
console.log(person2.name); // lynn
console.log(person1.sayName()); // bob
console.log(person2.sayName()); // lynn
console.log(person1.sayName === person2.sayName); // true

複製程式碼

看到這裡也許會有朋友問:動態原型模式中使用if的作用?不使用if語句有什麼問題或者弊端?

1.第一個問題:Person是一個建構函式,通過new Person(...)來生成例項物件。每當一個Person的物件生成時,Person內部的程式碼都會被呼叫一次。if的話,你每new一次(即每當一個例項物件生產時),都會重新定義一個新的函式,然後掛到Person.prototype.sayName屬性上。而實際上,你只需要定義一次就夠了,因為所有例項都會共享此屬性的。所以如果去掉if的話,會造成沒必要的時間和空間浪費;而加上if後,只在new第一個例項時才會定義sayName方法,之後就不會了。

2.第二個問題:假設除了:sayName方法外,你還定義了很多其他方法,比如:sayByecrysmile等等。此時你只需要把它們都放到對sayName判斷的if塊裡面就可以了。

if (typeof this.sayName != "function") {
    Person.prototype.sayName = function() {...};
    Person.prototype.sayBye = function() {...};
    Person.prototype.cry = function() {...};
    ...
}
複製程式碼

這樣一來,要麼它們全都還沒有定義(new第一個例項時),要麼已經全都定義了(new其他例項後),即它們的存在性是一致的,用同一個判斷就可以了,而不需要分別對它們進行判斷。

哇,你還在呀,買個萌可好

一個人就需要物件之js中八種建立物件方式

寄生建構函式模式

寄生(parasitic)建構函式模式,其基本思想是建立一個函式,該函式的作用僅僅是封裝建立物件的程式碼,然後返回新建立的物件。

function createPerson(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
        console.log(this.name);
    };
    return o;
}

var person1 = new createPerson('bob', 22, 'frontend');
var person2 = new createPerson('lynn', 22, 'doctor');

console.log(person1.name); // bob
console.log(person2.name); // lynn
console.log(person1 instanceof createPerson ) // false
console.log(person2 instanceof createPerson ) // false
複製程式碼

寄生建構函式模式,我個人認為應該這樣讀

寄生-建構函式-模式,也就是說寄生在建構函式的一種方法。

也就是說打著建構函式的幌子掛羊頭賣狗肉,你看建立的例項使用 instanceof 都無法指向建構函式!

穩妥建構函式模式

function Person(name,age,job){
    //建立要返回的物件
    var o = new Object();

    // 可以在這裡定義私有變數和函式

    //新增方法
    o.sayName = function(){
        console.log(name);
    };

    //返回物件
    return o;
}

var friend = Person("Nicholas", 29 , "dancer");
friend.sayName(); //Nicholas

複製程式碼

所謂穩妥物件,指的是沒有公共屬性,而且其方法也不引用 this 的物件

與寄生建構函式模式有兩點不同:

* 新建立的例項方法不引用 this
* 不使用 new 操作符呼叫建構函式
複製程式碼

穩妥物件最適合在一些安全的環境中。 穩妥建構函式模式也跟工廠模式一樣,無法識別物件所屬型別。

你都快看完了,最喜歡的一張圖片送給你 給個贊可好,謝謝您

一個人就需要物件之js中八種建立物件方式

小補充

new操作符經歷了啥?

  • 它建立了一個新的物件
  • 它會被執行[[Prototype]](也就是__proto__)連結。
  • 它使this指向新建立的物件
  • 通過new建立的每個物件將最終被[[Prototype]]連結到這個函式的prototype物件上。
  • 如果函式沒有返回物件型別Object(包含Functoin, Array, Date, RegExg, Error),那麼new表示式中的函式呼叫將返回該物件引用。

如何理解例項化

例項化的意思也差不多,你把一個類作為一個物件,就當成是車,你想開...所以你跟編譯器(也就是狹義的電腦)請求,var person = new Person();就是一個跟機器借車開的語法,這裡邊Person和Contructor就是個名字的差異(建構函式的名字),比如說你爸爸喜歡跟他自己的車叫親愛的,那麼你開著車的時候別管是誰的,你也可以叫它為寶貝...Person()是不能改的,因為你得告訴機器你借的是哪臺車...然後你借過來之後就隨便你叫什麼了....類下邊的方法啊,公共屬性都是可以借用過來的,好比說這臺車有四個輪子一個方向盤就是屬性,方法就是這臺車可以正著開,也可以倒著開....所以無論你想正著開還是倒著開,你的前提都需要把車借過來才能開...例項化就是借車...呼叫方法就是借車之後的操作!

相關文章