05-創(chuàng)建對象

創(chuàng)建對象

對象是無序?qū)傩缘募?,其屬性可以包含基本值,對象或者函?shù),即由若干個(gè)“鍵值對”(key-value)構(gòu)成。
創(chuàng)建對象有3種方法:1.對象字面量;2.構(gòu)造函數(shù);3.對象繼承。

  • Object對象
    JavaScript原生提供一個(gè)Object對象,所有其他對象都繼承自這個(gè)對象。
    Object本身也是一個(gè)構(gòu)造函數(shù),可以直接通過它來生成新對象。
    通過new Object()的寫法創(chuàng)建新對象,與字面量的寫法o = {}是等價(jià)的。

  • 工廠模式
    工廠模式就是我們提供一個(gè)模子,然后通過這個(gè)模子復(fù)制出我們需要的對象。

    function createPerson(name, age) {
        // 1.聲明一個(gè)中間對象,該對象就是工廠模式的模子
        var o = new Object();
        // 2.添加屬性和方法
        o.name = name;
        o.age = age;
        o.getName = function() {
            return this.name;
        }
        // 3.返回中間對象
        return o;
    }
    // 創(chuàng)建對象實(shí)例
    var p1 = createPerson('Leo', 26);
    var p2 = createPerson('Tom', 20);
    console.log(p1 instanceof Object); // true
    console.log(p1 instanceof createPerson); // false

工廠模式缺點(diǎn):無法識(shí)別對象實(shí)例的類型,因?yàn)闃?gòu)造函數(shù)都指向Object。

  • 構(gòu)造函數(shù)
    JavaScript中使用構(gòu)造函數(shù)作為對象的模板,專門用來生成對象。
    構(gòu)造函數(shù)特點(diǎn):
    1. 生成對象的時(shí)候,必需用new命令,調(diào)用構(gòu)造函數(shù)。
    2. 函數(shù)體內(nèi)部使用了this關(guān)鍵字,代表了所要生成的對象實(shí)例。
      new 命令
      new命令的作用就是執(zhí)行構(gòu)造函數(shù),并返回一個(gè)實(shí)例對象。
      new命令本身就可以指向構(gòu)造函數(shù),所有new后面的構(gòu)造函數(shù)可以帶括號,也可以不帶。
      如果沒有使用new命令,構(gòu)造函數(shù)就變成了普通函數(shù),并不會(huì)生成實(shí)例對象。
      new 命令的原理
    3. 創(chuàng)建一個(gè)新對象,作為將要返回的實(shí)例對象。
    4. 將這個(gè)新對象的原型,指向構(gòu)造函數(shù)的prototype屬性。
    5. 執(zhí)行構(gòu)造函數(shù),將構(gòu)造函數(shù)內(nèi)部的this,指向這個(gè)新對象。
    6. 返回新對象。
      在構(gòu)造函數(shù)內(nèi)部,this指的是這個(gè)新建的空對象,所有針對this的操作,都會(huì)發(fā)生在這個(gè)空對象上。
      如果構(gòu)造函數(shù)內(nèi)部有return語句,而且后面跟一個(gè)對象,new命令會(huì)返回這個(gè)對象,否則返回this對象。
      如果普通函數(shù)(內(nèi)部沒有this關(guān)鍵字)使用new命令,則會(huì)返回一個(gè)空對象。
var A = function () {
    this.x = 1;
    return {x: 2};
}
console.log( (new A()).x ); // 2

new 命令簡化的內(nèi)部流程:

    // 創(chuàng)建構(gòu)造函數(shù)
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.getName = function () {
            return this.name;
        }
    }
    Person.prototype.getAge = function () {
        return this.age;
    }
    // 模擬new命令
    function New(func) {
        // 將類數(shù)組arguments對象轉(zhuǎn)換為數(shù)組
        var args = [].slice.call(arguments);
        // 取出構(gòu)造函數(shù)
        var func = args.shift();
        // 創(chuàng)建新對象,該對象的原型指向繼承構(gòu)造函數(shù)的原型,繼承構(gòu)造函數(shù)的屬性和方法
        var obj = Object.create(func.prototype);
        // result為構(gòu)造函數(shù)執(zhí)行的結(jié)果,通過apply將構(gòu)造函數(shù)內(nèi)的this指向修改為實(shí)例對象
        var result = func.apply(obj, args);
        // 當(dāng)構(gòu)造函數(shù)指定了返回的對象時(shí),New的執(zhí)行結(jié)果就返回該對象,否則返回實(shí)例對象
        return (typeof result === 'object' && result != null) ? result : obj;
    }
    var p1 = New(Person, 'Tom', 20);
    console.log(p1.getName()); // Tom
    console.log(p1.getAge()); // 20
    console.log(p1 instanceof Person); // true
// 其他的一些特殊處理,將var p1=New(Person,'Tom',20)等效于var p1 = new Person('Tom',20);
  • 構(gòu)造函數(shù)模式
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.getName = function () {
            console.log(this.name);
        };
    }
    var p1 = new Person('Leo', 26);
    var p2 = new Person('Tom', 20);
    console.log(p1.getName === p2.getName); // false

構(gòu)造函數(shù)模式的缺點(diǎn):
每次創(chuàng)建實(shí)例都要?jiǎng)?chuàng)建一遍方法,這既沒有必要,又浪費(fèi)系統(tǒng)資源。

  • 原型
    每個(gè)函數(shù)都有一個(gè)prototype屬性,該屬性指向一個(gè)對象,這個(gè)對象就是原型對象。
    構(gòu)造函數(shù)的prototype屬性指向?qū)嵗龑ο蟮脑蛯ο蟆?br> 每個(gè)原型對象都有一個(gè)constructor屬性,該屬性默認(rèn)指回原型對象的構(gòu)造函數(shù)。
    每個(gè)實(shí)例對象都可以通過__proto__訪問該實(shí)例對象的原型對象。
    原型對象的作用就是定義所有實(shí)例對象共享的屬性和方法。
    實(shí)例對象可以看作從原型對象衍生出來的子對象,原型對象上的所有屬性和方法,都能被實(shí)例對象共享。
    實(shí)例對象本身沒有某個(gè)屬性或方法時(shí),會(huì)到原型對象去尋找該屬性或方法。
    原型對象上的變動(dòng)會(huì)立刻體現(xiàn)在所有實(shí)例對象上,原型上的屬性和方法被所有實(shí)例共享。

  • 組合模式
    組合使用構(gòu)造函數(shù)和原型:
    構(gòu)造函數(shù)中通過this綁定的屬性與方法稱為私有變量與方法,它們被當(dāng)前被某一個(gè)實(shí)例對象所獨(dú)有。
    原型中的屬性和方法稱之為共有屬性與方法,它們可以被所有的實(shí)例對象共享。

    // 私有屬性和方法
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.getAge = function () {
            console.log(this.age);
        }
    }
    // 共有屬性和方法
    Person.prototype = {
        constructor: Person,
        getName: function () {
            console.log(this.name);
        }
    };
    var p1 = new Person('Tom', 10);
    var p2 = new Person('Leo', 26);
    console.log(p1.getName === p2.getName); // true

組合模式的缺點(diǎn):構(gòu)造函數(shù)和原型沒有封裝在一起。

  • 動(dòng)態(tài)原型模式
    封裝在一個(gè)構(gòu)造函數(shù)中,在構(gòu)造函數(shù)中初始化原型。
    function Person(name, age) {
        this.name = name;
        this.age = age;
        // 第一調(diào)用構(gòu)造函數(shù)初始化原型
        if (typeof this.getName !== "function") {
            Person.prototype.getName = function () {
                console.log(this.name);
            }
        }
    }
    var person1 = new Person('Leo', 26); // 初次調(diào)用構(gòu)造函數(shù)時(shí),初始化原型
    var person2 = new Person('Tom', 20); // 原型已初始化
    person1.getName(); // Leo
    person2.getName(); // Tom

注意:使用動(dòng)態(tài)原型模式時(shí),不能使用對象字面量重寫原型,這樣做會(huì)切斷所有實(shí)例對象與新原型的聯(lián)系。
重寫原型對象會(huì)切斷現(xiàn)有原型與任何之前已存在的實(shí)例對象直接的聯(lián)系。

    function Person(name, age) {
        this.name = name;
        this.age = age;
        if (typeof this.getName !== "function") {
            Person.prototype = {
                constructor: Person,
                getName: function () {
                    console.log(this.name);
                }
            };
        }
    }
    var p1 = new Person('Leo', 26);
    var p2 = new Person('Tom', 20);
    p2.getName(); // Tom
    p1.getName(); // 報(bào)錯(cuò),p1.getName不存在
    console.log(Person.prototype === p1.__proto__); // false
    console.log(Person.prototype === p2.__proto__); // true
    console.log(p1.__proto__ === p2.__proto__); // false
// 第一次調(diào)用構(gòu)造函數(shù)時(shí),重寫原型,添加原型方法getName
// 而此時(shí)實(shí)例p1的__proto__仍然指向以前的原型對,此原型沒有g(shù)etName方法,報(bào)錯(cuò)
// 第二次調(diào)用構(gòu)造函數(shù)的時(shí)候,實(shí)例p2已繼承原型方法getName
解決辦法:
    function Person(name, age) {
        this.name = name;
        this.age = age;
        if (typeof this.getName !== "function") {
            Person.prototype = {
                constructor: Person,
                getName: function () {
                    console.log(this.name);
                }
            };
            return new Person(name, age); //返回一個(gè)實(shí)例
        }
    }
    var p1 = new Person('Leo', 26); // 第一次調(diào)用時(shí)執(zhí)行if語句返回一個(gè)具有新原型的對象實(shí)例
    var p2 = new Person('Tom', 20);
    p2.getName(); // Tom
    p1.getName(); // Leo
  • 寄生構(gòu)造函數(shù)模式
    除了使用new操作符,并把封裝的函數(shù)稱為構(gòu)造函數(shù)之外,這個(gè)模式和工廠模式一樣。
    function Person(name, age) {
        var o = new Object();
        o.name = name;
        o.age = age;
        o.getName = function () {
            console.log(this.name);
        };
        return o;
    }
    var p1 = new Person('Leo', 26); // 使用new操作符
    console.log(p1 instanceof Person) // false 該模式不能識(shí)別構(gòu)造函數(shù)
    console.log(p1 instanceof Object)  // true

這個(gè)模式可以在特殊情況下來為對象創(chuàng)建構(gòu)造函數(shù)。
假如我們需要?jiǎng)?chuàng)建一個(gè)具有額外方法的特殊數(shù)組,由于不能直接修改Array構(gòu)造函數(shù),可以使用該模式。

    function SpecialArray() {
        // 創(chuàng)建新數(shù)組
        var arr = new Array();
        // 給數(shù)組添加值
        arr.push.apply(arr, arguments);
        // 給數(shù)組添加額外的方法
        arr.toPipedString = function () {
            return this.join('|');
        }
        // 返回該數(shù)組
        return arr;
    }
    var prople = new SpecialArray('Leo', 'Tom', 'Bob');
    console.log(prople.toPipedString()); // Leo|Tom|Bob
  • 穩(wěn)妥構(gòu)造函數(shù)模式
    穩(wěn)妥對象,是指沒有公共屬性,而且其方法也不引用this的對象。
    與寄生構(gòu)造函數(shù)模式有兩點(diǎn)不同:
    新創(chuàng)建的實(shí)例方法不引用this;不使用new操作符調(diào)用構(gòu)造函數(shù)。
    穩(wěn)妥構(gòu)造函數(shù)模式也跟工廠模式一樣,無法識(shí)別對象所屬類型。
    function person(name, age){
        var o = new Object();
        o.getName = function(){
            console.log(name);
        };
        o.getAge = function(){
            console.log(age);
        };
        return o;
    }
    var p1 = person('Leo', 26);
    p1.getName(); // Leo
    p1.getAge(); // 26
    console.log(p1.name); // undefined,只能通過getName()方法訪問name值

《JavaScript高級程序設(shè)計(jì)》
《JavaScript 標(biāo)準(zhǔn)參考教程》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容