4

走在前端的大道上

最后更新 2018.12.27

本篇将自己读过的相关 javascript Object方法 文章中,对自己有启发的章节片段总结在这(会对原文进行删改),会不断丰富提炼总结更新。

1.Object.keys遍历

返回一个数组,包括对象自身的(不含继承的)所有可枚举属性

示例代码:

(1) 数组Array对象(返回索引值)

var arr=[1,2,3];            
Object.keys(arr)         //  ["0", "1", "2”]

(2) object对象(返回key值)

var obj = { foo: "bar", baz: 42 };
Object.keys(obj)        //  ["foo", "baz”]

(3) 类数组,对象

var obj = { 0 : "a", 1 : "b", 2 : "c”};
Object.keys(obj)       // ["0", "1", "2"]

(4) 类数组对象 随机key排序

var Obj = { 100: 'a’, 2: 'b’,7: 'c’ };
console.log(Object.keys(Obj));     // ['2', '7', '100’]. 返回从小到大排序后的结果

(5) Object.keys仅遍历对象本身,并将所有可枚举的属性组合成一个数组返回

var Person = function({name='none', age=18, height=170}={}){
  this.name = name;
  this.age = age;
  this.height = height;
}

Person.prototype = {
  type: 'Animal'
}

var qiu = new Person()

// 将height属性设置为 不可枚举
Object.defineProperty(qiu, 'height', {
  enumerable: false
})

var keys = Object.keys(qiu);
console.log(keys)
// output: ['name', 'age']

(6) 将键值类型的查询param转换成url的query

const searchObj = {
  title: 'javascript',
  author: 'Nicolas',
  publishing: "O'RELLY",
  language: 'cn'
}
let searchStr = Object.keys(searchObj)
                .map(item => `${item}=${searchObj[item]}`)
                .join('&');
let url = `localhost:8080/api/test?${searchStr}`

遍历键值对的数据时,使用Object.keys真是不二之选。

2.Object.values()

方法返回一个给定对象自己的所有可枚举属性值的数组,值的顺序与使用for..in循环相同,返回的对象的value值,与Object.key()相反

(1) 正常对象

var obj={a:1,b:2,c:3};
console.log(Object.values(obj))         //  [1, 2, 3]

(2) 类数组对象

var obj ={0:'a',1:'b',2:'c'};
console.log(Object.values(obj)).       //  [a,b,c]

(3) key值为无序number

var obj={100:'a',10:'b',1:'1'};
console.log(Object.values(obj)).   // ["1", "b", "a"]

3.Object.getOwnPropertyNames遍历

返回一个数组,包含对象自身(不含继承)的所有属性名

示例代码:

var Person = function({name='none', age=18, height=170}={}){
  this.name = name;
  this.age = age;
  this.height = height;
}

Person.prototype = {
  type: 'Animal'
}

var qiu = new Person()

// 将height属性设置为 不可枚举
Object.defineProperty(qiu, 'height', {
  enumerable: false
})

var keys = Object.getOwnPropertyNames(qiu);
console.log(keys)
// output: ['name', 'age', 'height']

与Object.keys的区别在于Object.getOwnPropertyNames会把不可枚举的属性也返回。除此之外,与Object.keys的表现一致。

3.Object.getPrototypeOf()

javascript中提供Object.getPrototypeOf()方法来获得对象的直接原型。

function Person() {
    this.name = 'sillywa'
}
var person1 = new Person()
Object.getPrototypeOf(person1)  // {constructor: ƒ Person()}
Object.getPrototypeOf(person1.__proto__)  // Object.prototype

var person = {
    name: 'sillywa'
}
var person2 = Object.create(person)
Object.getPrototypeOf(person2)  // {name: "sillywa"}

javascript有以下几种方法检测一个对象的原型:

  • isPrototypeOf():检测一个对象是否是另一个对象的原型
  • obj.constructor.prototype:检测非Object.create()创建的对象的原型
var obj1 = {
    name: 'sillywa'
}
var obj2 = Object.create(obj1)

// isPrototypeOf()方法
Object.prototype.isPrototypeOf(obj1)  // true
obj1.isPrototypeOf(obj2)  // true
Object.prototype.isPrototypeOf(obj2)  // true

// obj.constructor.prototype
obj1.constructor.prototype === Object.prototype  // true
// obj1是obj2的原型,以下等式应为true
obj2.constructor.prototype === obj1  // false
// 而实际上
obj2.constructor.prototype === Object.prototype  // true

以上代码中obj1obj2的原型,obj2.constructor.prototype === obj1应为true但是实际上却是false,因为obj2__proto__里面并没有一个constructor属性,obj2.constructor实际上是obj1__proto__里面的constructor,所以obj2.constructor.prototype === Object.prototype

4.Object.assign()

Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,它将返回目标对象,但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

Object.assign(target, ...sources)

参数:
target:目标对象;
sources:源对象;
返回值:目标对象

(1) 用来复制一个新对象,并不会影响原对象

var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy);    // { a: 1 }

(2) 用来合并对象属性,将源对象的所有可枚举属性,复制到目标对象

//object.assign(obj, obj2)  obj2是源对象,obj 是目标对象,返回目标对象

var obj = { a: 1 };
var obj2={b:2};

console.log(Object.assign(obj,obj2)===obj);  //true,返回目标对象
console.log(obj);       //{a:1,b:2} obj的值已被更改

(3) 如果目标对象和源对象中有相同的键,则属性将被源对象的属性覆盖,后面的源属性会覆盖之前的相同键的源属性

var obj = { a: 1 };
var obj2 = {a:5,b:2};
var obj3 = {b:1,d:0};
Object.assign(obj,obj2,obj3);

console.log(obj);       // {a: 5, b: 1, d: 0}
obj和obj2同时拥有相同的键a,但两个值不同,obj是目标对象,所以会被源对象obj2的值覆盖,obj2和obj3也同时拥有相同的键b,在拷贝时,obj3排在obj2的后面,所以obj2被覆盖 ,最终打印结果是:{a:5,b:1,d:0}

(4) 当assign只有一个对象时,则直接返回这个对象,不做任何操作

var obj = { a: 1 }
Object.assign(obj);
console.log(obj);        //{a:1}

(5) Object.assign()方法实行的是浅拷贝,而不是深拷贝

也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用

var obj1 = { a: 0 , b: { c: 0}};
var obj2 = Object.assign({}, obj1);
obj1.b.c=5;

console.log(obj2)       //{a:0,b:{c:5}};

当我们在改变obj1的值时,并没有想改变obj2,但obj2的值也发生了改变,这违背了我们的想法。

(6)深拷贝

var obj1 = { a: 0 , b: { c: 0}};
var obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(obj3);     //{ a: 0 , b: { c: 0}};

5.Object.create()

Object.create() 方法会使用指定的原型对象及其属性去创建一个新的对象。

语法:

Object.create(proto[, propertiesObject])

参数

proto 新创建对象的原型对象。
propertiesObject 可选。
如果没有指定为 undefined,否则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。

返回值

在指定原型对象上添加新属性后的对象。

例外

如果proto参数不是 null 或一个对象,则抛出一个 TypeError 异常。

使用 Object.create 的 propertyObject参数

var o;

// 创建一个原型为null的空对象
o = Object.create(null);


o = {};
// 以字面量方式创建的空对象就相当于:
o = Object.create(Object.prototype);


o = Object.create(Object.prototype, {
  // foo会成为所创建对象的数据属性
  foo: { 
    writable:true,
    configurable:true,
    value: "hello" 
  },
  // bar会成为所创建对象的访问器属性
  bar: {
    configurable: false,
    get: function() { return 10 },
    set: function(value) {
      console.log("Setting `o.bar` to", value);
    }
  }
});

function Constructor(){}
o = new Constructor();
// 上面的一句就相当于:
o = Object.create(Constructor.prototype);
// 当然,如果在Constructor函数中有一些初始化代码,Object.create不能执行那些代码


// 创建一个以另一个空对象为原型,且拥有一个属性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

//创建一个可写的,可枚举的,可配置的属性p
o2 = Object.create({}, {
  p: {
    value: 42, 
    writable: true,
    enumerable: true,
    configurable: true 
  } 
});

我们知道通过Object.create()创建的对象实际上等于将该对象的__proto__指向Object.create()里面的参数对象,那么当涉及到原型时它是怎么工作的呢?

var a = {
    name: 'sillywa'
}
var b = Object.create(a)

b.__proto__ === Object.prototype  // false
b.__proto__ === a  // true
b.__proto__.constructor === Object  // true
b.__proto__.hasOwnProperty('constructor')  // false

var b = Object.create(a)实际上是把b__proto__指向了a。当访问b.constructor时,实际上访问的是b.__proto__.__proto__.constructor

6.delete

我们要明确一点删除属性的方法只有一个是 delete ,但是在我们的开发中我们有时往往将属性值设为 null,undefined 就说成是将属性删除,这是不对的这样做仅仅是取消了属性和值之间的关联。
我们可以通过 Object.hasOwnProperty() 这个方法来看一看;

let foo = {
    name: 'foo',
    firstChild: '1st foo',
    twoChild: '2th foo'
};

foo.name = undefined; // undefined
foo.firstChild = null; // null
delete foo.twoChild; // true
for (let key in foo) {
    console.log('key:' + key + ', value:' + foo[key]);
}
/* 
key:name, value:undefined
key:firstChild, value: null
*/

我们发现如果用 undefined 和 null 相当于给属性赋值,只有当用 delete 才是删除属性。
注:定义属性的名字时要使用一般的字符串而且是连续的字符串,属性名的定义要避免JavaScript关键字,就像我们有的公司定的对象属性的访问用 [] , 对象的方法用 . 。

7.for in

for … in 这个循环体来遍历对象的属性,包括对象原型链上的属性。

let obj = {a:1, b:2, c:3}
for (let key in obj) {
    console.log(obj[key]);
}

/*
1
2
3
*/
// 自定义属性是否可枚举
function customObjectEnumerable(obj, prop, value, flag) {
    Object.defineProperty(obj, prop, {
        configurable: true,
        value: value,
        enumerable: flag,
        writable: true
    });
};

var obj = {};
customObjectEnumerable(obj, a, 1, false);
customObjectEnumerable(obj, b, 2, false);
for(let key in obj) {console.log(obj[key])}
// undefined

var obj2 = {};
customObjectEnumerable(obj2,'a', 1, true);
customObjectEnumerable(obj2, 'b', 2, true);
for(let key in obj2) {console.log(obj2[key])}
/*
1
2
*/
function Foo() {
    this.name = 'foo',
    this.age = '18'
};

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

function Bar() {
    this.name = 'bar';
};

Bar.prototype = new Foo();
Bar.prototype.constructor = Bar; // 修正Bar.prototype.constructor 指向它本身

// 实例化Bar
let myBar = new Bar();

'name' in myBar // true
myBar.hasOwnProperty('name'); // true
myBar.hasOwnProperty('age'); // false

clipboard.png

clipboard.png


8.9.10.11

Object的definePropertydefineProperties这两个方法在js中的重要性十分重要,主要功能就是用来定义或修改这些内部属性,与之相对应的getOwnPropertyDescriptorgetOwnPropertyDescriptors就是获取这行内部属性的描述


8.Object.defineProperty()

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

该方法允许精确添加或修改对象的属性。通过赋值来添加的普通属性会创建在属性枚举期间显示的属性(for...in 或 Object.keys 方法), 这些值可以被改变,也可以被删除。这种方法允许这些额外的细节从默认值改变。默认情况下,使用Object.defineProperty()添加的属性值是不可变的。

对象里目前存在的 属性描述符有两种主要形式数据描述符存取描述符

  • 数据描述符 是一个具有值的属性,该值可能是可写的,也可能不是可写的。
  • 存取描述符 是由getter-setter函数对描述的属性。

描述符必须是这两种形式之一不能同时是两者

如果一个描述符同时设置了value,writable,get和set关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有value或writable和get或set关键字,将会产生一个异常。

数据描述符存取描述符 均具有 以下可选键值:

configurable
当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
enumerable
当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。


数据描述符同时具有以下可选键值:
value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
writable

当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。


存取描述符同时具有以下可选键值:
get
一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为 undefined。
set
一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined。

clipboard.png

语法
Object.defineProperty(obj, prop, descriptor)

参数
obj 要在其上定义属性的对象。
prop 要定义或修改的属性的名称。
descriptor 将被定义或修改的属性描述符。

返回值
被传递给函数的对象。

创建属性

如果对象中不存在指定的属性,Object.defineProperty()就创建这个属性。当描述符中省略某些字段时,这些字段将使用它们的默认值。拥有布尔值的字段的默认值都是false。value,get和set字段的默认值为undefined。一个没有get/set/value/writable定义的属性被称为“通用的”,并被“键入”为一个数据描述符。

var o = {}; // 创建一个新对象

// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(o, "a", {
  value : 37,
  writable : true,
  enumerable : true,
  configurable : true
});

// 对象o拥有了属性a,值为37

// 在对象中添加一个属性与存取描述符的示例
var bValue;
Object.defineProperty(o, "b", {
  get : function(){
    return bValue;
  },
  set : function(newValue){
    bValue = newValue;
  },
  enumerable : true,
  configurable : true
});

o.b = 38;
// 对象o拥有了属性b,值为38

// o.b的值现在总是与bValue相同,除非重新定义o.b

// 数据描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
  value: 0x9f91102, 
  get: function() { 
    return 0xdeadbeef; 
  } 
});
// throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors

Writable 属性

当writable属性设置为false时,该属性被称为“不可写”。它不能被重新分配。

var o = {}; // Creates a new object

Object.defineProperty(o, 'a', {
  value: 37,
  writable: false
});

console.log(o.a); // logs 37
o.a = 25; // No error thrown
console.log(o.a); // logs 37. The assignment didn't work.

Enumerable 特性

enumerable定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。

var o = {};
Object.defineProperty(o, "a", { value : 1, enumerable:true });
Object.defineProperty(o, "b", { value : 2, enumerable:false });
Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false
o.d = 4; // 如果使用直接赋值的方式创建对象的属性,则这个属性的enumerable为true

for (var i in o) {    
  console.log(i);  
}
// 打印 'a' 和 'd' (in undefined order)

Object.keys(o); // ["a", "d"]

o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false
o.propertyIsEnumerable('d'); // true

Configurable 特性

configurable特性表示对象的属性是否可以被删除,以及除writable特性外的其他特性是否可以被修改。

var o = {};
Object.defineProperty(o, "a", { get : function(){return 1;}, 
                                configurable : false } );

// throws a TypeError
Object.defineProperty(o, "a", {configurable : true}); 
// throws a TypeError
Object.defineProperty(o, "a", {enumerable : true}); 
// throws a TypeError (set was undefined previously) 
Object.defineProperty(o, "a", {set : function(){}}); 
// throws a TypeError (even though the new get does exactly the same thing) 
Object.defineProperty(o, "a", {get : function(){return 1;}});
// throws a TypeError
Object.defineProperty(o, "a", {value : 12});

console.log(o.a); // logs 1
delete o.a; // Nothing happens
console.log(o.a); // logs 1

如果o.a的configurable属性为true,则不会抛出任何错误,并且该属性将在最后被删除

添加多个属性和默认值

考虑特性被赋予的默认特性值非常重要,通常,使用点运算符和Object.defineProperty()为对象的属性赋值时,数据描述符中的属性默认值是不同的,如下例所示。

var o = {};

o.a = 1;
// 等同于 :
Object.defineProperty(o, "a", {
  value : 1,
  writable : true,
  configurable : true,
  enumerable : true
});


// 另一方面,
Object.defineProperty(o, "a", { value : 1 });
// 等同于 :
Object.defineProperty(o, "a", {
  value : 1,
  writable : false,
  configurable : false,
  enumerable : false
});

一般的 Setters 和 Getters

下面的例子展示了如何实现一个自存档对象。 当设置temperature 属性时,archive 数组会获取日志条目。

function Archiver() {
  var temperature = null;
  var archive = [];

  Object.defineProperty(this, 'temperature', {
    get: function() {
      console.log('get!');
      return temperature;
    },
    set: function(value) {
      temperature = value;
      archive.push({ val: temperature });
    }
  });

  this.getArchive = function() { return archive; };
}

var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]

var pattern = {
    get: function () {
        return 'I alway return this string,whatever you have assigned';
    },
    set: function () {
        this.myname = 'this is my name string';
    }
};


function TestDefineSetAndGet() {
    Object.defineProperty(this, 'myproperty', pattern);
}


var instance = new TestDefineSetAndGet();
instance.myproperty = 'test';

// 'I alway return this string,whatever you have assigned'
console.log(instance.myproperty);
// 'this is my name string'
console.log(instance.myname);

当我们查询存储器属性时会调用getter方法(无参数)。这个方法返回值就是属性存取表达式返回的值。

当我们设置存储器属性时会调用setter方法(有参数)。这个方法修改存储器属性的值。

var obj = {
    num: 12,
    age: 13,
    get num1 () {
        return this.num
    },
    set num1 (value) {
        this.num = value
    },
    get age1 () {
        return this.age
    }
}
obj.num1   // 12
obj.num1 = 120
obj.num1  // 120

obj.age1  // 13
obj.age1 = 130
obj.age1  // 13

存储器属性定义为一个或者两个和属性同名的函数,这个函数定义没有使用function关键字而是使用get和set。

可以看出如果该属性只有getter方法则只能读取该属性不能设置该属性,同样如果只有setter方法就只能设置该属性,不能读取该属性,只有当两者都有时才能正常读取和设置属性。

描述符的原型与默认值

一般情况,我们会创建一个descriptor对象,然后传给defineProperty方法。如下:

var descriptor = {
    writable: false
}
Object.defineProperty(obj, 'key', descriptor);

这种情况是有风险的,如果descriptor的原型上面有相关特性,也会通过原型链被访问到,算入在对key的定义中。比如:

descriptor.__proto__.enumerable = true;
Object.defineProperty(obj, 'key', descriptor);
Object.getOwnPropertyDescriptor(obj,'key'); //返回的enumerable为true

为了避免发生这样的意外情况,官方建议使用Object.freeze冻结对象,或者是使用Object.create(null)创建一个纯净的对象(不含原型)来使用。

接下来的注意点是默认值,首先我们会想普通的赋值语句会生成怎样的描述符,如obj.key="value"

可以使用Object.getOwnPropertyDescriptor来返回一个属性的描述符:

obj = {};
obj.key = "value";
Object.getOwnPropertyDescriptor(obj, 'key');
/*输出
{
    configurable:true,
    enumerable:true,
    value:"value",
    writable:true,
}
*/

这也是复合我们预期的,通过赋值语句添加的属性,相关描述符都为true,可写可配置可枚举。但是使用defineProperty定义的属性,默认值就不是这样了,其规则是这样的:

configurable: false
enumerable: false
writable: false
value: undefined

所以这里还是要注意下的,使用的时候把描述符写全,免得默认都成false了。

vue的双向数据绑定

这个问题在很多前端面试中,会提及,是用Object.defineProperty( ),来监听数据get和set,来实现数据劫持的

var blog = {
  name: '文章1'
};
console.log(blog.name);  // 文章1

如果想要在执行console.log(blog.name)的同时,直接给 文章1 加个书名号,那要怎么处理呢?或者说要通过什么监听对象 blog的属性值。这时候Object.defineProperty( )就派上用场了,代码如下:

var blog= {}
var name = '';
Object.defineProperty(blog, 'name', {
  set: function (value) {
    name = value;
    console.log('欢迎查看' + value);
  },
  get: function () {
    return '《' + name + '》'
  }
})

blog.name = '文章1';  // 欢迎查看文章1
console.log(blog.name);  // 《文章1》

篡改浏览器userAgent

比如你想把浏览器的userAgent给改了,直接写navigator.userAgent = 'iPhoneX'.你再输出一下userAgent,发现并没有修改。这是为什么呢?我们用这行代码看一下:

Object.getOwnPropertyDescriptor(window, 'navigator');
//输出
{
    configurable:true,
    enumerable:true,
    get:ƒ (),
    set:undefined
}

原因就找到了,navigator是有setter的,每次取值总会执行这个set函数来做返回。但是好消息是什么呢?configurable为true,那就意味这我们可以通过defineProperty来修改这个属性,代码就相当简单了:

Object.defineProperty(navigator, 'userAgent', {get: function(){return 'iphoneX'}})
console.log(navigator.userAgent); //输出iphoneX

Object.prototype.isPrototypeOf()

isPrototypeOf() 与 instanceof 运算符不同。在表达式 "object instanceof AFunction"中,object 的原型链是针对 AFunction.prototype 进行检查的,而不是针对 AFunction 本身。isPrototypeOf() 方法允许你检查一个对象是否存在于另一个对象的原型链上。

语法

prototypeObj.isPrototypeOf(object)

参数

object
在该对象的原型链上搜寻

返回值

Boolean,表示调用对象是否在另一个对象的原型链上。

报错

TypeError
如果 prototypeObj 为 undefined 或 null,会抛出 TypeError。

示例

本示例展示了 Baz.prototype, Bar.prototype, Foo.prototypeObject.prototypebaz 对象的原型链上:

function Foo() {}
function Bar() {}
function Baz() {}

Bar.prototype = Object.create(Foo.prototype);
Baz.prototype = Object.create(Bar.prototype);

var baz = new Baz();

console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true

if (Foo.prototype.isPrototypeOf(baz)) {
  // do something safe
}

Baz.prototype.isPrototypeOf(baz) //true
baz instanceof Baz  //true
baz instanceof Bar //true
baz instanceof Foo //true
baz instanceof Bar //true
var obj1 = {
    name: 'eeee'
}
var obj2 = Object.create(obj1)

// isPrototypeOf()方法
Object.prototype.isPrototypeOf(obj1)  // true
obj1.isPrototypeOf(obj2)  // true
Object.prototype.isPrototypeOf(obj2)  // true

Object.prototype.hasOwnProperty()

hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性

语法

obj.hasOwnProperty(prop)

参数

prop 要检测的属性 字符串 名称或者 Symbol。

返回值

用来判断某个对象是否含有指定的属性的 Boolean 。

描述

所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。

示例

1.判断属性是否存在

下面的例子检测了对象 o 是否含有自身属性 prop:

o = new Object();
o.prop = 'exists';

function changeO() {
  o.newprop = o.prop;
  delete o.prop;
}

o.hasOwnProperty('prop');   // 返回 true
changeO();
o.hasOwnProperty('prop');   // 返回 false
o.hasOwnProperty('newprop');  // 返回 true

2.自身属性与继承属性

下面的例子演示了 hasOwnProperty 方法对待自身属性和继承属性的区别:

o = new Object();
o.prop = 'exists';
o.hasOwnProperty('prop');             // 返回 true
o.hasOwnProperty('toString');         // 返回 false
o.hasOwnProperty('hasOwnProperty');   // 返回 false

3.遍历一个对象的所有自身属性

下面的例子演示了如何在遍历一个对象的所有属性时忽略掉继承属性,注意这里 for...in 循环只会遍历可枚举属性,所以不应该基于这个循环中没有不可枚举的属性而得出 hasOwnProperty 是严格限制于可枚举项目的(如同 Object.getOwnPropertyNames())。

var buz = {
    fog: 'stack'
};

for (var name in buz) {
    if (buz.hasOwnProperty(name)) {
        alert("this is fog (" + name + ") for sure. Value: " + buz[name]);
    }
    else {
        alert(name); // toString or something else
    }
}

4.使用 hasOwnProperty 作为属性名
JavaScript 并没有保护 hasOwnProperty 属性名,因此某个对象是有可能存在使用这个属性名的属性,使用外部的 hasOwnProperty 获得正确的结果是需要的:

var foo = {
    hasOwnProperty: function() {
        return false;
    },
    bar: 'Here be dragons'
};

foo.hasOwnProperty('bar'); // 始终返回 false

// 如果担心这种情况,可以直接使用原型链上真正的 hasOwnProperty 方法
({}).hasOwnProperty.call(foo, 'bar'); // true

// 也可以使用 Object 原型上的 hasOwnProperty 属性
Object.prototype.hasOwnProperty.call(foo, 'bar'); // true

9.Object.defineProperties()

功能:方法直接在一个对象上定义一个或多个新的属性或修改现有属性,并返回该对象。

语法:

Object.defineProperties(obj, props)

// obj: 将要被添加属性或修改属性的对象
// props: 该对象的一个或多个键值对定义了将要为对象添加或修改的属性的具体配置
var obj = new Object();
Object.defineProperties(obj, {
    name: {
        value: '张三',
        configurable: false,
        writable: true,
        enumerable: true
    },
    age: {
        value: 18,
        configurable: true
    }
})

console.log(obj.name, obj.age) // 张三, 18

Object.defineProperty()功能大体相同,对比一下

var obj = new Object();

Object.defineProperty(obj, 'name', {
    configurable: false,
    writable: true,
    enumerable: true,
    value: '张三'
})

console.log(obj.name)  //张三

10.Object.getOwnPropertyDescriptor()

功能:该方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
语法: Object.getOwnPropertyDescriptor(obj, prop)

// obj: 需要查找的目标对象
// prop: 目标对象内属性名称
var person = {
    name: '张三',
    age: 18
}

var desc = Object.getOwnPropertyDescriptor(person, 'name'); 
console.log(desc)  结果如下
// {
//     configurable: true,
//     enumerable: true,
//     writable: true,
//     value: "张三"
// }

11.Object. getOwnPropertyDescriptors()

功能:所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。
语法: Object.getOwnPropertyDescriptors(obj)
obj: 需要查找的目标对象
var person = {
    name: '张三',
    age: 18
}
var desc = Object.getOwnPropertyDescriptors(person);
console.log(desc) // 结果如下图

clipboard.png


推荐阅读:
1.深入JS对象的遍历
2.重新认识javascript对象(一)——对象及其属性
3.重新认识javascript对象(三)——原型及原型链
4.[js中的Object.defineProperty()和defineProperties()
](https://segmentfault.com/a/11...
参考文章:
1.Object.defineProperty()
2.Object.create()
3.isPrototypeOf()
4.Object.prototype.hasOwnProperty()
5.理解defineProperty以及getter、setter
6.vue中v-model等父子组件通信
7.JavaScript 知识点串烧——对象
8.重新认识javascript对象(三)——原型及原型链
9.教科书式的object方法


于梦中2010
2.1k 声望181 粉丝

前端菜鸟儿,请多关照!