需求:创建3个人对象 (姓名name,年龄age,性别sex)
常规写法是创建三个对象,示例代码如下:
let person1 = {
name:'小张',
age:30,
sex:'男'
}
let person2 = {
name:'小王',
age:20,
sex:'男'
}
let person3 = {
name:'小红',
age:18,
sex:'女'
}
弊端是:(1)代码冗余 (2)维护不便
这个时候我们就可以尝试使用函数创建对象 : 解决创建多个对象代码冗余
工厂函数:用于批量创建对象的函数
示例代码如下:
function createPerson (name, age, sex) {
//(1)创建空对象
let p = {}
//(2)对象赋值
p.name = name
p.age = age
p.sex = sex
//(3)返回创建好的对象
return p
}
let p1 = createPerson('小张', 30, '男')
let p2 = createPerson('小王', 20, '男')
let p3 = createPerson('小红', 18, '女')
console.log(p1, p2, p3)
构造函数 : 使用new关键字调用一个函数
-
构造函数作用与工厂函数一致,都是创建对象。但是构造函数代码更加简洁
-
构造函数的四个流程
-
(1)创建空对象
-
(2)this指向这个对象
-
(3)对象赋值
-
(4)返回对象 示例代码如下:
-
function Person (name, age, sex) {
//(1)创建空对象 {}
//(2)将this指向这个对象 this = {}
//(3)对象赋值
this.name = name
this.age = age
this.sex = sex
//(4)返回这个对象 return this
}
let person1 = new Person('小张', 32, '男')
let person2 = new Person('小王', 38, '男')
let person3 = new Person('小李', 28, '男')
console.log(person1, person2, person3)
但是如果构造函数中有方法,每一次调用都会新生成一个方法,每一个对象的方法都不是同一个
示例代码如下:
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
所以构造函数中也存在 弊端 : 浪费内存资源
全局函数 :解决内存资源浪费问题
也许有人会说这还不简单,为了解决内存资源浪费问题,我们可以使用全局函数呀
示例代码如下:
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
但是我们又会发现弊端 : 全局变量污染的问题
对象 (1)解决内存资源浪费 (2)全局变量污染
那又有人说使用对象 : (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
这个时候又有新的弊端 : 对象自身还是全局的,造成新的全局变量污染
所以这几种方式都不是最佳的,我们可以使用原型
原型对象
定义
原型对象 : 当声明一个函数的时候,编译器会自动帮你创建一个与之对应的对象,称之为原型对象
作用
原型对象作用 : 解决构造函数内存资源浪费 + 全局变量污染
用法
谁可以访问原型对象中的成员(属性和方法)
- 构造函数自身:
构造函数名.prototype - 构造函数实例化的每一个对象:点语法直接访问
示例代码如下:
/* 1 原型 : 每一个构造函数在声明的时候,系统会自动的创建一个与之对应的对象,
称之为原型对象
*/
function Person(name,age){
this.name = name;
this.age = age;
};
/*2 如何获取原型对象
每一个函数都有一个 prototype 属性,指向这个原型对象
*/
console.log(Person.prototype);
/*
3 既然原型是一个对象 : 用于存储数据
*/
Person.prototype.sayHi = function(){
console.log('我喜欢敲代码');
};
/*
4 谁可以访问 原型中的成员(属性+方法)
a. 构造函数自身 : 构造函数名.prototype
b. 这个构造函数所创建(实例化)的每一个对象
*/
// 实例化对象
let p1 = new Person('小张',18);
p1.sayHi();
//实例化对象
let p2 = new Person('小花',16);
p2.sayHi();
console.log(p1.sayHi === p2.sayHi);//true
__proto__属性
-
属于实例对象,指向原型对象
-
proto属性不是W3C的标准属性,所以实际开发中一般不会使用它来访问原型
-
作用: 可以让实例对象访问原型中的成员
prototype属性
-
属于构造函数,指向原型对象
-
作用: 解决构造函数资源浪费+变量污染
constructor属性
-
属于原型对象,指向构造函数
-
作用: 可以让实例对象 知道自己被哪一个构造函数创建的
构造函数、原型对象、实例对象三角关系
示例代码如下:
//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('小花', 16)
//检查原型 : (1)先通过实例对象找构造函数
console.log(p1.__proto__.constructor) //Person
//(2)构造函数和原型对象一一对应关系
console.log(p1.__proto__ === Person.prototype)//true
静态成员和实例成员
-
静态成员 : 属于函数对象的成员
-
实例成员: 属于实例化对象的成员
示例代码如下:
//构造函数
function Person(name,age){
this.name = name;
this.age = age;
};
Person.study = '我爱敲代码';
console.log(Person.study);//静态成员
//实例化对象
let p1 = new Person('张三',20);
console.log(p1.name);//实例成员
console.log(p1.age);//实例成员