js中對象和對象建立方法

這一次咱們來講一說在JavaScript中常常會用到的一個複雜基本類型,對象,先從對象的屬性講起,再講對象的建立方法,基本涵蓋了建立對象的各類方法,你們一塊兒學習呀~vue

 

1、對象es6

    要掌握對象的使用及繼承,首先固然須要先理解它,接下來,將會對對象的屬性類型進行一個整理編程

    一、什麼是對象設計模式

    對象實際上是無序屬性的集合,其屬性能夠包含基本值,對象或者函數,好比像下面這個例子就是一個person對象啦數組

var person = {
    name: "NIcholas",
    age: 29,
    sayName: function() {
        console.log(this.name);
    }
}

    從上面的例子咱們能夠看到,對象能夠是由屬性和其相應的值構成,對象中能夠包含函數,也能夠包含其它對象瀏覽器

 

    二、屬性類型安全

    在JavaScript中,其實有兩種屬性,包括數據屬性和訪問器屬性app

    (1)數據屬性函數

    數據屬性包含一個數據值的位置,在這個位置能夠讀取和寫入值,通常來講,有4個描述其行爲的特性:學習

    a、[[Configurable]]:表示可否經過delete刪除屬性從而從新定義屬性,默認值爲true

    b、[[Enumerable]]:表示可否經過for-in循環返回屬性,默認值爲true

    c、[[Writable]]:表示可否修改屬性的值,默認值爲true

    d、[[Value]]:包含這個屬性的數據值,默認值爲undefined

    通常來講,數據屬性都有本身的默認值,那麼若是咱們要修改數據屬性默認的特性,應該怎麼辦呢?這個時候就須要用到Object,defineProperty()方法啦,這個方法接收三個參數:屬性所在的對象,屬性的名字和一個描述對象,來看下面的例子

var person = {};
Object.defineProperty(person, "name", {
    writable: false,
    value: "Nicholas"
});

console.log(person.name);  // Nicholas
// 從新賦值
person.name = "Greg";
console.log(person.name);  // Nicholas

    從上面的例子咱們能夠看到,由於設置了person對象的name屬性爲不可修改,所以不管你在後面怎麼修改person的name屬性的值,name屬性的值都不會發生改變

    (2)訪問器屬性

    訪問器屬性不包含數據值,它們包含一對兒getter和setter函數,在讀取訪問器屬性時,會調用getter函數,這個函數負責返回有效的值,在寫入訪問器屬性時,會調用setter函數並傳入新值,這個函數負責決定如何處理數據,訪問器屬性有如下4個特性

    a、[[Configurable]]:表示可否經過delete刪除屬性從而從新定義屬性,默認值爲true

    b、[[Enumerable]]:表示可否經過for-in循環返回屬性,默認值爲true

    c、[[Get]]:在讀取屬性時調用的函數,默認值爲undefined

    d、[[Set]]:在寫入屬性時調用的函數,默認值爲undefined

    訪問器屬性不能直接定義,必須調用Object.definedProperty()來定義的,來看下面的例子

var book = {
    _year:2004,
    edition:1
}

Object.defineProperty(book, "year", {
    get: function() {
        return this._year;
    },
    set: function(newValue) {
        this._year = newValue;
    }
});

    這裏要說明一下,book對象中_year前面的下劃線是一種經常使用的記號,用於表示只能經過對象方法訪問的屬性,還有呀,你們不要小看了Object.definedProperty()這個方法,這個方法但是很強大呀,像vue的雙向數據綁定,其實就是用到了這個方法去實現

    (3)讀取屬性的特性

    既然JavaScript有數據屬性和訪問器屬性,那麼咱們怎樣才能讀取它們呀,這個時候就須要用到Object.getOwnPropertyDescriptor()方法了,這個方法能夠取得給定屬性的描述符,接收兩個參數,分別是屬性所在的對象和要讀取其描述符的屬性名稱

 

2、對象的建立

    在瞭解了對象以後,接下來咱們就須要說下怎麼建立對象了,最簡單的方法,固然就是使用前面說的對象字面量的方法去建立啦,可是若是咱們須要建立好多個對象,用前面的方法就不行了,咱們須要用到其它更加簡便的方法,幫助咱們建立出多個對象

    一、工廠模式

    這種模式實際上是一個設計模式,抽象了建立具體對象的過程,主要是經過在函數內部建立一個對象,爲其添加屬性和方法,並將對象返回,從而實現建立多個對象的目的,來看下面的例子

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

var person1 = createPerson("Nicholas", 29);
var person2 = createPerson("Greg", 27);

    優勢:可以解決建立多個對象的問題,兼容各個瀏覽器

    缺點:沒有解決對象識別的問題,不能知道一個對象的類型

 

    二、構造函數模式

    這種模式主要經過建立自定義的構造函數,從而定義自定義對象類型的屬性和方法,來看下面例子

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.sayName = function() {
        console.log(this.name);
    }
}

var person1 = new Person("Nicholas", 29);
var person2 = new Person("Greg", 27);

    這裏要說明一下,構造函數須要以一個大寫字母開頭,而非構造函數應該以一個小寫字母開頭,這個主要是爲了區別構造函數和其它函數,構造函數其實自己也是函數,只是用來建立對象而已

    其中,要建立Person的新實例,須要使用new操做符,其實這裏會通過4個步驟,首先,將會建立一個新對象,接着會將構造函數的做用域賦給新對象,this指向了這個對象,接着會執行構造函數中的代碼,爲這個新對象添加屬性,最後會返回新對象

    優勢:能夠建立多個對象,解決對象的識別問題

    缺點:每一個實例都會建立不一樣的function實例,而其實建立完成一樣任務的function實例是很沒有必要的

    這裏仍是要說明一下,對象類型的檢測須要用到instanceof操做符,好比像上面的例子能夠用下面的方法檢測

console.log(person1 instanceof Person);  //  true
console.log(person2 instanceof Person);  //  true

    使用構造函數模式能夠解決對象的識別問題,而這也是工廠模式沒法辦到的

 

    三、原型模式

    咱們都知道,每一個函數都有一個prototype屬性,這個屬性是一個指針,指向一個對象,而這個對象就是原型對象,包含了全部實例共享的屬性和方法,若是咱們要建立的對象須要共享屬性和方法,就可使用這種方法建立

function Person() {
}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.sayName = function() {
    console.log(this.name);
}

var person1 = new Person();
var person2 = new Person();
person1.sayName();  //  Nicholas
person2.sayName();  //  Nicholas

    優勢:不用爲構造函數傳遞參數,能夠建立多個相同的對象

    缺點:原型中的屬性被不少實例共享,當屬性爲包含引用類型值的屬性時,修改一個實例中屬性的值,另外一個實例中的屬性的值也會改變

 

    四、組合使用構造函數模式和原型模式

    經過前面的分析,咱們應該能夠看到構造函數模式和原型模式的優勢和缺點啦,構造函數能夠建立多個不一樣屬性值的對象,原型模式能夠用於定義方法和共享的屬性,咱們能夠將這兩種模式結合起來,這種模式如今是使用最普遍的一種模式啦

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

Person.prototype = {
    constructor: Person,
    sayName: function() {
        console.log(this.name);
    }
}

var person1 = new Person("Nicholas", 29);
var person2 = new Person("Greg", 27);

person1.sayName();  //  Nicholas
person2.sayName();  //  Greg
person1.sayName === person2.sayName; // true

    從上面的例子咱們能夠看到,使用這種模式建立對象,每一個實例都會有本身的一份實例屬性的副本,但同時又共享着對方法的引用,最大限度地節省了內存,另外,這種混成模式還支持向構造函數傳遞參數,可謂是集兩種模式之長

 

    五、動態原型模式

    這種模式主要是將全部信息都封裝在了構造函數裏,由於在組合構造函數模式和原型模式中,構造函數和原型模式是獨立的,有些開發人員會感到很困惑,所以,這種模式也是爲了解決這個問題,經過在構造函數中初始化初始化原型,又保持了同時使用構造函數和原型的優勢,換句話說,就是能夠經過在構造函數中,檢查某個應該存在的方法是否有效,來決定是否須要初始化原型

 

function Person(name, age) {
    this.name = name;
    this.age = age;
    if(typeof this.sayName != "function") {
        Person.prototype.sayName = function() {
            console.log(this.name);
        }
    }
}

var person1 = new Person("Nicholas", 29);
person1.sayName();  // Nicholas

 

    這裏要說明一下,在if語句中的代碼,只有在首次調用構造函數時纔會執行,以後原型已經獲得初始化,不須要再作什麼修改了

 

    六、寄生構造函數模式

    這種模式其實和工廠模式很像,除了使用new操做符並把使用的包裝函數叫作構造函數以外,和工廠模式能夠說是如出一轍的,這種模式的基本思想是建立一個函數,該函數的做用僅僅是封裝建立對象的代碼,而後再返回新建立的對象

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

var person1 = new Person("Nicholas", 29);
person1.sayName();  //  Nicholas

    寄生構造函數模式和工廠模式真的是很像,那麼既然有了工廠模式,爲何還要有寄生構造函數模式呢?其實這個模式主要是用來給js原生的構造函數定義一些新的方法,咱們能夠看下面這個例子

function SpecialArray() {
    var values = new Array();
    values.push.apply(values, argumens);
    values.toPipedString = function() {
        return this.join("|");
    }
    return values;
}

var colors = new SpecialArray("red","blue");
console.log(colors.toPipedString());  //  red|blue

    從上面這個例子咱們能夠看到,咱們在構造函數裏面建立了一個新的數組,而後經過push方法初始化這個數組,而且又給數組的實例添加了一個toPipedString方法,而且將所建立的數組返回,所以呢,當咱們經過new建立了SpecialArray實例時,其實就獲得增長了新方法的數組實例啦,就能夠在這個實例上使用咱們添加的新的方法toPipedString

 

    七、穩妥構造函數模式

    在說這種模式以前,要先說一下穩妥對象,穩妥對象就是指沒有公共屬性,並且其方法也不引用this的對象,穩妥對象最適合在一些安全的環境中,或者防止數據被其餘應用程序改動時使用,穩妥構造函數模式遵循與寄生構造函數相似的模式,可是仍是有下面的不一樣,第一個是新建立的對象實例方法不引用this,第二個是不使用new操做符調用構造函數

function Person(name, age) {
    var o = new Object();
    _name = name;
    _age = age;
    o.sayName = function() {
        return _name;
    }
    return o;
}

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

    在上面這個例子中,咱們在構造函數中建立了一個對象後,能夠繼續添加一些私有的變量和函數,要修改這些私有的變量和函數,只能經過建立的對象的方法進行訪問,這裏在對象上建立的方法其實能夠看做就是公有方法,好比說這裏的_name和_age就是私有變量,而對象o的sayName方法就是訪問私有變量的公有方法了,這裏除了調用sayName()方法外,沒有其它方法能夠訪問其數據成員

 

    八、es6中建立對象的方法

    最後要來講下es6中建立對象新增的方法啦,在es6中,引入了 Class(類)這個概念,做爲對象的模板,經過class關鍵字,能夠定義類,基本上,ES6 的class能夠看做只是一個語法糖,它的絕大部分功能,ES5 均可以作到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayName() {
        console.log(this.name);
    }
}

var person1 = new Person("Nicholas", 29);
person1.sayName();  //  Nicholas

    上面這個例子其實就是組合構造函數模式和原型模式的改寫,其中,constructor屬性直接指向類自己,該方法會默認返回實例對象,在裏面定義的屬性和方法都是實例自己具備的方法,不是其它實例共享的,而像sayName方法就是定義在原型上的方法了,是全部實例一塊兒共享的

 

    好啦,今天就介紹到這裏了,不知道你們對對象和對象的建立是否有了一個比較詳細的瞭解了呢

相關文章
相關標籤/搜索