JS new Object.create()的区别

包含手写 new 以及 手写 Object.create (≖ᴗ≖)✧

起因

在整理原型这部分知识时,发现了两种修改原型的写法,好像并不影响测试结果

function Fruit() {
    this.type = 'fruit'
}

function Apple() {
    this.name = 'apple'
}

// Apple.prototype = new Fruit()
Apple.prototype = Object.create(Fruit.prototype);

var a = new Apple()
console.log(a instanceof Apple) // true
console.log(a instanceof Fruit) // true 


new 运算符的结果返回的是对象,Object.create() 方法返回的也是 它俩的 __proto__ 指向都是原有原型对象Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例 —— MDN

那… 这俩啥区别啊???

过程

1. 找茬

function Fruit() {
    this.type = 'fruit'
}

function AppleNew() {
    this.name = 'apple'
}

function AppleCreate() {
    this.name = 'apple'
}

AppleNew.prototype = new Fruit();
AppleCreate.prototype = Object.create(Fruit.prototype);

console.log(AppleNew.prototype, AppleCreate.prototype)
console.log(AppleNew, AppleCreate)


2. 溯源在这里插入图片描述两个都是 Fruit 的实例,但使用 Object.create 新建的对象,把属性值弄丢了

new 干了点啥呢?回顾笔记:

function myNew() {
    // 1. 新建对象
    // var obj = new Object();
    // 2. 连接原型链
    // 2.1 取得构造函数
    var Constructor = [].shift.call(arguments);
    // 2.2 为新建对象的 [[prototype]] 赋值
    // obj.__proto__ = Constructor.prototype;
    // Object.setPrototypeOf(obj, Constructor.prototype);
    var obj = Object.create(Constructor.prototype === null ? Object.prototype : Constructor.prototype);
    // 3. 调用构造函数,并改变 this 指向
    // var ret = Constructor.apply(obj, arguments);
    // 4. 如构造函数返回结果为对象,则直接返回,否则返回新建对象,忽略返回值
    // return typeof ret === 'object' ? ret : obj;
    return obj;
}


警告: 由于现代 JavaScript 引擎优化属性访问所带来的特性的关系,更改对象的 [[Prototype]]在各个浏览器和 JavaScript 引擎上都是一个很慢的操作。其在更改继承的性能上的影响是微妙而又广泛的,这不仅仅限于 obj.proto = … 语句上的时间花费,而且可能会延伸到任何代码,那些可以访问任何[[Prototype]]已被更改的对象的代码。如果你关心性能,你应该避免设置一个对象的 [[Prototype]]。相反,你应该使用 Object.create()来创建带有你想要的[[Prototype]]的新对象。使用 Object.create 进行关联原型的操作,是因为 MDN 的一段建议:

从 new 的内部原理可以发现,new 中的某一步就完成了 Object.create(Constructor.prototype) 的作用,两者的差别主要在于后续 this 的指向,使用 Object.create 创建的实例 this 没有指向实例

再来扒一扒 Object.create 的实现,翻阅 MDN 后,可以窥见一二,改写后,代码如下:

function myCreate(proto, propertiesObject) {
    // 1. 验证 proto 为构造函数/对象(/null)
    if (typeof proto !== 'object' && typeof proto !== 'function') {
        throw new TypeError('Object prototype may only be an Object: ' + proto);
    // } else if (proto === null) {
        // 在 ES5 中 Object.create支持设置为[[Prototype]]为null,但因为那些ECMAScript5以前版本限制,此 polyfill 无法支持该特性。
        // throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
    }
    // 2. 内部新建一构造函数
    function F() { }
    // 3. 将构造函数的原型指向传入的原型对象
    F.prototype = proto;
    // 4. 新建一构造函数的实例
    var obj = new F();
    // 5. 如为 null, 需要再将实例的原型指向 null。否则,使用 new 新建实例时,会将原型指向 Object.prototype
    if (proto === null) {
        obj.__proto__  = null;
    }
    // 6. 为实例添加属性
    if (propertiesObject) {
        Object.defineProperties(obj, propertiesObject)
    }
    return obj;
};


总结:用图表示它们的关系,就会变成:在这里插入图片描述前文最开始的,感觉两种写法好像没有区别的例子也就自然解释了。 根据图示,两种方法创建的实例都可以在原型链上找到 Fruit.prototype

两者的区别主要在于:

newObject.create
接受形式构造函数构造函数、普通对象,null
继承性

无(不会继承原有构造函数中的属性)

对象:则会继承原对象的属性

其它可以借 Object.create 实现没有原型的空对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值