1. 基本概念:
“原型属性”也可以叫做“原型”(prototype):所有函数都有prototype,我觉得可以理解为python中的类属性,不需要通过实例,直接用类(es5就是函数名)可以调用,下面列举了三种创建函数的方法,函数创建后都有prototype属性,prototype指向“原型对象”。
// 函数声明
function F1() {
};
// 表达式定义
let F2 = function () {
};
// 函数构造
let F3 = new Function('n1', 'n2', 'return n1+n2');
console.info(F1.prototype) //F1 {}
console.info(F2.prototype) //F2 {}
console.info(F3.prototype) //anonymous {}
原型对象(prototype所指向的对象):这玩意主要就是用来继承用的,包含实例的方法和属性。说白了也就是一个对象,用来定义函数对象的属性、方法,默认情况下它包含一个constructor属性,如果你重新定义可以覆盖constructor属性。
原型对象与构造函数配合一起,就形成一个类了,然后构造函数接收每次初始化对象的初始值,原型对象就提供类模板。而在其他java、python中都写在class中,当然es6也加入了class;以下的简单代码帮助理解上面说的,定义一个动物类,根据构造函数创造不同的动物,如果需要创建特殊动物,可以继承然后添加一些特殊属性、方法再创建。---------总结es5的类可以用“构造函数+构造函数的prototype”来定义,类的对象使用“new构造函数”来生成。
// 构造函数
let Animal = function (name) {
this.name = name
}
// 原型对象定义
Animal.prototype.getAnimal = function () {
return this.name
}
// 创建对象,会继承Animal.prototype
let dog = new Animal('dog')
let cat = new Animal('cat')
console.info(dog.getAnimal())
console.info(cat.getAnimal())
上图展示了构造函数、原型对象、具体对象的属性、方法及属性值。
普通对象与函数对象:通过函数对象 new一下可以得到普通对象,把函数对象理解为类,普通对象为实例。如下代码加强理解,可以看到函数对象new完以后生产的对象是object;函数对象中都有prototype前面已经说过了(为什么函数对象中会有这个属性以后读取更深以后再解答)
// Animal函数对象
let Animal = function (name) {
this.name = name
}
// 原型对象
Animal.prototype.getAnimal = function () {
return this.name
}
// 通过函数对象创建普通对象
let dog = new Animal('dog')
console.info(typeof dog) //object
总结: 函数对象通过new Function()可以得到,Function对象可以构造函数对象。而函数对象又可以new一个普通对象出来。
__proto__:所有对象都有__proto__这个属性,这个属性指向对应“函数对象(就理解为类)”的prototype,这也是实现原型链的根本,在书中一般都用[[prototype]]。
构造函数和constructor属性:其实前面的图已经标注了,概念后续补上。
原型链:ES中的继承主要用原型链来实现,记住了这玩意主要用来实现继承。其基本思想是:利用原型让一个引用类型继承另一个引用类型的属性和方法(js高级程序设计说的),还是用以上的例子来说明
let Animal = function (name) {
this.name = name
}
// 原型对象定义
Animal.prototype.getAnimal = function () {
return this.name
}
// 创建对象,会继承Animal.prototype
let dog = new Animal('dog')
let cat = new Animal('cat')
console.info(dog.valueOf())
console.info(cat.valueOf())
跟上面例子就差了最后console打印部分,我们定义的Animal没有写valueof()为什么可以调用这个方法呢?到底是什么鬼?这就是原型链搞的鬼。我再把上面的图形完善一下
上图还有两个属性的指向没有画,第一、Animal的__proto__指向;第二、Animal.prototype的__proto__指向。
这个图把Animal.prototype的__proto__指向添加了,
图形说明:
1. Animal.prototype是个普通对象,ES中Object是所有对象的基础。所有引用类型都继承了Object;所有函数的默认原型是都Object的实例,因此Animal.prototype的__proto__指向Object.prototype,这个一定要理解。
2. 我们调用cat.valueOf()解释器会先找dog实例中有没有这个方法,没有就在Animal.prototype中查找,没有再去Object.prototype中查找。然后就有了cat.valueOf()的运行结果:Animal { name: 'cat' }。
3. Object构造函数的prototype也指向Object.prototype。
最后把Animal的__proto__指向补完基本就差不多了。