对象、symbol

对象的扩展
1.属性的简洁表示

ES6允许在大括号里直接写入变量和函数,作为对象的属性和方法。在打印对象时也很有用。但简洁写法不能用于构造函数。

const o = {
  method() {
    return "Hello!";
  }
};

const baz = {foo: 'bar'};

let user = {
  name: 'test'
};
let foo = {
  bar: 'baz'
};
console.log(user, foo)
// {name: "test"} {bar: "baz"}
console.log({user, foo})
// {user: {name: "test"}, foo: {bar: "baz"}}

const obj = {
	f() {
		this.foo = 'bar';
		}
};
new obj.f() //报错
2.属性名表达式

ES6允许表达式作为对象的属性名,即把表达式放到方括号里,但不能与简洁表达式法同时使用。如果属性名表达式是一个对象,默认会将对象转为字符串[object Object]。

let obj = {
	[foo]:true;
	['a'+'bc']:123;
};
const 	keyB={b:2};
const myObejct = {
	[keyB]:'valueB';
};
myObject //Object {[object Object]:"valueB"}
3.方法的name属性

和函数的name属性一样,对象方法也是函数,也有name属性。

const person = {
  sayName() {
    console.log('hello!');
  },
};
person.sayName.name   // "sayName"

特殊情况:
(1)如果对象的方法使用了取值函数(getter)和存值函数(setter),返回值是方法名前加上get和set。
(2)bind方法创造的函数,name属性返回bound加上原函数的名字。(3)Function构造函数创造的函数,name属性返回anonymous。
(4)如果对象的方法是一个 Symbol 值,那么name属性返回的是这个 Symbol 值的描述。

4.属性的可枚举性和遍历

(1)可枚举性
对象的每个属性都有一个描述对象,用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。

let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
//  {
//    value: 123,
//    writable: true,
//    enumerable: true,   “可枚举性”
//    configurable: true
//  }

如果该属性为false,就表示某些操作会忽视当前属性。
目前,有四个操作会忽略enumerable为false的属性。
a.for…in循环:只遍历对象自身的和继承的可枚举的属性。
b.Object.keys():返回对象自身的所有可枚举的属性的键名。
c.JSON.stringify():只串行化对象自身的可枚举的属性。
d.Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。
(2)遍历(共有5种方法)
a.for…in
b.Object.keys(obj)
c.Object.getOwnPropertyNames(obj)
d.Object.getOwnPropertySymbols(obj)
e.Reflect.ownKeys(obj)

5.super关键字

this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象,只能用在对象的方法中。

const proto = {
  foo: 'hello'
};
const obj = {
  foo: 'world',
  find() {
    return super.foo;
  }
};
Object.setPrototypeOf(obj, proto);
obj.find() // "hello"
const proto = {
  x: 'hello',
  foo() {
    console.log(this.x);
  },
};
const obj = {
  x: 'world',
  foo() {
    super.foo();
  }
}
Object.setPrototypeOf(obj, proto);
obj.foo() // "world"

super.foo指向原型对象proto的foo方法,但绑定的this却还是当前对象obj,因此输出world。

6.对象的扩展运算符

(1)结构赋值
对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。

let {x,...y} = {x:1,a:2,b:3};
x//1
y//{a:2,b:3}

如果等号右边是undefined和null,会报错。
结构赋值必须是最后一个参数,否则报错。
注意:如果一个键的值是复合类型的值,那么拷贝的是这个值的引用,不是副本,修改这个值,引用会改变。
扩展运算的结构赋值,不能复制继承自原型对象的属性。

const o = Object.create({ x: 1, y: 2 });
o.z = 3;
let { x, ...newObj } = o;
let { y, z } = newObj;
x // 1
y // undefined
z // 3

结构赋值可扩展某个函数的参数,引入其他操作。
(2)扩展运算符
扩展运算符后必须是一个变量

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

因为数组是特殊的对象,所以对象的扩展运算符也可用于数组。

let foo = { ...['a', 'b', 'c'] };
foo
// {0: "a", 1: "b", 2: "c"}

如果扩展运算符后面不是对象,则将自动转为对象(空对象),但字符串除外,它会自动转为一个类似数组的对象。

{...'hello'}
// {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}

对象的扩展运算符等同于使用Object.assign()方法,也可用于合并两个对象。
如果用户自定义的属性放在扩展运算符的后面,则扩展运算符内部的同名属性会被覆盖,可以用来修改部分属性。

let newVersion = {
  ...previousVersion,
  name: 'New Name' // Override the name property
};
7.链判断运算符(?.)

直接在链式调用的时候判断,左侧的对象是否为null或undefined。如果是的,就不再往下运算,而是返回undefined。对于可能没有实现的方法,这个运算符尤其有用。

if (myForm.checkValidity?.() === false) {     
	//老式浏览器的表单可能没有checkValidity这个方法
  	// 表单校验失败
  return;
}

链判断运算符有三种用法。
(1)obj?.prop // 对象属性
(2)obj?.[expr] // 同上
(3)func?.(…args) // 函数或对象方法的调用
注意点:
(1)短路机制
链判断运算符一旦为真,右侧的表达式就不再求值。
(2)delete运算符
(3)括号的影响
如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。
(4)报错场合

// 构造函数
new a?.()
new a?.b()
// 链判断运算符的右侧有模板字符串
a?.`{b}`
a?.b`{c}`
// 链判断运算符的左侧是 super
super?.()
super?.foo
// 链运算符用于赋值运算符左侧
a?.b = c

(5)右侧不得为十进制数

8.Null判断运算符(??)

它的行为类似||,但是只有运算符左侧的值为null或undefined时,才会返回右侧的值。与&&和||运算时必须用括号表明优先级
(1)就是跟链判断运算符?.配合使用,为null或undefined的值设置默认值。

const animationDuration = response.settings?.animationDuration ?? 300;

上面代码中,如果response.settings是null或undefined,或者response.settings.animationDuration是null或undefined,就会返回默认值300。也就是说,这一行代码包括了两级属性的判断。
(2)适合判断函数参数是否赋值

对象的新增方法
1.Object.is()

它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。不同之处是+0不等于-0,二是NaN等于NaN。

2.Object.assign()

(1)基本用法
用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象,第一个参数是目标对象,后面的参数都是源对象。
如果目标对象与源对象有同名属性,后面的会覆盖前面的属性。
如果参数不是对象,会先转为对象,然后返回。但undefined和null无法转为对象,所以会报错。
(2)注意点
a.浅拷贝:拷贝得到对象的引用
b.同名属性的替换
c.数组的处理:会把数组视为对象
d.取值函数的处理:Object.assign()只能进行值的复制,如果要复制的值是一个取值函数,那么将求值后再复制。
(3)常见用途
a.为对象添加属性
b.为对象添加方法
c.克隆对象
d.合并多个对象
e.为属性指定默认值

3.Object.getOwnPropertyDescriptors()

返回指定对象所有自身属性(非继承属性)的描述对象。

const obj = {
  foo: 123,
  get bar() { return 'abc' }
};
Object.getOwnPropertyDescriptors(obj)
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }
4.__proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()

a.__proto__属性,用来读取或设置当前对象的原型对象。
b.Object.setPrototypeOf(),方法的作用与__proto__相同,用来设置一个对象的原型对象,返回参数对象本身。
c.Object.getPrototypeOf(),该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。

5.Object.keys(),Object.values(),Object.entries()

(1)Object.keys()。ES2017 引入了跟Object.keys配套的Object.values和Object.entries,作为遍历一个对象的补充手段,供for…of循环使用。

let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };
for (let key of keys(obj)) {
  console.log(key); // 'a', 'b', 'c'
}
for (let value of values(obj)) {
  console.log(value); // 1, 2, 3
}
for (let [key, value] of entries(obj)) {
  console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}

(2)Object.values()
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值。
(3)Object.entries()
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组。

6.Object.fromEntries()

Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。该方法的一个用处是配合URLSearchParams对象,将查询字符串转为对象。

Object.fromEntries([
  ['foo', 'bar'],
  ['baz', 42]
])
// { foo: "bar", baz: 42 }

Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
// { foo: "bar", baz: "qux" }
symbol
1.概述

表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。
Symbol 值不能与其他类型的值进行运算,会报错。但可转为字符串和布尔值。

let s1 = Symbol('foo');
let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"
2.Symbol.prototype.description

创建 Symbol 的时候,可以添加一个描述。

const sym = Symbol('foo');

ES2019 提供了一个实例属性description,直接返回 Symbol 的描述。

const sym = Symbol('foo');
sym.description // "foo"
3.作为属性名的 Symbol

由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。

let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"

Symbol 值作为对象属性名时,不能用点运算符。因为点运算符后面总是字符串,所以不会读取mySymbol作为标识名所指代的那个值,导致a的属性名实际上是一个字符串,而不是一个 Symbol 值。Symbol 值必须放在方括号之中。

const mySymbol = Symbol();
const a = {};
a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"
4.属性名的遍历

有一个Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

const obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';
const objectSymbols = Object.getOwnPropertySymbols(obj);
objectSymbols
// [Symbol(a), Symbol(b)]

Reflect.ownKeys()方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

let obj = {
  [Symbol('my_key')]: 1,
  enum: 2,
  nonEnum: 3
};

Reflect.ownKeys(obj)
//  ["enum", "nonEnum", Symbol(my_key)]
5.Symbol.for(),Symbol.keyFor()

(1)使用同一个 Symbol 值,Symbol.for()方法可以做到,它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。

let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2 // true

Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key。

5.内置的 Symbol 值

除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。
(1)Symbol.hasInstance,当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。
(2)Symbol.isConcatSpreadable,对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。
(3)Symbol.species,指向一个构造函数。创建衍生对象时,会使用该属性。
(4)Symbol.match,当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。
(5)Symbol.replace,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值。
(6)Symbol.search,当该对象被String.prototype.search方法调用时,会返回该方法的返回值。
(7)Symbol.split ,当该对象被String.prototype.split方法调用时,会返回该方法的返回值。
(8)Symbol.iterator ,指向该对象的默认遍历器方法。
(9)Symbol.toPrimitive,该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
(10)Symbol.toStringTag,在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。
(11)Symbol.unscopables,该对象指定了使用with关键字时,哪些属性会被with环境排除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值