首先让我们一起来了解一下什么是构造函数
构造函数的定义
构造函数:是通过new的方式来创建对象的一种函数,他与普通函数没有区别,但值得注意的是,构造函数的首字母应该大写
在我们的js中创建对象的方式一共有三种
一:通过Object构造函数来创建
const obj = new Object()
二:通过字面量的方式来进行创建
const obj = {}
三:通过自定义构造函数来创建
function People (name, age){
this.name = name
this.age = age
this.run = function (){
console.log('我会奔跑')
}
}
const xiaoming = new People('xiaoming',18)
那么让我们一起来思考一下,在new一个对象的时候,js会做哪些事情呢(这是一个经典的面试题)
一:在内存中开辟一个新的地址,并创建一个新的对象
二:让this指向这个新的对象
三:指向构造函数里面的方法,给这个新的对象添加属性和方法
四:返回这个对象
静态属性(方法)与实例属性(方法)
实例属性方法:指的是通过this来创建的实例属性方法,这类属性方法只能通过实例对象来访问,不可以通过构造函数来访问
function People(name){
this.name = name
this.say = function(){
console.log(name)
}
}
const xiaoming = new People('xiaoming')
console.log(xiaoming.name) // xiaoming
console.log(People.name) // undefinde
静态属性方法:指的是通过构造 . 的形式创建的属性方法,这类属性方法只能通过构造函数来访问,不可以通过实例对象来访问
function People(name){
this.name = name
this.say = function(){
console.log(name)
}
}
const xiaoming = new People('xiaoming')
People.age = '18'
console.log(People.age) // 18
console.log(xiaoming .age) // undefinde
原型对象
在我们的构造函数中,都一个prototype属性,这个属性指向我们的原型对象。当我们生产的属性方法为复杂数据类型的时候,如function,放到我们的prototype(原型对象)上,我们的实例对象就不会每个都重新开辟一块空间来存储我们的复杂数据类型了,共享我们的一个内存地址
function People(name) {
this.name = name
this.say = function () {
console.log(name)
}
}
const xiaoming = new People('xiaoming')
const xiaohong = new People('xiaohong')
console.log(xiaoming.say === xiaohong.say) // false
这里我们看出,没生成一个对象,对于他的复杂数据类型都会重新开辟一个新的内存空间,
这样我们当我们的数据多了后,就会造成我们的内存浪费,为了解决这个问题,提出将我们
的复杂数据类型,放在我们的原型对象中,共享一块内存
所以代码改造如下:
function People(name) {
this.name = name
}
People.protoType.say = function (){}
const xiaoming = new People('xiaoming')
const xiaohong = new People('xiaohong')
console.log(xiaoming.say === xiaohong.say) // true
对象原型
在我们的实例对象中,我们也存在一个原型,这个原型即为我们的对象原型 proto 这个属性对象,指向的是我们构造函数的原型对象及构造函数是的protoType属性
function People(name) {
this.name = name
}
const xiaoming = new People('xiaoming')
console.log(xioaming.__proto__ === People.protoType) // true
所以这样就形成了一个链式的结构(原型链:下文会详细讲解),在我们刚才定义的例子中,xiaoming和xiaohong这两个实例对象并没有创建我们的say方法,但是为什么可以调用我们的say方法呢,因为我们的构造函数原型对象上面有这个say,代码执行的时候,会根据原型链一步一步去查找这个say方法,一直找到顶层元素
constructor
在我们的实例对象原型和构造函数的原型对象上面都有constructor属性,都指向的是我们的构造函数本身,constructor属性的作用,亦在指明我们的实例对象是通过那个构造函数来构造出来的,下面让我们看一下例子
function People(name) {
this.name = name
}
const xiaoming = new People('xiaoming')
console.log(xiaoming.__proto__.constructor === People.protoType.constructor) // true
console.log(Pxiaoming.__proto__.constructor) // People f
console.log(Pxiaoming.protoType.constructor) // People f
当我们重新指定我们构造函数的原型对象的时候,constructor属性会丢失,需要我们重新指定
比如
People.protoType = {
say: function(){}
}
这个时候我们protoType上与xiaoming.__proto__都没有constructor属性了,需要我们重新指定为我们的People
People.protoType = {
constructor: People
say: function(){}
}
原型链
这里通过一张图来对我们原型链进行解释说明
这样的一个链式结构,即为我们的原型链
原型对象的this指向
看到了这里,相信不少小伙伴,这里有个疑惑,我们的构造函数的this指向的是我们的实例对象,那么原型对象的this指向谁呢。答案是:实例对象
function People(name) {
this.name = name
}
People.prototype.say = function () {
console.log(this)
}
const xiaoming = new People('xiaoming')
const xiaohong = new People('xiaohong')
xiaoming.say()
打印出来的结果如下:
好了,关于构造函数与原型的知识点就到这里了,希望各位小伙伴有所收获!!