详解javascript new的运行机制


Posted in Javascript onJanuary 26, 2016

和其他高级语言一样javascript 中也有new 运算符,我们知道new 运算符是用来实例化一个类,从而在内存中分配一个实例对象。 但在 javascript 中,万物皆对象,为什么还要通过new 来产生对象? 本文将带你一起来探索 javascript 中 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

如果你能理解上面输出的结果,说明你已非常了解js中new和this的运行机制,请忽略本文!

我们将通过解析这个例子来加深你对js中new运算符的理解! 【如果你对js的this还不了解,请先阅读:JS作用域和this关键字】

1、代码解读

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

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

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

  第8行通过new关键字创建了一个新对象cat

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

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

2、重点解析

  第8行代码是关键:

var cat = new Animal("cat");
JS引擎执行这句代码时,在内部做了很多工作,用伪代码模拟其工作流程如下:

new Animal("cat") = {

  var obj = {};

  obj.__proto__ = Animal.prototype;

  var result = Animal.call(obj,"cat");

  return typeof result === 'obj'? result : obj;
}

(1)创建一个空对象obj;

(2)把obj的__proto__ 指向Animal的原型对象prototype,此时便建立了obj对象的原型链:obj->Animal.prototype->Object.prototype->null

      【如果你不了解JS原型链,请先阅读:JS原型和原型链】

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

       当这句执行完之后,obj便产生了属性name并赋值为"cat"。【关于JS中call的用法请阅读:JS的call和apply】

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

理解new的运行机制以后,我们知道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先查找自身的name,找到了name,注意:但这个name不是我们定义的name,而是函数对象内置的属性。

一般情况下,函数对象在产生时会内置name属性并将函数名作为赋值(仅函数对象)。

Animal.say -> Animal在自身没有找到say方法,也会沿着其原型链查找,话说Animal的原型链是什么呢?

详解javascript new的运行机制

 从测试结果看:Animal的原型链是这样的:

 Animal->Function.prototype->Object.prototype->null

 因此Animal的原型链上没有定义say方法!

二、new存在的意义

认识了new运算符之后,我们再回到开篇提到的问题:JS中万物皆对象,为什么还要通过new来产生对象?要弄明白这个问题,我们首先要搞清楚cat和Animal的关系。

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

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

A instanceof B

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

cat instanceof Animal; //true

从执行结果看:cat确实是Animal实例,要想证实这个结果,我们再来了解一下JS中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的判断结果。因此,我们认为:cat是Animal的实例对象。

 在javascript中, 通过new可以产生原对象的一个实例对象,而这个实例对象继承了原对象的属性和方法。因此,new存在的意义在于它实现了javascript中的继承,而不仅仅是实例化了一个对象!

Javascript 相关文章推荐
腾讯与新浪的通过IP地址获取当前地理位置(省份)的接口
Jul 26 Javascript
iframe 异步加载技术及性能分析
Jul 19 Javascript
JavaScript高级程序设计(第3版)学习笔记11 内建js对象
Oct 11 Javascript
jquery实现省市select下拉框的替换(示例代码)
Feb 22 Javascript
jQuery实现的调整表格行tr上下顺序
Jan 10 Javascript
JS获取屏幕高度的简单实现代码
May 24 Javascript
Node.js 数据加密传输浅析
Nov 16 Javascript
jQuery实现元素的插入
Feb 27 Javascript
zTree jQuery 树插件的使用(实例讲解)
Sep 25 jQuery
vue2导航根据路由传值,而改变导航内容的实例
Nov 10 Javascript
jQuery niceScroll滚动条错位问题的解决方法
Feb 03 jQuery
vue特效之翻牌动画
Apr 20 Vue.js
在Linux系统中搭建Node.js开发环境的简单步骤讲解
Jan 26 #Javascript
基于JavaScript实现瀑布流布局(二)
Jan 26 #Javascript
AngualrJS中每次$http请求时的一个遮罩层Directive
Jan 26 #Javascript
JavaScript html5 canvas画布中删除一个块区域的方法
Jan 26 #Javascript
HTML5游戏引擎LTweenLite实现的超帅动画效果(附demo源码下载)
Jan 26 #Javascript
jQuery form插件的使用之处理server返回的JSON, XML,HTML数据
Jan 26 #Javascript
JavaScript+html5 canvas绘制缤纷多彩的三角形效果完整实例
Jan 26 #Javascript
You might like
PHP如何实现阿里云短信sdk灵活应用在项目中的方法
2019/06/14 PHP
php连接mysql之mysql_connect()与mysqli_connect()的区别
2020/07/19 PHP
用javascript获取当页面上鼠标光标位置和触发事件的对象的代码
2009/12/09 Javascript
javascript检测浏览器flash版本的实现代码
2011/12/06 Javascript
javascript 文件的同步加载与异步加载实现原理
2012/12/13 Javascript
图片无缝滚动代码(向左/向下/向上)
2013/04/10 Javascript
jQuery提交多个表单的小例子
2013/06/30 Javascript
编写js扩展方法判断一个数组中是否包含某个元素
2013/11/08 Javascript
我的Node.js学习之路(一)
2014/07/06 Javascript
JQuery中DOM加载与事件执行实例分析
2015/06/13 Javascript
js实现头像图片切割缩放及无刷新上传图片的方法
2015/07/17 Javascript
Jquery技巧(必须掌握)
2016/03/16 Javascript
深入理解MVC中的时间js格式化
2016/05/19 Javascript
vue.js初学入门教程(2)
2016/11/07 Javascript
Vue开发中整合axios的文件整理
2017/04/29 Javascript
jQuery操作DOM_动力节点Java学院整理
2017/07/04 jQuery
vue中父子组件注意事项,传值及slot应用技巧
2018/05/09 Javascript
vue.js 实现点击展开收起动画效果
2018/07/07 Javascript
详解基于Vue,Nginx的前后端不分离部署教程
2018/12/04 Javascript
Vue开发之封装分页组件与使用示例
2019/04/25 Javascript
JavaScript Blob对象原理及用法详解
2020/10/14 Javascript
[59:15]EG vs LGD 2018国际邀请赛淘汰赛BO3 第一场 8.26
2018/08/29 DOTA
Windows中安装使用Virtualenv来创建独立Python环境
2016/05/31 Python
Pytorch 之修改Tensor部分值方式
2019/12/27 Python
tensorflow图像裁剪进行数据增强操作
2020/06/30 Python
Python Web项目Cherrypy使用方法镜像
2020/11/05 Python
CSS3动画效果回调处理详解
2014/12/10 HTML / CSS
美国睫毛、眉毛精华液领导品牌:RevitaLash Cosmetics
2018/03/26 全球购物
香港家用健身器材、运动器材及健康美容仪器专门店:FitBoxx
2019/12/05 全球购物
竞聘自述材料
2014/08/25 职场文书
简单通用的简历自我评价
2014/09/21 职场文书
员工保密协议书
2014/09/27 职场文书
2019大学生预备党员转正思想汇报
2019/06/21 职场文书
Django项目如何正确配置日志(logging)
2021/04/29 Python
使用 Apache Dubbo 实现远程通信(微服务架构)
2022/02/12 Servers
铁头也玩根德 YachtBoy YB-230......
2022/04/05 无线电