1.构造函数
JS中的构造函数和普通函数没有本质区别,要用调用方式的不同来区分。
在调用构造函数时要用new Func()的方法来调用,此时函数会默认返回this
为了与普通函数区分,构造函数的函数名一般以大写字母开头。
function Person(name,age){
this.name=name;
this.age=age
}
var xiaoming=Person('xiaoming',20);//错误调用,xiaoming为undefined
var xiaoming=new Person('xiaoming',20)//正确调用,xiaoming是一个对象
xiaoming.constructor===Person//true
注意,任何对象都有构造函数,直接用大括号声明的对象其构造函数就是Object
var a={
name: 'obj'
}
a.constructor===Object//true
通过一个构造函数,我们就可以创造出很多不同的对象,有时这些对象需要公共的方法,最简单的方式是将方法直接放在构造函数中,但这样会造成内存的浪费:
function Person(name,age){
this.name=name;
this.age=age;
this.sayhi=function(){
console.log("hi,I'm "+this.name);
}
}
var xiaoming=new Person('xiaoming',20);
var xiaogang=new Person('xiaogang',20);
xiaoming.sayhi===xiaogang.sayhi//false,两个对象的公共函数不是用的同一份代码
2.原型对象
什么是原型对象?
JS中的所有对象,不管是用构造函数的方法创建的,还是直接用大括号创建的
都有一个_proto_ 属性指向它的原型对象,该原型对象有一个constructor属性指向它的实例对象的构造函数
有一个constructor属性指向它的构造函数,该构造函数有一个prototype属性指向它的原型对象
有点绕,画成图就是这个样子,从图中可以看出上面所说的各个属性的取值
再用代码来表示
function Person(name,age){
this.name=name;
this.age=age
}
var xiaoming=new Person('xiaoming',20)//
xiaoming.constructor===Person
//true,实例对象的constructor属性指向构造函数
Person===xiaoming.__proto__.constructor
//true,原型对象的constructor属性指向构造函数
xiaoming.__proto__===Person.prototyp;
//true,构造函数的prototype属性和实例对象的__proto__属性都只想原型对象
var xiaohong=new Person('xiaohong',20)
xiaohong.__proto__===xiaoming.__proto__;
//true,不同实例对象的原型对象是同一个
由此得出,xiaoming.__proto__属性和Person.prototype属性所指向的,就是由构造函数Person创建出的所有对象所共有的原型对象,那么只要将公共方法作为这个原型对象的方法,就可以实现一个方法供不同的实例对象调用
Person.prototype.sayHi=function(){
console.log("Hi,I'm "+this.name);
}
xiaoming.sayHi()//Hi,I'm xiaoming
xiaohong.sayHi()//Hi,I'm xiaohong
xiaoming.sayHi===xiaohong.sayHi///true,是同一个函数
3.原型链
现在我们知道,xiaoming的_proto_属性指向它的原型对象,而事实上,它的原型对象也有_proto__属性,指向它的原型对象
xiaoming.__proto__.__proto__//Object
xiaoming.__proto__.__proto__.__proto__//null
xiaoming.__proto__.__proto__.__proto__.__proto__//出错,null没有原型对象,也就没有__proto__属性
像这样,一个个的对象和原型对象串在一起,以实例对象为起点,以没有原型对象的null为终点,就组成了原型链。
上面我们将sayHi方法绑定到xiaoming的原型对象上,并且对象xiaoming可以调用该函数,那么是不是说sayHi是xiaoming自身的属性呢?我们用hasOwnProperty()方法来检验一下
xiaoming.hasOwnProperty('sayHi')//false
xiaoming.hasOwnProperty('sayhi')//true
可见,在构造函数内定义的函数sayhi是xiaoming自身的属性,而定义在原型对象上的函数sayHi则不是,那么为什么xiaoming可以调用该方法呢?
这是因为在JS中,调用一个对象的属性时,如果该对象自身没有这个属性,那么就会自动在它的原型对象上找,若还没有则顺着原型链往上找,若直到某一原型对象的原型对象为null,仍未找到,则会返回错误。也就是说,对象会继承其原型对象的属性
总结
- 对象都有构造函数,或是默认构造函数,或是自己写的构造函数
- 实例对象的__proto__属性指向其原型对象,constructor属性指向构造函数
- 构造函数的prototype属性指向原型对象,原型对象的constructor属性指向构造函数
- 实例对象->原型对象->原型对象的原型对象->…->null组成了原型链,对象会继承原型链上所有原型对象的属性
- 查找某一对象的某一属性时,若该对象自身没有该属性则会顺着原型链往上找,直到原型链的末尾