JS 对象生命周期的秘密( 二 )


var Tom = Object.create(Person, { age: { value: 34 }, name: { value: "Tom" }});以这种方式配置的属性默认情况下不可写,不可枚举,不可配置 。不可写意味着之后无法更改该属性,更改会被忽略:
var Tom = Object.create(Person, { age: { value: 34 }, name: { value: "Tom" }});Tom.age = 80;Tom.name = "evilchange";var tomAge = Tom.age;var tomName = Tom.name;Tom.greet();console.log(`${tomAge} ${tomName}`);// Hello Tom// 34 Tom不可枚举意味着属性不会在 for...in 循环中显示,例如:
for (const key in Tom) { console.log(key);}// Output: greet但是正如咱们所看到的,由于JS引擎沿着原型链向上查找,在“父”对象上找到greet属性 。最后,不可配置意味着属性既不能修改也不能删除 。
Tom.age = 80;Tom.name = "evilchange";delete Tom.name;var tomAge = Tom.age;var tomName = Tom.name;console.log(`${tomAge} ${tomName}`);// 34 Tom如果要更改属性的行为,只需配writable(可写性),configurable(可配置),enumerable(可枚举)属性即可 。
var Tom = Object.create(Person, { age: { value: 34, enumerable: true, writable: true, configurable: true }, name: { value: "Tom", enumerable: true, writable: true, configurable: true }});现在,Tom也可以通过以下方式访问greet():
var Person = { name: "noname", age: 0, greet: function() { console.log(`Hello ${this.name}`); }};var Tom = Object.create(Person);Tom.age = 34;Tom.name = "Tom";var tomAge = Tom.age;var tomName = Tom.name;Tom.greet();console.log(`${tomAge} ${tomName}`);// Hello Tom// 34 Tom暂时不要过于担心“this” 。拉下来会详细介绍 。暂且先记住,“this”是对函数执行的某个对象的引用 。在咱们的例子中,greet() 在Tom的上下文中运行,因此可以访问“this.name” 。
构建JAVAScript对象
目前为止,只介绍了关于“prototype”的一点知识,还有玩了一会 Object.create()之外但咱们没有直接使用它 。随着时间的推移出现了一个新的模式:构造函数 。使用函数创建新对象听起来很合理,假设你想将Person对象转换为函数,你可以用以下方式:
function Person(name, age) { var newPerson = {}; newPerson.age = age; newPerson.name = name; newPerson.greet = function() { console.log("Hello " + newPerson.name); }; return newPerson;}因此,不需要到处调用object.create(),只需将Person作为函数调用:
var me = Person("Valentino"); 
构造函数模式有助于封装一系列JS对象的创建和配置 。在这里, 咱们使用字面量的方式创建对象 。这是一种从面向对象语言借用的约定,其中类名开头要大写 。
上面的例子有一个严重的问题:每次咱们创建一个新对象时,一遍又一遍地重复创建greet()函数 。可以使用Object.create(),它会在对象之间创建链接,创建次数只有一次 。首先,咱们将greet()方法移到外面的一个对象上 。然后,可以使用Object.create()将新对象链接到该公共对象:
var personMethods = { greet: function() { console.log("Hello " + this.name); }};function Person(name, age) { // greet lives outside now var newPerson = Object.create(personMethods); newPerson.age = age; newPerson.name = name; return newPerson;}var me = Person("Valentino");me.greet();// Output: "Hello Valentino"这种方式比刚开始会点,还可以进一步优化就是使用prototype,prototype是一个对象,可以在上面扩展属性,方法等等 。
Person.prototype.greet = function() { console.log("Hello " + this.name);};移除了personMethods 。调整Object.create的参数,否则新对象不会自动链接到共同的祖先:
function Person(name, age) { // greet lives outside now var newPerson = Object.create(Person.prototype); newPerson.age = age; newPerson.name = name; return newPerson;}Person.prototype.greet = function() { console.log("Hello " + this.name);};var me = Person("Valentino");me.greet();// Output: "Hello Valentino"现在公共方法的来源是Person.prototype 。使用JS中的new运算符,可以消除Person中的所有噪声,并且只需要为this分配参数 。
下面代码:
function Person(name, age) { // greet lives outside now var newPerson = Object.create(Person.prototype); newPerson.age = age; newPerson.name = name; return newPerson;}改成:
function Person(name, age) { this.name = name; this.age = age;}完整代码:
function Person(name, age) { this.name = name; this.age = age;}Person.prototype.greet = function() { console.log("Hello " + this.name);};var me = new Person("Valentino");me.greet();// Output: "Hello Valentino"注意,使用new关键字,被称为“构造函数调用”,new 干了三件事情


推荐阅读