原型
构造函数创建对象带来的问题
实例:
function Person(name, age){
this.name = name
this.age = age
this.SayName = function(){
console.log(this.name)
}
}
var per1 = new Person('小白', 20 );
var per2 = new Person('小黑', 24);
此时会出现问题:
console.log(per1.SayName == per2.SayName) // false
这样就会带来内存浪费的问题
解决的方法:可通过原型来解决
原型作用之一:数据共享,节省内存空间
如果:
var sayname = function SayName(){
console.log(this.name);
}
function Person(name, age){
this.name = name
this.age = age
this.SayName = sayname;
}
var per1 = new Person('小白', 20 );
var per2 = new Person('小黑', 24);
此时会有:
console.log(per1.SayName == per2.SayName) // true
而通过原型来添加方法,解决数据共享,节省内存空间
Person.prototype.eat = function(){
console.log('dinner!')
}
console.log(per1.eat == per2.eat) // true
查看实例对象会有:
实例对象中并没有eat方法,但仍能够使用
per1.eat() // dinner!
console.log(per1.eat == per1.__proto__.eat) // true
实例对象中有__proto__这个属性,叫做原型,其也是一个对象,这个属性是给浏览器使用的,并不是标准的属性,__proto__也可叫做原型对象
构造函数中有prototype这个属性,叫做原型,也是一个对象,这个属性是给程序员使用的,是标准属性
注:
因为实例对象是通过构造函数来创建的,构造函数中有原型对象prototype
实例对象的__proto__指向了构造函数的原型对象prototype
console.log(per1.__proto__ == Person.prototype) // true
通过原型添加属性
Person.prototype.gender = 'male';
构造函数、实例对象和原型对象之间的关系
构造函数可以实例化对象;
构造函数中有一个属性,叫做prototype,是构造函数的原型对象;
构造函数的原型对象(prototype)中有一个constructor构造器,这个构造器指向的就是自己所在的原型对象所在的构造函数;
实例对象的原型对象__proto__指向的是该构造函数的原型对象;
构造函数的原型对象(prototype)中的方法是可以被实例对象直接访问的
利用原型共享数据
什么样子的数据需要写在原型中?
需要共享的数据就可用写在原型中
属性可以共享,方法也可以共享
而实例对象中的方法是可以相互访问的
实例:
// 构造函数
function Animal(name, age){
this.name = name;
this.age = age;
}
var dog = new Animal('MaoMao', 1)
图示有:
// 手动修改构造器的指向
Animal.prototype = {
constructor: Animal,
attribute: 'cute',
play: function(){
console.log('与主人玩耍!!!');
},
eat: function(){
console.log('寻食');
this.play();
}
}
var dog = new Animal('MaoMao', 1)
实例对象使用属性和方法层层的搜索
实例对象使用的属性或者方法,先在实例中查找,找到了则直接使用,找不到,就去实例对象的__proto__指向的原型对象prototype中找,找到则使用,找不到则报错
原型链
其是一种关系,实例对象与原型对象之间的关系是通过原型__proto__来联系的
原型链指向是否可以改变?
- 构造函数中的this就是实例对象
- 原型对象中的方法中的this就是实例对象
function Book(name){
this.name = name;
console.log(this);
}
Book.prototype.read = function(){
console.log('读书!');
console.log(this);
}
试图改变原型的指向
实例:
console.log(b1.__proto__ == Book.prototype) // true
console.log(b1.__proto__.__proto__ == Book.prototype.__proto__) //true
console.log(Book.prototype.__proto__.__proto__) // null
function Magzine(){
}
Magzine.prototype.Browse = function(){
console.log('翻阅杂志!');
}
此时有:
// 试图改变原型的指定
Magzine.prototype = new Book('network');
var m2 = new Magzine();
m2.read(); // 读书!
m2.Browser(); // 报错
原型的指向是可以改变的
如果原型指向改变了,那么就应该在原型改变指向之后添加原型方法
原型最终指向了哪里?
- 实例中有__proto__原型
- 构造函数中有prototype原型
因为prototype是对象,所以prototype这个对象也有__proto__,那么它是指向哪里?
实例对象中的__proto__指向的是构造函数prototype
所以,prototype这个对象中的__proto__指向就应该是某个构造函数中的原型prototype
console.log(Book.prototype.__proto__)
console.log(Book.prototype.__proto__ == Object.prototype) // true
实例对象中属性和原型对象中的属性的重名问题
实例对象中访问这个属性,应该先从实例对象中找,找到了就直接使用,找不到就去指向的原型对象中去找,找到了就使用
问题:如果找不到呢?
JS是一门动态类型的语言,对象没有的属性,点了,对象就有了这个属性,即对象.属性名字,该对象就有了这个属性,但是这个属性并没有被赋值,所以,结果为:undefined
Book.prototype.type = 'paper';
console.log(b1.type) // paper
可以通过实例对象来改变原型对象中的属性值吗?
不能
b1.type = 'electronic';
console.log(b1.type) // electronic
console.log(Book.prototype.type) // paper
若想改变原型对象中的属性的值,可直接通过 原型对象.属性 = 值; 改变
Book.prototype.type = 'online'
console.log(Book.prototype.type) // online
实例:
<body>
<div id="dv"></div>
</body>
<script>
var divObj = document.getElementById('dv');
console.log(divObj);
console.log(divObj.__proto__); // HTMLDivElement
console.log(divObj.__proto__.__proto__) // HTMLElement
console.log(divObj.__proto__.__proto__.__proto__) // Element
console.log(divObj.__proto__.__proto__.__proto__.__proto__) // Node
console.log(divObj.__proto__.__proto__.__proto__.__proto__.__proto__) // EventTarget
console.log(divObj.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__) // Object
console.log(divObj.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__) // null
</script>