js 中 Object.create()、new Object() 和 {} 的区别
参考:https://blog.csdn.net/qq_43293207/article/details/115920080
https://blog.csdn.net/sinat_27514587/article/details/102964631
平时代码中必定会使用对象,通常是用最直接的字面量方法创建var obj = {}
,Object.create()
也可以创建对象,另外,也可以用new Object()
关键字创建。
1. 无本质区别
对于 array,object,function 和正则表达式这类既可以通过字面量(如 let arr = [] )又可以通过构造函数(如 let arr = new Array() )来声明的对象类型,实际上,这两种声明方式的效果和底层实现是一样的,创建的值都是通过封装对象来包装。
2. 字面量直接创建{}
var objA = {}; objA.name = 'a'; objA.sayName = function() { console.log(`My name is ${this.name} !`); } // var objA = { // name: 'a', // sayName: function() { // console.log(`My name is ${this.name} !`); // } // } objA.sayName(); console.log(objA.__proto__ === Object.prototype); // true console.log(objA instanceof Object); // true
3. new Object()
var objB = new Object(); // var objB = Object(); objB.name = 'b'; objB.sayName = function() { console.log(`My name is ${this.name} !`); } objB.sayName(); console.log(objB.__proto__ === Object.prototype); // true console.log(objB instanceof Object); // true
new
操作符其实做了以下四步:
var obj = new Object(); // 创建一个空对象 obj.__proto__ = F.prototype; // obj的__proto__指向构造函数的prototype var result = F.call(obj); // 把构造函数的this指向obj,并执行构造函数把结果赋值给result if (typeof(result) === 'object') { objB = result; // 构造函数F的执行结果是引用类型,就把这个引用类型的对象返回给objB } else { objB = obj; // 构造函数F的执行结果是值类型,就返回obj这个空对象给objB }
这样一比较,其实字面量创建和new关键字创建并没有区别,创建的新对象的__proto__
都指向Object.prototype
,只是字面量创建更高效一些,少了__proto__
指向赋值和this
。
4. Object.create()
Object.create()
方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
。
const person = { isHuman: false, printIntroduction: function () { console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`); } }; const me = Object.create(person); // me.__proto__ === person me.name = "Matthew"; // name属性被设置在新对象me上,而不是现有对象person上 me.isHuman = true; // 继承的属性可以被重写 me.printIntroduction(); // My name is Matthew. Am I human? true
Object.create(proto[, propertiesObject])
proto
必填参数,是新对象的原型对象,如上面代码里新对象me
的__proto__
指向person
。注意,如果这个参数是null
,那新对象就彻彻底底是个空对象,没有继承Object.prototype
上的任何属性和方法,如hasOwnProperty()、toString()
等。
var a = Object.create(null); console.dir(a); // {} console.log(a.__proto__); // undefined console.log(a.__proto__ === Object.prototype); // false console.log(a instanceof Object); // false 没有继承`Object.prototype`上的任何属性和方法,所以原型链上不会出现Object
propertiesObject
是可选参数,指定要添加到新对象上的可枚举的属性(即其自定义的属性和方法,可用hasOwnProperty()
获取的,而不是原型对象上的)的描述符及相应的属性名称。
var bb = Object.create(null, { a: { value: 2, writable: true, configurable: true } }); console.dir(bb); // {a: 2} console.log(bb.__proto__); // undefined console.log(bb.__proto__ === Object.prototype); // false console.log(bb instanceof Object); // false 没有继承`Object.prototype`上的任何属性和方法,所以原型链上不会出现Object // ---------------------------------------------------------- var cc = Object.create({b: 1}, { a: { value: 3, writable: true, configurable: true } }); console.log(cc); // {a: 3} console.log(cc.hasOwnProperty('a'), cc.hasOwnProperty('b')); // true false 说明第二个参数设置的是新对象自身可枚举的属性 console.log(cc.__proto__); // {b: 1} 新对象cc的__proto__指向{b: 1} console.log(cc.__proto__ === Object.protorype); // false console.log(cc instanceof Object); // true cc是对象,原型链上肯定会出现Object
Object.create()
创建的对象的原型指向传入的对象。跟字面量和new
关键字创建有区别。
- 自己实现一个Object.create()
Object.mycreate = function(proto, properties) { function F() {}; F.prototype = proto; if(properties) { Object.defineProperties(F, properties); } return new F(); } var hh = Object.mycreate({a: 11}, {mm: {value: 10}}); console.dir(hh);
5. 避免使用构造函数
非必要情况下,尽量不要使用构造函数来声明一个变量,其往往存在一些缺陷:
- new Array()如果只有一个数字参数时,该参数会被视为数组的长度设置值,而不是数组的一个元素(如new Array(3))则会创建一个有3个”空单元”的数组。而ES6 Array.of()就是来修复这个问题的,并且替代Array()
- new Object()创建对象时,无法像字面量那样一次性设置多个属性,只能逐一设定
- 你基本不会通过new Function()来定义函数
- 使用字面量形式(如/~a*bt/g)创建正则表达式,相比构造函数形式语法简单,执行效率更高(js在执行前会对其进行预编译和缓存)。
6. 总结
字面量和
new
关键字创建的对象是Object
的实例,原型指向Object.prototype
,继承内置对象Object
Object.create(arg, pro)
创建的对象的原型取决于arg
,arg
为null
,新对象是空对象,没有原型,不继承任何对象;arg
为指定对象,新对象的原型指向指定对象,继承指定对象