原型
定义
每个对象创建时从另一个对象上继承属性,另一个对象就是原型(原型对象)
获取原型的几种方法
function Test() {}
const test = new Test()
//1
console.log(Object.getPrototypeOf(test));
//2
console.log(test.__proto__);
//3
console.log(Test.prototype);
//4
console.log(test.constructor.__proto__);
//5
console.log(Reflect.getPrototypeOf(test))
//6
console.log(Test.prototype === test.__proto__)//true
特点
__proto__
和constructor
属性是对象所独有的- prototype属性是函数所独有的
- 函数也是对象,所有函数也有
__proto__
和constructor
- proto属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的proto属性所指向的那个对象(父对象)里找,一直找,直到proto属性的终点null,然后返回undefined,通过proto属性将对象连接起来的这条链路即我们所谓的原型链。
- prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.proto === Foo.prototype。
- constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function()。
原型链
定义
实例对象和原型对象通过__proto__
层层关联,直到null为止,形成原型链
检测
instanceof
检测是否在原型链上
function Test() {
}
const test = new Test()
console.log(test instanceof Test)//true
console.log(test instanceof Object)//true
console.log(Test instanceof Function)//true
console.log(Test instanceof Object)//true
实现instanceof
function MyInstanceof(objInstance, Func) {
let objProto = objInstance.__proto__
let FuncP = Func.prototype
while (true) {
if (!objProto) return false
if (objProto === FuncP) return true
objProto = objProto.__proto__
}
}
继承
class继承
// ES6的继承
class Parent {
constructor() {
this.name = "大人"
this.hairColor = "黑色"
}
}
class Child extends Parent {
constructor() {
super() //调用父级的方法和属性
this.name = "小孩"
}
}
let c = new Child()
console.log(c.name, c.hairColor) //小孩 黑色
let p = new Parent()
console.log(p.name, p.hairColor) //大人 黑色
原型链继承
// 定义父类
function Parent() {
this.name = 'Jack';
}
// 父类原型添加方法
Parent.prototype.getName = function () {
return this.name;
};
// 子类
function Child() {
}
// 子类的原型设置为父类Parent的实例
Child.prototype = new Parent();
Child.prototype.constructor = Child
// 实例化子类
const child = new Child();
console.log(child.getName()); // Jack
缺点
- 原型上的引用类型会被共享
- 无法传参数
借用构造函数继承
function SuperType() {
this.colors = ['red', 'blue', 'green'];
}
function SubType() {
// 继承 SuperType
SuperType.call(this);
}
const instance1 = new SubType();
instance1.colors.push('black');
console.log(instance1.colors); // [ 'red', 'blue', 'green', 'black' ]
const instance2 = new SubType();
console.log(instance2.colors); // [ 'red', 'blue', 'green' ]
缺点
- 实例无法访问父类原型上的方法
- 每个字类都会对父类实例化
组合继承
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function () {
console.log(this.name);
};
function SubType(name, age) {
// 继承属性
SuperType.call(this, name);
this.age = age;
}
// 继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
console.log(this.age);
};
const usr1 = new SubType('Nicholas', 29);
usr1.colors.push('black');
console.log(usr1.colors); // [ 'red', 'blue', 'green', 'black' ]
usr1.sayName(); // Nicholas
usr1.sayAge(); // 29
const usr2 = new SubType('Greg', 27);
console.log(usr2.colors); // [ 'red', 'blue', 'green' ]
usr2.sayName(); // Greg
usr2.sayAge(); // 27
缺点
多次实例化
原型式继承
const person = {
name: 'Nicholas',
friends: ['Shelby', 'Court', 'Van'],
};
const anotherPerson = Object.create(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Rob');
const anotherPerson2 = Object.create(person);
anotherPerson2.name = 'Linda';
anotherPerson2.friends.push('Barbie');
console.log(person.friends); // [ 'Shelby', 'Court', 'Van', 'Rob', 'Barbie' ]
缺点
- 引用类型会被共享
寄生式继承
function createAnother(original) {
const clone = Object.create(original); // 通过调用函数创建一个新对象
clone.sayHi = function () {
// 以某种方式增强这个对象
console.log('hi');
};
return clone; // 返回这个对象
}
const person = {
name: 'Nicholas',
friends: ['Shelby', 'Court', 'Van'],
};
const anotherPerson = createAnother(person);
anotherPerson.sayHi(); // hi
console.log(anotherPerson.name); // Nicholas
console.log(anotherPerson.friends); // [ 'Shelby', 'Court', 'Van' ]
寄生组合式继承
最优的继承方式
function inheritPrototype(subType, superType) {
subType.prototype = Object.create(superType.prototype);
subType.prototype.constructor = subType;
}