面向对象的批量生产
- 工厂函数
// 3. 工厂函数
function createObj (name, age) {
// 1. 手动创建一个对象
const obj = {}
// 2. 手动向对象内部添加属性
obj.name = name
obj.age = age
// 3. 手动返回一个对象
return obj
}
const o1 = createObj('QF001', 18)
const o2 = createObj('QF002', 28)
const o3 = createObj('QF003', 38)
console.log(o1)
console.log(o2)
console.log(o3)
- 自定义构造函数
// 4. 自定义构造函数
function createObj () {
// 1. 自动创建出来一个对象
// 2. 手动向对象添加属性
// 3. 自动返回一个对象
}
const o1 = new createObj() // 构造函数调用方式
const o2 = createObj() // 普通函数调用方式
console.log(o1)
console.log(o2)
自定义构造函数的书写
-
- 一定是和 new 关键字 连用
- 如果没有和 new 连用, 那么他就是一个普通函数
-
- 当一个函数和 new 关键字连用的时候, 这个函数就被称为自定义构造函数, 这个函数内的 this 指向, 指向返回出来对象
-
- 构造函数不能使用给箭头函数
- 因为箭头函数内部没有 this
-
- 构造函数内部不需要 return
- return(返回) 了基本数据类型, 写了和没写一样
- return(返回) 了引用数据类型, 写了构造函数就没用了
-
- 书写构造函数时, 首字母建议大写
- 目的: 仅仅是为了和普通函数区分
-
- 我们构造函数 通过 new 关键字 创建出来的 对象, 叫做 实例化对象, 本质上还是一个对象, 只不过名字上叫做实例化对象(实例对象)
- 我们把 构造函数通过 new 关键字 创建对象 的过程 叫做 实例化
function createObj(num1, num2) {
// 此时 this === 将来被返回出去的对象
this.a = num1;
this.b = num2;
// return '我是 一个 普通字符串'; // 基本数据类型, 写了和没写一样
// return [1, 2, 3, 4, 5]; // 引用数据类型, 写了构造函数就没用了
}
const o1 = new createObj(1, 2)
const o2 = new createObj(10, 20)
console.log(o1);
console.log(o2);
function Person() {
this.name = 'QF001'
}
const p1 = new Person();
/**
* const p1 = new Person()
*
* new Person() 此时这个过程叫做 实例化, Person函数因为和new关键字连用, 我们叫做自定义构造函数
*
* 此时 常量 p1 我们叫做 实例化对象(实例对象)
*/
构造函数不合理的地方
function Person (name, age) {
this.name = name
this.age = age
this.fn = function () {
console.log('AAAAAA')
}
}
/*
这样写 实际功能也能完成, 但是多次创建对象时
会多次创建 功能代码完全相同一个函数
这对内存空间是一种浪费
*/
function fn() {
console.log('AAAAAA');
}
function Person(name, age) {
this.name = name;
this.age = age;
this.fn = fn;
/**
* 这样写 实际功能也能完成, 并且在多次创建的时候
*
* 每次给 this.fn 赋值时, 都会去找到 fn 函数
*
* 然后多个对象的 this.fn 的引用地址 都是一个
*/
}
/**
* 通过 new 关键字 实例化 一个对象, 叫做 p1
*
* 函数内部执行时:
* 1. 将参数 name 赋值给 this.name
* 2. 将参数 age 赋值给 this.age
* 3. 创建一个函数(XF001) 赋值给 this.fn
*/
const p1 = new Person('QF001', 18);
/**
* 通过 new 关键字 实例化一个对象, 叫做 p2
*
* 函数内部执行时:
* 1. 将参数 name 赋值给 this.name
* 2. 将参数 age 赋值给 this.age
* 3. 创建一个函数(XF002) 赋值给 this.fn
*/
const p2 = new Person('QF002', 19);
// console.log(p1);
// console.log(p2);
p1.fn();
p2.fn();
console.log(p1.fn == p2.fn);
原型
认识原型
-
- 每一个函数都有一个 原型(是一个空间,或者是一个对象, 内部能存储一些东西)
- 构造函数, 本质上也是一个函数, 所以他也有这个原型
- 原型内部都有一个 constructor ,这个属性表明当前这个原型, 是那个函数的
- 函数访问原型; 函数.prototype
-
- 每一个对象都有一个 proto(两个下划线), 可以去访问到自己构造函数的原型
- 实例化对象, 本质上也是一个函数, 所以他可以访问到自己构造函数的原型
- 对象访问原型: 对象.proto
- 对象的访问规则, 现在当前作用域(对象内部)查找, 找到就使用
- 如果没找到, 则会顺着 proto 向上查找
- 构造函数函数体内, 通常写属性
- 构造函数的原型内, 通常写方法(函数)
- 构造函数的原型内部添加方法, 并不是为了给构造函数使用
- 通常是为了给实例化对象使用
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.a = '我是后续通过 JS 代码添加到函数的原型内部的属性'
Person.prototype.sayHi = () => {
console.log('你好');
}
// console.log(Person.prototype); // 打印函数 Person 的原型
const p1 = new Person('QF001', 18);
const p2 = new Person('QF002', 28);
// p1.sayHi();
// console.log(p1.__proto__);
// console.log(p1.a);
// console.log(p1.__proto__ === Person.prototype); // 打印对象的构造函数的原型
console.log(p1.sayHi);
console.log(p2.sayHi);
console.log(p1.sayHi == p2.sayHi); // true
ES6 class类的语法
- ES5 构造函数
-
- 函数体和原型, 是需要分开写
-
- 构造函数如果不和 new 一起连用, 不会报错
-
- ES6 类的语法: class 类名 {xxxxx}
class Stu {
constructor(name) {
// 这个位置和构造函数的 函数一样
this.name = name
}
// 这个位置开始, 全都是原型
}
const s1 = new Stu('QF001')
console.log(s1)