成功者之所以能够登高望远,并不在于先天高人一筹,而是在于后天全力以赴。
今日学习内容
1、JS 红皮书 P224-227 第八章:对象、类与面向对象编程
今日笔记
1、原型模式: 每个函数都会创建一个 prototype 属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。原来在构造函数中直接赋给对象实例的值,可以直接赋值给它们的原型,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| function Person() {} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function() { console.log(this.name); }; let person1 = new Person(); person1.sayName(); let person2 = new Person(); person2.sayName(); console.log(person1.sayName == person2.sayName);
let Person = function() {}; Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function() { console.log(this.name); }; let person1 = new Person(); person1.sayName(); let person2 = new Person(); person2.sayName(); console.log(person1.sayName == person2.sayName);
|
这里,所有属性和 sayName()方法都直接添加到了 Person 的 prototype 属性上,构造函数体中什么也没有。但这样定义之后,调用构造函数创建的新对象仍然拥有相应的属性和方法。与构造函数模式不同,使用这种原型模式定义的属性和方法是由所有实例共享的。因此 person1 和 person2 访问的都是相同的属性和相同的 sayName()函数。要理解这个过程,就必须理解 ECMAScript 中原型的本质。
2、理解原型: 无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个 prototype 属性(指向原型对象)。默认情况下,所有原型对象自动获得一个名为 constructor 的属性,指回与之关联的构造函数。对前面的例子而言,Person.prototype.constructor 指向 Person。然后,因构造函数而异,可能会给原型对象添加其他属性和方法。
在自定义构造函数时,原型对象默认只会获得 constructor 属性,其他的所有方法都继承自Object。每次调用构造函数创建一个新实例,这个实例的内部[[Prototype]]指针就会被赋值为构造函数的原型对象。脚本中没有访问这个[[Prototype]]特性的标准方式,但 Firefox、Safari 和 Chrome会在每个对象上暴露__proto__属性,通过这个属性可以访问对象的原型。在其他实现中,这个特性完全被隐藏了。关键在于理解这一点:实例与构造函数原型之间有直接的联系,但实例与构造函数之间没有。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
function Person() {}
console.log(typeof Person.prototype); console.log(Person.prototype);
console.log(Person.prototype.constructor === Person);
console.log(Person.prototype.__proto__ === Object.prototype); console.log(Person.prototype.__proto__.constructor === Object); console.log(Person.prototype.__proto__.__proto__ === null); console.log(Person.prototype.__proto__);
let person1 = new Person(), person2 = new Person();
console.log(person1 !== Person); console.log(person1 !== Person.prototype); console.log(Person.prototype !== Person);
console.log(person1.__proto__ === Person.prototype); conosle.log(person1.__proto__.constructor === Person);
console.log(person1.__proto__ === person2.__proto__);
console.log(person1 instanceof Person); console.log(person1 instanceof Object); console.log(Person.prototype instanceof Object);
console.log(Person.prototype.isPrototypeOf(person1)); console.log(Person.prototype.isPrototypeOf(person2));
console.log(Object.getPrototypeOf(person1) == Person.prototype); console.log(Object.getPrototypeOf(person1).name);
let biped = { numLegs: 2 }; let person = { name: 'Matt' }; Object.setPrototypeOf(person, biped); console.log(person.name); console.log(person.numLegs); console.log(Object.getPrototypeOf(person) === biped);
let biped = { numLegs: 2 }; let person = Object.create(biped); person.name = 'Matt'; console.log(person.name); console.log(person.numLegs); console.log(Object.getPrototypeOf(person) === biped);
|
构造函数是创建对象的函数,原型对象(prototype)是这个构造函数“造出来”的所有对象所共享的属性和方法的集合。
你可以把构造函数 Person 看成一个「模具」,原型对象 Person.prototype 是这个模具自带的「说明书」,而通过这个模具造出来的每个实例对象(如 person1)都会自动连接到这份「说明书」。
Person() ← 构造函数(模具)
|
↓
Person.prototype ← 原型对象(说明书)
↑
|
person1.proto → 指向 Person.prototype