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

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

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

2017年08月06日 19:19:26

阅读数:1058

一、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. class D extends C {
  11. constructor(name, id, age) {
  12. super(name, id); // 这里必须先调用super方法
  13. this.age = age;
  14. }
  15. }
  16. let obD = new D(‘mht‘, 22, 33)
  17. obD.say()

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mht1829/article/details/76785231

个人分类: javascript

原文地址:https://www.cnblogs.com/chaoyuehedy/p/9084843.html

时间: 2024-10-03 05:42:28

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

js中的window.open返回object的错误

系统中用javascript中的window.open后,页面返回了一个[object].因为系统的原因,必需使用href="javascript:window.open()"这样的格式.所以只能通过以下办法解决. 解决window.open后返回object的错误 <a href="javascript:void(window.open('','','width=200,height=200'))">window.open()</a> 只在

模拟实现js中的new操作符

能否实现js的new操作符 new new运算符创建了一个用户自定义的对象类型的实例或具有构造函数的内置对象类型之一 定义一个构造函数Student,该函数接收两个参数name和age: function Student(name,age){ this.name=name this.age=age } let first=new Student('dylan','26') console.log(first.name);// dylan console.log(first.age);// 26

js中的new操作符

一.js中new操作符来声明一个对象:例如有如下一个实例function Person(){Person.prototype.fn=function(){console.log(1);}}var p=new Person(); 上例中使用new操作符来声明一个对象实际上是进行了三个动作: 1.var p={}; 2.p._proto_=Person.prototype; 3.Person.call(p); 二.以上的三个操作是在使用new操作符创建对象的时候实际执行的动作. 以上为本人对new操

js中,全局变量与直接添加在window属性的区别

在js中定义的全局变量是挂在window下的,而window的属性也一样,那么这两者有什么区别呢? 其实这两者还是有小小的区别的,全局变量是不能通过delete操作符删除的,而直接定义在window上的属性是可以删除的. 那么为什么全局变量不能删除呢? 因为全局变量也是个对象,这个对象时通过叫做PropertyDescriptor来定义的. 可以在浏览器中调用Object.getOwnPropertyDescriptor(对象, 属性)来查看 举例: var test = 2; window.t

JS中绑定事件顺序(事件冒泡与事件捕获区别)

在JS中,绑定的事件默认的执行时间是在冒泡阶段执行,而非在捕获阶段(重要),这也是为什么当父类和子类都绑定了某个事件,会先调用子类绑定的事件,后调用父类的事件.直接看下面实例 <!Doctype html> <html> <head> <meta charset="utf-8"> <title></title> <style type="text/css"> *{margin:0;p

js中return;、return true、return false的区别

一.返回控制与函数结果, 语法为:return 表达式; 语句结束函数执行,返回调用函数,而且把表达式的值作为函数的结果  二.返回控制, 无函数结果,语法为:return;  在大多数情况下,为事件处理函数返回false,可以防止默认的事件行为.例如,默认情况下点击一个<a>元素,页面会跳转到该元素href属性指定的页.    Return False 就相当于终止符,Return True 就相当于执行符.    在js中return false的作用一般是用来取消默认动作的.比如你单击一

JS中的delete操作符

首先,delete删除成功返回true,失败返回false. js代码: function wxCount ($element) { this.init($element); } wxCount.prototype = { init : function(){...}, //初始化方法 count : function(){...}, //计算方法 destroy : function(){ delete this; } //删除方法 } 仔细一看便知,这里的this指向的是function w

JS中的new操作符原理解析

1 var Person = function(name){ 2 this.name = name; 3 } 4 Person.prototype.sayHello = function() { 5 console.log('hello ' + this.name); 6 } 7 var p1 = new Person('HANMEI'); 8 p1.sayHello(); 1. 创建一个类的实例:创建一个空对象obj,然后把这个空对象的__proto__设置为Person.prototype(

js中的apply、call、callee、caller的区别

1.apply与call的区别与使用 相同点:2者是函数原型的一个方法,因此调用者都必须是函数,第1个参数都是对象.作用是,用另一个对象替换当前对象,另一对象也即是你传的第一个参数.通常用于改变调用的函数中this的指向.如果不传对象或者传入的为null,那么默认对象就是window. 不同点:第2个参数,为调用的函数的参数,区别是apply要求所有函数的参数放在一个数组中传递. 使用方法: fun.call(thisArg[, arg1[, arg2[, …]]]) fun.apply(thi