Object

2021/11/1 前端重点

# 原型链

# 原理

  1. 每个function上都有一个prototype属性,指向一个对象,叫做原型对象
  2. 原型对象上又有一个constructor属性能够找到构造他的function
    • 用 new 关键字调用function时主要做四件事:
      1. 生成一个新的对象 {}
      2. 给这个对象添加一个属性 _ _ proto _ _ = function的prototype;
      3. function 的this绑定到这个对象上去执行
      4. 返回新生对象(所以新对象就有一个_ _ proto _ _属性指向了这个function的prototype),就能说他的原型链上能找到这个function的原型对象
<head>
    <title>Document</title>
</head>
<body>
    <script>
        function A() {
            this.name = '张三';
            this.age = 18;
        }
        let a = new A();

        // 原型链判断理论(需要递归查找)
        console.log(a.__proto__ === A.prototype); // true
        console.log(a.__proto__.constructor === A); // true
        console.log(a.__proto__.constructor.name === 'A'); // true
        console.log(a instanceof A); // true
    </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 证明

  1. new出来的对象能够通过_ _ proto _ _ 属性找到function的 prototype 对象
  2. prototype对象里又有一个constructor函数
  3. constructor函数有一个name属性,能够拿到函数名
  4. constructor还有一个prototype属性,又指回它的原型对象(循环指向)

# 原型链顶层

  1. 每个对象的原型链顶层都是 obj._ _ proto _ _ = Object.prototype
  2. 而Object._ _ proto _ _ 指向的是 Function.prototype
  3. Function._ _ proto _ _ 指向的也是 Function.prototype
  4. 而Function.prototype是一个对象,根据原则1,它的_ _ proto _ _ 指向的也是Object.prototype,所以原型链顶端就会出现循环指向的问题。

原型链顶端

# create

  • 作用:创造一个带有指定原型链的对象
  • 调用:Object.create(obj[, descriptors])
  • 入参:Object[, definePropertiesObj]
  • 返回:Object[, Object]

# getPropertypeOf

  • 作用:获取_ _ proto _ _
  • 调用:Object.getPropertypeOf(obj)
  • 入参:Object
  • 返回:Object
class A {}
class B extends A {}
console.log(Object.getPrototypeOf(B.prototype) === A.prototype); // true
1
2
3
console.log(Object.getPrototypeOf(Object)=== Function.prototype); // true
1

# setPrototypeOf

性能消耗大

  • 官方说性能消耗较大,建议使用Object.create()替代写法

setPrototypeOf

  • 作用:设置对象原型链
  • 调用:Object.setPrototypeOf(obj, _ _ proto _ _)
  • 入参:Object, Object
  • 返回:Object(参数1)
const obj1 = { name: 'hdy' };
const obj2 = Object.setPrototypeOf({}, obj1);

console.log(obj2); // {}
console.log(obj2.name); // hdy
1
2
3
4
5
  • create替代
const obj1 = { name: 'hdy' };
const obj2 = Object.create(obj1);

console.log(obj2); // {}
console.log(obj2.name); // hdy
1
2
3
4
5

# isPrototypeOf

  • 作用:查看是否在指定对象的原型链上
  • 调用:obj1.isPrototypeOf(obj2)
  • 入参:Object
  • 返回:Boolean
const obj1 = {}
const obj2 = Object.setPrototypeOf({}, obj1);

console.log(obj1.isPrototypeOf(obj2)); // true
1
2
3
4
  • 原型链上
const obj1 = {};
const obj2 = Object.create(obj1);
const obj3 = Object.create(obj2);

console.log(obj2.isPrototypeOf(obj3)); // true
console.log(obj1.isPrototypeOf(obj3)); // true
1
2
3
4
5
6

# 迭代器

# for in

for in

  • 作用:迭代本身包括原型链可枚举属性
  • 调用:for (let key in obj) {}

# entries

  • 作用:返回对象实例可枚举属性的键值对(不包括原型链
  • 调用:Object.entries(obj);
  • 入参:Object
  • 返回:Array(二维数组)

# keys

  • 作用:拿到对象自身的可枚举属性不包括原型链属性
  • 调用:Object.keys(obj)
  • 入参:Object
  • 返回:Array
const obj = {
    name: 'hdy'
}
Object.defineProperty(obj, 'age', {
    enumerable: false,
    value: 18
})

console.log(Object.keys(obj)); // ['name']
1
2
3
4
5
6
7
8
9

# values

  • 作用:拿到自身所有可枚举属性的值,不包括原型链属性
  • 调用:Object.keys(obj)
  • 入参:Object
  • 返回:Array
const obj = {
    name: 'hdy'
}
Object.defineProperty(obj, 'age', {
    enumerable: false,
    value: 18
})

console.log(Object.values(obj)); // ['hdy']
1
2
3
4
5
6
7
8
9

# getOwnPropertyNames

  • 作用:拿到自身所有属性名(包括不可枚举属性
  • 调用:Object.getOwnPropertyNames(obj)
  • 入参:Object
  • 返回:Array

与Object.keys()对比

  • 相同:都不会去原型链拿属性
  • 不同:getOwnPropertyNames() 可以拿到自身不可枚举属性

# getOwnPropertySymbols

  • 作用:获取本对象的symbol
  • 调用:Object.getOwnPropertySymbols(obj)
  • 入参:Object
  • 返回:Symbol []
var obj = {};
var a = Symbol("a");
var b = Symbol.for("b");

obj[a] = "localSymbol";
obj[b] = "globalSymbol";

var objectSymbols = Object.getOwnPropertySymbols(obj);

console.log(objectSymbols.length); // 2
console.log(objectSymbols)         // [Symbol(a), Symbol(b)]
console.log(objectSymbols[0])      // Symbol(a)
1
2
3
4
5
6
7
8
9
10
11
12

# hasOwnProperty

  • 作用:查看属性是不是属于实例本身
  • 调用:obj.hasOwnProperty(key)
  • 入参:String
  • 返回:Boolean

# 描述符

# defineProperty

  • 作用:以描述符的形式给对象定义属性,可以支持get/set劫持
  • 调用:Object.defineProperty(obj, key, descriptor)
  • 入参:Object, String, Object
  • 返回:Object(第一个入参的引用)

writable与继承

  • 一个对象的writable设置成false时,其继承者只能通过defineProperty定义同名属性
  • getter/setter设置时就默认writable是false,且无法更改,所以实例也无法生成自己的同名属性
  • 类的constructor函数可以主动为实例对象添加实例属性,可以覆盖同名父类属性

# defineProperties

  • 作用:同时可定义多个属性,作用与defineProperty一样
  • 调用:Object.defineProperties(obj, {name: descriptor[, name: descriptor...] })
  • 入参:Object,Object
  • 返回:Object(第一个参数的应用)
const obj = {};
Object.defineProperties(obj, {
    name: {
        enumerable: true,
        value: 'hdy'
    },
    _age: {
        value: 1
    },
    age: {
        get() { return this._age + '岁'; },
        set(value) {this._age = value}
    }
})

console.log(obj); // { name: 'hdy' }
console.log(obj.age); // 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# getOwnPropertyDescriptor

  • 作用:查看对象某个属性的描述符对象
  • 调用:Object.getOwnPropertyDescriptor(obj, key)
  • 入参:Object, String
  • 返回:Object(描述对象)











 

const desc = {
    configurable: false,
    enumerable: true,
    writable: false,
    value: 'hdy'
}
const obj = Object.defineProperty({}, 'name', desc);

const d = Object.getOwnPropertyDescriptor(obj, 'name');

// 拿到描述符对象(键值对相同)
console.log(Object.entries(d).every(([key, value]) => desc[key] === value)); // true
1
2
3
4
5
6
7
8
9
10
11
12

# getOwoPropertyDescriptors

getOwnPropertyDescriptors

  • 作用:获取所有对象的属性描述符
  • 调用:Object.getOwnPropertyDescriptors(obj)
  • 入参:Object
  • 返回:Object

# propertyIsEnumerable

  • 作用:查看对象的属性是否可枚举
  • 调用:obj.propertyIsEnumerable(key)
  • 入参:String
  • 返回:Boolean

# 增加限制

preventExtensions/seal/freeze对比

限制_ _ proto _ _不可改(不能修改原型链的指向)、不可以增加属性 configurable:false(不可删除属性、其他配置不可改) writable:false
preventExtensions *
seal * *
freeze * * *

# freeze

  • 作用:冻结本对象的所有属性,禁止增删改属性,只能查看,且configurable也变成false
  • 使用:Object.freeze(obj)
  • 入参:Object
  • 返回:Object(入参引用)

# isFrozen

  • 作用:判断一个对象是否被冻结
  • 调用:Object.isFrozen(obj)
  • 入参:Object
  • 返回:Boolean

判断条件

  1. 所有属性不可增删改
  2. 所有属性 configurable: false
  3. 原型链指向不能改(_ _ proto _ _)
const obj = {};
console.log(Object.isFrozen(obj)); // false
Object.freeze(obj);
console.log(Object.isFrozen(obj)); // true
1
2
3
4
  • 空的、不可扩展的对象,也就是所有属性 configurable:false
const obj = Object.preventExtensions({});
console.log(Object.isFrozen(obj)); // true
1
2

# seal

  • 单词:seal 密封
  • 作用:将一个对象密封起来,不可新增属性,当前所有属性的configurable变成false
  • 调用:Object.seal(obj)
  • 入参:Object
  • 返回:Object(入参引用)

对比freeze

  • 相同:都是不可扩展、属性都configurable:false
  • 不同:freeze冻住的对象属性writable也是false,seal密封的没有限制
  • 自身密封前已有属性的configurable变为false(也就不能删除)
  • 自身不能新增属性









 

 
 
 

 


const obj = { name: 'hdy' };
Object.defineProperty(obj, 'age', {
    configurable: true,
    enumerable: true,
    writable: false,
    value: 18
})
Object.seal(obj);

// Object.defineProperty(obj, 'age', { writable: true }); // 报错,密封后configurable是false

obj.name = 'coderHdy'; // 生效
obj.age = 20; // 无效,writable是false
obj.book = '红宝书'; // 无效,已密封

delete obj.name; // 无效
console.log(obj); // { name: 'coderHdy', age: 18 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# isSealed

  • 作用:判断一个对象有没有被密封
  • 调用:Object.isSealed(obj)
  • 入参:Object
  • 返回:Boolean
const obj = {};
console.log(Object.isSealed(obj)); // false
Object.seal(obj);
console.log(Object.isSealed(obj)); // true
1
2
3
4

# preventExtensions

  • 作用:使对象不可扩展,此操作不可逆
  • 调用:Object.preventExtensions(obj)
  • 入参:Object
  • 返回:Object(入参)
  • tip:不会影响到原型链,但本对象_ _ proto _ _属性也不可变




 

 


 




 

const obj = { name: 'hdy' };
const obj2 = Object.create(obj);
obj2.age = 18;

Object.preventExtensions(obj2); // 此后属性只能删、改、查,不能增,且原型链不能变

obj2.book = '蝴蝶书'; // 无效
console.log(obj2); // { age: 18 }

obj2.age = 19; // 有效
console.log(obj2.age); // 19

// 改动原型链也无效
const obj3 = { name: '张三' };
// Object.setPrototypeOf(obj2, obj3); // 报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# isExtensible

  • 作用:对象是否可扩展
  • 调用:Object.isExtensible(obj)
  • 入参:Object
  • 返回:Boolean
  • 限制可扩展性的有:freeze/seal/preventExtensions
const obj = { name: 'hdy' };
console.log(Object.isExtensible(obj)); // true
1
2
const obj = Object.preventExtensions({ name: 'hdy' });
console.log(Object.isExtensible(obj)); // false
1
2
const obj = Object.freeze({ name: 'hdy' });
console.log(Object.isExtensible(obj)); // false
1
2
const obj = Object.seal({ name: 'hdy' });
console.log(Object.isExtensible(obj)); // false
1
2

# 其他属性

# assign

  • 作用:将多个对象的可枚举属性集成到第一个对象上,返回第一个对象的引用
  • 使用:Object.assign(obj1, obj2[, obj3...])
  • 入参:Object, Object[, Object]
  • 返回:Object
  • tip:浅拷贝
  • tip:相同属性以后来的为准
  • tip:不支持es6就没有的Symbols

# fromEntries

  • 作用:将键值对转化成对象,类似于entries反向操作
  • 调用:Object.fromEntries(entries)
  • 入参:Iterator
  • 返回:Object

# is

  • 作用:比较两个参数是否相等
  • 调用:Object.is(arg1, arg2)
  • 入参:any, any
  • 返回:Boolean
const obj1 = {};
const obj2 = {};
console.log(Object.is(obj1, obj2)); // false
console.log(Object.is(+0, -0)); // false
console.log(Object.is(NaN, NaN)); // true
1
2
3
4
5

# toString

  • 作用:检测对象类型
  • 调用:obj.toString()
  • 返回:[ object type ]
  • tip:只是最基础的toString,继承类也可以自定义自己的toString方法

# length

  • 值为1

# constructor

  • 原型对象上的属性,指向构造函数
  • 每个对象都能够通过原型链找到构造函数的原型对象,然后原型对象上就有对应的constructor属性,所以每个对象都能拿到自己的构造函数
上次更新: 9/17/2024