Gcalolin 2019-06-27
如题,这个问题我曾经在支付宝的电话面试里面最后一个问题被问到过,后来也没有去看到底为何不需要new,现在我们就来剖析下。
而且当你在看jquery源码的时候,如果一开始就搞不懂这样的问题,抑或jQuery.fn.init.prototype =jQuery.fn 这样的问题也搞不懂的话,那基本后面的东西都是懵的。
首先回顾下,我们一般是如何写插件的,通常是这样的
function Kissy () { } Kissy.prototype.alert = function () { } // 实例化必须要有,否则无法调用原型链上的alert方法 var kissy = new Kissy() kissy.alert() // 如果我们直接这样调用 Kissy().alert() // 就会报错alert不是一个function。这里涉及到原型继承,new 一个构造函数和 执行普通函数的区别,不在赘述。
回归一个知识点,有助于我们理解后面我们讲解的内容
构造函数有return值怎么办?
构造函数里没有显式调用return时,默认是返回this对象,也就是新创建的实例对象。
当构造函数里调用return时,分两种情况:
1.return的是五种简单数据类型:String,Number,Boolean,Null,Undefined。
这种情况下,忽视return值,依然返回this对象。
2.return的是Object
这种情况下,不再返回this对象,而是返回return语句的返回值。
如何改造上面的代码,可以不用new函数,直接调用到alert方法呢?很简单。改造如下:
function Kissy () { return Kissy.prototype }
当我们直接调用Kissy()函数的时候,不是返回的null或undefined而是返回Kissy的原型,即返回了一个原型对象,这个对象上是有alert方法的,这就好像我们熟悉的下面的代码一样
var obj = { name: 'zj', getName: function() { console.log(this.name) } } obj.getName() // zj
接下来,我们仿造jquery源码又来改造下:
function Kissy () { return new Kissy.fn.init() } Kissy.fn = Kissy.prototype = { constructor: Kissy, init: function() { console.log('init') } } Kissy.fn.init.prototype = Kissy.fn Kissy.fn.alert = function() { alert('0000') } Kissy().alert() // 00000
无非就是在Kissy上面增加了一个fn属性,这个属性指向了Kissy.prototype。这样的目的是便于我们开发插件的时候,在原型上增加方法可以直接这么写Kissy.fn.alert 仅此而已。为何构造函数不直接返回
Kissy.fn呢 而是在中间搞了一个init.读过jquery源码的都知道,jquery.fn.init 这个函数实际是jquery的初始化函数。这里就不在展开了。
return new Kissy.fn.init()
构造函数返回了一个Kissy.fn.init()这个函数的一个实例。当然就会继承这个实例上的原型,而原型又被我们重置了
Kissy.fn.init.prototype = Kissy.fn
所以就能调用到Kissy.fn上面的alert方法了,不信大家可以试试哦。
再来看jquery的源码,对比刚刚分析的代码,是不是基本一模一样,只是jquery源码fn.init方法有比较多初始化的内容。
jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); }, jQuery.fn = jQuery.prototype = { //fn即对应prototype constructor: jQuery, init: function( selector, context, rootjQuery ) { ... return this; } ... } jQuery.fn.init.prototype = jQuery.fn;
是一道经常出现在前端面试时的问题。如果只是简单的了解new关键字是实例化构造函数获取对象,是万万不能够的。更深入的层级发生了什么呢?同时面试官想从这道题里面考察什么呢?下面胡哥为各位小伙伴一一来解密。