原型
太长直接看总结
所有创建的实例对象都有一个__proto__属性,指向原型对象,所有实例对象的构造函数都有prototype属性,指向原型对象,原型对象有constructor函数,这个函数就是构造函数(__proto__是早期浏览器为了方便我们访问到原型而增加的一个属性,现在不推荐直接使用这个属性访问原型)
- 所有对象可以通过
__proto__找到Object.prototype - 所有函数可以通过
__proto__找到Function.prototype Function.prototype.__proto__又指向Object.prototype,Object.prototype.__proto__指向null- 如果试图引用对象的某个属性,会首先在对象内部寻找该属性,直至找不到,然后会在该对象的原型里去寻找这个属性,这种搜索轨迹,类似一条长链,prototype在这条链中充当链接的作用,所以把这种实例与原型的链条称作原型链
原型链经典图例
构造函数
构造函数从性质上说,就等同于普通函数,只是使用了new来构造实例对象
function Animal(type){
this.type = type;
}
// 通过new构造实例对象,这个时候Animal就是构造函数,也可以称作就是cat的构造函数
const cat = new Animal('cat');
我们一步一步来解析构造函数和对象的关系
首先明确
-
所有对象都有一个原型对象,对象以其原型为模板,从原型继承方法和属性
-
对象有一个
__proto__属性指向原型,构造函数有一个prototype属性同样指向原型 -
原型上有一个
constructor属性,指向构造函数
cat对象的控制台输出,可以看到原型上有constructor指向Animal,而cat的原型也是一个对象,这个原型的原型,又指向的是Object的原型
function Animal(type)函数的控制台输出,可以看到Animal.prototype和cat.__proto__指向的是同一个对象,也就是原型
这里可以看到Animal.__proto__指向了一个看起来很奇怪的东西,但是下面又可以看到 Animal.__proto__ === Function.prototype,这里就是因为,函数在js中很特殊,函数也是对象,只要是对象,就存在原型,而函数的构造函数就是Function(),所以函数的原型就是Function.prototype
这里总结一下原型、构造函数、对象的一个基本关系图
上面我们可以看到函数Animal,作为对象的原型是Function.prototype,那么Function.prototype的__proto__,又指向什么呢,根据输出我们可以看到,Function.prototype的__proto__ === Object.prototype,这里就追溯到了Object的原型
这里可能就会有点迷惑了,一会是Object,一会又是Function,下面一张图我们可以梳理出一个总结
这里做一个总结
- Animal作为函数有prototype指向原型,函数同时也是对象,对象就有
__proto__指向构造这个函数的原型,这是两个不同的原型 - Object和Function都是函数,所以同时存在prototype和
__proto__ - 所有函数的
__proto__都会指向Function.prototype Function.prototype和Function.__proto__相同Function.prototype.__proto__指向Object.prototypeObject.prototype.__proto__指向最终的结果null
原型链
每个对象拥有一个原型对象,通过__proto__指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null。这种关系被称为原型链 (prototype chain),通过原型链一个对象会拥有定义在其他对象中的属性和方法
可以看到,在原型上定义的属性,可以直接通过实例访问到
原型链的访问路径
总结
- 所有对象都有
__proto__,所有构造函数都有prototype属性,这两个属性不一样,但是指向的是同一个对象 __proto__是ES6标准化的一个参数,但是因为访问的性能问题,不推荐使用,获取原型推荐使用Object.getPrototypeOfFunction.prototype和Function.__proto__,是指向的同一个对象- 引用类型
constructor属性值可以修改,对于基本类型来说是只读的,null和undefined没有constructor - Symbol很特殊,Symbol不支持通过new调用,但是原型上有
constructor属性 - 每个对象都有一个原型对象,通过
__proto__一层一层往上,最终指向null,这就是原型链
最后附上两个常用与原型相关的必会手写原理方法
手写实现new
function create(fn, ...args) {
if (typeof fn !== 'function') {
throw new Error('类型错误');
}
const newObj = Object.create(fn.prototype);
const res = fn.apply(newObj, args);
return res instanceof Object ? res : newObj;
}
手写实现instanceof
x instanceof Y,通过原型链判断对象x的原型链上是否有Y.prototype
instanceof的原理就是基于原型链,一层一层查找 __proto__,如果和 Y.prototype 相等则返回 true,如果一直没有查找成功则返回 false。
function A(){}
function B(){}
const a = new A();
a instanceof A // true
a instanceof B // false
// 手动实现instanceof
function myInstanceof(left, right) {
if (typeof left !== 'object' || left === null) {
return false;
}
let prototype = Object.getPrototypeOf(left);
while (true) {
if (prototype === null) {
return false;
}
if (prototype === right.prototype) {
return true;
}
prototype = Object.getPrototypeOf(prototype);
}
}