深刻了解JavaScript中的对象

你们都知道循环一个对象,能够用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] 属性的值,默认为undefined
  • [writable] 该属性是否可写,若是设置成 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()方法时,若是不指定, configurableenumerablewritable特性的默认值都是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)有点像呢?

最后,咱们再来总结一下,有四个操做会忽略enumerablefalse的属性。

  • for...in循环:只遍历对象自身的和继承的可枚举的属性。
  • Object.keys():返回对象自身的全部可枚举的属性的键名。
  • JSON.stringify():只串行化对象自身的可枚举的属性。
  • Object.assign(): 忽略enumerablefalse的属性,只拷贝对象自身的可枚举的属性。

最后加一个题外的问题:

关于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,暂时先记住吧。

相关文章
相关标签/搜索