JS核心系列:理解 new 的运行机制
和其他高级语言一样 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
JS核心系列:理解 new 的运行机制的更多相关文章
- <转>ASP.NET学习笔记之理解MVC底层运行机制
ASP.NET MVC架构与实战系列之一:理解MVC底层运行机制 今天,我将开启一个崭新的话题:ASP.NET MVC框架的探讨.首先,我们回顾一下ASP.NET Web Form技术与ASP.NET ...
- 深入理解struts的运行机制
扫码关注公众号,不定期更新干活 在此申明本博文并非原创,原文:http://blog.csdn.net/lenotang/article/details/3336623,本文章是在此文章基础上进行优化 ...
- Java学习系列(一)Java的运行机制、JDK的安装配置及常用命令详解
俗话说:“十五的月亮十六圆”.那学习是不是也是如此呢?如果把月亮看成是我们的愿望,那十五便是我们所处的“高原期”,坚持迈过这个坎,我相信你的愿望终究会现实的.记得马云曾说:今天很残酷,明天更残酷,后天 ...
- 理解PHP的运行机制
PHP是一种纯解释型在服务端执行的可以内嵌HTML的脚本语言,尤其适合开发Web应用程序.请求一个 PHP 脚本时,PHP 会读取该脚本,并将其编译为 Zend 操作码,这是要执行的代码的一种二进制表 ...
- js 理解new的运行机制
先上段代码: function People(name) { this.name = name; } People.prototype.sayName = function () { console. ...
- 从template到DOM(Vue.js源码角度看内部运行机制)
写在前面 这篇文章算是对最近写的一系列Vue.js源码的文章(https://github.com/answershuto/learnVue)的总结吧,在阅读源码的过程中也确实受益匪浅,希望自己的这些 ...
- JS核心系列:浅谈原型对象和原型链
在Javascript中,万物皆对象,但对象也有区别,大致可以分为两类,即:普通对象(Object)和函数对象(Function). 一般而言,通过new Function产生的对象是函数对象,其他对 ...
- JS核心系列:浅谈 call apply 与 bind
在JavaScript 中,call.apply 和 bind 是 Function 对象自带的三个方法,这三个方法的主要作用是改变函数中的 this 指向,从而可以达到`接花移木`的效果.本文将对这 ...
- v8是怎么实现更快的 await ?深入理解 await 的运行机制
最近v8团队发表一篇博客Faster async functions and promises, 预计在v7.2版本实现更快的异步函数和promise. 文章内容看起来不是很容易理解,背后的原理比较隐 ...
随机推荐
- ASP.NET MVC 请求路径相关参数的获取
Request.ApplicationPath / Request.CurrentExecutionFilePath /Home/Index Request.FilePath /Home/Index ...
- Js new到底发生了什么
在Js中,我们使用了new关键字来进行实例化 那么在这个new的过程中到底发生了什么? 关于构造函数的return 正常来讲构造函数中是不用写return语句的,因为它会默认返回新创建的对象. 但是, ...
- 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...
- Android N开发 你需要知道的一切
title: Android N开发 你需要知道的一切 tags: Android N,Android7.0,Android --- 转载请注明出处:http://www.cnblogs.com/yi ...
- 【NLP】揭秘马尔可夫模型神秘面纱系列文章(一)
初识马尔可夫和马尔可夫链 作者:白宁超 2016年7月10日20:34:20 摘要:最早接触马尔可夫模型的定义源于吴军先生<数学之美>一书,起初觉得深奥难懂且无什么用场.直到学习自然语言处 ...
- 开发者最爱的Firebug停止更新和维护
近日,Firebug团队在其官网上宣布,Firebug将不再继续开发和维护,并邀请大家使用Firefox的内置开发工具. Firebug最初是2006年1月由Joe Hewitt编写, ...
- gRPC源码分析1-SSL/TLS
引子 前几天看到微信后台团队分享了TLS相关文章,正好gRPC里TLS数据加密是很重要的一块,于是整理出了这篇文章. 在gRPC里,如果仅仅是用来做后端微服务,可以考虑不加密.本文太长,先给个大纲. ...
- 似懂非懂的localStorage和sessionStorage
一.区别 相信很多人都见过这两个关于HTML5的新名词!HTML5种的web storage包含两种存储方式:localStorage和sessionStorage,这两种方式存储的数据不会自动发给服 ...
- BPM配置故事之案例3-参与者与数据自动加载
这才过了两天,阿海又来了. 阿海:公司决定改进管理方式,以后物资申请的申请人和申请部门要写具体使用人的名字和部门了. 小明:不是要让我改回去吧? 阿海:那太麻烦了,你能不能把申请人改成选择,选好人自动 ...
- oracle常用函数及示例
学习oracle也有一段时间了,发现oracle中的函数好多,对于做后台的程序猿来说,大把大把的时间还要学习很多其他的新东西,再把这些函数也都记住是不太现实的,所以总结了一下oracle中的一些常用函 ...