js new一个对象的过程,实现一个简单的new方法

对于大部分前端开发者而言,new一个构造函数或类得到对应实例,是非常普遍的操作了。下面的例子中分别通过构造函数与class类实现了一个简单的创建实例的过程。
// ES5构造函数
let Parent = function (name, age) {
this.name = name;
this.age = age;
};
Parent.prototype.sayName = function () {
console.log(this.name);
};
const child = new Parent('听风是风', 26);
child.sayName() //'听风是风' //ES6 class类
class Parent {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() {
console.log(this.name);
}
};
const child = new Parent('echo', 26);
child.sayName() //echo
但new不应该像一个黑盒,我们除了知道结果,更应该明白过程究竟如何。那么那么这篇文章主要围绕两点展开,第一,js中new一个对象时到底发生了什么,第二,知道了原理后我们通过js来实现一个简单的new方法。
一、new操作中发生了什么?
比较直观的感觉,当我们new一个构造函数,得到的实例继承了构造器的构造属性(this.name这些)以及原型上的属性。
在《JavaScript模式》这本书中,new的过程说的比较直白,当我们new一个构造器,主要有三步:
• 创建一个空对象,将它的引用赋给 this,继承函数的原型。
• 通过 this 将属性和方法添加至这个对象
• 最后返回 this 指向的新对象,也就是实例(如果没有手动返回其他的对象)
我们改写上面的例子,大概就是这样:
// ES5构造函数
let Parent = function (name, age) {
//1.创建一个新对象,赋予this,这一步是隐性的,
// let this = {};
//2.给this指向的对象赋予构造属性
this.name = name;
this.age = age;
//3.如果没有手动返回对象,则默认返回this指向的这个对象,也是隐性的
// return this;
};
const child = new Parent();
这应该不难理解,你应该在工作中看过类似下述代码中的操作,将this赋予一个新的变量(例如that),最后返回这个变量:
// ES5构造函数
let Parent = function (name, age) {
let that = this;
that.name = name;
that.age = age;
return that;
};
const child = new Parent('听风是风', '26');
为什么要这么写呢?我在前面说this的创建与返回是隐性的,但在工作中为了让构造过程更易可见与更易维护,所以才有了上述使用that代替this,同时手动返回that的做法;这也验证了隐性的这两步确实是存在的。
但上述这个解释我觉得不够完美,它只描述了构造器属性是如何塞给实例,没说原型上的属性是如何给实例继承的。
我在winter大神的重学前端专栏中,看到了比较符合我心意的,同时也是符合原理的描述:
• 以构造器的prototype属性为原型,创建新对象;• 将this(也就是上一句中的新对象)和调用参数传给构造器,执行;• 如果构造器没有手动返回对象,则返回第一步创建的新对象,如果有,则舍弃掉第一步创建的新对象,返回手动return的对象。
到这里不管怎么说,你都应该大概知道了new过程中会新建对象,此对象会继承构造器的原型与原型上的属性,最后它会被作为实例返回这样一个过程。知道了原理,我们来手动实现一个简单的new方法。
二、实现一个简单的new方法
// 构造器函数
let Parent = function (name, age) {
this.name = name;
this.age = age;
};
Parent.prototype.sayName = function () {
console.log(this.name);
};
//自己定义的new方法
let newMethod = function (Parent, ...rest) {
// 1.以构造器的prototype属性为原型,创建新对象;
let child = Object.create(Parent.prototype);
// 2.将this和调用参数传给构造器执行
let result = Parent.apply(child, rest);
// 3.如果构造器没有手动返回对象,则返回第一步的对象
return typeof result === 'object' ? result : child;
};
//创建实例,将构造函数Parent与形参作为参数传入
const child = newMethod(Parent, 'echo', 26);
child.sayName() //'echo'; //最后检验,与使用new的效果相同
child instanceof Parent//true
child.hasOwnProperty('name')//true
child.hasOwnProperty('age')//true
child.hasOwnProperty('sayName')//false
那么到这里就介绍完毕了。
new一个构造函数默认返回什么?调用构造函数不使用new能得到实例吗?如果你对这些有兴趣,可以阅读博主这篇博客:
精读JavaScript模式(三),new一个构造函数究竟发生了什么?
实例为什么能使用构造器prototype上的方法?继承之间的关系又是怎么样的?如果你对这些有兴趣,可以阅读博主这篇博客:
ES6新增的class类怎么用?与传统构造函数写法有哪些区别?如何快速上手class类?那你可以阅读博主这篇博客:
最后模拟实现new方法中,...rest是个什么参数?如果你对这个存疑,那怕是得了解下ES6中的取代arguments的rest参数,欢迎阅读这篇:
原谅我的厚颜无耻,如果你觉得本文对你有所帮助,欢迎评论,点赞与关注。
js new一个对象的过程,实现一个简单的new方法的更多相关文章
- 只是一个用EF写的一个简单的分页方法而已
只是一个用EF写的一个简单的分页方法而已 慢慢的写吧.比如,第一步,先把所有数据查询出来吧. //第一步. public IQueryable<UserInfo> LoadPagesFor ...
- 通过Knockout.js + ASP.NET Web API构建一个简单的CRUD应用
REFERENCE FROM : http://www.cnblogs.com/artech/archive/2012/07/04/Knockout-web-api.html 较之面向最终消费者的网站 ...
- 【 D3.js 入门系列 --- 3 】 做一个简单的图表!
前面说了几节,都是对文字进行处理,这一节中将用 D3.js 做一个简单的柱形图. 做柱形图有很多种方法,比如用 HTML 的 div 标签,或用 svg . 推荐用 SVG 来做各种图形.SVG 意为 ...
- 【 D3.js 入门系列 — 3 】 做一个简单的图表!
图1. 柱形图 1. 柱形图 前几章的例子,都是对文字进行处理.本章中将用 D3 做一个简单的柱形图.制作柱形图有很多种方法,比如用 HTML 的 <div> 标签,或在 SVG 上绘制 ...
- 从一个简单的main方法执行谈谈JVM工作机制
本来JVM的工作原理浅到可以泛泛而谈,但如果真的想把JVM工作机制弄清楚,实在是很难,涉及到的知识领域太多.所以,本文通过简单的mian方法执行,浅谈JVM工作原理,看看JVM里面都发生了什么. 先上 ...
- Java实现一个简单的缓存方法
缓存是在web开发中经常用到的,将程序经常使用到或调用到的对象存在内存中,或者是耗时较长但又不具有实时性的查询数据放入内存中,在一定程度上可以提高性能和效率.下面我实现了一个简单的缓存,步骤如下. 创 ...
- js利用点击事件做一个简单的计算器
先放一个样式图: 源代码如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"&g ...
- 一个简单的解决方法:word文档打不开,错误提示mso.dll模块错误。
最近电脑Word无故出现故障,无法打开,提示错误信息如下: 问题事件名称: APPCRASH应用程序名: WINWORD.EXE应用程序版本: 11.0.8328.0应用程序时间戳: 4c717ed1 ...
- Vue.js如何在一个页面调用另一个同级页面的方法
使用场景: 页面分为header.home.footer三部分,需要在home中调用header中的方法,这两个没有相互引入 官方给出方法: 需要在展示页里调用顶部导航栏页里的方法,两者之间没有引用关 ...
随机推荐
- MFC实现COM组件
一般而言,ATL实现了对COM组件最好的支持,所以不用MFC实现COM组件.但是MFC实际上也是可以实现COM组件的. 一.MFC DLL优点: MFC com组件可以将MFC的类型作为参数进行传递, ...
- Hibernate - 设置隔离级别
JDBC 数据库连接使用数据库系统默认的隔离级别. 在 Hibernate 的配置文件中可以显式的设置隔离级别. 每一个隔离级别都对应一个整数: 1. READ UNCOMMITED2. READ C ...
- LOJ2302 「NOI2017」整数
「NOI2017」整数 题目背景 在人类智慧的山巅,有着一台字长为$1048576$位(此数字与解题无关)的超级计算机,著名理论计算机科 学家P博士正用它进行各种研究.不幸的是,这天台风切断了电力系统 ...
- IronPython for ASP.NET 部署注意事项
用 IronPython for ASP.NET 开发的网站,在部署时,除了发布 bin 目录下的 IronPython.dll, IronMath.dll, Microsoft.Web.IronPy ...
- 自定义Panel中添加依赖属性需要注意的问题
今天帮忙同事调试一个自定义Panel的问题, 很奇怪, 利用Binding可以通过ItemSource来添加控件,但是在Listbox的xaml里添加几个ListboxItem却报异常: Visual ...
- asp.net分页asp.net无刷新分页高效率分页
项目中经常会用到分页的功能类似的项目做过无数个了,今个把自己常用的分页代码分享一下. 首先说说服务端处理的代码: 下面代码中重点是分页的sql语句的写法,其中的参数@n是当前的页码,总的来说本服务端主 ...
- HDOJ1213(并查集)
set容器中的值互异,非常好用. 水题,直接贴代码了 #include<iostream> #include<cstdio> #include<set> using ...
- 机器学习:scikit-learn中算法的调用、封装并使用自己所写的算法
一.scikit-learn库中的kNN算法 scikit-learn库中,所有机器学习算法都是以面向对象的形式进行包装的: 所有scikit-learn库中机器学习算法的使用过程:调用.实例化.fi ...
- RS-485半双工延时问题
学习485总线时,遇到延时问题,困扰很久.通过学习知道了485半双工收发时必须延时,以保证系统的稳定性.可靠性.好多资料都介绍了485 防静电.抗干扰电路.惟独没提 每一帧收发停止位(或第9位)的延时 ...
- Python模块-requests(二)
会话对象 会话对象能够跨请求保持某些参数. 它也会在同一个 Session 实例发出的所有请求之间保持 cookie, 期间使用 urllib3 的 connection pooling 功能. 所以 ...