js中的new操作符与Object.create()的作用与区别

Posted chaoyue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js中的new操作符与Object.create()的作用与区别相关的知识,希望对你有一定的参考价值。

js中的new操作符与Object.create()的作用与区别

 

https://blog.csdn.net/mht1829/article/details/76785231

 一、new 操作符

javascript 中 new 的机制实际上和面向类的语言完全不同。

在 JavaScript 中,构造函数只是一些使用 new 操作符时被调用的函数。它们并不会属于某个类,也不会实例化一个类。实际上,它们甚至都不能说是一种特殊的函数类型,它们只是被 new 操作符调用的普通函数而已。

使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
1. 创建(或者说构造)一个全新的对象。
2. 这个新对象会被执行 [[ 原型 ]]  ([[Prototype]])连接。
3. 这个新对象会绑定到函数调用的 this 。
4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。

二、Object.create

调用Object.create(..) 会凭空创建一个“新”对象并把新对象内部的 [[Prototype]] 关联到你指定的对象。

分别使用这两个方法进行原型继承,

进行一个小试验:

function A(id,name){
    this.id=5;
    this.name=name;
    this.sex=‘nan‘;

    this.colors=[‘red‘,‘blue‘];
   }
   A.prototype.speak=function(){
    console.log(this.id);
   }
   function B(id,name){

    A.call(this,id,name);
    this.id=id;
    this.name=name;
   }
   //B.prototype=Object.create(A.prototype);
   B.prototype=new A();//使用new会调用一次A的构造函数,在结合使用构造函数技术时就会调用2次构造,这是第一次
   var test1=new B(3,‘test1‘);
   var test2=new B(4,‘test2‘);
   test1.colors.push(‘black‘);
   console.log(test1.colors); //["red", "blue", "black"]
   console.log(test2.colors); //["red", "blue", "black"]
   console.log(test1.sex);//使用Object.create() 结果为undefined;使用new时结果为‘nan’。
   //test1.speak();

如上代码所示:当对象B的prototype通过new A()方式进行原型关联时,会产生一些副作用。第一:这里给A的this添加数据属性B也能通过[[Prototype]]链查找访问到此属性,因此此方法修改函数A会直接影响其后代(这里为函数B),后果不堪设想。

第二:使用原型链实现继承时,原型的所有属性和方法被实例共享,这种共享对函数非常合适,对于基本类型值的属性实例可以覆盖原型,但是对于包含引用类型值的属性来说,每个实例的修改都会改变原型的属性,因为引用类型的属性包含的是一个指向引用对象的指针(如数组类型)。因此,这里通过原型继承后,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章得变成了现在的原型属性。(如B的实例对象中的colors属性)。这个副作用使用Object.create()方法也存在。

可以使用借用构造函数技术解决这个问题。在B类型的构造函数第一行写上A.call(this,id,name);原理是:让B的实例对拥有引用类型值的属性,修改B实例时就不会修改它原型上的属性。

第三:在创建子类型的实例时,无法向超类型的构造函数中传递参数。(实际上,是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数)。
原因分析: 参看new 操作过程的第3步,new会将这个新对象会绑定到函数调用的 this (相当于java语言的实例化对象,对象拥有了类A的实例属性)。所以当执行B.prototype=new A(); B.prototype就拥有了id,name和sex属性。而对象test1并没有,访问test1.sex时会通过原型链找到sex属性。

new操作和create()操作前后B.prototype的变化具体如下图所示。图左为B默认的prototype对象,图中为new操作后B

的prototype对象,图右为create()操作后B点prototype对象。从中可以看出共同点:
1、B都没有B.prototype.constructor属性,(其实.constructor属性只是在B函数声明时默认的一个公有并且不可枚举

属性,如果创建了一个新对象并替换了函数默认的 .prototype 对象引用,那么新对象并不会自动获得 .constructor 属

性。)
2、B的原型链上都继承了A的原型,关联了A的原型方法。

技术分享图片技术分享图片技术分享图片

再来看看test1对象,图左为new操作后的test1对象原型链,图右为Object.create()操作后的原型链。可以清楚的知道为什么前者test1.sex能打印出‘nan‘,而后者为undefined。

技术分享图片技术分享图片

另外,关联原型还有1种常见的错误做法
B.prototype = A.prototype;//和你想要的机制不一样,此时并不会创建一个关联到A.prototype的新对象,它只是让

B.prototype直接引用了A.prototype对象。因此当你直接执行类似"B.prototype.sex=...."的赋值语句时会直接修改

A.prototype对象本身。

因此,要创建一个合适的关联对象,最好的方法是Object.create(..),而不是有副作用的new A()。这样做唯一的缺点

就是需要创建一个新对象然后把旧对象抛弃掉(有性能损失:抛弃的对象需要进行垃圾回收),不能直接修改已有的

默认对象。

这只是ES6之前最好的办法,ES6 添加了辅助函数Object.setPrototypeOf(..),可以用标准并且可靠的方法来修改关联。

如下图所示,此方法不必抛弃默认的 B.prototype,对其进行直接修改。

技术分享图片

 

ES6 class 继承方式

[javascript] view plain copy
 
  1. class C {  
  2.             constructor(name, id) {  
  3.                 this.name = name;  
  4.                 this.id = id;  
  5.             }  
  6.             say() {  
  7.                 console.log(this.name)  
  8.             }  
  9.         }  
  10.   
  11.         class D extends C {  
  12.             constructor(name, id, age) {  
  13.                 super(name, id); // 这里必须先调用super方法  
  14.                 this.age = age;  
  15.             }  
  16.         }  
  17.   
  18.         let obD = new D(‘mht‘, 22, 33)  
  19.         obD.say()  
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mht1829/article/details/76785231
个人分类: javascript




























以上是关于js中的new操作符与Object.create()的作用与区别的主要内容,如果未能解决你的问题,请参考以下文章

JS中new与Object.create()的区别深入解析

JavaScript 继承:Object.create 与 new

Object.create()和new object()和{}的区别

Object.create()和new object()和{}的区别

js创建对象

使用“Object.create()”而不是“new”关键字来理解原型对象的创建