前言
这里详细介绍了new
算子和Object.create()
,以及它们是如何实现的。最后,我们还简要介绍了 Object.create()、new Object() 和 {}
新干员
new
运算符创建用户定义对象类型的实例或具有构造函数的内置对象的实例。
新角色
我们看前三个栗子:
-
的常见用途new
function Test (name) { this.name = name } Test.prototype.sayName = function () { console.log(this.name) } const t = new Test('xx') console.log(t.name) // 'xx' t.sayName() // 'xx'
使用构造函数创建对象的过程大家都知道,无需过多赘述。如果构造函数的值为
return
怎么办? -
return
对象类型数据function Test (name) { this.name = name return {a: '啊啊啊'} } const t = new Test('xx') console.log(t) // '{a: '啊啊啊'}'
可见
return
之前的工作白费了,最终返回了return
后面的对象。 -
return
基本类型数据function Test (name) { this.name = name return 1 } const t = new Test('xx') console.log(t) // '{name: "xx"}'
与没有
return
的效果相同。
我们可以得出一个结论:如果构造函数的返回值是一个对象,那么正常使用该返回值,否则默认返回一个新对象
这个例子告诉我们一件事:构造函数应该尽量不返回值。由于返回的原始值不生效,因此返回的对象是new
操作符不起作用。
卷
-
创建一个新的空对象。
-
允许构造函数的作用域为新对象(因此this指向新对象)。
-
在构造函数中添加代码(为这个新对象添加属性)。
-
如果这个函数有返回值,并且返回值是一个对象,则返回它;否则,默认返回一个新对象。
箭头函数中没有 [[Construct]] 方法。不能用new
调用,会报错。
新认识
根据以上四个功能,分为四个步骤,实现一个new
在这个函数中,第一个参数是构造函数,第二个参数是构造函数中的参数。
第一步
创建一个新的空对象。
function myNew () {
let obj = {}
}
第二步
将构造函数的作用域分配给一个新对象是一个原型链来构造这个新对象并将其连接到构造函数的原型对象上,这样新对象就可以访问到构造函数中的属性和方法构造函数。
-
构造函数是我们传递的第一个参数。由于
arguments
是一个数组类,因此我们不能直接使用shift
方法。我们可以使用call
在Array
上调用shift
方法来得到constr
:let constr = Array.prototype.shift.call(arguments)
-
将对象的原型指向构造函数的原型对象:
obj.__proto__ = constr.prototype
或者,您可以使用
setPrototypeOf
方法(将指定对象的原型(即内部 [[prototype]] 属性)设置为另一个对象或null
:Object.setPrototypeOf(obj, constr.prototype)
-
简化:使用
Object.create()
来简化上述步骤:let obj = obj = Object.create(constr.prototype)
使用
Object.create(object, objectProps)
这个方法,第一个参数是要创建的对象的原型
第三步
执行构造函数中的代码(向这个新对象添加属性)。使用apply
到达:
constr.apply(obj, arguments)
第 4 步
如果这个函数有返回值,并且返回值是一个对象,则返回;否则默认返回一个新对象
-
首先你需要得到这个返回值:
let res = constr.apply(obj, arguments)
-
由上可知:
new
关键字,如果将undefined,null
返回到基类型,则返回一个新对象;并且只有当返回一个对象时,才返回构造函数的返回值。因此:判断
res
是否属于object
类型。如果类型为object
,则返回res
,否则返回obj
。return res instanceof Object ? res : obj
使用
res instanceof Object
确定res
是否为对象类型。
使用愚蠢的方式typeOf()
:target !== null && (typeof target === 'object' || typeof target === 'function')
。
最终代码
测试:myNew(Test, 'xuxu')
与上述一致。
Object.create()
方法Object.create()
创建一个新对象,使用现有对象作为新创建的对象 __proto__
语法
Object.create(proto, [propertiesObject])
-
proto
是必填参数,它是新对象的原型对象。注意,如果该参数为null,则新对象完全为空,不会继承
Object.prototype
上的任何属性和方法,如hasOwnProperty()、toString()
等。 -
propertiesObject
是可选参数。定义了可枚举属性或修改的属性描述符的对象。对象中存在两种主要类型的属性描述符:数据描述符和访问器描述符。了解更多请访问上:关于Object.defineProperty()和Object.defineProperties()
let xx = Object.create({a: 1}, { b: { value: 2, writable: false, configurable: true } }) console.log(xx) // {b: 2} console.log(xx.__proto__) // {a: 1} 新对象xx的__proto__指向{a: 1} xx.b = 77; // throws an error in strict mode console.log(xx.b); // expected output: 2
应用
最大的用处就是实现js的继承:原型继承和寄生组合继承♼❀❝
了解更多信息,请访问转上一篇:JavaScript继承详解(ES5和ES6)——每天一个小进步 的思路很简单:创建一个新对象,使用输入的第一个参数作为这个对象的原型,当找到第二个参数时,通过 我自己的实现,我认为没有问题:(todo:更正) 源代码:定义一个空的构造函数,然后指定构造函数的原型对象,并使用 个人理解: 已达到
Object.defineProperties
将key、value
设置为创建的对象,最后返回可以使用创建的对象。
Object.myCreate = function (proto, propertyObject = undefined) {
let obj = {}
obj.__proto__ = proto
if (propertyObject !== undefined) {
Object.defineProperties(obj, propertyObject)
}
return obj
}
new
运算符创建一个空对象。
Object.myCreate = function (proto, propertyObject = undefined) {
if (propertyObject === null) {
// 这里没有判断propertyObject是否是原始包装对象
throw 'TypeError'
} else {
function F() {}
F.prototype = proto
const obj = new F()
if (propertyObject !== undefined) {
Object.defineProperties(obj, propertyObject)
}
if (proto === null) {
// 创建一个没有原型对象的对象,Object.create(null)
obj.__proto__ = null
}
return obj
}
}
if (proto === null)
这个没有必要,因为当proto = null
时,上面这句F.prototype = proto
,的原型对象已经是❙
Object.create()、new Object() 和 {}
的区别
new Object()
和 {}
之间没有区别,并且 __proto__
指向创建的新对象的 Object.prototype
。 Object.create(proto, [propertiesObject])
创建的对象原型依赖于proto
,proto
为null
,新对象是一个空对象,没有原型,不继承任何对象; proto
为指定对象,新对象的原型指向指定对象,继承指定对象参考文献
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。