建议:忘掉关于所有构造函数的知识,看构造函数的代码逻辑是如何一步一步构建的
一、自制一个构造函数
要求:它可以创建一个含有 name 和 age 的对象
function Person(name, age) {
name = name || '匿名'
age = age || 0
const obj = {
name: name,
age: age
}
return obj
}
- 创建了一个构造函数,传入name和age两个参数,返回了一个带有name和age属性的对象
- 简易写法, 如下:
function Person(name = '匿名', age = 0) {
return {name, age}
}
const f = Person('fang', 18)
console.log(f)
// f就是由Person函数构造出来的对象
二、第二步
新要求: 所有 Person 构造出来的对象都能 有 sayHi
function Person(name = '匿名', age = 0) {
return {
name,
age,
syaHi() {
console.log(`你好,我是${name}`)
}
}
}
执行:
const f1 = Person('fang', 18)
f1.sayHi()
const f2 = Person('xin', 19)
f1.sayHi()
画个内存图吧!!
- 从上面的内存图可以发现,每执行一次Person就会创建一个SayHi函数,这显然是不合理的
三、解决sayHi的问题
为什么要把sayHi写在Person里呢,我们把它拆出来单独写在一个变量里
const sayHi = function() {
console.log(`你好,我是${this.name}`)
}
function Person(name = '匿名', age = 0) {
return { name, age, syaHi }
}
四、现在要求所有的Person构造出来的对象都能有 run函数
const sayHi = function() {
console.log(`你好,我是${this.name}`)
}
const run = function() {
console.log(`${this.name}在跑步`)
}
function Person(name = '匿名', age = 0) {
return { name, age, syaHi, run }
}
问题:那么是不是Person返回的对象每多返回一个函数,就要多增加一个全局变量。那么是不是占用了太多全局变量了呢
五、解决一个Person占用太多全局变量的问题
那就创建一个对象吧,这个对象里面放着Person所需要的所有共有属性。 注: Person共有属性 只是为了方便起的存放共有属性的变量名
const Person共有属性 = {
sayHi: function() {
console.log(`你好,我是${this.name}`)
},
run: function() {
console.log(`${this,name}正在跑步`)
}
}
function Person(name='匿名', age=0) {
return {
name,
age,
sayHi: Person共有属性.sayHi,
run: Person共有属性.run
}
}
六、那为什么要创建两个全局变量呢,只用一个不行么
提示: 构造函数既是一个函数,同时也是一个对象。那我们为什么不把共有属性写出Person的属性
function Person(name='匿名', age=0) {
return {
name,
age,
sayHi: Person.共有属性.sayHi,
run: Person.共有属性.run
}
}
Person.共有属性 = {
sayHi() {
console.log(`你好,我是${this.name}`)
},
run() {
console.log(`${this,name}正在跑步`)
}
}
问题:既然共有属性对象写在Person的属性上,那为什么不把它写在隐藏属性上呢。 注::::此处写的隐藏属性指的是原型 -> prototype,我们统一用隐藏属性表示
七、把共有属性写在隐藏属性上
function Person(name='匿名', age=0) {
const obj = Object.create(Person.共有属性)
// 相当于 obj = { 隐藏属性: Person.共有属性 }
obj.name = name
obj.age = age
return obj
}
Person.共有属性 = {
sayHi() {
console.log(`你好,我是${this.name}`)
},
run() {
console.log(`${this,name}正在跑步`)
}
}
- 创建f1,可以看到有name,age属性,隐藏属性里面有run,sayHi函数
八、新的问题:f1是谁构造出来的
console.log(f1.constructor)
// 得到 Object
因为Person函数返回的是一个对象赋值给f1,对象是Objec类创造的实例,对象的__proto__指向的所属类的prototype,所以f1的__proto__指向了Object类的prototype,而Object的prototype的constructor指向了prototype所在的类
问题:别人怎么不看源码知道f1是谁构造的呢,所以在共有属性上加个constructor
Person.共有属性 = {
constructor: Person,
sayHi() {
console.log(`你好,我是${this.name}`)
},
run() {
console.log(`${this,name}正在跑步`)
}
}
九、构造函数的写法
function Person(name='匿名', age=0) {
this.name = name
this.age = age
}
Person.prototype = {
constructor: Person,
sayHi() {
console.log(`你好,我是${this.name}`)
},
run() {
console.log(`${this,name}正在跑步`)
}
}
const f1 = new Person('xin', 19)
f1.sayHi()
f1.run()
- 用 new 执行的好处
- 创建出一个类的实例(对象)
- 把函数体中的this指向指这个实例
- 如果没有返回值,返回当前的this(this指向的对象)
class的写法
class Person {
constructor(name='匿名', age=0) {
this.name = name
this.age = age
}
sayHi() {
console.log(`你好,我是${this.name}`)
}
run() {
console.log(`${this,name}正在跑步`)
}
}
const f1 = new Person('xin', 19)
f1.sayHi()
f1.run()