function作为构造函数和非构造函数调用的区别

var currentTime = Date() 能生成一个当前时间的日期对象,var currentTime = new Date() 也能生成一个同样的对象。如果你看过一些框架,那么你会发现有的框架生成对象写法是 new ClassName(),有的框架是 className()。 那么两种方式有什么区别呢?

普通函数/方法调用

假设我们定义了一个函数:

function normalFunc() {
  console.log( this );
}
// 第一种调法
normalFunc();
// 第二种调法
normalFunc.call( null );
// 第三种调法
var obj = {
  method: normalFunc
}
obj.method();

我们把一个函数被当作一个普通函数或者方法调用归为一类,其被调用时发生的主要步骤:

  1. 生成一个新的执行上下文和对应的作用域。(如果对执行上下文是什么不了解的话,可以参考我上一篇《什么是作用域和执行上下文》
  2. 把当前函数和这个新的执行上下文和作用域关联起来。
    2.1. 如果当前函数是箭头函数,那么把作用域中的 environment record 对象的内部属性 [[thisBindingStatus]] 设置成 lexical。
  3. 把这个执行上下文压入调用栈的顶部,即设置成运行执行上下文(running execution context)。
  4. 接下来处理当前函数的属性 this 的取值:
    4.1. 如果当前函数是箭头函数,那么这步就不做任何处理(因为已经在步骤2.1中做了标志位)。
    4.2. 如果不是箭头函数,那么先查看当前函数是否处在严格模式下。
    4.2.1. 严格模式:this 的取值取决于如何调用当前函数,譬如上例代码中第一种调法,取值为 undefined,第二种调法取值为 normalFunc.call( 的第一个参数,第三种调法取值为 obj。
    4.2.2. 非严格模式:先按4.2.1的分类获得 this 的取值,如果是 null 或者 undefined,用全局对象代替 null 或者 undefined。如果 this 的取值是非空值那么把 this 指向这个非空值(注1)。
    4.3. 把 this 的取值保存在作用域中的 environment record 对象的内部属性 [[thisValue]] 中(步骤4中并非把 this 直接指向这些取值,而是把值保存在作用域特定内部属性中,this 的寻值过程还有额外一步,下面会说明)。
  5. 执行函数体。
  6. 把当前执行上下文弹出调用栈。
  7. 如果步骤5有返回,则返回这个结果。如果步骤5没有返回,则返回 undefined。

注1:这里非空值还要判断是原始类型(primitive value),还是对象类型。如果是原始类型,取值还要再把原始类型包装成对象才能作为 this 的取值。步骤中避免太繁琐,省略了细节顾特地加上注释。

函数中 this 的取值过程(ResolveThisBinding)

结合上面描述的步骤,我们来看看当你在函数中使用 this 时(上面的步骤5中 this 已经可用),程序时如何寻找 this 的:

  1. 根据当前执行上下文查找到对应的 enviroment record(execution context -> scope -> environment record)。
  2. 判断当前这个 record 是否存储过[[thisValue]],如果没有的就沿着作用域链向上查找,以全局作用域为终点。
  3. 如果找到了,则返回。

如上所述,箭头函数本身的作用域并没有存储[[thisValue]],所以其内部使用 this 会去定义箭头函数的地方(函数)去取 this,如果取不到继续向上查找。

函数作为构造函数调用

// 没有继承关系
function normalFuncAsContructor() {
  // return a new object?
  // return {}
  // or not
  // [return]
}
var o = new normalFuncAsContructor();
// 有继承关系
function Parent(){}
function Child(){}
Child.prototype = new Parent();
var c = new Child();

我们把一个函数被当作构造函数,使用 new 操作符调用时发生的主要步骤:

  1. 新建一个普通对象,把其原型 [[Prototype]] 指向构造函数的 prototype 属性的值。
  2. 如普通函数调用的步骤1一样,生成一个新的执行上下文和对应的作用域,并把当前构造函数和两者关联起来。
  3. 把这个执行上下文压入调用栈的顶部。
  4. 把第一步生成的对象当作 this 的取值保存到作用域中的 environment record 对象的内部属性 [[thisValue]] 中。
  5. 执行函数体。
  6. 把当前执行上下文弹出调用栈。
  7. 处理函数执行的结果,即 new 了之后返回啥:
    7.1 如果步骤5返回一个对象,那么就把这个对象作为此次 new 操作的返回值。
    7.2 如果返回的不是对象,而且这个函数不是 generator 函数,那么返回第一步生成的对象(generator 就先不在这里讨论了)。

知道了两者的区别,我们就能在函数体里面搞文章了,你可以通过如下代码检测用户怎么调用你的函数。如果你知道了用户怎么调用,你自然可以根据你想要的结果限制用户的使用方法。

function myFunc() {
  if ( this && myFunc.prototype.isPrototypeOf( this ) ) {
    console.log( 'called by new operator' );
  } else {
    console.log( 'commonly invoked' );
  }
}
myFunc(); // commonly invoked
new myFunc(); // called by new operator
var obj = {
  method: myFunc
}
obj.method(); // commonly invoked
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容