写的不好的地方麻烦各位大佬指点一下,轻喷,(捂脸逃)
对象:目录
- 对象声明
- JS 内置对象
- 对象属性
- 获取、添加属性
- 属性描述符
- 遍历对象属性的方法
- 对象的扩展、密封、冻结
- 对象的解构赋值
- 常见对象相关操作
- 判断对象是否是对象
- 判断是否是空对象
- 合并对象
- 深浅拷贝对象
- 操作原型对象
- 对象其他方法
1. 对象声明
- 文字形式:{}
- 属性缩写(属性名、方法)
- 属性名用变量 / 表达式
var a = 1var b = 'sayHi'var c = 'name'var o = { a, // 属性缩写 test () { // 方法缩写 console.log('test') }, [c + ' is']: 'Lin', [b] () { // 方法缩写 + 变量 console.log('hi') } }console.log(o)o.test()复制代码
- 构造形式:new Object() (或者 new 其他构造函数/class)
文字形式和构造形式唯一的区别是,在文字声明中你可以添加多个 键 / 值对,而且可以用缩写,但是在构造形式中你必须逐个添加属性
- Object.create()
2. 内置对象
- String、Number、Boolean、Object、Function、Array、Date、RegExp、Error
- js 会自动把字符串字面量转化为一个 String 对象(如果需要的话,如通过字符串调用方法:
'123'.indexOf(1)
)
3. 对象属性
- 属性名永远是 string 或者 symbol,如果用了其他类型作为属性名,就会转为 string(PS:所以才有了 Map)
var myObject = {}; myObject[true] = "foo"; myObject[3] = "bar"; myObject[myObject] = "baz"; myObject["true"]; // "foo" myObject["3"]; // "bar" myObject["[object Object]"]; // "baz"复制代码
- 存储在对象内部的是属性的名称,像指针一样,指向这些值真正的存储位置
3.1 访问对象属性
- . 操作符
- []
- 变量
- 表达式
var pre = 'haha'var obj = { [pre + 'aaa']: 'aaa', [pre + 'bbb']: 'bbb', [pre] () { console.log('haha') }}console.log(obj['hahabbb']) // bbbobj[pre]()复制代码
3.2 添加对象属性
- .
- []
- Object.defineProperty()
3.3 属性描述符
用来描述对象的某个属性的一些特性的,分为数据描述符和存取描述符
- 两者都是
- configurable(default: false):属性是否可配置(修改属性描述符)、删除
- enumerable(default: false):属性是否可枚举(会忽略
enumerable = false
的属性的操作)- for...in:只遍历对象自身的和继承的可枚举的属性。
- Object.keys():返回对象自身的所有可枚举的属性的键名
- JSON.strinify():只串行化对象自身的可枚举的属性。
- Object.assign():只拷贝对象自身的可枚举的属性
- 数据描述符
- value(defalut: undefined)
- writable(defalut: false):value 是否可以被赋值运算符修改
var obj = {}Object.defineProperty(obj, 'a', { writable: true, value: 100})console.log(obj.a)obj.a = 1console.log(obj.a)复制代码
- 存取描述符
- get(default: false):是一个隐藏函数,当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入 this 对象(由于继承关系,这里的 this 并不一定是定义该属性的对象)
- setter(defalut: false):是一个隐藏 函数,当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值
setter 会覆盖单个属性默认的 [[Put]](也被称为赋值)操作。通常来说 getter 和 setter 是成对出现的
如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。
- 用 get、set 的话,会产生一个属性的 u.u
Object.defineProperty(obj, 'b', { configurable: true, enumerable: true, get () { return this._b || 'haha' }, set (value) { this._b = value }})console.log(obj.b)obj.b = 1console.log(obj.b)复制代码
相当于这样写
var obj = { get b () { return this._b || 'haha' }, set b (value) { console.log(this) this._b = value }}复制代码
seter 和 getter 可以拦截赋值和获取操作,实际上用的那个等号就可以理解成一个语法糖
// 本来可以直接这样的,不用写 get、set 的,但是!obj.b = 100复制代码
- 可以用来解决 Object.assgin() 拷贝对象的时候,无法拷贝 get、set 属性的问题
const shallowMerge = (target, source) => Object.defineProperties( target, Object.getOwnPropertyDescriptors(source))复制代码
3.3.1 定义属性描述符
Object.defineProperty(obj, prop, descriptor)
上面说的默认值,都是在用 Object.defineProperty 定义属性及其描述符的时候才有效的;在其他时候(. 操作符、[])时,无影响(默认都是 true)
var o = {}Object.defineProperty(o, 'a', {})o.a = 10console.log(o.a) // undefinedObject.defineProperty(o, 'a', { writable: true}) // Uncaught TypeError: Cannot redefine property: a复制代码
3.3.2 获取属性描述符
- Object.getOwnPropertyDescriptor(obj, prop)
/** * 获取对象的某个属性的描述对象 * Object.getOwnPropertyDescriptor(obj, prop) * * @param {object} obj * @param {string} prop * @returns {object | undefined} */obj.c = 10console.log(Object.getOwnPropertyDescriptor(obj, 'c')) // {value: 10, writable: true, enumerable: true, configurable: true}console.log(Object.getOwnPropertyDescriptor(obj, 'b')) // {get: ƒ, set: ƒ, enumerable: true, configurable: true}复制代码
- Object.getOwnPropertyDescriptors(obj)
/** * 获取对象自身的所有属性的描述对象(非继承) * @param {object} obj * @returns {object} prop: descriptor */复制代码
3.4 遍历对象的方法
主要看,能不能遍历继承的、symbol、不可枚举的(只要是打 X 的,就不能访问到)
方法 | 继承 | symbol | 不可枚举 |
---|---|---|---|
for...in | √ | X | X |
Object.keys(obj)、Object.values(obj)、Object.entries(obj) | X | X | X |
Object.getOwnPropertyNames(obj) | X | X | √ |
Object.getOwnPropertySymbols(obj) | X | √ | (可以遍历 symbol) |
Reflect.ownKeys(obj) | X | √ | √ |
- 可以通过
for...in
+obj.hasOwnProperty(prop)
来遍历自身属性(这样就相当于Object.keys()
) Object.getOwnPropertyNames(obj)
+Object.getOwnPropertySymbols(obj)
=Reflect.ownKeys(obj)
// 遍历相关function Super () { this.super = 'superValue' this[Symbol('superSymbol')] = 'haha' // 可枚举的 Symbol}function Sub () { this.sub = 'subValue' this[Symbol('subSymbol')] = 'xixi'}var sup = new Super()// 定义不可枚举的属性Object.defineProperty(sup, 'superEnum', { enumerable: false, configurable: false, value: 'superEnumValue'})// 定义不可枚举的 SymbolObject.defineProperty(sup, Symbol('superEnumSymbolValue'), { enumerable: false, configurable: false, value: 3})// 定义继承Sub.prototype = supvar sub = new Sub()Object.defineProperty(sub, 'subEnum', { enumerable: false, configurable: false, value: 'subEnumValue'})Object.defineProperty(sub, Symbol('subEnumSymbolValue'), { enumerable: false, configurable: false, value: 1})// 可遍历继承、不可遍历 symbol、不可遍历不可枚举的for (var i in sub) { console.log(i) // sub // super}// 不可遍历继承、不可遍历 symbol、不可遍历不可枚举的console.log(Object.keys(sub)) // [ 'sub' ]console.log(Object.values(sub)) // [ 'subValue' ]console.log(Object.entries(sub)) // [ [ 'sub', 'subValue' ] ]// 不可遍历继承、不可遍历 symbol、可遍历不可枚举的(包括不可枚举的 Symbol)console.log(Object.getOwnPropertyNames(sub)) // [ 'sub', 'subEnum' ]// 遍历自身的所有 symbols,包括不可枚举的console.log(Object.getOwnPropertySymbols(sub)) // [Symbol(subSymbol), Symbol(subEnumSymbolValue)]// 不可遍历继承、可遍历 Symbol、可遍历不可枚举的console.log(Reflect.ownKeys(sub)) // [ 'sub', 'subEnum', 'subEnumSymbol', Symbol(subSymbol) ]// 不可遍历继承、可遍历 Symbol、可遍历不可枚举的console.log(Object.getOwnPropertyDescriptors(sub)) // 复制代码
4. 对象的扩展、密封、冻结(这个再看看高设)
属性描述符是针对对象的属性的,而扩展、密封、冻结则是针对对象的。
可配置、可写与扩展、密封、冻结没有什么关系
var o = {}Object.defineProperty(o, 'a', {})console.log(Object.getOwnPropertyDescriptor(o, 'a'))console.log(Object.isExtensible(o)) // trueconsole.log(Object.isSealed(o)) // falseconsole.log(Object.isFrozen(o)) // false复制代码
-
可扩展:可以添加新属性
- Object.isExtensible(obj):是否可扩展
- Object.preventExtensions(obj):让一个对象永远不可扩展,返回操作后的对象
-
密封:不可添加、删除属性,且所有属性不可配置(configurable: false
- Object.isSealed(obj):是否密封
- Object.seal(obj):密封对象,返回密封后的对象
-
冻结:密封 + 不可修改所有属性的值(地址)(属性是对象的话,除非也是冻结,否则可改)
- Object.isFrozen(obj):是否冻结
- Object.frozen(obj):冻结对象,返回冻结后的对象
浅冻结与深冻结~
深冻结:如果属性是对象,那么那个对象也是冻结的
5. 解构赋值
看解构赋值一文
6. 与对象相关的操作
6.0 判断是否是对象
Object.prototype.toString.call(obj) === '[object Object]'复制代码
6.1 判断对象是否为空
这里是要看空对象的概念是什么,这里空对象表示:
- 不考虑 null
- 对象自身的不可枚举、非 symbol 的属性
- for...in
var obj = {}function objectIsEmpty (obj) { for (var key in obj) { if (obj.hasOwnProperty(key)) { return false } } return true}console.log(objectIsEmpty(obj))复制代码
- Object.keys()
function objectIsEmpty (obj) { return Object.keys(obj).length === 0 ? true : false}console.log(objectIsEmpty(obj))复制代码
- JSON.stringify()
function objectIsEmpty (obj) { return JSON.stringify(obj) === '{}' ? true : false}console.log(objectIsEmpty(obj))复制代码
总结:其实本质就是用对象遍历的方法而已,具体要看空对象的定义是什么。
6.2 复制对象(深浅拷贝)
看 深浅拷贝 一文
6.3 合并对象
特点:拷贝源对象自身可枚举属性(包括 Symbol 类型的属性)
o = Object.assgin({}, o1, o2) // 会修改目标对象复制代码
缺点:不能复制对象属性的 get
、set
属性。如果要避免这个问题,就用:
const shallowMerge = (target, source) => Object.defineProperties( target, Object.getOwnPropertyDescriptors(source))复制代码
- ... 扩展运算符
o = {...o1, ...o2}复制代码
- mixin 的实现(多个类的合并)
- 拷贝实例属性和原型属性
6.4 操作原型对象
原型对象:当前对象的 prototype 对象
- Object.setPrototypeOf(obj, proto)
- 设置对象的原型对象
- Object.getPrototypeOf(obj)
- 获取对象的原型对象
- Object.create(obj)
- 返回一个指定对象原型的对象
6.5 其他方法
- 判断两个值是否相等
- Object.is
- 是否有某个属性
-
Object.prototype.hasOwnProperty(prop)
-
返回布尔值,指示对象自身属性中是否包含具体指定的属性
-
in 操作符(prop in object)
-
属性是否在对象或它的原型链上,返回 true 或者 false
var o = {a: 1}console.log('a' in o) // true复制代码
- Object.assign()
/** * 将所有可枚举属性的值从一个或多个源对象**浅拷贝**到目标对象 * 它将返回目标对象。(目标对象会被修改) * Object.assgin(targetObj, sourceObj1, sourceObj2, ...) * * @param {object} targetObj * @param {object} sourceObj1 * @returns {object} targetObj */let obj = { a: 1, b: 2}Object.assign(obj, {a: 3, c: 4})console.log(obj) // { a: 3, b: 2, c: 4 }复制代码
- target 如果不是对象,会自动转为对象. 如果是 null 或者是 undefined 就会报错
console.log(Object.assign(1, {a: 1})) // Number {1, a: 1}复制代码
- obj1、obj2,原始类型会被包装为对象。
var v1 = "abc";var v2 = true;var v3 = 10;var v4 = Symbol("foo")var obj = Object.assign({}, v1, null, v2, undefined, v3, v4); // 原始类型会被包装,null 和 undefined 会被忽略。// 注意,只有字符串的包装对象才可能有自身可枚举属性。console.log(obj); // { "0": "a", "1": "b", "2": "c" }复制代码
- 同名属性会发生替换。以后面出现的为准。
- 无法正确拷贝 get 属性和 set 属性。Object.assign 方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法。
var obj = Object.assign({}, { get g () { return 'abc' }})console.log(obj) // { g: 'abc' }复制代码
用途
(1) 为对象添加属性、方法
let o = Object.assign({}, { a: 1, sayHi () { console.log('hi') }})console.log(o) // {a: 1, sayHi: ƒ}复制代码
(2) 克隆对象
- 不含继承(只克隆对象自身可枚举属性)
function clone(origin) { return Object.assign({}, origin);}复制代码
- 克隆继承链
function clone (obj) { return Object.assign(Object.create(Object.getPrototypeOf(obj)), obj)}复制代码
(3) 合并多个对象
const merge = (target, ...sources) => Object.assign(target, ...sources);复制代码