Object.prototype上的属性及方法
-
hasOwnProperty()
function Person(name) { this.name = name; } Person.prototype.sayName = function() { console.log(this.name); } var person = new Person('hx'); person.name; // 'hx' person.hasOwnProperty('name'); // true person.sayName(); // hx person.hasOwnProperty('sayName'); // false
-
isPrototypeOf()
// 类似于instanceof function Animal() {} function Cat() {} Cat.prototype = Object.create(Animal.prototype); // Cat.prototype.__proto__ === Animal.prototype // Cat.prototype ---> Animal.prototype // 实现了原型继承,副作用是:Cat.prototype.constructor指向Animal,而不指向Cat; var cat = new Cat(); Cat.prototype.isPrototypeOf(cat); // ture Animal.prototype.isPrototypeOf(cat); // ture
-
propertyIsEnumerable()
function Person(name) { this.name = name; } Person.prototype.age = 18; var person = new Person('hx'); console.log(person.name); // hx console.log(person.age); // 18 person.propertyIsEnumerable('name'); // true person.propertyIsEnumerable('age'); // false
Tips:需要特别注意!
- 属性是否可枚举,与属性是否在原型对象上没有一点关系;
- hasOwnProperty为true的属性不一定可枚举,反之亦然(等同于i);
- propertyIsEnumerable,其命名不太“严谨”,实际上它判断的是:① 不在原型对象上;② 且可枚举的属性;
- propertyIsEnumerable为false的属性不一定不可枚举,有可能是因为该属性在原型对象上(等同于iii)
Object对象自身的属性及方法
-
Object.assign()
var obj1 = {a:1,b:2}; var obj2 = {a:0,c:3}; var newObj1 = Object.assign({},obj1,obj2); console.log(obj1); // {a:1,b:2} console.log(obj2); // {a:0,c:3} console.log(newObj1); // {a:0,b:2,c:3} var newObj2 = Object.assgin(obj1,obj2); console.log(obj1); // {a:0,b:2,c:3} console.log(obj2); // {a:0,c:3} console.log(newObj2); // {a:0,b:2,c:3}
Tips:assign无法实现多维对象的深拷贝,不过可以实现一维对象的深拷贝
var obj = {a:1,b:2}; var newObj = Object.assign({},obj); console.log(obj,newObj); // {a:1,b:2} {a:1,b:2} newObj.a = 0, console.log(obj); // {a:1,b:2} obj.b = 100, console.log(newObj); // {a:0,b:2}
-
Object.create()
// 使用指定的对象(做为新对象原型对象)和属性(可选)创建一个新对象实例 function Person(name) { this.name = name; } Person.prototype.sayName = function() { console.log(this.name); } var a = Object.create(Person.prototype,{ name:{ writable:true, configurable:true, value:'hx' } }) console.log(a); // {name:'hx'} a.sayName(); // 'hx'
var Student = { name:'robot', height:1.2, run:function(){ console.log(this.name+'is running') } } function createStudent(name) { var s = Object.create(Student); s.name = name; return s; } var xiaoming = createStudent('小明'); xiaoming.run(); // 小明is running xiaomi.__proto__ === Student
Tips:实际上就是接受两个参数,第一参数是个对象,并赋给即将创建的对象的__proto__;第二个参数(可选)是一个自定义的属性。
-
Object.defineProperty()
// 3个参数,为对象定义一个属性 var obj = {name:'hx'}; Object.defineProperty(obj,'age',{ writable:true, configurable:true, value:18 }) console.log(obj); // {name:'hx',age:18} // 没有配置的enumerable默认为false
-
Object.defineProperties()
// 2个参数,为对象定义多个属性 var obj = {}; Object.defineProperties(obj,{prop1:{ value: 'hx', writable: true },prop2:{ value:18, writable:true }}) console.log(obj); // {prop1:'hx',prop2:18} // 没有配置的configurable,enumerable默认为false // 即:obj的属性prop1,prop2不可枚举,不可删除
-
Object.getOwnPropertyNames()
var obj = {name:'hx',age:18}; var names = Object.getOwnPropertyNames(obj); console.log(names); // ['name','age']
-
Object.getOwnPropertyDescriptor()
var obj = {name:'hx',age:18}; var prop = Object.getOwnPropertyDescriptor(obj,'age'); console.log(prop); // {value:18,writable:true,enumerable:true,configurable:true}
Tips:上述的两个get方法都不会上原型链上去找
-
Object.getPrototypeOf()
// 返回指定对象的原型对象(prototype) function Person(name) { this.name = name } Person.prototype.sayName = function() { console.log(this.name); } var a = Object.create(Person.prototype,{name:{ value:'hx', writable:true, enumerable:true, configurable:true, }}) Object.getPrototypeOf(a); // {sayname: f, construtor: f}
-
Object.setPrototypeOf()
// 设置对象的原型 // 示例 1 var proto = {sayName:function(){ console.log('hello world'); }}; var obj = {}; Object.setPrototypeOf(obj,proto); obj.sayName(); // 'hello world' // 示例 2 function Person(name) { this.name = name; } var person = new Person('hx'); Object.setPrototypeOf(person,{sayAge:function(){ console.log(18); }}) console.log(person); // {name: 'hx'} person.sayAge(); // 18
跟Object.new()有点类型,相当于直接给目标对象的隐式原型__proto__赋值(赋一个新对象),造成的影响是改变原型链(见文末)
-
Object.keys()
// 返回一个包含所有给定对象自身可枚举属性名称的数组 var obj = {name:'hx',age:18}; console.log(Object.keys(obj)); // ["name", "age"]
-
Object.values()
// 返回一个包含所有给定对象自身可枚举属性名称的数组 var obj = {name:'hx',age:18}; console.log(Object.values(obj)); // ["hx", 18]
-
Object.entries()
var obj = {name:'hx',age:18}; console.log(Object.entries(obj)); // [["name", "hx"],["age", 18]]
-
Object.preventExtensions()
// 禁止对象扩展(不可扩展,可修改,可删除) var obj = {name:'hx'}; Object.preventExtensions(obj); obj.name = 'huangxin'; obj.age = 18; console.log(obj.name,obj.age); // huangxin undefined delete obj.name; // true console.log(obj); // {}
-
Object.seal()
// 阻止添加新属性并将所有现有属性标记为不可配置(不可删除,不可扩展,可修改) var obj = {name:'hx'}; Object.seal(obj); obj.name = 'huangxin'; obj.age = 18; console.log(obj.name,obj.age); // huangxin undefined delete obj.name; // false console.log(obj); // {name:'huangxin'}
-
Object.freeze()
// 禁止对象变更(不可扩展,不可修改,不可删除) var obj = {name: 'hx'}; Object.freeze(obj); obj.name = 'huangxin'; obj.age = 18; console.log(obj.name,obj.age); // hx undefined delete obj.name; // false console.log(obj); // {name: 'hx'}
Tips:
执行完preventExtensions(),seal(),freeze()后,分别执行getOwnPropertyDescriptor():
{value: "hx", writable: true, enumerable: true, configurable: true}
{value: "hx", writable: true, enumerable: true, configurable: false}
{value: "hx", writable: false, enumerable: true, configurable: false}
可见:writable指可否更改,enumerable指可否枚举,configurable指可否删除,至于可否扩展,这里体现不出来,那么对象到底支不支持扩展就要用下面这个方法了 -
Object.isExtensible()
var obj = {name: 'hx'}; Object.isExtensible(obj); // true Obj.preventExtensions(obj); Object.isExtensible(obj); // false
Tips:
类似的对象属性判断还有 isFrozen(),isSealed()
写在最后
js的经典继承很大一部分体现在原型链的变化上,比如:
你想让Child继承Parent,只需:Child.prototype.__proto === Parent.prototype
;
这样当你在new一个Child实例的时候,child的原型对象上就有了parent的属性(实现了继承);
因此如何实现Child.prototype ---> Parent.prototype
是原型继承的关键所在,其方法有:
- 通过new();
`
Child.prototype = new Parent();
`
- 通过Object.create();
`
Child.prototype = Object.create(Parent.prototype)
`
- 通过Object.setPrototypeOf();
`
Object.setPrototypeOf(Child.prototype,Parent.prototype)
`
以上三种方法都相当于改变了Child.prototype
,不同的是对于Child.prototype
来说,前两种属于赋值型改变,相当于把Child.prototype整体作为一个对象,做整体性赋值,结果导致了Parent.prototype.hasOwnProperty('constructor'); // false
;而第三种属于扩展型改变,只是改变了Parent.prototype.__proto__
;
也就是说:
- 本来
Child.prototype.constructor
是指向Child
自身的。 - 当采用了方法1后,
Child.prototype
被重新赋值,失去了constructor
属性(此时Child.prototype
已经被赋值成一个Parent
实例),其constructor
属性只能从其原型对象上找,也即Parent.prototype.constructor
,最后的结果就是Child.prototype.constructor
指向Parent
; - 采用方法2也一样,
Child.prototype
被直接赋值成了一个空对象,Child.prototype.__proto__
被赋值成了Parent.prototype
,也便造成了同样的结果:Child.prototype.constructor
指向Parent
。 - 因此采用了方法1、2的话,还需要手动地把
constructor
指回来:Child.prototype.constructor = Child
对象赋值 vs. 对象扩展:
-
我们都知道:
var obj1 = {name:'hx'}; var obj2 = obj1; obj1 === obj2; // true // 对象扩展 obj1.age = 18; obj1; // {name:'hx',age:18} obj1 === obj2; // true // 对象赋值 obj1 = {name:'hx',age:18}; obj1.name === obj2.name; // true obj1.age === obj2.age; // true obj1 === obj2; // false
-
同样:
var o1 = {}; o1.__proto__ === Object.prototype; // true o1.__proto__.__proto__; // null // 对象扩展 o1.__proto__.name = 'hx'; o1.__proto__ === Object.prototype; // true o1.__proto__.__proto__; // null // 对象赋值 o1.__proto__ = {name:'hx'}; o1.__proto__ === Object.prototype; // false o1.__proto__.__proto__ === Object.prototype; // ture o1.__proto__.__proto__.__proto__; // null
结论:
在实现js原型继承时,最好使用 ++Object.setPrototypeOf()++,相比于 ++new++ 和 ++Object.create++,其副作用更小,代码量也最小:
Object.setPrototypeOf(Swimmer.prototype, Man.prototype);
等同于:
Swimmer.prototype = Object.create(Man.prototype);
Swimmer.prototype.constructor = Swimmer;
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。