先理解几个概念:
一、proto
产生
对象的proto属性并非ECMAScript标准,由于早期无法获取对象原型即对象内部[[Prototype]]属性,各大浏览器厂家对Object.prototype通过访问描述符实现proto的[1]getter与setter来达到访问调用对象的[[Prototype]],[[Prototype]]属性属于对象内部属性无法直接访问,此属性指向对象原型。
大致实现
Object.defineProperty(Object.prototype,'__proto__',{
get: function(){
return Object.getPrototypeOf(this) // 获取引用对象的[[Prototype]]
},
set: function(o){
Object.setPrototypeOf(this,o) // 设置引用对象[[Prototype]]属性关联的原型为o
return o
}
})
总结
所以本质上是通过访问器属性来获取与设置对象关联的原型,可以理解通过__proto__
能获取与设置原型的引用
Reflect.getPrototypeOf({}) === Object.getPrototypeOf({}) === {}.__proto__
二、函数的prototype属性
所有函数都有的prototype属性,js中函数也属于对象的子类型,所以函数也具备对象的__proto__
与普通对象类似都指向其原型。而这里的prototype属性,是函数独有的**。当函数使用new关键字修饰时,我们可以理解为此函数被当作构造函数使用也就是构造器。当函数被用作构造函数调用时,其prototype发挥了作用,使得由构造器new出来对象的__proto__
指向构造函数的prototype。**
最后一句话的意思即
function Foo(){}
const foo = new Foo()
console.log(foo.__proto__===Foo.prototype) //true 因为foo由构造函数构造而来
三、各类方法与属性的统称
构造函数中定义的方法,我们统称为静态方法,构造函数中定义的属性我们统称为静态属性。
在原型中定义的属性,我们统称为原型属性,在原型中定义的方法,我们统称为原型方法。
实例中的属性以及方法,我们也就称呼为实例属性/方法**。
当然方法也属于属性,只是我们通常把定义在对象中的函数称为方法。
原型
- 只有对象类型才有原型概念
- 普通对象(即使用对象字面量或者Object构造器创建的对象)的原型为
__proto__
属性,此属性其实是个访问器属性,并不是真实存在的属性。**或者可以使用es6的Reflect.getPrototypeOf(obj)
和Object.getPrototypeOf(obj)
方法获取对象的原型。** - 普通函数有2个属性,一个是是
__proto__
(与普通对象类似),还有一个是函数专有的prototype
属性,因为函数有双重身份,即可以是实例也可以是构造器,所以关系比较特殊。 - 不是所有的对象都会有原型,比如对象原型
Object.prototype
的原型Object.prototype.__proto__
就指向null,字典对象的原型也为null(把对象的__proto__
设置为null,或者使用Object.create(null)
创建一个没有原型的字典对象,但是这个对象还是属于对象类型),所以原始对象原型(Object.prototype)就是最原始的原型,其他对象类型都要继承自它。 - 箭头函数虽然属于函数,由Function产生,但是没有prototype属性没有构造器特性,所以也就没有所谓的constructor,就不能作为构造器使用。
原型链
由上图可以看出:
- 所有函数都是由Function函数构造器实例化而来
- 所有实例的原型都指向构造它的构造器的prototype
- 每个构造器自身特有的方法就是静态方法,原型上的方法可供所有继承它或间接继承它的实例使用
- 构造器也是函数,也是被Function实例化出来的,所以构造器的
__proto__
就是Function,但是构造器的prototype属性指向的原型,是此构造器实例化出来的实例所指向的原型;简单说构造器的prototype就是作为它的实例的原型
函数的原型链
- 在js中函数有多重身份,函数可以作为类就是构造器使用,定义静态方法,作为普通函数调用,
- 只有由原始函数构造器(Function)实例化的函数才拥有直接使用函数原型(Function.prototype)上面的内置方法,创建函数只能通过原始函数构造器生成,
- 普通函数作为构造器使用(new)时相当于类(class)使用,类的prototype就是实例的原型,我们可以给原型添加属性,给类添加属性时就相当于给构造器添加静态属性
- 普通函数在创建实例的时候,会生成一个实例的原型,此原型指向Object.prototype即原始对象原型,也就是继承对象原型,这么一来实例也继承了对象的原型,则实例也属于对象类型
———————————————————————————————————————————-
[1] js中的对象有两种属性,一种是数据属性,另外一种是访问器属性,也称存取器属性,存取器属性就是一组获取和设置值的函数,getter负责获取值,不带任何参数,setter负责存储值,在setter函数中,一切的return都是无效的。