构造函数
说起原型对象不得不先说一说构造函数,什么是构造函数?使用new调用一个函数,构造函数是用来创造对象的函数。
- 构造函数new的工作原理
- 创建空对象
- this指向这个对象
- 给对象赋值
- 返回这个对象
function Person(name,age,sex){
//(1)创建空对象 {}
//(2)this指向这个对象 this = {}
//(3)对象赋值
this.name = name
this.age = age
this.sex = sex
this.eat = function(){
console.log('eat');
}
//(4)返回这个对象 return this
}
let p1 = new Person('张三',20,'男')
let p2 = new Person('李四',20,'女')
console.log(p1,p2)
- 一些注意的点
-
构造函数首字母一般大写,为了提醒调用者不要忘记new关键字
-
如果在构造函数内部 手动return
(1) return 值类型 : 无效,还是返回new创建对象
(2) return 引用类型:有效,会覆盖new创建的对象
-
构造函数内部的方法:会浪费内存资源
让我们来思考这样一个问题:p1和p2都有eat方法,而是函数体相同。但是他们是同一个函数吗?答案当然是否定的,不是同一个,因为每次调用一次构造函数,内部都会执行一次
function,就会在堆中开辟出一个新的空间。虽然里面的代码都是一样的,但是地址不同。所以每调用一次就会开辟一个新空间造成了内存资源的浪费。console.log( p1.eat == p2.eat )//false -
使用全局函数解决内存的浪费
let eat = function() {
console.log("吃东西")
}
let learn = function() {
console.log("学习")
}
function Person(name, age) {
this.name = name
this.age = age
this.eat = eat
this.learn = learn
}
let p1 = new Person("张三", 18)
let p2 = new Person("李四", 20)
console.log(p1, p2)
将方法变成全局函数,现在p1里面的eat和p2里面的eat是不是同一个呢?没错,现在是的了,因为构造函数内部并没有重新创建一个函数,而是拷贝了eat的地址赋值。无论你调用构造函数多少次,都是直接拷贝的eat的地址。
console.log( p1.eat == p2.eat )//true但是这又导致了一个新的问题:全局变量污染。怎么解决呢?使用对象。
- 使用对象解决内存浪费+全局变量污染
let obj = {
eat: function() {
console.log("吃东西")
},
learn: function() {
console.log("学习")
}
}
function Person(name, age) {
this.name = name
this.age = age
this.eat = obj.eat
this.learn = obj.learn
}
let p1 = new Person("张三", 18)
let p2 = new Person("李四", 20)
console.log(p1, p2)
这样看上貌似就解决了内存浪费和全局变量污染的问题,但是屠龙勇士终成恶龙,现在obj成了最大的变量污染。怎么解决呢?原型对象应运而生。
原型对象
当你创建一个构造函数的时候,JS就会自动给你创建一个原型对象来给你装这些方法。
- 原型对象是什么? 任何函数在声明的时候,系统会自动帮你创建一个对象,称之为原型对象
- 原型对象的作用? 解决 内存浪费+变量污染
- 原型对象相关属性
prototype: 属于构造函数,指向原型对象__proto__: 属于实例对象,指向构造函数constractor: 属于原型对象,指向构造函数
用图来表示三者之间的关系可能更直观一点,自己画的,比较丑,不过影响不大。
__proto__:这个属性不是web标准,很多浏览器都不会显示,在实际开发中都不会用,用于学习研究。
让我们来验证一下构造函数、原型对象、实例对象三者之间的关系。
console.log( p1.__proto__.constructor )//Person
console.log( Person.prototype === p1.__proto__ )//true
ok,今天的原型对象就聊到这里,下次进一步的聊一下原型链。
respect