1、原型(原型对象)
创建函数的方式有一种为构造函数进行创建
//利用构造函数创建对象
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function() {
console.log('我会唱歌');
}
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
console.log(ldh);
ldh.sing();
zxy.sing();
但是,我们通过构造函数创建出来的对象会把复杂数据类型单独开辟一个独立的内存空间,就像我们代码中不同的对象会把sing()方法放在单独的空间,这样会造成内存浪费;
JS中规定,每个构造函数都有一个prototype属性,他是一个对象(简称为原型对象),这个对象的所有属性和方法,都会被构造函数所拥有
因此我们可以把不变的方法定义在构造函数中的prototype属性上,这样所有的对象示例都可以共享这些方法了
function Star(uname, age) {
this.uname = uname;
this.age = age;
// this.sing = function() {
// console.log('我会唱歌');
// }
}
Star.prototype.sing = function() {
console.log('我会唱歌');
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
console.log(ldh.sing === zxy.sing); //true
// console.dir(Star);
2、对象原型
上面我们代码可以看到,构造函数的实例,会调用sing()方法,但是ldh本身并没有此方法,那它是怎么调用的呢?
因为对象身上有一个__proto__属性(简称为对象原型),它指向构造函数的prototype原型对象,因此我们可以使用构造函数prototype原型对象的属性和方法
在此我们涉及到了,方法的查找规则: 首先先看ldh 对象身上是否有 sing 方法,如果有就执行这个对象上的sing;如果没有sing 这个方法,因为有__proto__ 的存在,就去构造函数原型对象prototype身上去查找sing这个方法
附上图,更好理解:
3、构造函数constructor
原型prototype和对象原型__proto__都有一个 constructor属性,称为构造函数,它指向构造函数本身,主要用于记录该对象引用哪个构造函数
Star.prototype.sing = function () {
console.log('我会唱歌');
};
Star.prototype.movie = function () {
console.log('我会演电影');
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
console.log(Star.prototype);
console.log(ldh.__proto__);
但是有时候,我们会在prototype上定义很多方法,利用 以上这种一一赋值的方法太麻烦,因此我们想采取对象的形式来赋值
Star.prototype = {
sing: function() {
console.log('我会唱歌');
},
movie: function() {
console.log('我会演电影');
}
}
如果我们这样做了,相当于赋值给prototype整个属性一个对象形式的数据,就会把prototype中的constructor覆盖掉,这样constructor的指向就出现了问题,因此我们大部分使用constructor的目的是:手动的利用constructor 这个属性指回 原来的构造函数
Star.prototype = {
// 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
constructor: Star,
sing: function() {
console.log('我会唱歌');
},
movie: function() {
console.log('我会演电影');
}
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
console.log(Star.prototype);
console.log(ldh.__proto__);
console.log(Star.prototype.constructor);
console.log(ldh.__proto__.constructor);
这样就不会出问题了
4、三者关系
5、原型链
我们在这,再重新捋一遍:
1、首先构造函数Star有prototype属性, prototype属性中也有一个constructor属性指向引用的构造函数,这是Star和原型prototype两者的关系。
2、接着我们再来说,Star的实例对象ldh,ldh他有__proto__属性,用来指向__prototype__属性。
我们还知道不管是prototype还是proto都有constructor属性,它用于指向该对象引用了哪个构造函数,但是此图,我们并没有看到ldh.proto身上的constructor属性指回Star构造函数,是因为它通过proto指向prototype,prototype中的constructor再指向Star构造函数,最终也指向了Star构造函数
3、最后我们说一下原型链的问题:
通过Star构造函数创建出来的ldh对象实例,用proto指向prototype原型对象,但是prototype原型对象本身也是一个对象,他也有proto属性,指向更高一级的Object原型对象,Object原型对象也有自己的一套(互相指来指去),同时Object原型对象他也是一个对象,也拥有proto属性,它再指向更高一级,以此类推,直到指向null或undefined结束,这就是原型链
补充:js成员查找机制