面试官真的会问:new的实现以及无new实例化

面试官很忙,但我不单纯是蹭热点,今天聊的主题绝对是面试中命中率很高的知识点。我在复习javascript函数这块知识时,注意到一个有意思的点,就是构造函数显式return,并由此引起了一波头脑风暴......javascript

咱们知道,若是不作特殊处理,new构造函数时会发生下面这几步。前端

  • 首先建立一个新对象,这个新对象的 __proto__属性指向构造函数的 prototype属性
  • 此时构造函数执行环境的 this指向这个新对象
  • 执行构造函数中的代码,通常是经过 this给新对象添加新的成员属性或方法。
  • 最后返回这个新对象。

下面咱们来验证下:java

function Test() {
 console.log(JSON.stringify(this));  console.log(this.__proto__.constructor === Test);  this.name = 'jack';  this.age = 18;  console.log(JSON.stringify(this)); } var a = new Test(); // Chrome控制台会输出如下内容 // {} // true // {"name":"jack","age":18} 复制代码

这彻底符合咱们的认知,没毛病。web

实现一个new

那么在认识到new实例化过程的几个关键步骤后,咱们也能解答一道面试中常见的题目:如何实现一个new?面试

实现一个new也就意味着不能用new关键字,那么要完成这么一系列步骤,固然是经过函数实现了。app

// func是构造函数,...args是须要传给构造函数的参数
function myNew(func, ...args) {  // 建立一个空对象,而且指定原型为func.prototype  var obj = Object.create(func.prototype);  // new构造函数时要执行函数,同时指定this  func.call(obj, ...args);  // 最后return这个对象  return obj; } 复制代码

以这四个关键步骤做为指导思想,咱们很快就写出了代码实现。从这一点我也能体会到思路的重要性,别当工具人,代码才是工具!编辑器

从实现逻辑上来看没什么问题,咱们来验证下。ide

function Test(name, age) {
 this.name = name;  this.age = age; }  myNew(Test, '小明', 18); // Chrome控制台会输出如下内容 // Test {name: "小明", age: 18} 复制代码

构造函数显式return

所谓显式return,就是在构造函数中主动return一个对象,这里说的对象不只包括Object,也包含ArrayDate等对象哦。函数

咱们能够试一试:工具

function Test() {
 this.name = 'jack';  this.age = 18;  return {  content: '我有freestyle'  } } new Test(); // Chrome控制台会输出如下内容 // {content: "我有freestyle"} 复制代码

那么return一个普通类型数据有没有用呢?好比字符串,数字?试试便知。

function Test() {
 this.name = 'jack';  this.age = 18;  return '我有freestyle' } new Test(); // Chrome控制台会输出如下内容 // Test {name: "jack", age: 18} 复制代码

能够看到,当咱们return一个普通类型数据时,不会影响结果,依然会返回new出来的这个新对象。

咱们也应该知道,new构造函数就是为了建立对象,你return一个字符串之类的普通类型数据是没有任何意义的,因此咱们的关注点应该是return一个特殊的对象。请接着往下看。

无new实例化

所谓“无new实例化”,就是指不经过new关键字实例化对象(固然,这里说的不经过new,只是调用层面的,底层仍是用了new)。这一点咱们使用jQuery的时候已经体验过了。

// 实例化了一个jQuery对象,可是没有用到new
var ele = jQuery('<div>freestyle</div>'); 复制代码

那么这种黑科技是怎么实现的呢?

前面已经提到了,咱们能够在构造函数中经过显式return来返回一个自定义的对象,那么这里就有发挥的空间了。咱们经过一个简单的例子来感觉下:

function Shadow() {
 this.name = 'jack';  this.age = 18; }  function jQuery() {  return new Shadow(); }  var obj1 = jQuery(); console.log(obj1) // Chrome控制台会输出如下内容 // Shadow {name: "jack", age: 18} 复制代码

jQuery()用了移花接木的障眼法完成了对象实例化,一手隐藏的new Shadow()让咱们误觉得不用new直接调用函数也能建立实例。

咱们再来试下new jQuery(),会发现,“卧槽,怎么跟jQuery()执行结果如出一辙!”

var obj2 = new jQuery();
console.log(obj2) // Chrome控制台会输出如下内容 // Shadow {name: "jack", age: 18} 复制代码

这是由于new构造函数显式return了new Shadow(),这样返回的结果也就是new Shadow()实例化出来的对象,而不使用new直接调用jQuery(),只是把jQuery()当成一个普通的函数执行,其结果不言而喻是new Shadow()实例化出来的对象。

因此,这里new jQuery()jQuery()是等价的。

虽然jQuery已经用得愈来愈少,可是其设计思路很是值得咱们学习。那么jQuery到底妙在哪里?能够说是不少,链式操做,插件体系这些特点都是咱们耳熟能详的。不扯太多了,就让咱们来简单分析下jQuery实例化的过程。

我这里拿到了jQuery v1.12.4版本的代码,大概1W行,很舒服。

翻啊翻啊,翻到了第71行,看到了这么一串代码:

jQuery = function( selector, context ) {
 return new jQuery.fn.init( selector, context ); } 复制代码

这不就是咱们熟悉的移花接木技术吗?jQuery.fn.init彷佛就是上面例子中的Shadow。看着有点像了,可是仍是要好好研究下。

为啥要搞个jQuery.fn?

jQuery.fn是jQuery.prototype的别名,是为了代码简洁的考虑。这一点参考源码第91行就能够知道。

jQuery.fn = jQuery.prototype = {
// ...... 复制代码

移花接木如何保证原型指向?

咱们知道,若是仅仅经过new jQuery.fn.init(selector, context)是存在一个问题的,问题就是获得的实例不是jQuery的实例,而是jQuery.fn.init的实例。那么如何处理这个问题呢?

咱们翻到源码2866行,能够看到:

init = jQuery.fn.init = function( selector, context, root ) {
 // 建立实例的具体逻辑 } 复制代码

具体init方法怎么建立一个jQuery对象,作了哪些判断逻辑,这些都不是本文关注的重点。咱们须要关注的是,jQuery是如何保证明例化的对象的原型指向是正确的?否则实例化的对象如何使用jQuery.prototype上面挂载的诸多方法呢,好比this.show()this.hide()

紧接着翻到2982行,我有了答案:

init.prototype = jQuery.fn;
复制代码
牛逼
牛逼

妙啊,这一手修改原型指向的操做,完美解决了这个问题。这样一来,new init()获得的实例天然也是jQuery的实例。

jQuery.prototype.init.prototype === jQuery.prototype; // true
var a = $('<div>123</div>') a instanceof jQuery // true a instanceof jQuery.fn.init // true 复制代码

这样一来,咱们能够获得一个基本的设计思路:

function myModule(params) {
 return new myModule.fn.init(params); } myModule.fn = myModule.prototype = {  constructor: myModule } myModule.fn.init = function(params) {  // 能够对实例对象进行各类操做 } myModule.fn.init.prototype = myModule.prototype; 复制代码

在这个基础上,咱们能够扩展静态方法和原型方法,这个myModule模块就变得愈来愈丰富。

最后

妙啊,一个构造函数,让我陷入了思考......扶我起来,我还能学!

一块儿学前端吗
一块儿学前端吗
关注交流
关注交流

本文使用 mdnice 灵动蓝主题 排版

相关文章
相关标签/搜索