一.前言
假如我们创建了一个对象,只有一个成员变量name,
1
2
3
4
|
let ss = { name: 'shucheng' , } console.log(ss); |
结果如下:可以看出该对象有很多原型方法
通过上面的铺垫,我们知道::::::::::::::::::::::
JavaScript 的每个对象都继承另一个父级对象,父级对象称为原型 (prototype)对象。原型也是一个对象,原型对象上的所有属性和方法,都能被子对象 (派生对象) 共享。通过构造函数生成实例对象 时,会自动为实例对象分配原型对象。
而每一个构造函数都有一个prototype属性,这个属性就是实例对象的原型对象。
上图就可以明了的证明构造函数和原型对象之间的关系,那么我们再来看实例和原型对象的关系。依照上面的关系图,实例的内部指针 proto 指向原型对象,构造函数的原型也指向同一个原型对象。
对于原型的理解如下:
(1)所有的引用类型(包括数组,对象,函数)都有隐性原型属性(_proto_), 值也是一个普通的对象。(见上图,可以看出原型对象就是灰色的部分,其里面的属性值也是可以访问的。)
(2)所有的函数,都有一个 prototype 属性,值也是一个普通的对象。
(3)所有的引用类型的proto属性值都指向构造函数的 prototype 属性值。
要理解原型就要明白构造函数、实例对象、原型之间的关系,我用一张图来表示出他们之间的关系,如下图所示:
构造函数new出来一个对象,而每个对象都有一个constructor属性,该属性指向创建该实例的构造函数,构造函数的prototype属性是这个new出来的实例化对象的原型,实例对象通过__proto__或者object.getPrototype的方法获取原型。转化关系实例如下图所示
2.1.原型链
那么明白了三者之间的关系我们就来说一下原型链,从一个实例对象向上找有一个构造实例的原型对象,这个原型对象又有构造它的上一级原型对象,如此一级一级的关系链,就构成了原型链。原型链的最顶端就是Object.prototype ;
原型链的形成就是对象的属性和方法,有可能是定义在自身内的,也有可能会定义他的原型对象上。由于原型本身也是对象,又有了自己的原型,所以就会形成。
总的来说它是实现继承的主要方法,基本思想就是利用原型让一个引用类型继承另一个引用类型的属性和方法。
二.原型链的理解实例
1
2
3
4
5
6
7
8
9
10
11
|
function MyTest(name,age){ this .name=name; this .age=age;}MyTest.prototype={ "constructor" :MyTest, "showName" : function (){ console.log( this .name); }, "showAge" : function (){ console.log( this .age); }} var newMyTest01= new MyTest( '安静的木马' ,18); |
代码中我们通过new构造器MyTest,生成实例化对象newMyTest01,那么newMyTest01就有一个隐形属性__proto__指向MyTest.prototype原型对象。
①现在访问newMyTest01.name,因为newMyTest01对象上有name属性,值为“安静的木马”,这个可以直接访问到
②现在我们访问newMyTest01.showName方法呢,newMyTest01对象上并没有直接定义showName方法,访问不到,然后newMyTest01就会通过__proto__属性找到MyTest.prototype,也就是MyTest的原型对象,看看能不能访问到showName方法,现在是可以访问到了。
③如果MyTest.prototype对象里面也没有showName方法呢?那就通过MyTest.prototype对象的__proto__继续找,在原型链查找中,一般到Object.prototype.__proto__还查找不到时,就会终止查找,因为ECMA规范里说明Object.prototype.__proto__是原型链终点,值为null,而这个时候那个没有访问到的属性值设置为undefined,并不爆出语法错误,这是原型链查找和作用域链查找的一个显著区别。