封装的理解

构造函数用于初始化实例对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const p1 = {
name: '张三',
age: 20,
run: function () {
console.log('调用此方法张三就开始奔跑');
},
};

const p2 = {
name: '李四',
age: 22,
run: function () {
console.log('调用此方法李四就开始奔跑');
},
};

const p3 = {
name: '王五',
age: 21,
run: function () {
console.log('调用此方法王五就开始奔跑');
},
};

这种场景很常见,每个对象都有nameage属性和run方法,我们可以用一个函数封装一下。

封装

封装的目的是为了减少重复代码的编写。

提取共性,将不同点作为参数传入。可以如下封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function CreatePerson(name, age) {
this.name = name;
this.age = age;
this.run = function () {
console.log(`调用此方法${this.name}就会开始奔跑`);
};
}

const p1 = new CreatePerson('张三', 20);
const p2 = new CreatePerson('李四', 30);
const p3 = new CreatePerson('王五', 40);
p1.run();
p2.run();
p3.run();

这种方式有个问题,就是每次使用 CreatePerson 函数创建对象时,run 方法被反复创建了多次。同样的内容,占据了多份内存空间。

原型对象
属于所有实例共享的属性和方法,抽离出来放在原型对象中,而每个实例特有的属性和方法,都会留在构造函数中
例如,每一个实例的名字,名字是每个实例特有的,不可能被共享。

将 run 方法挂载到原型对象上,这样就可以实现一次创建,被多个实例对象共同使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function CreatePerson(name, age) {
this.name = name;
this.age = age;
// this.run = function () {
// console.log(`调用此方法${this.name}就会开始奔跑`)
// }
}

CreatePerson.prototype.run = function () {
console.log(`调用此方法${this.name}就会开始奔跑`);
};

const p1 = new CreatePerson('张三', 20);
const p2 = new CreatePerson('李四', 30);
const p3 = new CreatePerson('王五', 40);
p1.run();
p2.run();
p3.run();

new 关键字都干了什么?

  1. 创建一个新对象,该对象为最终返回的实例
  2. 将新对象的原型对象指向构造函数的原型对象
  3. 将构造函数内部的 this 指向这个新对象,即为实例
  4. 如果构造函数明确返回了对象或函数,那么这个返回值取代第一部的新对象

实现一个 new

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function myNew(Fn, ...args) {
// 1.创建一个新对象,该对象为最终返回的实例
let res = {};

// 2.将新对象的原型执行构造函数的原型
if (Fn.prototype !== null) {
// res.__proto__ = Fn.prototype
Object.setPrototypeOf(res, Fn.prototype);
}

// 3.将构造函数的 this 指向新对象
ret = Fn.apply(res, args);

// 4.如果函数返回了对象或者函数,那么这个返回值取代第一部创建的新对象
if ((typeof ret === 'Object' || typeof ret === 'function') && ret !== null) {
return ret;
}

return res;
}

const p1 = myNew(CreatePerson, '张三', 20);
const p2 = myNew(CreatePerson, '李四', 30);
const p3 = myNew(CreatePerson, '王五', 40);
p1.run();
p2.run();
p3.run();

小结:构造函数的返回值为基本类型,其返回值是实例化后的对象。构造函数的返回值为引用类型,其返回值即为 new 之后的返回值。

构造函数和原型对象的访问优先级
如果在构造函数和原型对象中,同时声明了属性/方法,那么会优先访问构造函数中的属性/方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function CreatePerson(name, age) {
this.name = name;
this.age = age;
this.run = function () {
console.log(`构造函数中的:调用此方法${this.name}就会开始奔跑`);
};
}

CreatePerson.prototype.run = function () {
console.log(`调用此方法${this.name}就会开始奔跑`);
};

const p1 = new CreatePerson('张三', 20);
const p2 = new CreatePerson('李四', 30);
const p3 = new CreatePerson('王五', 40);
p1.run(); // 构造函数中的:调用此方法张三就会开始奔跑
p2.run(); // 构造函数中的:调用此方法李四就会开始奔跑
p3.run();
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×