跳到主要内容

原型、原型访问方式、原型操作方法

访问方式

不建议使用 __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 属于原始类型,与 stringnumbernullundefinedboolean 同属一类。

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...infor...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) ]