Skip to content

Latest commit

 

History

History
323 lines (302 loc) · 14.6 KB

对象属性定义与使用.md

File metadata and controls

323 lines (302 loc) · 14.6 KB

对象属性定义、使用与检测

对象属性定义

对象的属性名不强制遵循标识符的命名规范,可以是任意的名字,但是,为了可阅读性:

  • 尽量遵循标识符的命名规范

属性名与引号

我们都知道,定义一个对象的属性时,其属性名有两种形式:带引号的不带引号的,二者之间有区别么?

答案:

在许多情况下,使用或不使用字符串都没有太大区别。
即使这样,仍建议在代码中强制使用一致的样式,这使得我们的代码风格更有利于阅读与理解。

var object1 = {
    property: true
};

var object2 = {
    "property": true
};

属性名规范时:

  • 带不带引号 没有任何区别

属性名不规范时:

  • 我们必须加上 引号

哪些属性名不规范呢?

  1. 属性名以除 字母、下划线或者美元符号 以外的字符 开头(例如数字,¥等,影响属性调用)
  2. 属性名为JavaScript关键字(例如if)
  3. 属性名内含有除 字母,下划线,美元符或数字 以外的非标识符字符(例如空格)

大小写混乱、不使用易理解单词命令 也是不规范的,不过这种逻辑上的混乱并不需要引号

必须将数字文字用作属性键时(尽管非常不推荐),如不确定是否该用,也需要使用引号:

var object = {
    1e2: 1,
    100: 2
};

上面代码会出现属性100的值将上面1e2属性值覆盖的现象,原因很简单:

  • 1e2100在被用作属性名之前被强制转换为字符串。
  • 两者String(1e2)和String(100)恰好等于"100"

诸如此类的问题很难调试,因此有些人倾向于要求在所有属性名称周围都加上引号。

var obj = {}
obj.age = 18
obj.'coder name' = 11// Uncaught SyntaxError: Unexpected string  ---> 未捕获的SyntaxError:意外的字符串
obj.5 = 555// Uncaught SyntaxError: Unexpected number  ---> 未捕获的SyntaxError:意外的数字
obj.coder name = '代码哥'// Uncaught SyntaxError: Unexpected identifier
obj[coder name] = '代码哥'// Uncaught SyntaxError: Unexpected identifier ---> 未捕获到的SyntaxError:意外的标识符
obj['coder name'] = '代码哥'
var testName = 'test value'
obj[testName] = '代码测试'
obj[5] = 555// 和 obj['5'] = 555 效果一致
obj['true'] = 'Boolean'
obj// {age: 18, coder name: "代码哥", test value: "代码测试", 5: 555, true: "Boolean"}
  • obj.key表示对象obj的属性key,既可以设置也可以读取
  • key为常量时,object[key]等价于object.key
  • 属性名key为变量时,只能用中括号形式:object[key]

中括号运算符:[] 与 JS运算

var age = 'myAge';
var obj = {
	name: '对象名',
	[age]: 19,
	[true?'flage':'test']: '运算测试'
}
obj// {name: "对象名", myAge: 19, flage: "运算测试"}
Object.prototype.toString = function(){
	return '666'
}
obj[obj] = 999// {666: 999, name: "对象名", myAge: 19, flage: "运算测试"}
  • 使用变量作为对象的属性名时,我们需要将属性名加上[]表示这里不是单纯的固定字符串
  • []进行简单的JS的运算,将结果作为对象属性名
    • []里为变量时,将其值作为属性名
    • []里为函数、数组、Object等对象时,将调用其原型的String()将其转化为固定字符串
    • 其他简单可执行运算(字符型表达式,例如乘除加减、函数调用、三元运算符等)则将返回值作为属性名

对象属性调用

中括号运算符:[] 与 点运算符:.

点方法是在对象名后面跟上属性名,而中括号方法里的索引存放的与属性名字相同的字符串

var obj = {
	age: 18,
	'coder name': '代码哥',
	6: 666
}
obj// {6: 666, age: 18, coder name: "代码哥"}
obj.age// 18
obj['age']// 18
obj.coder name// Uncaught SyntaxError: Unexpected identifier
obj['coder name']// "代码哥"
obj['6']// 666
obj.6// Uncaught SyntaxError: Unexpected number

从上我们可知:

  1. 点运算符调用时,对象的属性名 不用加引号,JS直接当做固定字符串属性名,如不是字符串形式则报错(例如数字)
  2. 中括号运算符调用时,对象属性名在带引号时表示为字符串,不带引号则先试图当做变量名去寻找变量值做属性名,如未寻找到则通过String()强制转换成字符串充当变量名

对象属性名总结

推荐用.号访问对象的属性值,因为在性能上.号要比[]中括号方式更佳,在应对一些非法属性名时,再使用[]中括号方式来访问(未找到相关资料,暂不确定性能问题是否为真。)

对象只支持字符串和 Symbol 两种类型的属性,如果我们使用的是任何其他类型值作为属性名,最后都会被转为字符串。

中括号运算符总是能代替点运算符,但点运算符却不一定能全部代替中括号运算符。

中括号运算符可以用字符串变量的内容作为属性名,点运算符不能。

中括号运算符可以用纯数字为属性名,点运算符不能。

中括号运算符可以用js的关键字和保留字作为属性名,点运算符不能。

对象检测是否含有某属性

in: 检测属性是否存在对象及其原型链中

var obj = {
  name: 'trump'
}
console.log('name' in obj,'__proto__' in obj)// true true
  • in检查一个对象(包括原型链上)中是否存在指定的属性,如果有则返回true,否则返回false

缺点:

  • 无法判断是否只是自身属性,而非原型链属性

.或者[]:检测是否属性值是否定义(间接判断)

obj.name1// undefined
obj["name1"]// undefined
//简单定义个判断函数
function hasProperty(obj,propertyName){
	return obj[propertyName] !==undefined
}
hasProperty(obj,'name')//true
hasProperty(obj,'name1')//false
  • 通过点或者方括号可以获取对象的属性值,如果对象上不存在该属性,则会返回undefined。
  • 当然,这里的“不存在”指的是对象自身和原型链上都不存在,如果原型链有该属性,则会返回原型链上的属性值。

缺点:

  • 当对象具有该属性,只是其属性值为undefined时无法判断,会造成误判!

hasOwnProperty(): 判断对象自身是否存在属性

obj.hasOwnProperty('name')// true
// Object.create(null) 会创建一个空(null)原型链接的对象,所以就不存在 Object.prototype 上面的方法 hasOwnProperty()。这时候可以通过使用 call / apply 强行让对象使用方法 
//为了安全也推荐下面这种写法:
Object.prototype.hasOwnProperty.call(obj,'name')// true

更多:无原型内置插件-规则-ESLint

Object.keys(): 判断对象自身是否存在可枚举字符串属性

Object.keys(obj).indexOf('name') > -1// true
Object.keys(obj).indexOf('name1') > -1//false
Object.keys(obj).indexOf('__proto__') > -1// false
  • 本质上依靠的keys()自然继承了它的缺点。

Object.propertyIsEnumerable(): 判断对象自身是否存在可枚举属性

//为了缩短代码,采用不推荐写法。
obj.propertyIsEnumerable('name')//true
obj.propertyIsEnumerable('__proto__')//false
  • 用法与hasOwnProperty()相同,但当检测属性是自有属性(非继承)且这个属性是可枚举的,才会返回true。

总结

不同方式各有优缺点,有时还需要结合使用,比如遍历自身属性的时候,就会把 for··· in···hasOwnProperty()结合使用。

对象获取全部属性

定义一个obj对象,将其属性设置不同特性(可枚举、不可枚举、字符串、Symbol):

私有属性:用户在自身本地定义的属性;原型属性:原型链上的,继承的属性

var obj = {
  str: 'This is a String property',
  [Symbol()]: 'This is a Symbol property'
};
console.log(obj);// {str: "This is a String property", Symbol(): "This is a Symbol property"}

// 定义一个不可枚举字符串属性
Object.defineProperty(obj, 'unenum', {
  value: 'This is a non-enumerable property',
  writeable: true,
  enumerable: false,
  configurable: true
})
console.log(obj);// {str: "This is a String property", unenum: "This is a non-enumerable property", Symbol(): "This is a Symbol property"}

// 再定义一个不可枚举Symbol属性
Object.defineProperty(obj, Symbol('unenum'), {
    value: 'This is a non-enumerable Symbol property',
    writeable: true,
    enumerable: false,
    configurable: true
})
console.log(obj);// {str: "This is a String property", unenum: "This is a non-enumerable property", Symbol(): "This is a Symbol property", Symbol(unenum): "This is a non-enumerable Symbol property"}

// 将obj对象的原型设置为第二个参数对象
Object.setPrototypeOf(obj, { foo: 'bar', [Symbol('foo')]: 'bar' })
console.log(obj);// {str: "This is a String property", unenum: "This is a non-enumerable property", Symbol(): "This is a Symbol property", Symbol(unenum): "This is a non-enumerable Symbol property", __proto__: { foo:"bar", Symbol(foo):"bar", __proto__:Object }}

Object.keys/values/entries(obj) : 对象自身可枚举字符串属性 的获取

Object.keys(obj)// ["str"]
Object.keys(obj)// ["str"]
var pairs = Object.entries(obj)// [["str", "This is a String property"]]
console.log(`key:${pairs[0][0]},value:${pairs[0][1]}`)// key:str,value:This is a String property
  • Object提供的方法返回的是数组类型
  • Object.entries()返回的二维数组,结果数组 的 每一项 是['key','value']格式的数组

Object.getOwnPropertyNames(obj) : 对象自身字符串属性 的获取

Object.getOwnPropertyNames(obj)// ["str", "unenum"]
  • 返回 obj 自身的所有字符串属性(包括不可枚举的)的数组

Object.getOwnPropertySymbols(obj) : 对象自身可枚举Symbol属性 的获取

Object.getOwnPropertySymbols(obj)// [Symbol(), Symbol(unenum)]
  • 返回 obj 自身的所有Symbol属性(包括不可枚举的)的数组

Reflect.ownKeys(obj) : 对象自身所有属性 的获取

Reflect.ownKeys(obj)// ["str", "unenum", Symbol(), Symbol(unenum)]
  • 返回 obj 自身的所有属性(包括不可枚举的)的数组

Object.getOwnPropertyDescriptors(obj) : 获取对象属性的描述符

var objProp = Object.getOwnPropertyDescriptors(obj)
console.log(objProp)
/**
{
  str: {
    value: 'This is a String property',
    writable: true,
    enumerable: true,
    configurable: true
  },
  unenum: {
    value: 'This is a non-enumerable property',
    writable: false,
    enumerable: false,
    configurable: true
  },
  [Symbol()]: {
    value: 'This is a Symbol property',
    writable: true,
    enumerable: true,
    configurable: true
  },
  [Symbol(unenum)]: {
    value: 'This is a non-enumerable Symbol property',
    writable: false,
    enumerable: false,
    configurable: true
  }
}
**/
  • 返回一个描述 obj 自身的所有属性与属性特性(包括不可枚举的)的对象
  • 搭配for...in...等循环代码可获取全部字符串属性与属性配置(包括不可迭代的属性)
  • 虽然Symbol属性我们可以查看,但可以发现:Symbol属性难以使用,因为Symbol的唯一性决定了我们无法调用
  • 每个Symbol值都是唯一的,各不相同,除非在将Symbol在作为属性前用变量装起来,但是这和我们获取属性再通过结果调用属性相违背!

// console.log(objProp.str.value)// This is a String property
for (const key in a) {
  console.log(key, a[key].value)
}
// str   This is a String property
// unenum   This is a non-enumerable property

我们就是要获取Symbol属性的特性与对应值怎么办?

console.log(Object.getOwnPropertyDescriptor(obj, Object.getOwnPropertySymbols(obj)[0]))
/**
{
  value: 'This is a Symbol property',
  writable: true,
  enumerable: true,
  configurable: true
}
**/
  • 我们通过Object.getOwnPropertyDescriptor(Object, key)指定获取某个属性的特性
  • 对于唯一性的Symbol属性,我们通过Object.getOwnPropertySymbols(obj)来获取

更多:Object.getOwnPropertyDescriptor()

for...in... : 对象自身和原型链上的所有可枚举字符串属性 的获取

for (const key in obj) {
  console.log(key)// "str" -> "foo"
}
  • for...in...获取对象自身和原型链上的全部可枚举字符串属性
  • for...in 循环出来的对象属性顺序:先遍历出整数属性(integer properties,按照升序),然后其他属性按照创建时候的顺序遍历出来。

更多:Objects属性的for in 结果顺序

总结

| 方法 | 返回值 | 备注 | | --- | --- | --- | --- | |Object.keys/values/entries|对象自身的可枚举字符串属性|返回的是 数组类型 | |Object.getOwnPropertyNames(obj)|对象自身的所有可枚举、不可枚举的字符串属性|返回的是 数组类型 | |Object.getOwnPropertySymbols(obj)|对象自身的所有可枚举、不可枚举的 Symbol 属性|返回的是 数组类型 | |Reflect.ownKeys(obj)|对象自身的所有可枚举、不可枚举的字符串属性、Symbol 属性。|等同于 Object.getOwnPropertyNames(obj) +  Object.getOwnPropertySymbols(obj)  的效果。| |for (let prop in obj) {}|对象自身及所在原型链上的所有可枚举字符串属性|prop变量值为 迭代获取到的可枚举属性 | |Object.getOwnPropertyDescriptors(obj)|对象自身属性及属性特性|返回 包含属性描述的对象|

自定义方法:获取对象的所有字符串属性(包括原型链上的)

function inheritedPropertyNames(obj) {
  var props = {};
  while(obj) {
    Object.getOwnPropertyNames(obj).forEach(function(p) {
      props[p] = true;
    });
    obj = Object.getPrototypeOf(obj);
  }
  return Object.getOwnPropertyNames(props);
}

代码出处:怎样获取对象的所有属性

更多: