你们都知道循环一个对象,能够用for ..in..
来作,好比:javascript
var myObject = {foo: 'bar'};
for(var name in myObject) {
// 这里能获得你想要的属性名
// 也能够获得对应属性值: myObject[propertyName]
}
复制代码
这是显而易见的,可是若是有人在这个对象的原型上添加了一些属性或方法呢?vue
Object.prototype.baz = 'quux';
for (var name in myObject) {
alert(name); //foo baz
}
复制代码
myObject
也会跟着改,那么别人改动原型,对本身的代码形成很大的影响,结果确定是不能接受的。java
既然对象存在继承,那么咱们必须考虑哪些是我本身写的(私有的),哪些是从原型上继承的,单纯的for ..in..
循环已经不能知足需求 了。数组
hasOwnProperty
就是用来判断是不是私有属性,而不是继承过来的ui
myObject.hasOwnProperty('foo'); // true
myObject.hasOwnProperty('baz'); // false
复制代码
若是仅仅想要私有的属性或方法,咱们就能够这么写了spa
for(var name in myObject) {
if(myObject.hasOwnProperty(name)){
alert(name);// 'foo'
}
}
复制代码
for...in..
循环,中间还要作判断,是否是很麻烦,不过咱们有专门的方法来解决这些麻烦事。prototype
Object.keys(myObject);//['foo'] 返回一个对象,是一个私有属性的集合
复制代码
说到这里,确定还有一些人会疑惑,既然for...in
能遍历出对象自身的属性或方法,也能遍历出继承过来的属性或方法,那么Object
类上还有不少的属性和方法的,好比说toString
,这是被myObject
继承的,可是没有被for...in
遍历到啊。code
'toString' in myObject; //true
myObject.toString();//"[object Object]"
复制代码
这里引入一个概念“可枚举”enumerable
,这个概念的最初目的,就是让某些属性能够规避掉for...in
操做,否则全部内部属性和方法都被遍历到,就能够放肆的更改了。cdn
写到这里,你应该会明白了,上面提到的toString
都是不可枚举的。你会不会有这样的问题?若是我在对象里面写了一些属性,可是不想被遍历出来,是否是也能够把这些属性变成不可枚举的呢?固然是能够的了。对象
Object.defineProperty(myObject, 'name', {
value : 'cover',
enumerable: false//不可枚举
})
Object.prototype.baz = 'quux';
// myObject:{foo: "bar", name: "cover"}
for (var name in myObject) {
alert(name); //foo baz
}
复制代码
经过这种方法定义出来的属性或方法就能够变成不可枚举。
新的知识点:Object.defineProperty
,顾名思义,就是为对象定义属性。 定义:直接在一个对象上定义一个新的属性,或者是修改已存在的属性。最终这个方法会返回该对象。
参数
object
必需。 要在其上添加或修改属性的对象。 这多是一个本机JavaScript
对象(即用户定义的对象或内置对象)或DOM
对象。propertyname
必需。 一个包含属性名称的字符串。descriptor
必需。 属性描述符。 它能够针对数据属性或访问器属性。其中descriptor
的参数值是咱们须要重点关注的,该属性可设置的值有:
value
] 属性的值,默认为undefinedwritable
] 该属性是否可写,若是设置成 false,则任何对该属性改写的操做都无效(但不会报错),对于像前面例子中直接在对象上定义的属性,这个属性该特性默认值为为 true
。Object.defineProperty(myObject, 'age', {
value : 18,
writable:false
});
myObject.age =19;
console.log(myObject.age);//18;
复制代码
configurable
]若是为false
,则任未尝试删除目标属性或修改属性的行为将被无效化,对于像前面例子中直接在对象上定义的属性,这个属性该特性默认值为为 true
。Object.defineProperty(myObject, "name", {
value:"gogo" ,
configurable: false
});
delete myObject.name;
console.log(myObject.name);// 输出 gogo
myObject.name = "gg";
console.log(myObject.name); // 输出gogo
复制代码
enumerable
] 是否能在for-in循环中遍历出来或在Object.keys中列举出来。对于像前面例子中直接在对象上定义的属性,这个属性该特性默认值为为 true。在调用
Object.defineProperty()
方法时,若是不指定,configurable
,enumerable
,writable
特性的默认值都是false
,这跟以前所 说的对于像前面例子中直接在对象上定义的属性,这个特性默认值为为true
。并不冲突,以下代码所示:
//调用Object.defineProperty()方法时,若是不指定
var someOne = { };
someOne.name = 'coverguo';
console.log(Object.getOwnPropertyDescriptor(someOne, 'name'));
//输出 Object {value: "coverguo", writable: true, enumerable: true, configurable: true}
//直接在对象上定义的属性,这个特性默认值为为 true
var otherOne = {};
Object.defineProperty(otherOne, "name", {
value:"coverguo"
});
console.log(Object.getOwnPropertyDescriptor(otherOne, 'name'));
//输出 Object {value: "coverguo", writable: false, enumerable: false, configurable: false}
复制代码
Object.getOwnPropertyDescriptor
方法会返回某个对象属性的描述对象(descriptor
)。ES2017 引入了Object.getOwnPropertyDescriptors
方法,返回指定对象全部自身属性(非继承属性)的描述对象。
set
] 修改器,一旦目标对象访问该属性,就会调用这个方法, 并返回结果。默认为undefined
。get
] 获取器,一旦目标对象设置该属性,就对调用这个方法。默认为undefined
其实,在vue
中就用到了 Object.defineProperty
方法,主要用于双向数据绑定。扯得有点远了,咱们再回来看,如何判断对象上的属性是否可枚举?
obj.propertyIsEnumerable(prop)
Object.defineProperty(myObject, "name", {
value:"gogo" ,
configurable: false ,
enumerable:false,
});
myObject.age =18
myObject.propertyIsEnumerable('name'); //false
myObject.propertyIsEnumerable('age');//true
复制代码
另外介绍一个方法:
Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames
返回一个数组,包含对象自身的全部属性(不含 Symbol 属性,可是包括不可枚举属性)的键名。是否是和Object.keys(obj)
有点像呢?
最后,咱们再来总结一下,有四个操做会忽略enumerable
为false
的属性。
for...in
循环:只遍历对象自身的和继承的可枚举的属性。Object.keys()
:返回对象自身的全部可枚举的属性的键名。JSON.stringify()
:只串行化对象自身的可枚举的属性。Object.assign()
: 忽略enumerable
为false
的属性,只拷贝对象自身的可枚举的属性。最后加一个题外的问题:
关于instanceof 的大概实现原理:
function myInstanceof(left,right){
let rightVal = right.prototype;
let letfVal = left.__proto__;
while(true){
if(leftVal === null)return false;
else if(letfVal === rightVal) return true;
letfVal = letfVal.__proto__;
}
}
复制代码
在加上一张著名的图:
这张图不太好理解的地方在于 Object.__proto__
的值是 Function.prototype
,暂时先记住吧。