(js)使用new对函数进行构造调用

js中的函数有两种调用方式,一种是通过普通的声明之后进行的调用。一种是通过new关键字进行构造调用。普通的调用就是依次执行函数内部的函数语句,如果有返回值则返回返回值,如果没有则函数内部的声明周期结束。但是,函数还有另一个调用方式,使用new关键字,将函数当做构造函数。js中没有所谓独立出来的构造函数的概念,所有的函数都用同样的方式声明,所以有了new这个关键字,js(ES6之前)只能通过这种方式实现构造器的构造。那么使用new关键字跟普通的调用有什么区别呢?

使用new关键字,比普通的函数调用,主要分为以下四个步骤:

  1. 建立一个空对象 。
  2. 将函数内部的this修正到上面新建的对象上来。
  3. 继续执行函数的其它语句,并且将上面新建的对象的[[prototype]]属性(ES6中称为"__proto__")指向该构造函数。
  4. 如果函数没有返回值或着函数有返回值,但是该返回值不是对象的话,则返回第一步建立的对象;如果函数有返回值,且返回值是对象的话,则忽略第一步建立的对象,返回默认返回的对象。

稍后我们会对上面的四句话逐一解析,首先我们看一看函数调用的两种方式:

var fun = function () {
  var a = 2;
  console.log(a * 2);
}
//新建函数fun

fun();    //4
new fun();  //4

这样看来,两者似乎没有区别,但是这里要注意,使用new进行构造调用时,函数是有返回值的。

...
var return1 = fun();    //4
var return2 = new fun();    //4

console.log(return1);    //undefined
console.log(return2);    //fun {}

上面的return2并没有返回一个函数。

...
typeof return2;    //object
Object.prototype.toString.call(return2);    //[object Object]

其实它会返回一个空的对象。这也就是上面使用new关键词的第一步,内部创建一个新的空对象

那么当函数内部有this时,结果会是怎样的呢?

var fun = function () {
  this.a = 2;
  console.log(this.a * 2);
}

fun();    //4
new fun();    //4

var return1 = fun();    //4
var return2 = new fun();    //4

console.log(return1);    //undefined
console.log(return2);    //{ a: 2 }

使用new关键字后,在函数内部如果出现了this,则自动将this指向内部新建的对象上。最后返回时,因为this的缘故,对象上新建了a属性,并且赋值返回。

修正定义的对象Object的[[prototype]]
虽然实例上的[[prototype]]属性__proto__是ES6才作为规范出版的。但是在这之前chrome已经支持__proto__属性,他指向对象的原型。

原型的问题相当复杂,单独拿出来也可以当好几篇文章的量来讲。但是这并不是本文的重点。但是每一个对象从根部来说,继承自Object。而Object.prototype上面定义了一些方法,有类似toString,valueOf等等等方法。对于对象来说,支持通过属性链和方法链向上查询。所以在一个对象实例中,如果没有定义toString方法,但它还可以向上查询,找到原型中的toString方法,进行调用。

同时的,有很多元素通过Object实现继承。比如Function, Array, RegExp等等对象,它们也是对象,但是却是继承来自Object。

在这里,内部定义的对象,让他继承来自构造函数。

最后一步,也是最容易被忽略的一步,那就是当构造函数存在返回值时,并且返回值为对象时,返回对象而不返回之前定义的对象。

var fun = function () {
  this.a = 1;
  return {
    a: 2
  }
}
var obj = new fun();  //{ a:2 }

当然,上面说的Function,Array,RegExp也算Object的一种,如果返回他们同样也会阻止默认的对象返回。

var fun = function () {
  this.a = 1;
  return function () {
    this.a = 2;
  }
}
var obj = new fun();  //function () { this.a = 2; }

推荐阅读更多精彩内容

  • 普通创建对象和字面量创建对象不足之处:虽然 Object 构造函数或对象字面量都可以用来创建单个对象,但这些方式有...
    believedream阅读 1,260评论 2 18
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    千机楼阅读 1,013评论 0 4
  • 一 常见命令 1.初始化一个代码仓库git init 2.如果使用git必须给git配置一个用户名和邮箱给当前的g...
    cornerAnt阅读 377评论 0 7
  • 明天就是除夕啦,今年年夜饭的餐桌上怎么少得了网红鸡这道硬菜咧!今天跟大家分享的这道菜叫大盘鸿运鸡,寓意新的一年鸿运...
    桃之夭夭里阅读 78评论 2 2
  • 姑娘托着腮,望着江边,炊烟袅袅。 身边的姐妹都取笑你,哟,又在想念哪个少年郎? 姑娘托着腮,一言不发,望着江边的炊...
    楠木枝阅读 36评论 0 0