创建对象的方式(需要能够方便我们批量创建对象)
-
- 字面量的方式
- 这个方式不合适,不利于批量创建
let obj = {
name: 'QF001',
age: 18
}
-
- 内置构造函数
- 这个方式不合适,不利于批量创建
let obj = new Object()
let obj2 = new Object()
let obj3 = new Object()
-
- 工厂函数的方式
- 其实就是 创建一个函数,但是函数内部可以创建一个对象,我们把这种函数叫做工厂函数
function createObj(num) {
// 3.1 手动创建一个对象
const obj = {}
// 3.2 手动给对象上添加一些属性
obj.name = 'QF001'
obj.age = num
// 3.3 手动给对象返回
return obj
}
let obj1 = createObj(18)
console.log(obj1) //{name: 'QF001', age: 18}
let obj2 = createObj(66)
console.log(obj2) //{name: 'QF001', age: 66}
自定义构造函数的方式
什么是构造函数:本质上就是一个普通函数,如果在调用的时候,前边加上一个关键字new,那么我们把这种函数叫做 构造函数
- 工厂函数和自定义函数的区别
// 工厂函数
function createObj(num) {
// 1.手动创建一个对象
const obj = {}
// 2.手动给对象上添加一些属性
obj.name = 'QF001'
obj.age = num
// 3.手动给对象返回
return obj
}
// 构造函数
function createObj2(num) {
// 1.自动创建一个对象
// 2.手动给对象上添加一些属性
this.name = 'QF001'
this.age = num
// 3.自动返回对象
}
自定义构造函数的书写
- 1. 构造函数的函数名首字母 大写(建议,非强制)
- 目的就是为了和普通函数做一个区分
- 2. 构造函数内 不要写return
- 如果return的是一个基本数据类型,写了也没用
- 如果return的是一个引用数据类型,写了就会导致构造函数没用
- 3. 构造函数调用时,必须和 new 关键字连用
- 如果不连用,也能调用,但是构造函数就没用了
- !4. 构造函数内部的this
- 当一个函数和 new 关键字连用的时候,那么我们说这个函数是 构造函数
- 然后这个函数内部的this指向本次调用被自动创建出来的那个对象
- 5. 构造函数不能使用 箭头函数
- 因为箭头函数内部没有 this
面试题: 构造函数有什么注意点?
// 自定义构造函数的书写
function Person(name, age) {
// 因为构造函数自动创建出来的对象可以通过this来访问,所以我们需要向这个对象上添加属性的时候,可以通过this来添加
this.name = name
this.age = age
this.a = '我是随便添加的一个属性,我是指定内容'
}
const p1 = new Person('QF666', 18)
console.log(p1)
const p2 = new Person('QF999', 666)
console.log(p2)
构造函数的缺点
构造函数内部 如果有这个引用数据类型,比如函数,在多次调用构造函数时,每一次都会重新创建一个函数,必然会造成这个内存空间的浪费
- 原版构造函数(有小问题)
function Person(name, age) {
this.name = name
this.age = age
this.a = '我是随便添加的一个属性,我是指定内容'
this.fn = function() {
console.log('我是fn函数')
}
}
const p1 = new Person('QF666', 18)
/**
* 第一次调用Person 构造函数
* 自动创建一个对象
* 1. 添加一个属性 name, 值为形参 name
* 2. 添加一个属性 age, 值为形参 age
* 3. 添加一个属性a,值为一个固定的字符串
* 4. 添加一个属性fn,值为一个函数,此时的函数是我们在这个函数内部定义的一个函数,假设地址是QF001
* 自动返回一个对象
* */
p1.fn() //我是fn函数
const p2 = new Person('QF009', 666)
/**
* 第二次调用Person 构造函数
* 自动创建一个对象
* 1. 添加一个属性 name, 值为形参 name
* 2. 添加一个属性 age, 值为形参 age
* 3. 添加一个属性a,值为一个固定的字符串
* 4. 添加一个属性fn,值为一个函数,此时的函数是我们在这个函数内部定义的一个函数,假设地址是QF002
* 自动返回一个对象
* */
p2.fn() //我是fn函数
console.log('验证两个对象内部的 fn 函数是否为同一个内存地址:', p1.fn === p2.fn) //false
- 更新版构造函数
function winFn() {
console.log('我是fn函数')
}
function Person(name, age) {
this.name = name
this.age = age
this.a = '我是随便添加的一个属性,我是指定内容'
this.fn = winFn
}
const p1 = new Person('QF666', 18)
/**
* 第一次调用Person 构造函数
* 自动创建一个对象
* 1. 添加一个属性 name, 值为形参 name
* 2. 添加一个属性 age, 值为形参 age
* 3. 添加一个属性a,值为一个固定的字符串
* 4. 添加一个属性fn,值为一个函数,此时的函数是我们在这个函数内部定义的一个函数,假设地址是QF001
* 自动返回一个对象
* */
p1.fn() //我是fn函数
const p2 = new Person('QF009', 666)
/**
* 第二次调用Person 构造函数
* 自动创建一个对象
* 1. 添加一个属性 name, 值为形参 name
* 2. 添加一个属性 age, 值为形参 age
* 3. 添加一个属性a,值为一个固定的字符串
* 4. 添加一个属性fn,值为一个函数,此时的函数是我们在这个函数内部定义的一个函数,假设地址是QF002
* 自动返回一个对象
* */
p2.fn() //我是fn函数
console.log('验证两个对象内部的 fn 函数是否为同一个内存地址:', p1.fn === p2.fn) //true
原型 (请熟读并背诵全文)
* 原型空间 原型对象
- 什么是原型:
- 每一个函数天生拥有一个属性 prototype,它的属性是一个对象,我们通常把这个对象叫做 这个函数的原型(空间|对象)
- 原型这个对象中 有一个属性 叫做constructor,这个属性表示的是当前这个 原型 是那个 函数的原型
- 每一个对象 天生拥有一个属性__proto__(前后都是两个下划线),这个属性指向 自己构造函数的原型
- 每一个函数天生拥有一个属性 prototype,它的属性是一个对象,我们通常把这个对象叫做 这个函数的原型(空间|对象)
function Person() { }
// 向 Person 函数原型(对象)上 添加一个属性age, 值为18
Person.prototype.age = 18
Person.prototype.name = 'qf'
Person.prototype.fn = function () {
console.log('我是添加在Person函数的原型对象上的一个函数')
}
console.log(Person.prototype)
//{age: 18, name: 'qf', fn: ƒ, constructor: ƒ}
let p1 = new Person()
/**
* 使用new结合Person() 这个过程叫做构造函数的实例化,最终会得到一个对象
* 在我们当前案例中,是将这个对象放在变量p1里边
*
* 所以有些开发管这个p1叫做实例化对象,实例对象,本质上依然是一个对象
*
* */
//console.log(p1.__proto__)
//{age: 18, name: 'qf', fn: ƒ, constructor: ƒ}
console.log(p1.__proto__ === Person.prototype) //true
/**
* __proto__属性指向自己构造函数的原型
* 因为 p1 这个对象的构造函数 是Person
* 那么也就是说,p1.__proto__实际指向就是Person这个函数的原型
* */
- 原型的作用:
- 把构造函数中 公共方法 提取出来,放在原型中
- 为什么要这样做?
- 构造函数的原型上的方法或者属性,在每一个实例化对象中都能正常访问
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.age = 18
Person.prototype.name = 'qf'
Person.prototype.fn = function () {
console.log('我是添加在Person函数的原型对象上的一个函数')
}
let p1 = new Person('QF001', 18)
console.log(p1.__proto__)
let p2 = new Person('QF002', 28)
console.log(p2.__proto__)
/**
* 对象的访问规则
* 访问对象的某一个属性时,会先在对象本身去查找,找到就直接使用,如果没有找到
* 那么会去 对象的__proto__中查找,找到就使用,如果没有找到
* 那么会去这个对象(原型)的__proto__查找
* 直到查找到JS的顶层对象Object.prototype,如果还没找到 就不再向上查找
* */
console.log(p1)
console.log(p1.fn)
p1.fn() //我是添加在Person函数的原型对象上的一个函数
console.log(p1.abc) //undefined
原型练习题
// 了解
// const fn = new Function('console.lang(123')
// fn() //123
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function() {
console.log('你好~~~~')
}
const p1 = new Person('QF001', 18)
/**
* 1. p1 的__proto__指向谁
* __proto__属性指向自己构造函数的原型
* 所以相当于指向了Person.prototype
*
* p1.__proto__ === Person.prototype
*
* 2. Person的__proto__指向谁
* Person 是一个构造函数,构造函数本质上就是一个函数而已
* 在JS中,只要是一个函数,那么就属于构造函数Function实例化对象
* __proto__属性指向自己构造函数的原型
* 所以相当于 指向了Function.prototype
* Person.__proto__ === Function.prototype
*
* 3.Person.prototype的__proto__指向谁
* Person.prototype 是Person构造函数的原型,本质上就是一个对象而已
* 在JS中,只要是一个对象,那么就属于构造函数Object的实例化对象
* __proto__属性指向自己构造函数的原型
* 所以相当于指向了Object.prototype
*
* Person.prototype.__proto__ = Object.prototype
*
* 4. Function的__proto__指向谁
* Function是一个构造函数,构造函数本质上就是一个函数而已
* 在JS中,只要是一个函数,那么就属于构造函数Function实例化对象
* __proto__属性指向自己构造函数的原型
* 所以相当于 指向了Function.prototype
*
* Function.__proto__ === Function.prototype
*
* 5. Function.prototype的__proto__指向谁
* Function.prototype是Function 构造函数的原型,本质上就是一个对象而已
* 在JS中,只要是一个对象,那么就属于构造函数Object的实例化对象
* __proto__属性指向自己构造函数的原型
* 所以相当于指向了Object.prototype
*
* Function.prototype.__proto__ === Object.prototype
*
* 6. Object的__proto__指向谁
* Object 是一个构造函数,构造函数本质上就是一个函数而已
* 在JS中,只要是一个函数,那么就属于构造函数Function实例化对象
* __proto__属性指向自己构造函数的原型
* 所以相当于 指向了Function.prototype
*
* Object.__proto__ === Function.prototype
*
* 7. Object.prototype 的__proto__指向谁
* 因为Object.prototype是JS 的顶层对象,再往上就没有了,所以这个值为null
* console.log(Object.prototype.__proto__) null
* */
console.log(p1.__proto__ === Person.prototype) //true
console.log(Person.__proto__ === Function.prototype) //true
console.log(Person.prototype.__proto__ = Object.prototype) //true
console.log(Function.__proto__ === Function.prototype) //true
console.log(Function.prototype.__proto__ === Object.prototype) //true
console.log(Object.__proto__ === Function.prototype) //true
console.log(Object.prototype.__proto__) //null