JS 的 new 做了那些事情

JS中的new运算符,从一个自定义对象类型或者包含constructor构建函数的内建对象类型中实例化一个对象。JS中已经“万物皆对象”。为什么还要存在实例化的操作呢?

首先,先看一段代码:
function Animal(name) {
  this.name = name
}
Animal.color = 'black'
Animal.say = function () {
  console.log('it is an ' + this.name)
}
Animal.prototype.say = function () {
  console.log('i am a ' + this.name)
}

var cat = new Animal('cat')
console.log(
  cat.name,  // cat
  cat.height  // undefined
)
cat.say()  // i am a cat
console.log(
  Animal.name, //Animal
  Animal.color //black
);
Animal.say(); //it is an Animal
  1. 首先定义了一个函数对象Animal。作为函数对象,Animal拥有原型对象prototype,prototype中拥有一个constructor函数,指向Animal函数自身。
  2. 给Animal函数对象添加了一个color属性,赋值为black
  3. 给Animal函数对象添加了一个say方法,该方法,读取当前调用的this对象中的name属性,打印字符串。
  4. 给Ainimal的原型对象添加一个say的方法,该方法,读取当前调用的this对象中的name属性,打印字符串。
  5. 通过new,从Animal函数对象中创建一个实例,将其赋值为变量cat
  6. 打印新建cat对象中的两个属性,name和height,分别打印为catundefined
  7. 调用cat对象的say方法,将cat对象作为this传递到Animal.prototype.say函数中,并执行打印输出
  8. 打印Animal函数对象中的两个属性,name和color,分别打印为Animalblack。其中的name属性是从Function.prototype上继承而来,color是Animal函数对象的自有属性。
  9. 调用Animal函数对象的say方法,将Animal函数对象作为this传递到Animal.say函数中,并执行打印输出

关于Animal,以及Animal的say,name属性的调用,都可以从函数对象的原型链的继承上得到解释。它的原型链是

Animal->Function.prototype->Object.prototype->null
我们重点关注下
var cat = new Animal('cat')

当JS中使用new操作符 添加到一个函数对象的前面并执行调用的时候。函数对象起到了一个自定义对象的constructor,也既构建函数的作用。
JS的new本身是一个“语法糖”,当JS解释器碰到new的时候,它会按照下面的伪代码执行:

// var cat = new Animal('cat')
var cat = (function () {
  let obj = {}
  obj.__proto__ = Animal.prototype
  let result = Animal.call(obj, 'cat')
  return (typeof result == 'object') ? result : obj
})()

将其转成规则,则new所起到的作用流程如下:

  1. 首先凭空创建一个空对象obj
  2. 把 obj 的proto 指向构造函数 Animal 的原型对象 prototype,此时便建立了 obj 对象的原型链:obj->Animal.prototype->Object.prototype->null
  3. 在 obj 对象的执行环境调用 Animal 函数并传递参数 “ cat ” 。 相当于 var result = obj.Animal("cat")。这句话,将this指向新创建的obj对象。并执行构建函数Animal。当这句执行完之后,obj 便产生了属性 name 并赋值为 "cat"。关于 call 的用法请参考:深入理解 call、apply 和 bind
  4. 考察第 3 步的返回值,如果无返回值 或者 返回一个非对象值,则将 obj 作为新对象返回;否则会将 result 作为新对象返回。

此时cat的原型链是

cat -> Animal.prototype -> Object.prototype -> null
为什么要使用new来创建对象呢?

new的出现,让JS拥有了对象的继承能力,从例子中看到,通过new,成功在cat和Animal之间建立了继承的关系。cat可以调用Animal的原型对象上的方法。

通过 new 创建的 对象 和 构造函数 之间建立了一条原型链,原型链的建立,让原本孤立的对象有了依赖关系和继承能力,让JavaScript 对象能以更合适的方式来映射真实世界里的对象,这是面向对象的本质

测试下
function Foo(){
    getName = function(){
        console.log(1)
    }
    return this;
}
Foo.getName = function(){
    console.log(2)
}
Foo.prototype.getName = function(){
    console.log(3)
}
var getName = function(){
    console.log(4)
}
function getName(){
    console.log(5)
}
// ouput:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
  1. Foo.getName(): 调用Foo函数对象的getName方法,此时打印 2
  2. getName(); 调用全局的getName方法。JS代码执行分为两个阶段,具体参考执行上下文的文章,首先在代码为执行前,getName指的是打印 5 的函数声明,但在执行到此时的时候,上面 getName 全局变量的执行,将getName的赋值指向了 打印4 的函数。因此此时打印 4
  3. Foo().getName(); 将Foo函数执行后返回的对象作为this,并调用该this中包含的getName方法。首先,Foo()的调用是在全局作用域,因此return this 等价于 return window。Foo() == window。此时Foo().getName()变成了this.getName()。但此时并不等价于第二条,因为在Foo()执行的过程中,在Foo函数内部,对全局变量getName进行了重新赋值,此时全局函数getName打印输出 1
  4. getName(); 此时的结果和上一条打印结果输出相同,打印输出 1
  5. new Foo.getName(); 此时出现了new操作符,它将后面的函数Foo.getName作为了构建函数,创建了一个新的实例,在创建的过程中,会执行Foo.getName函数,因此此时输出 2
  6. new Foo().getName(); 此时出现了new操作符,根据就近原则,等价于(new Foo()).getName()。即先创建了Foo()对象的一个实例 obj = new Foo()。此时return的this等同于新建实例对象obj。接下来的调用变成了obj.getName,也既Foo.prototype.getName。打印输出 3
  7. new new Foo().getName(); 首先出现了两次new操作符,而每次new操作符,都需要跟随一个构建函数的调用。在表达式中一共有两次调用(函数的调用通过()实现)。因此按照就近原则,表达式等价于 new (new Foo()).getName()。new Foo(),根据new的执行原则,返回新建实例obj。此时等价于new obj.getName()。和第6条一样。因此此时,打印输出 3
最后打印顺序为 2,4,1,1,2,3,3

参考链接:
https://zhuanlan.zhihu.com/p/23987456
https://stackoverflow.com/questions/1646698/what-is-the-new-keyword-in-javascript
https://www.cnblogs.com/onepixel/p/5043523.html
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new

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

推荐阅读更多精彩内容