对象
- 什么是对象
- 对象属性
-
- Object.assign()
- Object.create()
- Object.is()
- Object.keys()、Object.values()、Object.entries()、Object.fromEntries()
- Object.toString()、Object.toLocaleString()
- Object.getPrototypeOf()、 Object.setPrototypeOf()、Object.prototype.isPrototypeOf()
- Object.freeze()、Object.isFrozen()、Object.preventExtensions()、Object.seal()、Object.isExtensible()
- Object.hasOwn () 、Object.prototype.hasOwnProperty()
- Object.defineProperty()、Object.defineProperties()、Object.getOwnPropertyDescriptor()、Object.getOwnPropertyDescriptors()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()
- deep clone
- 对象defineProperty 与 Proxy代理对象区别
- 对象原型链
什么是对象
生活中,我们经常会听到对象两个字,你对象呢、你谈对象了吗?诸如此类~~
在工作中,我们依然逃不了面对对象的问题,那什么是对象呢?
对象是一个人?没错,你就是一个对象。
对象是一个物体?没错,那个物体就是一个对象。
对象是一个电影?没错,电影也是一个对象。
啥???怎么什么都是一个对象。没错,世间万物都可以看作是一个对象,对象是一个抽象的概念,可以用来概括你描述物体的属性和方法,在生活中,比如:汽车是一个对象,汽车有颜色、大小、重量等属性,方法有启动、停止等,在工作中,可以把某一类规划为一个对象,我们可以把这个类的属性和方法规划到这个对象上
对象属性
Object.assign()
1.1 浅拷贝对象 相同的值会被覆盖为新值
1.2 参数1为接受拷贝的对象,参数2为传递拷贝的对象
1.3 结果会改变第一个参数对象,同时会返回这个参数对象
```javascript
var aaa = { 'a': 1, 'b': 2 }
var bbb = { 'b': 3, 'c': 4 }
var ccc = { ...aaa, ...bbb }
console.log(aaa) // {"a":1,"b":2}
console.log(bbb) // {"b":3,"c":4}
console.log(ccc) // {"a":1,"b":3,"c":4}
console.log('----------------------')
const target = { a: 1, b: 2 }
const source = { b: 3, c: 4 }
const result = Object.assign(target, source)
console.log(target) // { a: 1, b: 3, c: 4 }
console.log(source) // {"b":3,"c":4}
console.log(result) // {"a":1,"b":3,"c":4}
console.log(target === result) // true
console.log(source === result) // false
```
注意:如果是扩展运算符就不会影响到原对象,如果是Object.assign会把对象和新对象都执行新的内存地址,会被修改为新对象
Object.create()
1.1 创建一个新对象,新对象继承原来创建的对象
1.2 Object.create() 是一个深拷贝
1.3 Object.create于传统创建对象的区别是在新旧对象之间增加了一层,这样引用地址就是解耦的 obj === newObj 是false 新旧对象之间就互不影响
```javascript
var aaa = {
a: 1,
b: 2,
c: {
d: 3,
e: 4
}
}
var bbb = Object.create(aaa)
console.log(aaa) // {"a":1,"b":2,"c":{"d":3,"e":4}}
console.log(bbb) // {}
console.log(bbb.a) // 1
bbb.a = 2
console.log(aaa) // {"a":1,"b":2,"c":{"d":3,"e":4}}
console.log(bbb) // {"a":2}
```
Object.create() 可应用于继承
```javascript
function Person(name) {
this.name = name;
this.permission = ["user", "salary", "vacation"];
}
Person.prototype.say = function () {
console.log(`${this.name} 说话了`);
};
function Staff(name, age) {
Person.call(this, name);
this.age = age;
}
Staff.prototype = Object.create(Person.prototype, {
constructor: {
// 若是不将Staff constructor指回到Staff, 此时的Staff实例 zs.constructor则指向Person
value: Staff,
},
});
Staff.prototype.eat = function () {
console.log("吃东西啦~~~");
};
```
Object.is()
1.1 用于判断两个对象是不是一致
```javascript
Object.is('foo', 'foo') // true
Object.is([], []) // false
Object.is(null, null) // true
Object.is(NaN, NaN) // true
// 特别的:
Object.is(0, -0) // false
Object.is(+0, -0) // false
Object.is(0, +0) // true
Object.is(-0, -0) // true
Object.is(NaN, 0/0) // true
```
Object.keys()、Object.values()、Object.entries()、Object.fromEntries()
1.1 获取对象的键、值、键值对、键值对数组转化为对象
```javascript
let obj = {
name: ‘ruovan’,
age: 24
}
console.log(Object.keys(obj)) // ["name","age"]
console.log(Object.values(obj)) // ["ruovan",24]
console.log(Object.entries(obj)) // [["name","ruovan"],["age",24]]
let arr = Object.entries(obj)
console.log(Object.fromEntries(arr)) // {"name":"ruovan","age":24}
```
Object.toString()、Object.toLocaleString()
1.1 对象字符串(每一个对象都有这个方法)
1.2 使用toLocaleString可以将对象根据语言环境来转换字符串
```javascript
let date = new Date()
console.log(date.toString()) // Fri Apr 07 2023 17:11:04 GMT+0800 (中国标准时间)
console.log(date.toLocaleString()) // 2023/4/7 17:11:04
let num = 123456789
num.toLocaleString() // '123,456,789'
let obj = { a: 1, b: 2 }
let arr = [1,2,3]
let str = '123'
obj.toString() // [object Objetc]
arr.toString() // 1,2,3
str.toString() // 123
```
Object.getPrototypeOf()、 Object.setPrototypeOf()、Object.prototype.isPrototypeOf()
1.1 Object.getPrototypeOf() 用于返回指定对象的原型,如果没有则是null
1.2 Object.setPrototypeOf() 用于设置指定对象的原型
1.3 Object.prototype.isPrototypeOf() 用于检测一个对象是否存在于另一个对象的原型链上
```javascript
let obj = {
name: 'zs',
age: 20
}
console.log(Object.getPrototypeOf(obj)) // true
console.log(Object.getPrototypeOf(obj) === obj.__proto__) // true
console.log(Object.getPrototypeOf(Object) === Function.prototype) // true
/***
* getPrototypeOf 作用和 __proto__ 效果一样 但是 __proto__有兼容性问题 在生产环境下 还是用 getPrototypeOf
* Object 也是构造函数, 因此,返回的是函数的原型对象
*/
console.log('------获取原型-------')
function PersonObj(name, age) {
this.name = name
this.age = age
}
function testObj(like) {
this.like = like
}
PersonObj.prototype.say = function () {
console.log(this.name + '说话了')
}
const person1 = new PersonObj('zs', 20)
console.log(Object.getPrototypeOf(person1)) // {}
console.log(person1.__proto__) // {}
Object.setPrototypeOf(person1, testObj)
console.log(Object.getPrototypeOf(person1))
// function testObj(like){
// this.like = like
// }
console.log('----设置原型------')
let obj1 = {}
let obj2 = new Object()
console.log(obj1.__proto__.isPrototypeOf(obj2)) // true
/**
* isPrototypeOf 检测一个对象是不是另一个对象的实例
* 类似方法还有 instanceof A instanceof B
*/
```
Object.freeze()、Object.isFrozen()、Object.preventExtensions()、Object.seal()、Object.isExtensible()
1.1 Object.freeze()可用于冻结一个对象,冻结后,该对象的所有属性、方法均不可修改
1.2 Object.freeze() 冻结是一个浅对象,对象里面的对象可以修改
1.3 被冻结的对象不能被解冻,只能重新定义一个对象再操作
1.4 可利用Object.freeze()来提升性能
1.5 Object.freeze()是所有的属性只读,不能修改
1.6 Object.preventExtensions()对象不可扩展,即不可新增属性和方法。但可以修改和删除
1.7 Object.seal()对象可以修改,不能删除
1.8 Object.isFrozen() 判读对象有没有被冻结
1.9 Object.isExtensible()对象可扩展,可新增属性和方法,一般与Object.preventExtensions() 一起用
var obj = {
a: 1,
b: 2,
c: {
aa: 11,
bb: 22
}
}
Object.freeze(obj)
console.log(Object.isFrozen(obj)) // 是否冻结对象
obj.c.aa = 33
obj.a = 0
console.log(obj) // 浅冻结 {"a":1,"b":2,"c":{"aa":33,"bb":22}}
var bbb = Object.assign({
}, obj) //解冻对象
bbb.a = 0
console.log(bbb) // {"a":0,"b":2,"c":{"aa":33,"bb":22}}
console.log('-------freeze---------')
var obj1 = {
a: 1,
b: 2,
c: {
aa: 11,
bb: 22
}
}
Object.seal(obj1) // 对象只能修改 不能删除
obj1.a = 2
delete obj.b
console.log(obj1) // {"a":2,"b":2,"c":{"aa":11,"bb":22}}
console.log('-----seal-----')
var obj2 = {
a: 1,
b: 2,
c: {
aa: 11,
bb: 22
}
}
Object.preventExtensions(obj2) //不可新增 可编辑和删除
obj2.a = 11
delete obj2.b
obj2.d = 000
console.log(obj2) // {"a":11,"c":{"aa":11,"bb":22}}
在vue2中,双向绑定的原理就是利用了 Object.defineProperty ,vue实例化的对象,vue将遍历此对象所有的属性,把这些属性全部转化为getter和setter,在属性被访问和修改的时候来通知变化。但如果遇到 Object.freeze()来冻结了对象,将不会为改对象加上getter和setter等数据劫持的方法。性能提升效果对比如下:
比如我们渲染一个1000*10的表格,开启Object.freeze对比
为什么Object.freeze() 的性能会更好?
Object.freeze() 可提升性能,如果在操作大数据量的时候,如果确定这个数据量后期不会被改动,可冻结该数据,可大幅度的提升性能
Object.hasOwn () 、Object.prototype.hasOwnProperty()
1.1 这两个方法都是来检测对象上有没有属性,均是只能检测静态属性,不能检测继承属性
1.2 Object.hasOwn() 是 Object.prototype.hasOwnProperty()的替代方案,现在一般用Object.hasOwn()
let obj = {
foo: '222'
}
console.log(Object.hasOwn(obj, "foo")) // true
console.log(Object.hasOwn(obj, "foo1")) // false
console.log(Object.hasOwn(obj, 'toString')) // false
console.log(obj.hasOwnProperty("foo")) // true
console.log(obj.hasOwnProperty("foo1")) // false
console.log(obj.hasOwnProperty('toString')) // false
为什么用 Object.hasOwn() 来替代 Object.hasOwnProperty() ,Object.hasOwnProperty() 是原型上面的方法,当对象的原型被改了之后,这个方法就能用了,于是就给对像自己定义了一个方法,其功能与Object.hasOwnProperty() 一样
Object.defineProperty()、Object.defineProperties()、Object.getOwnPropertyDescriptor()、Object.getOwnPropertyDescriptors()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()
1.1 Object.defineProperty()会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
1.2 Object.defineProperties()是Object.defineProperty()复写形式
1.3 Object.getOwnPropertyDescriptor()返回指定对象上一个自有属性对应的属性描述符(自有属性即静态属性,不需要从原型链上进行查找)
1.4 Object.getOwnPropertyDescriptors()方法用来获取一个对象的所有自身属性的描述符
1.5 Object.getOwnPropertyNames() 方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组
1.6 Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组
let Person = {
}
let value = ''
Object.defineProperty(Person, 'name', {
// value: 'jack',
// writable: true, // 是否可以改变
enumerable: true, // 是否可以枚举,
configurable: true, // 是否可配置
set(val) {
value = val
},
get() {
return value
}
})
Person.name = "rose"; // 当writable是false 是{"name":"jack"} 当时true时 是{"name":"rose"}
console.log(Person) // 当enumerable是false 是空对象 当时true时 是{"name":"rose"}
delete Person.name // 无法删除 configurable是true才可删除
console.log('--------Object.defineProperty()------')
var obj3 = {
};
Object.defineProperties(obj3, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
})
console.log('--------Object.defineProperties()------')
const object1 = {
property1: 42,
property2: 42
};
var a = Symbol("a");
var b = Symbol.for("b");
object1[a] = "localSymbol";
object1[b] = "globalSymbol";
const descriptor1 = Object.getOwnPropertyDescriptor(object1, 'property1');
console.log(descriptor1) // {"value":42,"writable":true,"enumerable":true,"configurable":true}
const descriptor2 = Object.getOwnPropertyDescriptors(object1);
console.log(descriptor2) // {"property1":{"value":42,"writable":true,"enumerable":true,"configurable":true},"property2":{"value":42,"writable":true,"enumerable":true,"configurable":true}}
const descriptor3 = Object.getOwnPropertyNames(object1);
console.log(descriptor3) // ["property1","property2"]
const descriptor4 = Object.getOwnPropertySymbols(object1);
console.log(descriptor4) // [Symbol(a), Symbol(b)]
deep clone
```javascript
function deepClone(obj) {
let newObj = {}
for (let key in obj) {
let item = obj[key]
if (Object.prototype.toString.call(item) === '[object Object]') {
newObj[key] = deepClone(item)
} else {
newObj[key] = obj[key]
}
}
return newObj
}
var aaaa = {
a: 1,
b: 2,
c: {
d: 3,
e: 4
}
}
var bbbb = deepClone(aaaa)
bbbb.a = 11
console.log(aaaa.a) // 1
console.log(bbbb.a) // 11
console.log(bbbb) // {"a":11,"b":4,"c":{"d":3,"e":4}}
```
对象defineProperty 与 Proxy代理对象区别
Proxy对象用于创建一个对象的代理,是用于监听一个对象的相关操作。代理对象可以监听我们对原对象的操作
在实例化Proxy对象时,get方法用来获取对象属性,set方法用来设置对象属性
const object = {
name: 'zhangsan'
}
const objProxyPrototype = {
count: 2
}
const objProxy = new Proxy(object, {
get(target, key) {
console.log('获取属性')
return target[key]
},
set(target, key, val) {
console.log('设置属性')
target[key] = val
},
has(target, key) {
return !!target[key]
},
getPrototypeOf(target) {
// return Object.getPrototypeOf(target)
return objProxyPrototype
},
isExtensible(target) {
return Object.isExtensible(target);
},
preventExtensions(target) {
return Object.preventExtensions(target);
}
})
console.log(objProxy.name)
objProxy.name = 'lisi'
console.log('name' in objProxy) // true
console.log('age' in objProxy) // false
console.log(Object.getPrototypeOf(objProxy)) // {"count":2}
console.log(Object.getPrototypeOf(objProxy) === objProxyPrototype) // true
console.log(Object.isExtensible(objProxy)) // true
Object.preventExtensions(objProxy);
console.log(Object.isExtensible(objProxy)) // false
proxy代理对象内部并没有对代理的对象进行this拦截,因此不能保证与内部对象this一致。如下:
const obj={
name:'_island',
foo:function(){
return this === objProxy
}
}
const objProxy=new Proxy(obj,{
})
console.log(obj.foo()); // false
console.log(objProxy.foo()); // true
vue2中双向绑定的原理是利用Object.defineProperty() 来实现的,而vue3中是利用Proxy代理来实现的。那为什么在vue3中要对这块进行优化呢???
我们在用vue2的时候,有没有遇到一个问题,当我们的数据明明发生了变化,但是为什么页面没有更新呢??? 因为 Object.defineProperty 设计的初衷并不是为了去监听拦截一个对象,它也实现不了更加丰富的操作,比如新增、删除等操作,所以我们在操作数据的时候,明明数据变化了,但是Object.defineProperty监听不到改变,我们的DOM就不会进行更新,在vue3中,我们的尤大大就用Proxy进行了优化
对象原型链
在介绍对象属性的时候,经常会用到自有属性(静态属性)、继承属性等
自有属性是对象自己的属性
继承属性是不是对象自己的属性,但是又可以用的属性,是继承过来的,就像你是一个富二代,明明你自己没有挣到钱,但依然有花不完的钱一样
每一个对象都会有一个原型对象,原型对象上有一些属性和方法,那这个对象的实例方法就继承了这个对象的原型对象上的属性和方法。
感兴趣的宝们可以去查下原型链。这里不做多的介绍了。。。
原型链图如下: