深入了解JavaScript中的new关键字
作者:胖鱼集市
手撕代码
手写new
是面试题高频考点,实现代码也非常简单,但在实现后我对其为什么要这样设计有着很深的疑惑,也看了许多文章,今天也来讲一下为什么要这样设计new
的实现
先来看下实现代码:
function MyNew(Con,...args){ const obj = Object.create(Con); const result = Con.apply(obj, args); return result instanceof Object ? result : obj; }
我最初的感受是三行代码很费解,又是原型继承,又是this
绑定,又是判断类型的,其实这其中涉及到的知识点很多,虽然实现了new
的功能,但不知道为什么要这样做
知其所以然
举例
假设我们要开始全国人口普查,所以我们要制造一个表示人信息的对象,有名字,年龄,省份等信息,会吃东西,说话,行走,睡觉等行为
const person = { ID: i, name: "pangyu", age: "18", province: "山东", say() { console.log("说话"); }, eat() { console.log("吃东西"); }, walk() { console.log("行走"); }, sleep() { console.log("睡觉"); }, };
接下来查了100个人,那我们就利用for循环创建100个人的对象,实现批量统计
const roster = []; // 花名册 for (let i = 0; i < 100; i++) { const person = { ID: i, name: "pangyu", age: "18", province: "山东", say() { console.log("说话"); }, eat() { console.log("吃东西"); }, walk() { console.log("行走"); }, sleep() { console.log("睡觉"); }, }; roster.push(person); }
避免重复
统计了100个人之后,暴露了一个问题:
每个人的say,eat,walk,sleep方法都是一样的,但每创建一个人都会重新创建这四个方法,那么100个人就创建了400个函数,要是统计成千上万人,消耗的内存相当大,这样不妥
如何解决:
既然方法都是一样的,那么就单独抽离到一个对象中,实现复用这个对象就不会再重新创建了
const roster = []; // 花名册 const personBehavior = { say() { console.log("说话"); }, eat() { console.log("吃东西"); }, walk() { console.log("行走"); }, sleep() { console.log("睡觉"); }, }; for (let i = 0; i < 100; i++) { const person = { ID: i, name: "pangyu", age: "18", province: "山东", __proto__: personBehavior, }; roster.push(person); }
现在person的__proto__
属性是personBehavior
公共方法,这其实就是原型的作用,避免公共方法的重复声明,节省内存,利于维护
功能模块化
这样实现了复用,但还不够完美,我们可以把创建人信息放在一个单独的createPerson
函数中,并让公共属性和这个方法联系起来
const roster = []; // 花名册 function createPerson(i) { const person = {}; // 创建临时对象 person.__proto__ = createPerson.prototype; // 临时对象绑定原型(公共属性) person.ID = i; person.name = "pangyu"; person.age = "18"; person.province = "山东"; return person; // 返回临时对象 } createPerson.prototype = { say() { console.log("说话"); }, eat() { console.log("吃东西"); }, walk() { console.log("行走"); }, sleep() { console.log("睡觉"); }, }; for (let i = 0; i < 100; i++) { roster.push(createPerson(i)); }
这样的功能模块划分较为清晰,那么在createPerson
函数中,JS之父发现这样创建对象的操作非常常见,于是他就发明了new关键字来简化createPerson
中的操作
其中红框内的代码其实属于模版代码,每创建一个对象都会走这样的一套逻辑,只有给person
赋值自身属性时才需要用户手动编写,所以红框中的代码片段都会在new
中实现
使用new创建person
const roster = []; // 花名册 function createPerson(i) { // const person = {}; // 【省略】创建临时对象 // person.__proto__ = createPerson.prototype; // 【省略】临时对象绑定原型 // 绑定自身属性 this.ID = i; this.name = "pangyu"; this.age = "18"; this.province = "山东"; // return person; // 【省略】返回临时对象 } createPerson.prototype = { say() { console.log("说话"); }, eat() { console.log("吃东西"); }, walk() { console.log("行走"); }, sleep() { console.log("睡觉"); }, }; for (let i = 0; i < 100; i++) { roster.push(new createPerson(i)); }
回看new的实现
function MyNew(Con,...args){ const obj = Object.create(Con); // 创建临时对象,绑定原型 const result = Con.apply(obj, args); // 调用构造函数,绑定this到临时对象(赋值this自身属性) return result instanceof Object ? result : obj; // 如果构造函数返回值为对象类型,那么就返回,否则返回临时对象 }
现在是不是对于new
有了更深的理解,其实它就是一个语法糖,把生成对象的模版代码实现了, 用户只需要关注自身属性即可
总结
new执行后做了什么
- 创建临时对象/新对象
- 绑定原型(公共属性)
- 指定this = 临时对象(为this赋值自身属性做准备)
- 执行构造函数(this赋值自身属性)
- 返回临时对象(判断构造函数返回值是否为对象)
new的本质是什么
语法糖,省略了创建对象所需的模版代码
到此这篇关于深入了解JavaScript中的new关键字的文章就介绍到这了,更多相关JavaScript new内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!