JavaScript中new运算符的运行机制与存在意义解析

作者:小括号

时间:2026-10-29

浏览:334

本文深入解析JavaScript中new运算符的内部执行流程,包括空对象创建、原型链绑定及构造函数调用。通过Animal与cat示例,阐明new如何实现属性继承与实例化,并解释instanceof判断规则,揭示new在JS对象模型中实现继承的核心意义。

文章目录

JavaScript中new运算符的运行机制与存在意义解析

与其他高级编程语言类似,JavaScript也提供了new运算符。通常认为,该运算符用于实例化类,进而在内存中分配一个具体的实例对象。然而,在JavaScript中“万物皆对象”,为何还需要借助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

若能理解上述输出结果,说明你对JavaScript中new和this的运行机制已相当熟悉,可直接跳过本文。

我们将通过解析具体案例,进一步加深你对JavaScript中new运算符的理解。【若对JavaScript的this关键字尚不熟悉,建议先阅读:JS作用域和this关键字】

1、代码解读

第1至3行定义了一个名为Animal的函数,并在其this上下文中定义了属性name,该属性的值取决于函数执行时传入的形参。

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

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

第8行使用new关键字创建了一个新对象cat。

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

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

new执行流程详解

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

【若不了解JavaScript原型链,建议先阅读:JS原型和原型链】

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

当该语句执行完毕后,obj对象便产生了属性name,并赋值为"cat"。【关于JavaScript中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方法中还访问了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存在的意义

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

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

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


A instanceof B

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


cat instanceof Animal; //true

从执行结果看:cat确实是Animal的实例。要想证实这个结果,我们再来了解一下JavaScript中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中的继承,而不仅仅是实例化了一个对象!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章