对象

创建对象

对象是一组属性的集合,无序的数据结构,类似于key-value
创建对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 方式一
let person = new Object()
person.name = 'lucy'
person.age = 20
person.sayName = function() {
console.log(this.name)
}
// 方式二:
let person = {
name: 'lucy',
age: 20,
sayName() {
console.log(this.name)
}
}
// 方式三:
Object.defineProperty(obj, prop, options)

Object.defineProperty(obj, prop, options)

参数:

  • obj: 对象
  • prop: 属性
  • options: 选项配置
    • writable: true/false 是否可修改
    • value:属性的值,默认undefined
    • enumable: 是否可枚举,for…in遍历
    • configurable: 属性是否可删除;是否可配置其他配置项
    • get: 读取属性时的前置调用
    • set:设置属性时的前置调用

Object.getOwnPropertyDescriptor返回对象的属性描述

Object.assign(target, sourceObj, …sourceObj)合并对象

接收参数为一个目标对象和一个或多个源对象作为参数,只复制源对象的可枚举属性和自由属性。先使用符合条件的源对象的属性的get访问器获取属性的值,再用目标对象的set方法设置属性的值。
注意:Object.assign()是浅拷贝,异常会终止并提前退出,返回一个尽量部分完成的版本。

Object.is() 静态方法确定两个值是否为相同值。

语法: Object.is(value1, value2)
返回值:指示两个参数是否相同的值
判断两个值是否相同值,如果以下其中一项成立,则两个值相同:

  • 都是undefiend
  • 都是null
  • 都是true或者都是false
  • 都是长度相同、字符形同、顺序相同的字符串
  • 都是相同的对象(意味着两个值引用了内存中同一个对象)
  • 都是BigInt且具有相同的数值
  • 都是Symbol且引用相同的symbol值
  • 都是数字,且:
    • 都是+0
    • 都是-0
    • 都是NaN
    • 都有相同的值,非零且都不是NaN

Object.is() 和 === 之间的唯一区别在于它们处理带符号的 0 和 NaN 值的时候。=== 运算符(和 == 运算符)将数值 -0 和 +0 视为相等,但是会将 NaN 视为彼此不相等。

1
2
3
4
5
6
7
8
-0 === 0
// true
NaN === NaN
// false
Object.is(-0, 0)
// false
Object.is(NaN, NaN)
// true

为什么vue的data是函数形式(data() {return {} }),methods是对象(methods: {})?

组件是可复用的,如果data是一个对象,则所有组件实例共享同一个对象,造成数据污染;调用data函数返回一个的新的独立对象,如果可以保证组件是唯一的也可以不写函数直接写对象。

methods装的是函数方法,只需要调用不需要改变,所以多个组件实例共享同一个函数没有影响,反而只需要更少的空间,和把公有方法放原型链上相似。

原型

每个对象(object)都有一个私有属性指向另一个名为原型(prototype)的对象。原型对象也有一个自己的原型,层层向上直到一个对象的原型为 null。根据定义,null 没有原型,并作为这个原型链(prototype chain)中的最后一个环节。

JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

所有引用类型都有__proto__隐式原型属性,是一个对象
所有函数都有一个prototype原型属性,是一个对象
所有引用类型的__proto__都指向它构造函数的prototype

工厂模式:批量创建结构类似的对象

1
2
3
4
5
6
7
8
9
10
function createPerson(name, age, job) {
let o = new Object()
o.name = name
o.age = age
o.job = job
o.sayName = function() {
console.log(this.name)
}
return o
}

构造函数模式:

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = function() {
console.log(this.name)
}
}
let person1 = new Person('lucy', 20, 'software engineer')
let person2 = new Person('greg', 20, 'Doctor')
person1.sayName()
person2.sayName()

构造函数需要使用new关键字

new 关键字会进行如下操作:

  • 创建一个空的简单js对象(即{});
  • 为新创建的对象添加属性proto,将该属性链接到构造函数的原型对象;
  • 将新创建的对象作为this的上下文;
  • 如果该函数没有返回对象,则返回this;

每一个实例的公共方法和属性都是再new语法和构造过程中创建的

每个构造函数都有一个原型属性,所有该构造函数创建的实例都共享这个构造函数的原型属性。
当访问对象的一个属性时,会先在这个对象的属性中查找,如果没有找到就去它的proto__隐式原型中查找,即它的构造函数的prototype,如果还没有找到就去构造函数的prototype.__proto__中查找,这样一层一层的向上查找形成的链式结构叫原型链。一直往上层查找,直到null还没有找到,返回undefined
Object.prototype.__proto
=== null

constructor属性

除了null原型对象外,任何对象都会再其[[prototype]]上有一个constructor属性。使用字面量创建的对象也会有一个指向该对象构造函数类型的 constructor 属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const o1 = {};
o1.constructor === Object; // true

const o2 = new Object();
o2.constructor === Object; // true

const a1 = [];
a1.constructor === Array; // true

const a2 = new Array();
a2.constructor === Array; // true

const n = 3;
n.constructor === Number; // true

constructor 属性通常来自构造函数的 prototype 属性

1
2
3
4
5
function Fn() {}
Fn.prototype = new Array()
let f = new Fn()
f.constructor === Array
// true
1
2
3
4
5
6
7
let obj = {}
obj.__proto__ === Object.prototype
// true
obj.constructor === Object
// true
obj.__proto__ === obj.constructor.prototype
// true

判断对象自有属性还是原型属性

  1. Object.hasOwnProperty(‘property’)方法返回一个布尔值,表示对象自有属性(而不是继承来的属性)中是否具有指定的属性。
  2. for…in 语句以任意顺序迭代一个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性。
  3. Object.keys()静态方法返回一个由给定对象自身的可枚举的字符串键属性名组成的数组(不包含Symbol)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var triangle = {a: 1, b: 2, c: 3};

function ColoredTriangle() {
this.color = 'red';
}

ColoredTriangle.prototype = triangle;

var obj = new ColoredTriangle();

for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
console.log(`obj.${prop} = ${obj[prop]}`);
}
}

// Output:
// "obj.color = red"

for (var prop in obj) {
console.log(`obj.${prop} = ${obj[prop]}`);
}
// Output:
// obj.color = red
// obj.a = 1
// obj.b = 2
// obj.c = 3
console.log(Object.keys(triangle))
//  ['a', 'b', 'c']

键的顺序:

1
2
3
4
5
6
7
8
9
10
// 键的顺序随机的类数组对象
const anObj = { 100: "a", 2: "b", 7: "c" };
console.log(Object.keys(anObj)); // ['2', '7', '100']
// (3) ['2', '7', '100']
for(let i in anObj) {
console.log(i)
}
// 2
// 7
// 100

先数字字符串按生序再字母字符串按添加顺序再Symbol键名

Object.keys()和for…in枚举顺序是不固定的

Object.keys()获取自身可枚举属性所有键的数组(不包含Symbol健名)
Object.values()获取自身可枚举属性所有值的数组(不包含Symbol健值)
Object.entries()获取自身可枚举属性所有键值对的数组(不包含Symbol健值对)