知识点:
- Object.create( ) 的用法,
- 原型继承,
- Object.assign() 的区别,
- Object.getPrototypeOf() 获取原型
- Object.getOwnPropertyDescriptors() 获取对象可枚举的属性
小结:
1、Object.create() 是把现有对象的属性,挂到新建对象的原型上,新建对象为空对象
2、Object.create() 的第二个参数,为添加的可枚举属性(即自身属性,不是原型上的),例子见下文第二点
可以把Object.create()的参数理解为:第一个参数是放在新对象的原型上的,第二个参数是放在新对象的实例上的。
Object.create(proto, [propertiesObject])
//方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
用我理解的话来说,就是把现有对象的属性,挂到新建对象的原型上(__proto__指向原型),新建的对象享有现有对象的属性。
案例说明:
创建对象的方式不同
// new Object() 方式创建 var a = { rep : 'apple' } var b = new Object(a) console.log(b) // {rep: "apple"} console.log(b.__proto__) // {} console.log(b.rep) // {rep: "apple"} // Object.create() 方式创建 var a = { rep: 'apple' } var b = Object.create(a) console.log(b) // {} console.log(b.__proto__) // {rep: "apple"} console.log(b.rep) // {rep: "apple"}
是它通过原型链 proto来访问到b的值。
创建对象属性的性质不同
// 创建一个以另一个空对象为原型,且拥有一个属性p的对象 o = Object.create({}, { p: { value: 42 } }) // 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的: o.p = 24 o.p //42 o.q = 12 for (var prop in o) { console.log(prop) } //"q" delete o.p //false
Object.create() 用第二个参数来创建非空对象的属性描述符默认是为false的,而构造函数或字面量方法创建的对象属性的描述符默认为true。看下图解析:
创建空对象时,对象没有原型属性
当用构造函数或对象字面量方法创建空对象时,对象时有原型属性的,即有_proto_
;
当用Object.create()方法创建空对象时,对象是没有原型属性的。
__proto__
属性
__proto__
属性(前后各两个下划线),用来读取或设置当前对象的prototype对象。目前只有浏览器环境必须部署有这个属性,其他运行环境不一定要部署,因此不建议使用这个属性,而是使用下面这些来
Object.setPrototypeOf()
(写操作)、
Object.getPrototypeOf()
(读操作)、
Object.create()
(生成操作)代替。
var proto = { y: 20, z: 40, showNum(){} }; var o = Object.create(proto);
如果是不用Object,create()方法,我们是如何给对象原型添加属性和方法的?
------ 通过构造函数或者类,例如:
//创建一个构造函数或者类 var People = function(){} People.prototype.y = 20 People.prototype.showNum = function() {} //通过构造函数创建实例 var p = new People(); console.log(p.__proto__ === People.prototype) // true
现在有 Object.create() 就简单的多了
Object.setPrototypeOf
__proto__
相同,用来设置一个对象的
prototype
对象,返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。
格式:Object.setPrototypeOf(object, prototype)
var proto = { y: 20, z: 40 }; var o = { x: 10 }; Object.setPrototypeOf(o, proto);
obj.__proto__ = proto;
Object.getPrototypeOf()
描述:用于读取一个对象的原型对象;
格式:Object.getPrototypeOf(obj);
Object.getPrototypeOf('foo') === String.prototype // true Object.getPrototypeOf(true) === Boolean.prototype // true
[1] 原型属性的继承
这里结合一个例子来说说这几个方法的使用:
场景:拷贝一个构造函数的实例。
var triangle = {a: 1, b: 2, c: 3}; function ColoredTriangle() { this.color = 'red'; } //ColoredTriangle.prototype = triangle; //ColoredTriangle.prototype.constructor === ColoredTriangle// false Object.assign(ColoredTriangle.prototype, triangle) //ColoredTriangle.prototype.constructor === ColoredTriangle// true var c = new ColoredTriangle();
打印出 实例c 看看结构是怎样的
其中 color 属性在实例上,而其他的原型上。
现在来拷贝一个 实例 c2
var c2 = Object.assign({},c) console.log(c2.color); //red console.log(c2.a); //undefined
因为 Object.assign() 是不能拷贝到继承或原型上的方法的。所以 实例c2 没有 a 这个属性。那要怎么要才能拷贝到原型上的方法呢?
var triangle = { a: 1, b: 2, c: 3 } function Colored(){ this.color = 'red' } Object.assign(Colored.prototype,triangle) var c = new Colored() //Object.assign 无法将现有对象原型对象拷贝到新的对象 var c2 = Object.assign({},c) //使用 Object.assign() 实现拷贝到原型 //下面实现 c2 拷贝实例对象c 上的属性和原型对象 //先讲讲思路,获取c的原型对象,因为assgin不会拷贝原型的东西,c2 = Object.assign(c.prototype,c) //以下方法思路基本相同 //方法一: var originProto = Object.getPrototypeOf(c)//获取到实例 c 的原型 //originProto2想当于一个中间值,将 originProto 的原型添加上去 var originProto2 = Object.create(originProto) //如果这里直接 拷贝实例 c 的原型上的东西,则会发现 第一层原型为 color,第二层才为{a:1,b:2,c:3} // var originProto3 = Object.create(c) var c2 = Object.assign(originProto2,c) //根据上面的思路,我写了一个方法二 //方法二: var c3 = {} c3.__proto__ = c.__proto__ c3 = Object.assign(c3,c) console.log(c3.color) // red console.log(c3.a) // 1 //方法三(推荐)(浅拷贝): var c = new Colored() var c4 = Object.create(Object.getPrototypeOf(c), Object.getOwnPropertyDescriptors(c)) //Object.getOwnPropertyDescriptors(Obj) 返回所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。 console.log(Object.getOwnPropertyDescriptors(c)) //{color:red} console.log(c4)
可以把Object.create()的参数理解为:第一个参数是放在新对象的原型上的,第二个参数是放在新对象的实例上的。
Object.getPrototypeOf() 得到的是 c 对象的原型,然后作为第一个参数,所以会在新对象c4的原型上。
Object.getOwnPropertyDescriptors() 得到是 c 对象自身的可枚举属性,作为第二个参数,放在 c4 的实例上。
为什么说推荐这个方法呢?因为Object.assign() 方法不能正确拷贝 get ,set 属性。
例如,我们给 c 实例添加一个 "colorGet" 属性,并设置该属性的get 描述符:
var c = new ColoredTriangle(); Object.defineProperty(c,'colorGet', { enumerable: true, // 设为可枚举,不然 Object.assign 方法会过滤该属性 get(){ return "Could it return " + this.color } }); var c3 = Object.assign(Object.create(Object.getPrototypeOf(c)), c)

这里没有拷贝到 "colorGet" 的 get 描述符,而是直接把获取到的值赋值给 "colorGet" 。
那对于 get 描述符要怎么获取呢? Object.getOwnPropertyDescriptors就专为解决这问题而生。
而又因为要拷贝原型上的属性,所以结合Object.create、Object.getPrototypeOf 方法一起使用。即上面的第二种实现方法,如下:
var c = new ColoredTriangle(); Object.defineProperty(c,'colorGet', { enumerable: true, // 设为可枚举,不然 Object.assign 方法会过滤该属性 get(){ return "Could it return " + this.color } }); var c3 = Object.create(Object.getPrototypeOf(c), Object.getOwnPropertyDescriptors(c));
此时已经成功的拷贝到了get描述符啦。
虽然说实际开发上很少会要去修改 get 描述符,但是知道多一种方法,遇到这种情况时就知道该怎么去解决了。
注意:这些都只是一个层级的深拷贝。
Object.getOwnPropertyDescriptors
、
Object.assign()
、
Object.create
、
Object.getPrototypeOf()
方法,通常这几种方法都有一起结合使用。
如果上面的例子还不理解,这里把他简单的拿到 对象的继承 来讲解。理解的话就可以忽略啦。