Object.create(..)和new(..)的内部实现

Object.create()

经常会有这样的疑问?Object.create()到底做了什么工作? 像这样两行代码有什么不同?

var obj ={a: 1}
var b = obj
var c = Object.create(obj)
复制代码

我们来做一点事情,

var obj ={a: 1}
var b = obj
console.log(obj.a) // 1
console.log(b.a) // 1
b.a = 2
console.log(obj.a) //2
复制代码
var obj ={a: 1}
var b = Object.create(obj)
console.log(obj.a) // 1
console.log(b.a) // 1
b.a = 2
console.log(obj.a) //1
复制代码

所以我们立马可以想到Object.create貌似创建了一个新的对象,这个对象继承(关联)了obj的属性,改变新对象的同名属性并不会影响原对象。

如果直接用“=”来赋值,只是一个对象的引用。

那么,为什么会这样呢?是因为Object.create()复制了一个新对象么?实际上并不是,只是Object.create()返回了一个新的空对象,并且这个空对象的构造函数的原型(prototype)是指向obj的。所以当我们访问新对象b.a的时候实际上是通过原型链访问的obj中的a。

当我们试图修改b.a的时候,这里有一个知识点(对象的遮蔽效应,如果修改对象的一个与原型链同名属性,那么会在当前对象中新建一个改属性,这个属性拥有更高级的访问优先级,所以就会遮蔽原型链中的同名属性)

所以Object.create的具体内部实现模拟

_create = function (o) {
    let F = function () {}
    F.prototype = o
    return new F()
}
复制代码

再来看这个例子

var person = {
	friends : ["Van","Louis","Nick"]
};
var anotherPerson = _create(person);
anotherPerson.friends.push("Rob");
var yetAnotherPerson = _create(person);
yetAnotherPerson.friends.push("Style");
alert(person.friends);//"Van,Louis,Nick,Rob,Style"

复制代码

相当于做了一次浅复制,新创建的各个对象实际上是会共享原始对象中的引用类型的值,这意味着person.friends不仅属于person所有,而且也会被anotherPerson以及yetAnotherPerson共享

实际上真正的Object.create()还可以传入第二个参数,这个参数与Object.defineProperties方法的第二个参数格式相同, 通过第二个参数是会在新对象中重新创建一个属性的,然后通过属性遮蔽原理避免修改原对象。

var person = {
	name : "Van"
};
var anotherPerson = Object.create(person, {
	name : {
		value : "Louis"
	}
});
alert(anotherPerson.name);//"Louis"
复制代码

Object.create(null) 会创建一个真正的空对象,并没有继承Object原型链上的方法

var a = {} 这并不是一个纯粹的空对象,它会继承原型链上的很多方法

new()

关于new的内部实现模拟

function _new () {
  // arguments实际上是一个类数组对象,需要转成数组
  let args = [].slice.call(arguments)
  // 第一个参数是构造函数,把它拿出来
  let constructor = args.shift()
  // Object.create()返回一个新对象,这个对象的构造函数的原型指向Foo
  let context = Object.create(constructor.prototype)
  // 在返回的context对象环境中执行构造函数,为新的context添加属性
  let result = constructor.apply(context, args)
  // 如果Foo显示的返回了一个对象,那么应该直接返回这个对象,而不用理会以上所有的操作,一般不会发生这种情况,但是new的实现的确是这样的逻辑
  // 这里之所以判断类型是否为object还要添加 != null 的判断,是因为null的typeof结果也是‘object’
  // 不同的对象在底层都表示为二进制,在Javascript中二进制前三位都为0的话会被判断为Object类型,null的二进制表示全为0,自然前三位也是0,所以执行typeof时会返回"object"
  return (typeof result === 'object' && result != null) ? result : context
}

function Foo (name) {
  this.name = name
}

Foo.prototype.getName = function() {
  console.log(this.name)
}

var a = _new(Foo, 'tom')
a.getName()

复制代码

实际上new操作符, 就是通过Object.ctreate()创建一个新的对象,这个对象的原型指向构造函数,并且在新建对象的上下文环境中执行构造函数,初始化新建对象的属性。

总结

当然这里的实现只是一个模拟实现,至于就是内部真正的实现方式必然是复杂得多。比如说这里的new方法和Object.create()必然不会相互引用,这样会产生一个无限循环的函数,所以说这里只是一个大概思路上的引导,对于理解js的对象继承,原型链的概念会有帮助。

转载于:https://juejin.im/post/5bf37a5ee51d4552da47dae9

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值