原文链接:https://blog.csdn.net/qq_34645412/article/details/105997336
原文链接:https://blog.csdn.net/yucihent/article/details/79424506
一、原型
什么是原型?
原型:每一个javascript对象(除null外)创建的时候,都会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中“继承”属性。
var obj = new Object();
创建一个对象obj的同时,会关联一个对象。它所关联的对象就是新建对象obj的原型。
二、prototype
在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。
prototype其实就是对象与对象的原型关联的属性。
例如:
var obj = new Object();
再例如:
function Animal(weight) {
this.weight = weight
}
函数本身也是一个对象,所以并不冲突。
每一个对象都会从原型中“继承”属性。
举个例子:
function Animal(weight) {
this.weight = weight;
// console.log(this); //Animal
}
Animal.prototype.height = 10
var cat1 = new Animal(20);
var cat2 = new Animal();
console.log(cat1);
console.log(cat2);
console.log('cat1', cat1.height)//10
console.log('cat2', cat2.height)//10
cat1和cat2都是实例化的Animal,它们的区别在于参数weight,一个20,一个undefined,但是cat1和cat2本身都是没有hieght属性的,但是却都能打印出height值均为10,产生这种结果的原因是cat1和cat2继承了原型Animal.prototype中的height属性。
三、proto
每个对象(除null外)都会有的属性,__proto__
,这个属性会指向该对象所在的构造函数的原型对象。
console.log(cat1);
console.log(cat2);
console.log(cat1.__proto__);
console.log('cat1.__proto__ === Animal.prototype', cat1.__proto__ === Animal.prototype) //true
console.log('cat2.__proto__ === Animal.prototype', cat2.__proto__ === Animal.prototype) //true
__proto__是实例指向原型的属性
prototype是对象或者构造函数指向原型的属性
四、constructor
每个原型都有一个constructor属性,指向该关联的构造函数。
console.log('Animal===Animal.prototype.constructor', Animal === Animal.prototype.constructor) //true Animal就是上面的构造函数
// 获取原型对象
console.log('Object.getPrototypeOf(cat1) === Animal.prototype', Object.getPrototypeOf(cat1) === Animal.prototype) //true
console.log('cat1.constructor === Animal',cat1.constructor === Animal) //true
更新图解:
为什么cat1.constructor === Animal为true ?
cat1中并没有属性constructor ,但是它的原型
cat1.__proto__
指向的是Animal.prototype,每一个对象都会从原型中“继承”属性,而Animal.prototype原型中是有属性constructor的,于是cat1的constructor属性继承自原型中的constructor属性。这里便能看出一点点原型链的影子 !!
五、实例与原型
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性。
function Animal(weight){
this.weight=weight;
}
Animal.prototype.name='animal';
var cat1=new Animal();
cat1.name='littleCat';
console.log('cat1.name',cat1.name);
delete cat1.name;
console.log('cat1.name',cat1.name);
删除属性前,是littleCat,删除属性后,该实例没有了name属性,找不到name属性的时候,它就会去 它的对象原型中去找也就是去cat1.__proto__
中也就是Animal.prototype中去寻找,而Animal.prototype中的name属性的值是animal,所以删除name属性后的值变成了原型中属性name的值animal。
如果cat1的原型中也没有name属性呢?会怎么办?去原型的原型中找?那么原型的原型是什么?
我们说原型是对象创建的时候关联的另一个对象,那么既然原型也是一个对象,那它也应该关联一个对象是原型的原型。
//原型链的终点
var obj=new Object();
console.log('Object.prototype.__proto__===null',Object.prototype.__proto__===null); //true
六、原型链
将原型的实例赋值给另一个对象,另一个对象再赋值给其他的对象,就会形成一条原型链。
function Animal(weight) {
this.weight = weight
}
Animal.prototype.name = 'animal'
var cat1 = new Animal();
console.log('cat1',cat1);
var pinkCat = cat1;
console.log('pinkCat',pinkCat)
console.log('pinkCat.name', pinkCat.name)
console.log('pinkCat.__proto__ === cat1.__proto__ === Animal.prototype', pinkCat.__proto__ === cat1.__proto__ === Animal.prototype)
console.log('pinkCat.__proto__ === cat1.__proto__',pinkCat.__proto__ === cat1.__proto__);
console.log('cat1.__proto__ === Animal.prototype',cat1.__proto__ === Animal.prototype);
console.log('pinkCat.__proto__ === Animal.prototype',pinkCat.__proto__ === Animal.prototype);
var samllPinkCat = pinkCat;
console.log('samllPinkCat',samllPinkCat);
console.log('samllPinkCat.name', samllPinkCat.name)
console.log('samllPinkCat.__proto__ === pinkCat.__proto__ === cat1.__proto__ === Animal.prototype',samllPinkCat.__proto__ === pinkCat.__proto__ === cat1.__proto__ === Animal.prototype)
console.log('samllPinkCat.__proto__ === pinkCat.__proto__',samllPinkCat.__proto__ === pinkCat.__proto__)
console.log('pinkCat.__proto__ === cat1.__proto__',pinkCat.__proto__ === cat1.__proto__);
console.log('cat1.__proto__ === Animal.prototype',cat1.__proto__ === Animal.prototype);
console.log('samllPinkCat.__proto__ === Animal.prototype',samllPinkCat.__proto__ === Animal.prototype);
原型链一层一层链接上去形成一条链条就是所谓的原型链。以上cat1实例化了Animal,cat1赋值给了pinkCat,pinkCat又赋值给了samllPinkCat,就形成看原型链,从samllPinkCat,pinkCat到cat1最后到Animal。
七、练习
//小练习1
Function.prototype.a = "a";
Object.prototype.b = "b";
function Person() {}
console.log(Person); //function Person()
let p = new Person();
console.log(p); //Person {} 对象
console.log(p.a); //undefined
console.log(p.b); //b
在这里,主要说一下p.__proto__.__proto__
p是Person()的一个实例,是一个Person对象,它拥有一个属性值__proto__。并且__proto__也是一个对象,它包含两个属性值constructor和__proto__。
console.log(p.__proto__.constructor); //function Person(){} 构造函数本身
console.log(p.__proto__.__proto__); //对象 Object{},拥有很多属性值
Person.prototype的隐式原型的constructor指向Object(),即
Person.prototype.__proto__.constructor == Object()
得到一个拥有多个参数的Object()函数。
所以虽然p没有b属性,会一直通过__proto__
向上查找,最后当查找到Object.prototype时找到,最后打印出b,向上查找过程中,得到的是Object.prototype,而不是Function.prototype,找不到a属性,所以结果为undefined,这就是原型链,通过__proto__向上进行查找,最终到null结束。
总结:
1.
p.__proto__.constructor == function Person(){}
2.p.___proto__.__proto__== Object.prototype
3.p.___proto__.__proto__.__proto__== Object.prototype.__proto__ == null
4.通过__proto__
形成原型链而非protrotype
//小练习2
//Function
function Function() { }
console.log(Function); // f Function(){}
console.log(Function.prototype.constructor); // f Function(){}
console.log(Function.prototype.__proto__); //Object.prototype
console.log(Function.prototype.__proto__.__proto__); //NULL
console.log(Function.prototype.__proto__.constructor); // f Object()
console.log(Function.prototype.__proto__ === Object.prototype); //true
八、原型的作用
1.数据共享 节约内存内存空间
2.实现继承
当方法写在构造函数Person内时,每次使用构造函数Person()实例化出对象的时候,就会给每个实例对象的方法开辟空间。可是当实例化许多对象的时候,就会浪费大量的空间,因为每个实例对象的方法功能是一样的,所以没有必要,我们将方法添加到了Person()构造函数的原型(prototype)上了,因此就只有一份方法。当实例对象需要使用这些方法的时候就在自己的__proto__
属性中找到并调用这些方法实现相应的功能。