和其他高级语言一样 JavaScript 也有 new 操作符,我们知道 new 可以用来实例化一个类,从而在内存中分配一个实例对象。 但在 JavaScript 中,万物皆对象,为什么还要通过 new 来产生对象? 带着这个问题,我们一步步来分析和理解 new 的一些特性:

认识 new 操作符

function Animal(name){
this.name = name;
}
Animal.color = "black";
Animal.prototype.say = function(){
console.log("I'm " + this.name);
};
var cat = new Animal("cat"); console.log(
cat.name, //cat
cat.height //undefined
);
cat.say(); //I'm cat console.log(
Animal.name, //Animal
Animal.color //back
);
Animal.say(); //Animal.say is not a function

代码解读如下:

L1-3: 创建了一个函数Animal,并在其 this 上定义了属性:name,name的值是函数被执行时的形参。

L4 : 在 Animal 对象(Animal本身是一个函数对象)上定义了一个静态属性:color,并赋值“black”

L5-7:在 Animal 函数的原型对象 prototype 上定义了一个 say() 方法,say方法输出了 this 的 name 值。

L8: 通过 new 关键字创建了一个新对象 cat

  L10-14: cat 对象尝试访问 name 和 color 属性,并调用 say 方法。

L16-20: Animal 对象尝试访问 name 和 color 属性,并调用 say 方法。

剖析 new 的内部原理

第8行代码是关键:

var cat = new Animal("cat");

Animal 本身是一个普通函数,但当通过new来创建对象时,Animal 就是构造函数。

JS引擎执行这句代码时,在内部做了很多工作,用伪代码模拟其内部流程如下:

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

将上述流程分为 4 个步骤来理解:

【1】创建一个空对象 obj;

【2】把 obj 的__proto__ 指向构造函数 Animal 的原型对象 prototype,此时便建立了 obj 对象的原型链:obj->Animal.prototype->Object.prototype->null

【3】在 obj 对象的执行环境调用 Animal 函数并传递参数 “ cat ” 。 相当于 var result = obj.Animal("cat")。

当这句执行完之后,obj 便产生了属性 name 并赋值为 "cat"。关于 call 的用法请参考:深入理解 call、apply 和 bind

【4】考察第 3 步的返回值,如果无返回值 或者 返回一个非对象值,则将 obj 作为新对象返回;否则会将 result 作为新对象返回。

根据以上过程,我们发现 cat 其实就是【4】的返回值,因此我们对 cat 对象的认知就多了一些:

  • cat 的原型链是:cat->Animal.prototype->Object.prototype->null
  • cat上新增了一个属性:name

分析完了 cat 的产生过程,我们再分析一下输出结果:

  • cat.name - 在【3】中,obj 对象就产生了 name 属性。因此 cat.name 就是这里的 obj.name
  • cat.color - cat 对象先查找自身的 color,没有找到便会沿着原型链查找,在上述例子中,我们仅在 Animal 对象上定义了 color,并没有在其原型链上定义,因此找不到。
  • cat.say -  cat会先查找自身的 say 方法,没有找到便会沿着原型链查找,在上述例子中,我们在 Animal 的 prototype 上定义了say,因此在原型链上找到了say 方法。

另外,在 say 方法中还访问 this.name,这里的 this 指的是其调用者 obj,因此输出的是 obj.name 的值。

对于Animal来说,它本身也是一个对象,因此它在访问属性和方法时也遵守上述查找规则,所以:

  • Animal.color >  " black "
  • Animal.name >  " Animal "
  • Animal.say() >   Animal.say is not a function

需要注意的是,Animal 先查找自身的 name,找到了 name,但这个 name 并不是我们定义的 name,而是函数对象内置的属性,一般情况下,函数对象在产生时会内置 name 属性并将函数名作为赋值(仅函数对象)。

另外,Animal 在自身没有找到 say() 方法,也会沿着其原型链查找,Animal 的原型链如下所示:

Animal 的原型链: Animal->Function.prototype->Object.prototype->null

由于 Animal 的原型链上也没有定义 say 方法,因此返回异常提示。

探索 new 的意义

对 new 运算符有了较深入的理解之后,我们再回到开篇提到的问题:在JavaScript 中,万物皆对象,为什么还要通过 new 来产生对象?

要弄明白这个问题,我们首先要搞清楚 cat 和 Animal 的关系:

【1】cat 继承了 Animal 对象

通过上面的分析我们发现, cat 通过原型链继承了 Animal 中的部分属性,因此我们可以简单的认为:Animal 和 cat 是继承关系。

【2】cat 是 Animal 的实例

cat 是通过 new 产生的对象,那么 cat 到底是不是 Animal 的实例对象? 我们先来了解一下JS是如何来定义实例对象

A instanceof B

如果上述表达式为 true,JavaScript 认为 A 是 B 的实例对象,我们用这个方法来判断一下 cat 和 Animal 。

cat instanceof Animal; //true

从结果看,cat 确实是 Animal 实例,要想更加证实这个结果,我们再来了解一下 instanceof 的内部原理:

var L = A.__proto__;
var R = B.prototype;
if(L === R)
return true;

如果 A 的__proto__ 等价于 B 的 prototype,就返回 true 。

在 new 的执行过程【2】中,cat 的 __proto__ 指向了Animal 的 prototype,所以 cat 和 Animal 符合 instanceof 的判断结果。

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

实战演练 

下面是一个经典例子,涉及 new 、this、以及原型链相关问题,请看代码:

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();

    

原创发布 by @一像素  2015.12

深入理解 new 操作符的更多相关文章

  1. JavaScript漫谈之理解类型操作符typeof

    在本文中,将简述JavaScript类型系统和数据类型,以及如何使用typeof操作符执行类型检查. 还讲解了使用typeof操作符进行某些数据类型检查是不完善的,并介绍其他几种类型检查的方法. 更多 ...

  2. 深入理解typeof操作符

    typeof可以检测数据的类型 typeof返回结果的其实是字符串:可以通过以下测试出来 console.log( typeof(typeof(a))); // string typeof返回的数据类 ...

  3. Linq 标准查询操作符三

    本文介绍了LINQ标准查询操作符.没有这些操作符,LINQ就不会存在.本文为理解这些操作符的功能提供了很好的基础.了解它们将会很有帮助,因为LINQ的各种Provider都是基于这些操作符来完成各自丰 ...

  4. LINQ查询操作符 LINQ学习第二篇[转]

    一.投影操作符 1. Select Select操作符对单个序列或集合中的值进行投影.下面的示例中使用select从序列中返回Employee表的所有列: using (NorthwindDataCo ...

  5. LINQ查询操作符 LINQ学习第二篇

    一.投影操作符 1. Select Select操作符对单个序列或集合中的值进行投影.下面的示例中使用select从序列中返回Employee表的所有列: using (NorthwindDataCo ...

  6. LINQ 标准查询操作符

    本文介绍了LINQ标准查询操作符.没有这些操作符,LINQ就不会存在.本文为理解这些操作符的功能提供了很好的基础.了解它们将会很有帮助,因为LINQ的各种Provider都是基于这些操作符来完成各自丰 ...

  7. [转]深入详解javascript之delete操作符

    最近重新温习JS,对delete操作符一直处于一知半解的状态,偶然发现一篇文章,对此作了非常细致深入的解释,看完有茅塞顿开的感觉,不敢独享,大致翻译如下. 原文地址:http://perfection ...

  8. C# 使用布尔操作符

    布尔操作符(Boolean operator)是求值结果要么为true,要么为false的一种操作符.C#提供了几个非常有用的布尔操作符,其中最简单的是NOT(求反)操作符,它使用感叹号(!)来表示. ...

  9. 加深对 JavaScript This 的理解

    我相信你已经看过很多关于 JavaScript 的 this 的谈论了,既然你点进来了,不妨继续看下去,看是否能帮你加深对 this 的理解. 最近在看 <You Dont Know JS> ...

随机推荐

  1. PHP 的 uniqid 函数产生的 id 真的是唯一的么?

    PHP 的 uniqid 函数产生的 id 真的是唯一的么? 最近使用到了 uniqid,就产生了疑问?uniqid 生成的 id 由什么组成?真的是唯一的么?什么情况下会产生冲突? 从文档中看到 u ...

  2. VMware 非简易安装centos6(静态ip配置)

    1.选择新建虚拟机 在弹出框中我选择推荐安装 然后点击下一步 2.选择稍后安装操作系统(不然会默认简易安装) 接着一直下一步就可以了,最后到这个页面 4.如下图选择事先下载好的安装文件 5.配置桥接模 ...

  3. jQuery学习之旅 Item2 选择器【二】

    这里接着上一个Item1 把jQuery的选择器讲完.主要有:属性过滤器和子元素过滤器 点击"名称"会跳转到此方法的jQuery官方说明文档. 5. 属性过滤器 Attribute ...

  4. rest_framework之解析器、路由控制、分页

    解析器 我们都知道,网络传输数据只能传输字符串格式的,如果是列表.字典等数据类型,需要转换之后才能使用 但是我们之前的rest_framework例子都没有转换就直接可以使用了,这是因为rest_fr ...

  5. springMVC简单的一些操作

    SpringMVC的模型-视图-控制器(MVC)框架是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器进行处理,由DispatcherServlet来统一 ...

  6. BZOJ_1003_[ZJOI2006]物流运输_最短路+dp

    BZOJ_1003_[ZJOI2006]物流运输_最短路+dp 题意:http://www.lydsy.com/JudgeOnline/problem.php?id=1003 分析: 这种一段一段的显 ...

  7. djanogo class meta

    1.指定字段名: 在定义字段的时候,增加参数db_column='real_field': 2.指定表名: 在model的class中,添加Meta类,在Meta类中指定表名db_table 例如在某 ...

  8. 【转】干货,Kubernetes中的Source Ip机制。

    准备工作 你必须拥有一个正常工作的 Kubernetes 1.5 集群,用来运行本文中的示例.该示例使用一个简单的 nginx webserver 回送它接收到的请求的 HTTP 头中的源 IP 地址 ...

  9. Apache Mina-1

    一.mina基础知识: Mina 官方网站:(http://mina.apache.org/) 1.1.Apache Mina是一个能够帮助用户开发高性能和高伸缩性网络应用程序的框架.它通过Java ...

  10. 从壹开始前后端分离 39 || 想创建自己的dotnet模板么?看这里

    缘起 开工是利啦!哈喽各位小伙伴,周三好呀,新的一年又开始了,老张给大家做个榜样,新的一年也要好好学习哟,这两天闲的无事就整理整理了这个系列的 Github 代码,增加了一个英文的 README ,然 ...