1.工厂函数
- 工厂函数: 用于批量创建对象的函数
<script>
/* 学习总结
1.工厂函数(了解概念) :用于创建对象的函数
2.构造函数 :
3.new关键字工作原理(重点) :
*/
/* 需求 : 需要创建很多个对象 (姓名、年龄、性别) */
//封装一个函数解决代码冗余
let p1 = {
name: '万叶',
age: 18,
}
let p2 = {
name: '公子',
age: 28,
}
let p3 = {
name: '雷神',
age: 20,
}
function fun(name, age) {
// 创造空对象
let p = {}
p.name = name
p.age = age
return p
}
console.log(fun('万叶', 18)); // name: "万叶" age: 18
console.log(fun('公子', 28)); // name: "公子" age: 28
console.log(fun('雷神', 20)); // name: "雷神" age: 20
</script>
以往以普通对象(命名空间)形式封装的代码只是单纯把一系列的变量或函数组合到一起,所有的数据变量都被用来共享(使用 this 访问)。
2.构造函数
<script>
// 构造函数
function fun(name, age) {
// 1.创建空对象
// 2.this只想这个对象
// 3.对象赋值
// 4.默认返回这个值
this.name = name
this.age = age
}
let p1 = new fun('公子', 18)
console.log(p1); // name: "公子" age: 18
function fu() {
//1.创建空对象
//2.自定返回对象
}
let f1 = fu() //普通函数
let f2 = new fu() //构造函数
console.log(f1, f2);
//new关键字注意点
// 1. 构造函数首字母一般大写,为了提醒调用者记得加new
// 2.如果手动在构造函数里面加 return
// 2.1 如果是简单值数据类型,无效,还是返回new创建的对象
// 2.2 如果是引用数据类型,无效会覆盖new创建的对象
</script>
同样的将变量和函数组合到了一起并能通过 this 实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间是彼此不影响的。
总结:
- 构造函数体现了面向对象的封装特性
- 构造函数实例创建的对象彼此独立、互不影响
- 命名空间式的封装无法保证数据的独立性
3.原型对象
实际上每一个构造函数都有一个名为 prototype 的属性,译成中文是原型的意思,prototype 的是对象类据类型,称为构造函数的原型对象,每个原型对象都具有 constructor 属性代表了该原型对象对应的构造函数。
<script>
/*
//1.构造函数 : 调用一个函数使用了new关键字
// 构造函数中的方法 弊端 : 浪费内存资源
/*new关键字工作原理
//(1)创建空对象 {}
//(2)this指向这个对象 this = {}
//(3)执行赋值代码
//(4)返回这个对象 return this
*/
// function Person(name,age){
// //(1)创建空对象 {}
// //(2)this指向这个对象 this = {}
// //(3)执行赋值代码
// //(4)返回这个对象 return this
// this.name = name;
// this.age = age;
// this.sayHi = function(){
// console.log('万叶');
// }
// };
// let p1 = new Person('公子',28);
// console.log(p1);
// p1.sayHi();
// let p2 = new Person('钟离',18);
// p2.sayHi();
// //每一个对象的方法都不是同一个
// console.log(p1.sayHi == p2.sayHi);//false
//2. 使用全局函数 : 解决内存资源浪费问题
//弊端 : 全局变量污染的问题
// function fn(){
// console.log('雷神');
// };
// function eat(){
// console.log('八重');
// };
// function Person(name,age){
// //(1)创建空对象 {}
// //(2)this指向这个对象 this = {}
// //(3)执行赋值代码
// //(4)返回这个对象 return this
// this.name = name;
// this.age = age;
// this.sayHi = fn;
// this.eat = eat;
// };
// let p1 = new Person('雷神',28);
// console.log(p1);
// p1.sayHi();
// let p2 = new Person('八重',18);
// p2.sayHi();
// console.log(p1.sayHi == p2.sayHi);//true
//3.使用对象 解决 : (1)解决内存资源浪费 (2)全局变量污染
//弊端 : 对象自身还是全局的,造成新的全局变量污染
// let obj = {
// fn:function(){
// console.log('月下风起');
// },
// eat:function(){
// console.log('稍纵即逝');
// }
// }
// function Person(name,age){
// //(1)创建空对象 {}
// //(2)this指向这个对象 this = {}
// //(3)执行赋值代码
// //(4)返回这个对象 return this
// this.name = name;
// this.age = age;
// this.sayHi = obj.fn;
// this.eat = obj.eat;
// };
// let p1 = new Person('雷神',28);
// console.log(p1);
// p1.sayHi();
// let p2 = new Person('八重',18);
// p2.sayHi();
// console.log(p1.sayHi == p2.sayHi);//true
/* 4.使用原型 : */
/* 4.1 原型 : 每一个构造函数在声明的时候,系统会自动的创建一个与之对应的对象,
称之为原型对象
*/
function Person(name,age){
this.name = name;
this.age = age;
};
/*4.2 如何获取原型对象
每一个函数都有一个 prototype 属性,指向这个原型对象
*/
console.log(Person.prototype);
/*
4.3 既然原型是一个对象 : 用于存储数据
*/
Person.prototype.sayHi = function(){
console.log('万叶');
};
/*
4.4 谁可以访问 原型中的成员(属性+方法)
a. 构造函数自身 : 构造函数名.prototype
b. 这个构造函数所创建(实例化)的每一个对象
*/
// 实例化对象
let p1 = new Person('雷神',18);
p1.sayHi();
//实例化对象
let p2 = new Person('八重',20);
p2.sayHi();
console.log(p1.sayHi === p2.sayHi);//true
</script>
结果: 当访问对象的属性或方法时,先在当前实例对象是查找,然后再去原型对象查找,并且原型对象被所有实例共享。
4.原型对象的属性
1. __ proto __ 属性介绍

<script>
__proto__属性: 属于实例对象,指向原型对象
* 作用: 可以让实例对象访问原型中的成员
//1.构造函数
function Person (name, age) {
this.name = name
this.age = age
}
//2.原型对象
Person.prototype.eat = function () {
console.log('我中午真的吃了红烧武昌鱼')
}
Person.prototype.country = '中国人'
//3.实例对象
let p1 = new Person('班长', 28)
console.log(p1)
/*
__proto__ : 属于实例对象,指向原型对象
*/
console.log( Person.prototype === p1.__proto__ )//true
//实例对象为什么可以直接访问原型中的成员,其实都是通过__proto__来访问的
/*
1.虽然实例对象可以直接访问原型的原理是 __proto__,但是开发中不推荐使用
2.原因: __proto__不是ECMA标准语法,有一些浏览器不支持。
3.结论: 学习阶段,学习原型可以用__proto__. 实际开发中,建议省略__proto__
*/
p1.eat()//p1.__proto__.eat()
</script>
注意:
往原型添加属性方法,最好使用构造函数来访问构造函数名.prototype
2.constructor属性介绍
<script>
* 作用: 可以让实例对象 知道自己被哪一个构造函数创建的
//1.构造函数
function Person (name, age) {
this.name = name
this.age = age
}
//2.原型对象
Person.prototype.type = '哺乳动物'
Person.prototype.eat = function () {
console.log('吃东西')
}
//3.实例对象
let p1 = new Person('布丁', 3)
p1.eat()
console.log(p1.type)
/*
constructor : 属于原型对象,指向构造函数
作用:可以让实例对象知道自己被那个构造函数创建的
*/
console.log(Person.prototype.constructor)//Person
console.log(Person.prototype.constructor === Person )//true
console.log( p1.constructor )//Person p1.__proto__.constructor
</script>
静态成员和实例对象
<script>
/*
构造函数也可以有自己的属性: 例如prototype
静态成员 : 属于函数对象的成员
实例成员: 属于实例化对象的成员
*/
//构造函数
function Person(name,age){
this.name = name;
this.age = age;
};
Person.aaa = '啊啊啊';
console.log(Person.aaa);//静态成员
//实例化对象
let p1 = new Person('张三',20);
console.log(p1.name);//实例成员
console.log(p1.age);//实例成员
</script>