javascript使用构造函数和原型对象来进行面向对象编程
构造函数
在 JavaScript 中,构造器其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)。
以大写字母写构造函数(Capitalizing Constructors)
构造函数是用new创建对象
{
function Person(){
this.name ='millet'
}
let person1 =new Person();
console.log(person1.name)//millet
}
- 根据需要,构造函数可以接受参数,如果没有参数,可以省略括号
{
function Person(){
this.name ='millet'
}
let person1 =new Person;
console.log(person1.name)//millet
}
- 如果忘记使用new操作符,则this将代表全局对象window
{
function Person(){
this.name ='millet'
}
let person1 =Person;
console.log(person1.name)//Uncaught TypeError: Cannot read property 'name' of undefined
}
- constructor,每个对象在创建时都自动拥有一个构造函数属性constructor,其中包含了一个指向其构造函数的引用。而这个constructor属性实际上继承自原型对象,而constructor也是原型对象唯一的自有属性
{
function Person(){
this.name ='millet'
}
let person1 =new Person();
console.log(person1)//
**
Person {
name:"millet"
__proto__:
constructor:ƒ Person()
arguments:null
caller:null
length:0
name:"Person"
prototype:{constructor: ƒ}
__proto__:ƒ ()
[[FunctionLocation]]:VM206:3
[[Scopes]]:Scopes[1]
__proto__:Object
}
**
console.log(person1.constructor === Person)//true
console.log(person1.__proto__.constructor === Person)//true
}
- 返回值:
函数中的return语句用来返回函数调用后的返回值,而new构造函数的返回值有点特殊。
如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果
{
function Person(){
this.name ='millet'
return;
}
let person1 =new Person;
console.log(person1)//{name:"millet"}
}
- instanceof操作符可以用来鉴别对象的类型,针对丢失new的构造函数的解决办法是在构造函数内部使用instanceof判断是否使用new命令,如果发现没有使用,则直接使用return语句返回一个实例对象
{
function Person(){
this.name ='millet'
}
let person1 =new Person;
console.log(person1 instanceof Person)//true
}
** 优化构造函数写法 **
{
function Person(){
if(!(this instanceof Person)){
return new Person()
}
this.name ='millet'
}
let person1 =Person;
console.log(person1.name)//millet
let person2 =new Person;
console.log(person1.name)//millet
}
原型对象
为了解决构造函数的属性和方法无法被对象实例所共享的问题,我们可以把需要共享的属性和方法放在原型(prototype)对象上。原型对象上的所有属性和方法,都会被对象实例所共享。对于构造函数来说,prototype是作为构造函数的属性;对于对象实例来说,prototype是对象实例的原型对象。所以prototype即是属性,又是对象。
{
function Person(){
this.name ='millet'
}
let person1 =Person;
原型对象和实例对象的关系:
console.log(person.prototype=== Person1.__proto__ )//true
原型对象和构造函数的关系 :
console.log(Person.prototype.constructor === Person)//true
而实例对象和构造函数则没有直接关系,间接关系是实例对象可以继承原型对象的constructor属性:
console.log(person1.constructor === Person)//true
}
constructor属性是原型对象上的一个属性,可以被所有实例对象所共享。要注意的是,prototype是构造函数的属性,而constructor则是构造函数的prototype属性所指向的那个对象,也就是原型对象的属性。由于constructor属性是一种原型对象和构造函数的关系,所以在修改原型对象的时候,一定要注意constructor的指向问题。
构造函数和原型组合模式
{
function Person(){
this.name ='millet'
}
Person.prototype ={
constructor:Person,//重点,不写这一句的话console.log(person1.constructor === Person);//false
say: function(){
cosole.log(this.name )
}
}
let person1 =new Person;
console.log(person1.name)//millet
}
原型链的特点有
a:读取对象的某个属性时,JavaScript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。
b:如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overiding)。
c:一级级向上在原型链寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。