构造函数,原型对象,实例对象

Posted qiqi715

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了构造函数,原型对象,实例对象相关的知识,希望对你有一定的参考价值。

一、构造函数,原型对象,实例对象

1.1 基本概念

1、对象:

  • 属性和方法的集合,即变量和函数的封装。
  • 调用构造函数产生的实例对象, 每个对象都有一个__proto__属性,指向这个对象的构造函数的原型对象

2、构造器函数:

  • 用于创建对象的函数,通过new关键字生成对象。
  • 函数名一般首字母大写的。
  • 每创建一个函数, 该函数都会自动带有一个prototype属性。该属性是一个指针,指向一个对象,该对象称之为原型对象(后期我们可以使用这个原型对象帮助我们在js中实现继承)

3、原型对象:

  • 默认有一个属性constructor,该属性也是一个指针,指向其相关联的构造函数
  • 原型对象其实就是普通对象(但 Function.prototype 除外,它是函数对象,但它很特殊,它没有prototype属性(前面说到函数对象都有prototype属性))
function Person(){};
console.log(Person.prototype) //Person{}

console.log(typeof Person.prototype) //Object
console.log(typeof Function.prototype) // Function,这个特殊
console.log(typeof Object.prototype) // Object
console.log(typeof Function.prototype.prototype) //undefined

1.2 总结

三者的关系是

  • 每个构造函数都有一个指向原型对象的指针,
  • 原型对象上包含着一个指向构造函数的指针,
  • 而实例都包含着一个指向原型对象的内部指针

1.3 举例

function People(){
    this.type='人'
}
People.prototype.showType=function(){
    alert(this.type);
}

var person=new People();
//调用原型对象上面的方法
person.showType();//最后结果弹框弹出人
  • 构造函数People(), People.prototype指向原型对象,其自带属性construtor又指回了People,即People.prototype.constructor==People.
  • 实例对象person由于其内部指针__proto__指向了原型对象,所以可以访问原型对象上的showType方法。

技术图片

person1.__proto__ == Person.prototype

Person.prototype.constructor = Person

person1.constructor == Person

二 原型链

举例1

在第一部分我们说到,所有的实例都有一个内部指针指向他的原型对象,并且可以访问到原型对象上的所有属性和方法。

  • person实例对象的__proto__属性,指向了People的prototype属性,即原型对象,可以访问People原型对象上的所有属性和方法
  • 如果People原型对象变成了某一个类的实例aaa,这个实例又会指向一个新的原型对象AAA,那么person此时能访问aaa的实例属性和AAA原型对象上的所有属性和方法了。
  • 同理新的原型对象AAA碰巧又是另外一个对象的实例bbb,这个对象实例指向原型对象BBB,那么person就能访问bbb的实例属性和BBB原型上的属性和方法了。
    技术图片

举例2

function People(){
    this.type='人'
}
People.prototype.showType=function(){
    alert(this.type);
}

function Woman(){
    this.sex='女';
    this.age=34;
}
Woman.prototype = new People();

var w=new Woman();
console.log('大家好,我的种类是:'+w.type+",我的年龄是:"+w.age+",我的性别是:"+w.sex);
//输出结果:
//大家好,我的种类是:人,我的年龄是:34,我的性格是:女
//w.type是People上面定义的type

解释一下以上代码.

首先先定义了People构造函数,通过new People()得到实例,会包含一个实例对象type和一个原型属性showType。

另外定义一个Woman构造函数,然后情况发生变化,本来构造函数Woman的prototype会执行Woman的原型对象,但是我们这里稍有改变,将Woman构造函数的prototype指向了People实例对象覆盖了Woman的原型对象。

当Woman的实例对象woman去访问type属性时,js首先在woman实例属性中查找,发现没有定义,接着去Woman的原型对象上找,woman的原型对象这里已经被我们改成了People实例,那就是去People实例上去找。先找People的实例属性,发现没有type,最后去People的原型对象上去找,终于找到了。这个查找就是这么一级一级的往上查找。

举例3

function People(){
    this.type='人'
}
People.prototype.showType=function(){
    alert(this.type);
}
function Woman(){
    this.sex='女';
    this.age=34;
    this.type='女生';//如果这里定义了type属性,就不会层级查找,最后在People找到该属性
}
Woman.prototype=new People();
var w=new Woman();
console.log('大家好,我的种类是:'+w.type+",我的年龄是:"+w.age+",我的性别是:"+w.sex);
//输出结果:
//大家好,我的种类是:女生,我的年龄是:34,我的性格是:女

这就说明,我们可以通过原型链的方式,实现 Woman继承 People 的所有属性和方法。

总结
就是当重写了Woman.prototype指向的原型对象后,实例的内部指针也发生了改变,指向了新的原型对象,然后就能实现类与类之间的继承了

练习

练习1

构造函数Person 实例对象person1

// 题目
1 : person1.__proto__ 是什么?
2 : Person.__proto__ 是什么?
3 : Person.prototype.__proto__ 是什么?
4 : Object.__proto__ 是什么?
5 : Object.prototype.__proto__ 是什么?

// 答案
1 : person1.__proto__ === Person.prototype (person1的构造函数Person)

2 : Person.__proto__ === Function.prototpye (Person的构造函数Function)

3 : Person.prototype.__proto__  === Object.prototype (Person.protyotype是一个普通对象,因为一个普通对象的构造函数都是Object)

4 : Object.__proto__ === Function.prototpye (Object的构造函数Function)

5 : Object.prototype.__proto__ === null (Object.prototype 也有__proto__属性,但是它比较特殊,是null,null处于原型链的顶端。)

原型链的形成是真正是靠__proto__ 而非prototype

练习2

var FunctionExample = function () {}

Object.prototype.a = function() {}

Function.prototype.b = function() {}

var f = new FunctionExample();

这时候f能否访问到a和b ??
// 所有普通对象都源于这个Object.prototype对象,只要是对象,都能通过原型链访问到a

f.__proto__ === FunctionExample.prototype;

FunctionExample.prototype.__proto__ === Object.prototype;
// 取b我们可通过 f.constructor.b就能访问到b,因为 f.constructor == FunctionExample

f.constructor === FunctionExample;

FunctionExample.__proto__ === Function.prototype;
console.log(f) // FunctionExample {}
console.log(f.constructor)  // [Function: FunctionExample]
console.log(FunctionExample.prototype) // FunctionExample {}, 其实可以理解成FunctionExample.prototype就是一个实例
console.log(FunctionExample.prototype.constructor) // [Function: FunctionExample]
console.log(f.__proto__) // FunctionExample {} , 可以这么理解,实例的proto指向它的构造函数的原型对象,也就是f.__proto__ == FunctionExample.prototype
console.log(f.constructor.b) // Function,因为f.constructor指向 FunctionExample, 而 FunctionExample.prototype相当是Function的一个实例,所以在Function.prototype上有个b函数,FunctionExample照样可以访问的到
console.log(f.constructor.prototype.__proto__); // { a: [Function] } 可以访问到a函数,因为f.constructor.prototype其实就是等于FunctionExample {},而每个对象都有个__proto__属性,Function.prototype.__proto__ == Object.prototype,所以也能访问到a方法

练习3

function SuperType() {
  this.colors = ['red', 'yellow']
}

function SubType() {

}
// 继承了SuperType
SubType.prototype = new SuperType();

var instance1 = new SubType() // intance.constructor = SuperType
instance1.colors.push('black')
console.log(instance1.colors) // ['red', 'yellow', 'black']

var instance2 = new SubType()
console.log(instance2.colors) // ['red', 'yellow', 'black']

理解一下原型和原型链

// 为什么instance1.constructor = SuperType ?
// 为什么 SubType.prototype.constructor = SuperType ?

console.log(instance1.constructor) // SuperType
console.log(SubType.prototype.constructor) // SuperType

console.log(instance1.__proto__ == SubType.prototype) // true
console.log(SubType.prototype.__proto__ == SuperType.prototype) // true

console.log(SubType.__proto__ == SuperType.prototype) // false
console.log(SubType.__proto__ == Function.prototype) // true

console.log(SuperType.prototype.constructor == SuperType) // true
console.log(SuperType.__proto__ == Function.prototype) // true
console.log(SuperType.prototype.__proto__ == Object.prototype) // true

练习4

function SuperType() {
  this.colors = ['red', 'yellow']
}

function SubType() {
  // 继承了SuperType
  SuperType.call(this);
}

var instance1 = new SubType()
instance1.colors.push('black')
console.log(instance1.colors) // ['red', 'yellow', 'black']

var instance2 = new SubType()
console.log(instance2.colors) // ['red', 'yellow']

思考一哈?

console.log(instance1.constructor) // SubType
console.log(SubType.prototype.constructor) // SubType
console.log(SubType.prototype.__proto__) // {}

console.log(instance1.__proto__ == SubType.prototype) // true
console.log(SubType.prototype.__proto__ == SuperType.prototype) // false
console.log(SubType.prototype.__proto__ == Object.prototype) // true

console.log(SubType.__proto__ == SuperType.prototype) // false
console.log(SubType.__proto__ == Function.prototype) // true

console.log(SuperType.prototype.constructor == SuperType) // true
console.log(SuperType.__proto__ == Function.prototype) // true
console.log(SuperType.prototype.__proto__ == Object.prototype) // true

以上是关于构造函数,原型对象,实例对象的主要内容,如果未能解决你的问题,请参考以下文章

构造函数可以实例化对象

JS中构造函数、实例、原型对象之间的关系

面向对象中构造函数,原型对象和实例的关系图

构造函数原型和实例的关系

构造函数原型实例化对象

实例对象构造函数原型之间的关系