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
- class C {
- constructor(name, id) {
- this.name = name;
- this.id = id;
- }
- say() {
- console.log(this.name)
- }
- }
- class D extends C {
- constructor(name, id, age) {
- super(name, id); // 这里必须先调用super方法
- this.age = age;
- }
- }
- let obD = new D(‘mht‘, 22, 33)
- obD.say()
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mht1829/article/details/76785231
个人分类: javascript
原文地址:https://www.cnblogs.com/chaoyuehedy/p/9084843.html