[[Prototype]]
在 JavaScript 中,对象具有一个隐藏属性 [[Prototype]],它指向另一个对象(或者 null),称为原型对象。当我们尝试访问对象缺失的属性或方法时,JavaScript 会自动从原型对象上找。
这个属性是隐藏在内部的,我们无法直接访问,但是,浏览器给我们提供了访问它的方式:__proto__。
let person = {name: 'Tom',age: 18};let student = {studentId: 1};student.__proto__ = person;console.log(student.name); // 'Tom'
给对象添加原型对象可以称为原型继承,这个继承关系可以一直串联,形成原型链。
注意:
- 引用不能形成闭环,否则 JavaScript 会报错
__proto__是 [[Prototype]] 的 getter/setter,它们并不是一个东西,实现不相同for...in会遍历继承的属性,但是 Object.keys() 不会F.prototype
如果 F.prototype 是一个对象,那么 new 操作符会使用它为新对象设置 [[prototype]]。 ```javascript let person = { name: ‘Tom’, age: 18 }; function Student(studentId) { this.studentId = studentId; } Student.prototype = person;
let student = new Student(1); console.log(student.name); // ‘Tom’ console.log(student.proto === Student.prototype); // true
注意:只有在指定了 F.prototype 的值之后 new 出来的对象才有效果,之前已经存在的保持自身的值<br />当然,如果我们没有指定 F.prototype,它会有一个默认值,默认值中只有有一个属性 constructor,指向构造函数本身。```javascriptfunction Student() {}console.log(Student.prototype.constructor === Student); // true
对于一开始的 Student.prototype = person;,我们直接重写了它的整个 prototype,使得 constructor 丢失了。
有关原型方法
Object.getPrototype(obj)
Object.setPrototype(obj, proto)
Object.create(proto [, descriptors])
利用给定的 proto 和属性描述创建一个空对象
let person = {name: 'Tom',age: 18};let student = Object.create(person, {studentId: {value: 1}});console.log(student.studentId); // 1
继承
原型链
function Parent() {this.parentProperty = 'parentProperty';}Parent.prototype.getParentProperty = function () {return this.parentProperty;}function Son() {this.sonProperty = 'sonProperty';}Son.prototype = new Parent();Son.prototype.getSonProperty = function () {return this.sonProperty;}let instance = new Son();console.log(instance.getParentProperty()); // 'parentProperty'
原型链实现继承存在一个问题:如果父构造函数包含引用类型时,会在所有实例中共享。
盗用构造函数
function Parent() {this.nums = [1, 2, 3];}function Son() {Parent.call(this);}let instance1 = new Son();instance1.nums.push(4);console.log(instance1.nums); // [1, 2, 3, 4]let instance2 = new Son();console.log(instance2.nums); // [1, 2, 3]
盗用构造函数使得每个实例不再共享父构造函数的引用类型的值,但是没法重用原型的方法,必须在构造函数中创建。
组合继承
function Parent(name) {this.name = name;this.nums = [1, 2, 3];}Parent.prototype.sayName = function () {console.log(this.name);}function Son(name, age) {Parent.call(this, name);this.age = age;}Son.prototype = new Parent();Son.prototype.sayAge = function () {console.log(this.age);}let instance1 = new Son('Alice', 20);instance1.nums.push(4);console.log(instance1.nums); // [1, 2, 3, 4]instance1.sayName(); // 'Alice'instance1.sayAge(); // 20let instance2 = new Son('Bob', 18);console.log(instance2.nums); // [1, 2, 3]instance2.sayName(); // 'Bob'instance2.sayAge(); // 18
原型式继承
function object(o) {function F() {};F.prototype = o;return new F();}
这个主要用于在现有对象的基础上再创建一个新对象,新对象的原型就是 o。
ES5 新增的方法 Object.create() 就是这个东西的规范化,并且支持属性描述符定义属性。
寄生式继承
function createAnother(original) {let clone = object(original); // 通过调用函数创建一个新对象clone.sayHi = function () { // 以某种方式增强对象console.log('Hi');};return clone; // 返回这个对象}
终极方案:寄生式组合继承
function inheritPrototype(parent, son) {let prototype = Object.create(parent.prototype);prototype.constructor = son;son.prototype = prototype;}function Parent() {}function Son() {Parent.call(this);}inheritPrototype(Parent, Son);
