原型链理解(1)
1.首先定义一个构造函数Set
function Set() { //构造函数的开头要大写,区别于普通函数 }
2.这个构造函数,有一个原型对象 prototype。这个属性即使不手动定义,是每个function定义以后就会默认自带的属性
Set.prototype = {
}
1)什么是原型对象,就是Set 构造函数构造出来的对象的原始对象,new 出来的对象,都是基于这个原型对象生成的
2)不可以直接这样等号右边写{ } 这样会把原型和构造的联系给覆盖掉
3.Set.prototype 打印出来就是一个对象,这个对象就是原型对象。
4.这个原型对象有一个constructor 属性,指向这个原型对象的构造方法(构造函数)。所以这个原型是不可以被重写的。只能
Set.prototype.div = "sdds"; 这样给它赋值。
5. 这个原型有一个指针指向它的构造函数,。函数是一个对象,Function类的实例对象,所以constructor在内存里面存放的是对函数的引用,是引用类型。
6.定义一个对象setChile,由Set构造函数构造出来。(什么是构造函数,就是构造它的函数,谁创建了这个对象,谁就是这个对象的构造函数。)
function Set() { this.age = 32; } Set.prototype.name = 'set'; var setChile = new Set();
7.那么setChile 拥有一个属性__proto__,这个属性指向它的原型(这个对象原始的形态,就叫它的原型, 这个对象的初始形态)。
先把这句话打印出来直观的解释一下
这个对象有一个__proto__指向原型。其实属于原型里面的属性和方法,并没有在对象里面单独创建出来,而是通过这个__proto__去获取出来的。
我把这个对象打印出来,可以看到setChild其实只有一个属性age。但是都知道如果打印setChild.name也是可以打印出来的。其实本质上是通过
__proto__这个引用获取出来的,并不会去重新创建出来。但是构造Set()里面的age属性是会去内存里面创建出来的。
左边就是这个对象的属性,我把我理解的画出来了。
8.所谓的setChild.__proto__指向构造的原型,就是setChild.__proto__ == Set.prototype。指向就是 =,为什么用指向,因为它们等号右边是一个对象,对象是引用类型,所以它们是指针,指向内存里面的对象。其实把指向理解成 = 就可以了。
9.__proto__ 和 prototype 其实是一个东西。没什么区别。所以如果是原型里面的属性,所有的对象的那个属性都指向的是内存里面同一个地方。
10.修改构造出来实例对象的__proto__属性,就是去更改原型对象(Set.prototype)的属性。__proto__指向对象的原型。(原型对象,对象的原型听着就感觉很糊涂,听起来有什么区别,真的想骂人)(但是他们本来就是一个东西。只是一个单纯的说它是原始的那个对象,而对象的原型,针对的setChile这个对象来讲的,它的原型对象)
11.setChile 这个对象的构造函数就是Set,构造它的函数。但是这个constructor并不是这个对象本身的属性,是从它的__proto__里面获取的。
我可以证明为什么是从__proto__里面获取的(从原型的属性里面获取的)。
证明1:
如果更改了原型,把原型给重置了,原型里面的constructor就没有了,打印
证明2:
直接打印Set,它本身属性里面是没有constructor的,它的construtor就是在__proto__里面获取的。
12.setChile这个对象的属性,会先去{ age: 32 } 里面找,找不到这个属性,才会去查询它的原型,看看它有没有这个属性,再读出来。原型的所有属性它都有
即使prototype.age在下面去重写age,没有用的。打印出来仍然是 32。但是这个对象的原型里面的age仍然是100(
13.
对象的__proto__,这个对象的原始,这个对象的原型
对原型链的理解其实就这些了。
通过原型链继承的时候,就是运用的_proto__这个东西,一个对象的拥有 它.__proto__上面的所有属性,包括__proto__的构造constructor。(已经在上面详细的阐述了这个过程)
下面的不看也可以
其他1. function函数 是 Function 的对象,所以所有函数的构造都是Function
console.log(Set.constructor == Function); //true
其他2.如果要关系setChild 和 Object 对象的关系,那要看继承了。准备晚点再写
其他3.为什么要用构造和原型混合的写法,这样去写js创建对象。
因为,构造函数创建出来的对象里面的属性都是重新创建的,会去占内存。那么全都用原型去创建,不使用构造怎么样,也不行。已经知道了对象对原型属性的
获取的时候,都是使用的引用,全是同一个内存。所以,你把值定义在原型上面的时候,如果是引用类型,就会出现数据被覆盖的问题。举个例子
在实例对象上修改name值,怎么也不应该去修改创建它的初始值。但是js的机制就是通过原型链引用获取值。为了避免这样的情况,所以把所有的引用类型的变量都定义在构造里面,这样就避免了初始化数据(公用且不应该被修改的数据)会被重写这样的事情。