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 继承方式
- 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()
js中的new操作符与Object.create()的作用与区别的更多相关文章
- C#中泛型的解释(object,list,var,dynamic的区别)
泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个新功能.泛型将类型参数的概念引入 .NET Framework,类型参数使得设计如下类和方法成为可能:这些类和方法将一个或多个类型的 ...
- eclipse中build path与Web Deployment Assembly的作用,区别
转自:https://blog.csdn.net/heart_mine/article/details/79402792 以下内容只为做个笔记记录已下,有问题可以留言,欢迎补充. 今天在eclipse ...
- JS中的 new 操作符简单理解
首先上一一个简单的 new 操作符实例 var Person = function(name){ this.name = name; this.say = function(){ return &qu ...
- JS中的一元操作符
表达式 一元操作符 优先级 结合性 运算顺序 表达式是什么? 就是JS 中的一个短语,解释器遇到这个短语以后会把对它进行计算,得到一个结果参与运算,我们把这种要参与到运算中的各种各样的短语称为表达式. ...
- JS中的new操作符
在JS中定义一个构造函数,然后用new操作符构造对象obj,JS代码如下. function Base(){ this.name = "swf"; this.age =20; } ...
- JS中的相等性判断===, ==, Object.is()
首发地址:http://www.geeee.top/2019/11/15/equality-comparisons/,转载请注明出处 相信刚接触JS的人都会被他的想等性判断给整糊涂,看看下面代码,你能 ...
- js中,全局变量与直接添加在window属性的区别
在js中定义的全局变量是挂在window下的,而window的属性也一样,那么这两者有什么区别呢? 其实这两者还是有小小的区别的,全局变量是不能通过delete操作符删除的,而直接定义在window上 ...
- js中的等值运算符(抽象相等==与严格相等===的区别)
js中的等值运算符 js中的相等分为抽象相等和严格相等,他们有什么区别呢. 在说具体算法前,先提下JS数据类型,JS数据类型分为6类:Undefined Null String Number Bool ...
- js中slice(),splice(),split(),substring(),substr()的使用方法和区别
1.slice(): Array和String对象都有 在Array中 slice(i,[j]) i为开始截取的索引值,负数代表从末尾算起的索引值,-1为倒数第一个元素j为结束的索引值,缺省时则获取 ...
随机推荐
- 五)使用 easyui-tabs 遭遇错误 Unexpected Exception caught setting '_' on
十月 10, 2015 3:08:35 下午 com.opensymphony.xwork2.interceptor.ParametersInterceptor error 严重: Developer ...
- HALCON机器视觉软件
HALCON是德国MVtec公司开发的一套完善的标准的机器视觉算法包,拥有应用广泛的机器视觉集成开发环境.它节约了产品成本,缩短了软件开发周期——HALCON灵活的架构便于机器视觉,医学图像和图像分析 ...
- linux 管道与重定向
命令行shell数据流有如下定义: 通过管道和重定向可以控制CLI的数据流
- Delphi IOS开发环境安装
RAD Delphi XE/10 Seattle 安装IOS.OSX环境安装,IOS模拟器,MAC X 真机可以调试 http://community.embarcadero.com/blogs/en ...
- asp.net——地址栏传递中文参数乱码解决方案
地址栏传递中文参数乱码解决方案: 很多人在使用地址栏传递参数的时候都会遇到一个麻烦的问题(参数为中文时乱码了),那要怎么解决呢? 其实解决这个问题也不怎么难,无非就是给要传递的中文参数一个编码解码的过 ...
- 高德地图之c#后台获取一个或多个起点到单个终点的直线距离
首先我们需要一个控制台添加一个新Key(可使用服务选择Web服务,测试的时候IP白名单先不填); 直线距离是通过后台get方式请求API服务地址http://restapi.amap.com/v3/d ...
- HttpWebRequest(System.Net)模拟HTTP发送POST
相关参考网上很多,但需要理解并转成自己的情况 public static string HttpWebRequestPost(string url, string param) { HttpWebRe ...
- 达梦数据库(DaMeng)如何删除IDENTITY自增属性字段
今天工作中使用到达梦数据库,要求删除具有IDENTITY自增属性的字段. 直接执行删除:ALTER TABLE <表名> DROP COLUMN <列名> CASCADE; 删 ...
- 洛谷P3613 睡觉困难综合征
传送门 题解 人生第一道由乃…… 做这题之前应该先去把这一题给切掉->这里 我的题解->这里 然后先膜一波zsy大佬和flashhu大佬 大体思路就是先吧全0和全1的都跑答案,然后按位贪心 ...
- MySQL直接导出CSV文件,并解决中文乱码的问题
需求: 需要导出hr_users 表中的部分字段的数据,以前是用PHP写脚本,然后导出CSV文件. 在MySQL中,它自己就能导出CSV文件 ,只不过是有如下几个问题需要大家解决. 1. 生成文件不成 ...