目录
一、原型
1、原型的特点
- 任何函数都有prototype属性
- prototype的属性值是object类型,它默认拥有constructor属性,其属性值是function,“指回函数”
图示关系
代码测试及运行结果
2、原型的作用
- prototype属性普通函数中没有实际用途,主要在构造函数(使用new调用的函数,通常首字母大写)中发挥作用
- 构造函数的prototype属性是它的实例对象的原型
注释:
new 表示xiaoming对象是通过new 关键字调用Peopel()构造函数创建的
任何对象都默认包含构造函数的原型对象,通过__proto__
属性可以访问到对象中的原型
测试代码及运行结果
2、原型链查找
原型链查找概念(重要)
每个 JavaScript 对象都拥有一个[[Prototype]]对象。 访问一个对象的属性或方法(对象中属性值为function类型的属性称为方法)时首先会查找其自身,然后就是它的 [[Prototype]]对象,之后再搜索此[[Prototype]]对象的 [[Prototype]]对象,直到找到这个属性后返回属性值或者查找到原型链的终点。这个查找过程称为原型链查找。
###(2) 原型链中的属性及其它类型的属性介绍
图示关系
简单说明:xiaoming对象本身有三个属性:name,age,sex;nationality是People.prototype对象中的属性。
测试代码及运行结果
nationality单词的意思是国籍
. 是成员访问运算符,可以访问对象的成员,语法 对象.成员属性
。
从xiaoming对象的构造函数People可知,xiaoming对象本身并不包含nationality属性,因此xiaoming.nationality在xiaoming对象本身上并未查找到该属性,因此去xiaoming的原型对象(根据构造函数的prototype是实例对象的原型,可知xiaoming的原型对象是People.prototype)中继续查找,由于我们提前在People.prototype对象中添加了nationality属性,因此可以查找到并返回。如下图所示
而我们没有通过People构造函数添加属性address,因此会去People.prototype的原型中继续查找,People.prototype中也没有定义,从此,继续递归的去原型的原型中查找,直到原型链的顶端也没有查找到该属性 (下文会介绍People.prototype的原型,以及原型链的顶端的概念,不要着急。这里重点关注在访问对象中的属性时,如果没有找到,会去原型链中查找),因此返回undefined
【了解一下】
我使用的是Google Chrome浏览器,不同类型或者不同版本的浏览器关于原型对象在浏览器中的显示方式可能不同,但是本质上的原型对象没有任何区别。浏览器提供的访问原型对象的属性名为__proto__ 在Google浏览器的新旧版本中相同。这样看到话,旧版本原型对象属性名的控制台显示和访问方式(名字)相同。之所以要介绍这一点区别,是因为我在MDN网站(前端技术CSS、HTML、JavaScript的官方文档网站)上看到一些文章,在使用__proto__表示原型,而不是[[Prototype]],以免大家以后遇到产生疑惑。
a)属性的遮蔽现象
遮蔽现象,如果对象本身定义了和原型中同名属性,会优先选择操作对象本身具有的属性。 这一特点,从原型链查找的概念中可以推断出。
测试代码及运行结果(本代码在上文代码的环境下继续书写)
访问了对象本身的nationality属性,而不是People.prototype中的nationality属性。
另外,可以看到,如果xiaoming.nationality如果出现在赋值运算符的左边,语义是给xiaoming对象添加属性,并不会进行原型链查找,进而修People.prototype.nationality。
我们可以对对象的属性进行增、删、改、查操作,对象.属性 出现在赋值运算符的左边,如果属性不在对象中,那么这时是添加属性。如果属性在对象中,那么这时是修改属性。删除的属性如果在对象中存在,则返回true,不存在则返回false;
从原型链查找的定义可知, 只有在访问属性的值时才会进行原型链查找。添加,删除,修改属性都是针对对象本身进行的,不会影响原型中的属性。
你也许可能产生过这样的疑惑:nationality属性定义在原型中,所有People类型的对象,都可以通过原型链查找访问到该属性。xiaoming对象设置它的国籍为“中国",如果在创建一个People类型的对象bob,它实际的国家是美国,于是修改People.prototype.nationality的属性为美国(开始为中国),那么xiaoming的国籍不也跟着修改了吗?的确会修改,这不符合现实啊。根据构造函数的prototype属性是它的实例对象的原型,可知同一构造函数创建的所有对象共享构造函数的原型,进而共享原型中的属性。nationality应该是每个对象“独享”的属性,而不是所有对象“共享”的属性。
因此,实例属性,属于每个实例对象,可以通过构造函数添加到对象。代码如下
function People(name, age, sex, nationality) {
this.name = name;
this.age = age;
this.sex = sex;
this.nationality = nationality;
}
哪么,什么样的属性适合添加到构造函数的原型中呢?答案很简单,需要被相同类型(构造函数所创建)的所有对象共享的属性。
举个例子,你开了一家公司,想为每个入职的员工分配一个编号,初始编号为1,后续有新人入职编号为最后一个入职人员的编号加一。
实现代码如下
function Employee(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.id = Employee.prototype