写该系列文章的初衷是“让每位前端工程师掌握高频知识点,为工作助力”。这是前端百题斩的第18斩,希望朋友们关注公众号“执鸢者”,用知识武装自己的头脑。
18.1 基础
new的作用是通过构造函数来创建一个实例对象,该实例与原型和构造函数之间的关系如下图所示:

18.2 new过程中发生了什么
当一个构造函数new的过程到底发生了什么?简要概述主要分为以下几个步骤:
一个新对象被创建;
该对象的__ proto __属性指向该构造函数的原型,即Fn.prototype;
将执行上下文(this)绑定到新创建的对象中;
如果构造函数有返回值(对象或函数),那么这个返回值将取代第一步中新创建的对象。
new真的做了这几步吗?秉承着“实践是检验真理的唯一标准”的原则,下面将这几个关键点进行逐一验证。
function Fun() {
this.a = 10;
this.b = 20;
this.method1 = () => {
return this.a + this.b;
}
this.method2 = () => {
return this;
}
}
Fun.prototype = {
method2: () => {
console.log('原型上的method1被访问');
}
}
18.2.1 验证点1——新对象被创建
验证点1是新对象被创建,其实这个里面有两层含义:
new之后返回的内容是一个对象
const fun = new Fun();
console.log(fun); // { a: 10, b: 20, method1: [Function] }
console.log(typeof(fun)); // object
通过打印其内容,并通过typeof进行验证,其返回内容确实是一个对象。
每次返回的都是一个新创建的对象
const fun1 = new Fun();
const fun2 = new Fun();
console.log(fun1 === fun2); // false
通过创建两个实例,通过判断两个实例不相等,则证明确实每次返回的是一个新的对象。
18.2.2 验证点2——该对象可访问原型上的属性和方法
验证点2是新创建的实例可访问原型上的属性和方法,验证该关键点只需要访问原型上的方法即可实现,若原型上的方法能够被正常访问,则表示该验证点通过,负责不通过。
const fun3 = new Fun();
fun3.method3(); // 原型上的method3被访问
通过验证,原型上的方法确实能够被访问。
18.2.3 验证点3——this指向
验证this指向只需要将this指向打印出来即可。
const fun4 = new Fun();
console.log(fun4.method2()); // { a: 10, b: 20, method1: [Function], method2: [Function] }
console.log(fun4.method2() === fun4); // true
18.2.4 验证点4——构造函数有返回值的处理逻辑
一个函数的返回值可以有多种,例如:string、boolean、number、Object、function等,下面我们验证一些内容,看构造函数有不同的返回值,其实例为何值。
返回值为string
function Fun() {
this.a = 10;
this.b = 20;
return 'test';
}
Fun.prototype = {
method: () => {
console.log('原型上的method被访问');
}
}
const fun = new Fun();
console.log(fun); // { a: 10, b: 20 }
观察其最终结果,字符串没有没正常返回,返回值是一个新的实例。
返回值为Object
function Fun() {
this.a = 10;
this.b = 20;
return {
c: 30
};
}
Fun.prototype = {
method: () => {
console.log('原型上的method被访问');
}
}
const fun = new Fun();
console.log(fun); // { c: 30 }
观察其结果,返回值是函数中返回的对象,则表征当构造函数返回值为对象时,会返回其对象,不返回实例化后的内容。
返回值为function
function Fun() {
this.a = 10;
this.b = 20;
return function() {
this.d = 40;
};
}
Fun.prototype = {
method: () => {
console.log('原型上的method被访问');
}
}
const fun = new Fun();
console.log(fun); // [Function]
返回函数的效果和返回对象的效果一致。
通过不断尝试总结,可以得出以下结论:
构造函数的返回值为基本类型,其返回值是实例化后的对象,不受返回值的影响;
构造函数的返回值是引用类型,其返回值即为new之后的返回值。
18.3 实现一个new
介绍了这么多,已经理解了new时发生的事情并经过了验证,下面就手动实现一个自己的new函数。
function myNew(Fn, ...args) {
// 一个新的对象被创建
const result = {};
// 该对象的__proto__属性指向该构造函数的原型
if (Fn.prototype !== null) {
Object.setPrototypeOf(result, Fn.prototype);
}
// 将执行上下文(this)绑定到新创建的对象中
const returnResult = Fn.apply(result, args);
// 如果构造函数有返回值(对象或函数),那么这个返回值将取代第一步中新创建的对象。
if ((typeof returnResult === 'object' || typeof returnResult === 'function') && returnResult !== null) {
return returnResult;
}
return result;
}
小试牛刀
function Fun() {
this.a = 10;
this.b = 20;
}
Fun.prototype = {
method: () => {
console.log('原型上的method被访问');
}
}
const fun1 = new Fun();
console.log(fun1); // { a: 10, b: 20 }
const fun2 = myNew(Fun);
console.log(fun2); // { a: 10, b: 20 }
1.如果觉得这篇文章还不错,来个分享、点赞、在看三连吧,让更多的人也看到~
2.关注公众号执鸢者,领取学习资料,定期为你推送原创深度好文
3.关注公众号进群,里面大佬多多,一起向他们学习
1. 前端百题斩[001]——typeof和instanceof
3. 前端百题斩【003-004】——从基本类型、引用类型到包装对象
6. 前端百题斩【007】——js中必须知道的四种数据类型判断方法
7. 前端百题斩【008-009】——从JavaScript的代码执行过程到函数执行过程
8. 前端百题斩【010】——通俗易懂的JavaScript执行上下文
10. 前端百题斩【012】——js中作用域及作用域链的真面目
12. 前端百题斩【014】——js中的这些“this”指向都值得了解
13. 前端百题斩【015】——快速手撕call、apply、bind
14. 前端百题斩【016】——原型、构造函数和实例之间的奇妙关系
15. 前端百题斩【017】——一基础、二主线、双机制理解原型链
17. 一文彻底搞懂前端监控
18. 前端的葵花宝典——架构
19. canvas从入门到猪头
21. 2021 年前端宝典【超三百篇】
22. 前端也要懂机器学习(上)
23. 前端也要懂机器学习(下)
24. 学架构助力前端起飞
26. Vue源码思想在工作中的应用
27. 一文搞定Diff算法