原型、原型访问方式、原型操作方法
访问方式
不建议使用 __proto__
访问原型,原因如下:
语义化不好,是浏览器的内部属性。
访问效率低。
所有继承自该原型的对象都会受到影响。
function Person() {
this.name = 'z3';
this.age = '18';
}
const person = new Person();
console.log(person.__proto__);
三种操作原型的方法
// 修改原型
Object.setPrototypeOf(obj, prototype);
// 读取原型
Object.getPrototypeOf(obj);
// 创建原型对象
Object.create(proto, propertiesObject);
Object.setPrototypeOf()
Object.setPrototypeOf
用于修改对象的原型,返回被修改的对象。
const proto = {
y: 20,
z: 40,
};
const obj = {
x: 10,
};
const updatedObj = Object.setPrototypeOf(obj, proto);
console.log(updatedObj === obj, updatedObj);
// 输出: true { x: 10, y: 20, z: 40 }
原始值设置原型
将原始值的原型设置后,访问的是对应的包装类原型。
const obj2 = Object.setPrototypeOf(1, {
c: 3,
});
console.log(Object.getPrototypeOf(obj2)); // Number.prototype
const a = 1;
console.log(Object.getPrototypeOf(a)); // Number.prototype
console.log(Number.prototype === Object.getPrototypeOf(obj2)); // true
Object.getPrototypeOf()
Object.getPrototypeOf
用于获取对象的原型。
const proto = {
c: 3,
};
const obj = Object.create(proto);
console.log(Object.getPrototypeOf(obj)); // { c: 3 }
Object.keys()
Object.keys
遍历对象自身可枚举属性的键名,不包含继承属性和 Symbol
类型的键。
const foo = {
a: 1,
b: 2,
c: 3,
};
Object.defineProperties(foo, {
d: {
value: 4,
enumerable: false,
},
f: {
value: 5,
enumerable: true,
},
});
// ['a', 'b', 'c', 'f']
console.log(Object.keys(foo));
Object.values()
Object.values
遍历对象自身可枚举属性的值,不包含继承属性和 Symbol
类型的值。
const foo = {
a: 1,
b: 2,
c: 3,
};
Object.defineProperties(foo, {
d: {
value: 4,
enumerable: false,
},
f: {
value: 5,
enumerable: true,
},
});
// [1, 2, 3, 5]
console.log(Object.values(foo));
Object.entries()
Object.entries
返回对象自身可枚举属性的键值对数组,不包含继承属性。
const foo = {
a: 1,
b: 2,
c: 3,
};
Object.defineProperties(foo, {
d: {
value: 4,
enumerable: false,
},
f: {
value: 5,
enumerable: true,
},
});
console.log(Object.entries(foo));
// [['a', 1], ['b', 2], ['c', 3], ['f', 5]]
const str = '134';
console.log(Object.entries(str));
// [['0', '1'], ['1', '3'], ['2', '4']]
super
super
关键字用于访问和调用对象的父对象上的函数。
const proto = {
x: 20,
y: 30,
};
const obj = {
x: 10,
foo() {
// 对象的方法简写语法才能生效
console.log(super.y);
},
};
Object.setPrototypeOf(obj, proto);
console.log(obj);
obj.foo(); // 30
Symbol
ES6
引入 Symbol
主要是为了防止对象属性名的冲突。
Symbol
属于原始类型,与 string
、number
、null
、undefined
、boolean
同属一类。
Symbol()
不是构造函数,是普通函数,每次调用都会生成一个唯一的值。
const symA = Symbol();
const symB = Symbol();
console.log(symA === symB); // false
console.log(typeof symA); // symbol
Symbol() 只接受字符串
Symbol
构造函数可以接受一个描述字符串,用于标识 Symbol
。
const obj = { a: 1 };
const sym = Symbol(obj); // 使用 Object.prototype.toString
console.log(sym); // Symbol([object Object])
Symbol 类型转换
const sym = Symbol('desc');
console.log(sym.toString()); // 'Symbol(desc)'
console.log(!sym); // false
console.log(String(sym)); // 'Symbol(desc)'
console.log(Boolean(sym)); // true
// Number(sym) 会抛出错误
try {
console.log(Number(sym));
} catch (error) {
console.error(error); // TypeError: Cannot convert a Symbol value to a number
}
使用
Symbol
作为属性名,确保属性的唯一性。
const name = Symbol();
const person = {};
person[name] = 'zhangsan';
console.log(person); // { [Symbol()]: 'zhangsan' }
const name = Symbol();
const person = {
[name]: 'zhangsan',
};
console.log(person); // { [Symbol()]: 'zhangsan' }
const person = {};
const name = Symbol();
Object.defineProperty(person, name, {
value: 'z3',
});
console.log(person); // { [Symbol()]: 'z3' }
定义方法
使用 Symbol
定义方法,防止方法名冲突。
const name = Symbol();
const eat = Symbol();
const person = {
[name]: 'zhangsan',
[eat]() {
console.log(this[name]);
},
};
person[eat](); // 'zhangsan'
访问
通过 Symbol
访问属性。
const person = {};
const name = Symbol();
Object.defineProperty(person, name, {
value: 'z3',
});
console.log(person[name]); // 'z3'
Symbol 不是构造函数
Symbol
不能被用作构造函数,尝试使用 new
会抛出错误。
try {
const sym = new Symbol();
} catch (error) {
console.error(error); // TypeError: Symbol is not a constructor
}
Symbol.for()
Symbol.for
在全局注册 Symbol
,相同的键会返回相同的 Symbol
。
const sym1 = Symbol.for('foo');
const sym2 = Symbol.for('foo');
console.log(sym1 === sym2); // true
Symbol.keyFor()
Symbol.keyFor
获取全局注册的 Symbol
的键。
const sym = Symbol.for('foo');
const key1 = Symbol.keyFor(sym);
const key2 = Symbol.keyFor(sym);
console.log(key1, key2); // 'foo' 'foo'
遍历
for...in
和 for...of
不能遍历 Symbol
类型的属性。
const obj = {};
const symA = Symbol('a');
const symB = Symbol('b');
obj[symA] = 'hello1';
obj[symB] = 'hello2';
obj.c = 1;
for (const key in obj) {
console.log(key); // 'c'
}
try {
for (const value of obj) {
console.log(value);
}
} catch (error) {
console.error(error); // TypeError: obj is not iterable
}
Object.assign()
Object.assign
可以复制包含 Symbol
属性的对象。
const obj = {};
const symA = Symbol('a');
const symB = Symbol('b');
obj[symA] = 'hello1';
obj[symB] = 'hello2';
obj.c = 1;
const obj1 = {};
Object.assign(obj1, obj);
console.log(obj1);
// { c: 1, [Symbol(a)]: 'hello1', [Symbol(b)]: 'hello2' }
针对遍历 Symbol 属性的对象
使用 Object.getOwnPropertySymbols
获取对象的 Symbol
属性。
const obj = {};
const symA = Symbol('a');
const symB = Symbol('b');
obj[symA] = 'hello1';
obj[symB] = 'hello2';
obj.c = 1;
const symbols = Object.getOwnPropertySymbols(obj);
console.log(symbols); // [ Symbol(a), Symbol(b) ]
例子
const obj = {
c: 1,
d: 2,
};
const symA = Symbol('a');
const symB = Symbol('b');
const sym_a = Symbol('_a');
const sym_b = Symbol('_b');
obj[symA] = 'hello1';
obj[symB] = 'hello2';
Object.defineProperties(obj, {
e: {
value: 5,
enumerable: true,
},
f: {
value: 6,
enumerable: false,
},
[sym_a]: {
value: -1,
enumerable: true,
},
[sym_b]: {
value: -2,
enumerable: false,
},
});
const symH = Symbol('h');
const symI = Symbol('i');
const symJ = Symbol('j');
const obj1 = {
g: 7,
[symH]: 8,
};
Object.defineProperties(obj1, {
[symI]: {
value: 9,
enumerable: true,
},
[symJ]: {
value: 10,
},
k: {
value: 11,
},
});
// 设置原型
Object.setPrototypeOf(obj, obj1);
console.log(obj);
// for...in 遍历自身和继承的可枚举属性,不包含 Symbol 类型的值
for (const key in obj) {
console.log(key); // 'c', 'd', 'e', 'g', 'k'
}
// Object.keys 只遍历自身且不包含 Symbol 类型的值
console.log(Object.keys(obj)); // ['c', 'd', 'e']
// 获取自身的 Symbol 类型的属性
console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(a), Symbol(b), Symbol(_a), Symbol(_b) ]