原型
什么是原型?
- 实例对象中有一个属性:__ proto__,是对象,叫原型,但不是标准的属性,是浏览器使用的,有的浏览器如谷歌和火狐支持,但是IE8不支持
- 构造函数中有一个属性:prototype,也是对象,也叫原型,是标准的属性,是程序员使用过的,所有的浏览器都支持
- 原型:__ proto__或者prototype(两者指向的是同一个对象),都是原型对象
原型的作用
通过原型来添加方法(把方法添加到构造函数的原型对象中,构造函数的所有实例对象就都有这个方法,并且是同一个方法),解决数据共享的问题,节省内存空间
构造函数、实例对象和原型对象之间的关系
通过构造函数创建的实例对象,构造函数中有一个prototype属性指向的是其原型对象,原型对象中有一个constructor属性指向的是其所在的构造函数自身,实例对象中有一个__ proto__属性指向的是其构造函数的原型对象,构造函数的原型对象(prototype)的方法可以被其实例对象直接访问
(注意一点:实例对象可以调用其构造函数的原型对象的属性和方法,但不是说实例对象就有了这些属性和方法 —— console.dir(对象)的结果中是没有的,这些属性和方法在其下划线原型中)
原型的简单语法
指的是将需要添加给构造函数的原型的属性和方法组成对象直接赋给构造函数的原型对象
Person.prototype = { // 这种方式需要手动修改构造器的指向,不然输出的实例对象和构造函数的原型对象中没有构造器
constructor: Person,// 手动在原型对象中添加构造函数属性
sayHi: function () { // 需要进行共享的数据
console.log("您好");
}
};
- 原型对象中的方法是可以相互调用的
- 实例对象调用的属性和方法,先在实例对象中查找是否有这些属性和方法,找到了则直接使用,找不到就到实例对象的__ proto__指向的原型对象prototype中找,找到了就使用,找不到就报错 —— 原型链
- 我们可以为系统的内置对象的原型对象中添加方法,也就是我们可以修改系统的源码
- 把局部变量变为全局变量 —— 把局部变量给window作为window的一个属性即可
原型链
什么是原型链?
指的是一种关系,是实例对象和原型对象之间的关系,这种关系是通过实例对象的下划线原型(__ proto__)联系的
- 构造函数中的this就是实例对象,原型对象方法中的this也是实例对象
- 实例对象的原型__ proto__指向的是其所在构造函数的原型prototype,构造函数的原型对象(prototype)的指向改变,那么实例对象的原型指向也会改变
- 原型的指向可以改变,即原型链是可以改变的
- 实例对象中有__ proto__原型,构造函数中有prototype原型,prototype是对象,所以,prototype对象中也有下划线原型,实例对象的下划线原型指向的是其所在的构造函数的原型对象,那么prototype对象的下划线原型对象指向的也是某一个构造函数中的原型对象 —— 只要是对象就有下划线原型,只要是下划线原型那么它指向的一定是某一个构造函数的原型对象,原型链的终端是Object的原型对象,Object的原型对象的下划线原型是null
- 如果原型对象指向改变了,欲添加原型方法,应该先改变指向再给原型对象添加方法
实例对象和原型对象属性重名问题
实例对象访问某个属性,会先在实例对象中找是否存在这个属性,找不到就到其原型对象中找,最终找不到就是undefined(不会报错,因为JS是一门动态类型的语言,只要点了对象就有了这个属性,又由于属性没有赋值所以就是undefined)—— 层层查找
一个很神奇的原型链
实例对象中的下划线原型一定指向的是某个构造函数的原型对象
- divObj.__ proto__ —— HTMLDivElement.prototype
- HTMLDivElement.prototype中的__ proto__ —— HTMLElement.prototype
- HTMLElement.prototype中的__ proto__ —— Element.prototype
- Element.prototype中的__ proto__ —— Node.prototype
- Node.prototype中的__ proto__ —— EventTarget.prototype
- EventTarget.prototype中的__ proto__ —— Object.prototype
- Object中没有下划线原型对象,即Object.prototype中的__ proto__ —— null