2016-05-03 68 views
3

我最近開始學習對象,閉包,範圍等等。我試圖用這些技術來實現我自己的代碼,但遇到了問題。初學JavaScript模式

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

    return { 
     ageUp: function ageUp() { 
      this.age++; 
     }, 
     printInfo: function printInfo() { 
      console.log(this.name + " is " + this.age + " years old"); 
     }, 
     changeName: function changeName(newName) { 
      this.name = newName; 
     } 
    } 
} 


var jeff = new Person('jeff', 28); 
jeff.printInfo(); 

奇怪的是,這返回undefined is undefined years oldprintInfo屬性在其範圍中沒有this.name,因爲返回的對象沒有該函數的回憶嗎?

而且奇怪的是,如果我改變的this.namethis.age所有實例定期私有變量(如var personName = name;),不知何故返回的對象都能正常工作,我可以使用ageUpprintInfo預期。

我結合了兩種不應該是的設計模式嗎?什麼是最好的方式去做這件事?

回答

3

的問題是,當你這樣做:

return { ... 

您正在創建一個新的對象,從對象分開的new關鍵字先前創建(您分配了兩個屬性的一個)。您可能會注意到jeff instanceof Person爲false,並且jeff.constructor === Object

見這個例子:

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

    return { 
     theObjectThatNewCreated: this 
    } 
} 

var jeff = new Person('jeff', 28); 
console.log(JSON.stringify(jeff)); 
// {"theObjectThatNewCreated":{"name":"jeff","age":28}} 
console.log(jeff.constructor); 
// Object 
console.log(jeff.theObjectThatNewCreated.constructor); 
// Person 

你可以通過指定的名字和年齡的屬性來回報您,而不是給this對象修復:

function Person(name, age) { 
    return { 
     name: name, 
     age: age, 
     ageUp: function ageUp() { 
      this.age++; 
     }, 
     printInfo: function printInfo() { 
      console.log(this.name + " is " + this.age + " years old"); 
     }, 
     changeName: function changeName(newName) { 
      this.name = newName; 
     } 
    } 
} 


var jeff = new Person('jeff', 28); 
jeff.printInfo(); 

但後來的人是不是真的一個構造函數,它只是一個對象工廠,沒有一點用new來調用它。它返回的對象不是Person的實例,它們是普通的舊對象。有一個更好的辦法。

我結合了兩種設計模式,我不應該這樣做嗎?什麼是最好的方式去做這件事?

我會說你正在將揭示模塊模式與普通的JavaScript構造函數相結合。

,而不是返回一個新的對象,你可以只使用this一路過關斬將,分配這些功能作爲Person對象,而不是一個新的對象的屬性:

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

    this.ageUp = function ageUp() { 
     this.age++; 
    }; 

    this.printInfo = function printInfo() { 
     console.log(this.name + " is " + this.age + " years old"); 
    }; 

    this.changeName = function changeName(newName) { 
     this.name = newName; 
    }; 
} 


var jeff = new Person('jeff', 28); 
jeff.printInfo(); 

但是,因爲這些功能不使用任何從構造函數關閉的變量,它們實際上應該被添加到構造函數的原型中:

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

Person.prototype.ageUp = function ageUp() { 
    this.age++; 
}; 

Person.prototype.printInfo = function printInfo() { 
    console.log(this.name + " is " + this.age + " years old"); 
}; 

Person.prototype.changeName = function changeName(newName) { 
    this.name = newName; 
}; 

var jeff = new Person('jeff', 28); 
jeff.printInfo(); 
+0

好的答案,您能否澄清一句「這些函數不使用任何從構造函數關閉的變量」? 聽不懂...... – chenop

+1

@chenop我是指在構造函數中聲明瞭'var'的變量,或者構造函數的參數。 – Paulpro

0

刪除班級中的周圍return,它應該可以工作。

2

你是對的,你正在組合兩種模式。首先,在正常情況下,構造函數不會返回任何東西。 (有些例外,我不會在這裏鑽研)。

你可能想要做這樣的事情:

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

Person.prototype.printInfo = function() { 
    console.log(this.name + " is " + this.age + " years old"); 
}; 

// and so on. Now you can say 

var jeff = new Person('jeff', 28); 
jeff.printInfo(); // => jeff is 28 years old 

我假設你想上,使他們共享原型那些方法(它並不是強制性)。

2

您正在構造函數中返回一個新對象。因此,當調用new Person(...)而不是具有您定義的屬性的轉儲對象時,您不會收到Person的實例。

嘗試定義你的方法對象作爲構造函數的原型:

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

Person.prototype = { 
    ageUp: function ageUp() { 
     this.age++; 
    }, 
    printInfo: function printInfo() { 
     console.log(this.name + " is " + this.age + " years old"); 
    }, 
    changeName: function changeName(newName) { 
     this.name = newName; 
    } 
} 

另外,如果你想保持你的屬性nameage私人,你可以定義你的構造方法。只需將方法分配到this.即可。對於私有屬性,您不必將屬性本身分配給您的實例:

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

    this.printInfo = function printInfo() { 
     console.log(name + " is " + age + " years old"); 
    }; 

    this.changeName = function changeName(newName) { 
     name = newName; 
    }; 
} 

但我更喜歡第一個示例。第二,每個新實例都會創建新的方法。在第一個示例中,所有實例都引用一個應該使用較少內存的原型對象。