JS/TS中的原型对象和原型链是整个类型系统的底层原理,示例代码为JS,TS和UTS也是一样。实际应用开发中,尽少会碰原型,但听说面试时经常会问,我是纯野生程序员,没有面过,不清楚!
- 定义一个类或函数时,JS引擎会自动创建一个对应的原型对象,你把它想象成一个配套设施。
- 每个类或函数,都有一个属性prototype,指向自己的原型对象。如下例中的Person.prototype。prototype不是实例属性,也不是静态属性,但它属于类层面,与静态属性比较相似。
- 当实例化一个对象时,会自动为对象创建一个__proto__属性,这个属性指向该对象所属类型的原型对象。如下例中,p1.proto==p2.proto==Person.prototype。即某类型的所有实例对象的__proto__属性,都指向该类型的原型对象。
- ①构造函数、②类中以"xxx(){}"方式定义的方法、③类中以"xxx(){this.property = "mc"}"方式定义的属性,都会被放到原型对象上,如下例中的sayHi()方法和somePropertyInMethod属性。除此之外,还可以通过p1.__proto__或者Person.prototype,直接定义原型对象里的方法或属性,但JS框架建议通过获取原型对象后,再进行相关操作,获取方式为:Object.getPrototypeOf(p1)。
- 类中通过等号方式定义的属性和方法,会被放在实例对象上,如下例中的name、age、jump()、sing()均在实例对象自身上。
- 原型对象也是一个对象,每个原型对象上也有一个__proto__属性,该属性指向父类的原型对象,依次类推,形成一个原型对象组成的引用链条,即为原型链。原型链的最后的原型对象是Object的原型对象,而Object的原型对象的__proto__属性,指向了null。
- 当实例对象调用一个属性或方法时,会先在自身上找,如果找不到,就到所属类型的原型对象上找,如果还找不到,就延着原型链找,直到Object类型的原型对象为止。所以下例中,如果我们在类中重新定义一个方法"sayHi=()=>{}",这个方法在实例对象本身上,则调用p1.sayHi()时,会优先调用这个sayHi方法,而不是原型对象中的那个。
- 由于所有实例对象都可以访问原型对象,所以,它适合放公用的方法或属性,这样就不需要每实例化一个对象,就要创建所有方法或属性。同时,在原型对象的基础上,JS/TS实现了原型链,从而实现了类型的继承。
class Person{
//通过等号定义的属性和方法,在实例对象上
constructor(name,age){
this.name = name;
this.age = age;
this.jump = ()=>(console.log("跳"););
}
sing = ()=>(console.log("唱"););
//xxx(){}方式定义的方法,在类的原型对象上
sayHi(){
console.log("我是"+this.name+"今年"+this.age+"岁了");
}
//这里定义的属性,在类的原型对象上
someMethod() {
this.somePropertyInMethod = 'value';
}
}
const p1 = new Person("zs",18);
const p2 = new Person("ls",28);
console.log(p1, p2, Person.prototype)
//访问原型对象的方式
p1.__proto__
Object.getPrototypeOf(p1)
Person.prototype