对象

__proto__ 属性

  1. let obj = {};
  2. obj.__proto__ = 5; // 分配一个数字
  3. alert(obj.__proto__); // [object Object] — 值为对象,与预期结果不同

对象和 Map 的差异:

  • Map 可以使用对象作为键
  • 迭代的顺序与插入值得顺序相同,与普通的Object不同,Map保留了此顺序 ```javascript let john = { name: “John” };

// 存储每个用户的来访次数 let visitsCountMap = new Map();

// john 是 Map 中的键 visitsCountMap.set(john, 123);

alert( visitsCountMap.get(john) ); // 123

  1. ```javascript
  2. let john = { name: "John" };
  3. let jo = { age: 23 };
  4. let visitsCountObj = {}; // 尝试使用对象
  5. visitsCountObj[john] = 123; // 尝试将 john 对象作为键
  6. visitsCountObj[jo] = "dd";
  7. console.log(visitsCountObj[john]);
  8. // 是写成了这样!
  9. alert( visitsCountObj["[object Object]"] );

原型、继承

设置 原型的限制:

  1. 引用不能形成闭环。如果我们试图在一个闭环中分配proto,JavaScript 会抛出错误。
  2. proto的值可以是对象,也可以是null。而其他的类型都会被忽略。

    for … in 循环

    for..in循环也会迭代继承的属性

  1. let animal = {
  2. eats: true
  3. };
  4. let rabbit = {
  5. jumps: true,
  6. __proto__: animal
  7. };
  8. // Object.keys 只返回自己的 key
  9. alert(Object.keys(rabbit)); // jumps
  10. // for..in 会遍历自己以及继承的键
  11. for(let prop in rabbit) alert(prop); // jumps,然后是 eats

使用 obj.hasOwnProperty(key) 过滤掉继承的属性

  1. let animal = {
  2. eats: true
  3. };
  4. let rabbit = {
  5. jumps: true,
  6. __proto__: animal
  7. };
  8. for(let prop in rabbit) {
  9. let isOwn = rabbit.hasOwnProperty(prop);
  10. if (isOwn) {
  11. alert(`Our: ${prop}`); // Our: jumps
  12. } else {
  13. alert(`Inherited: ${prop}`); // Inherited: eats
  14. }
  15. }

F.prototype

默认的 ‘prototype’ 是一个只有属性的 constructor 的对象,属性 constructor 指向函数自身

  1. function Rabbit() {}
  2. // by default:
  3. // Rabbit.prototype = { constructor: Rabbit }
  4. alert( Rabbit.prototype.constructor == Rabbit ); // true

如果F.prototype是一个对象,那么new操作符会使用它为新对象设置[[Prototype]]

  1. let animal = {
  2. eats: true
  3. };
  4. function Rabbit(name) {
  5. this.name = name;
  6. }
  7. Rabbit.prototype = animal;
  8. let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal
  9. alert( rabbit.eats ); // true

class User {…}构造实际上做了如下的事儿:

  1. 创建一个名为User的函数,该函数成为类声明的结果。该函数的代码来自于constructor方法(如果我们不编写这种方法,那么它就被假定为空)。
  2. 存储类中的方法,例如User.prototype中的sayHi。 ```typescript class User { constructor(name) { this.name = name; } sayHi() { alert(this.name); } }

// class 是一个函数 alert(typeof User); // function

// …或者,更确切地说,是 constructor 方法 alert(User === User.prototype.constructor); // true

// 方法在 User.prototype 中,例如: alert(User.prototype.sayHi); // alert(this.name);

// 在原型中实际上有两个方法 alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi

  1. ![微信截图_20210523132214.png](https://cdn.nlark.com/yuque/0/2021/png/651859/1621747376092-a645acc4-858a-4444-88c2-d1b943932329.png#clientId=u6fb0a92c-2c7c-4&from=ui&id=u1880c9e6&margin=%5Bobject%20Object%5D&name=%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20210523132214.png&originHeight=131&originWidth=548&originalType=binary&size=6981&status=done&style=none&taskId=ud32dcd55-5c42-4109-a182-2e28064a33f)
  2. ```typescript
  3. // 用纯函数重写 class User
  4. // 1. 创建构造器函数
  5. function User(name) {
  6. this.name = name;
  7. }
  8. // 函数的原型(prototype)默认具有 "constructor" 属性,
  9. // 所以,我们不需要创建它
  10. // 2. 将方法添加到原型
  11. User.prototype.sayHi = function() {
  12. alert(this.name);
  13. };
  14. // 用法:
  15. let user = new User("John");
  16. user.sayHi();

私有的和受保护的属性和方法

通过设置 get 和 set

  1. class CoffeeMachine {
  2. _waterAmount = 0;
  3. set waterAmount(value) {
  4. if (value < 0) throw new Error("Negative water");
  5. this._waterAmount = value;
  6. }
  7. get waterAmount() {
  8. return this._waterAmount;
  9. }
  10. constructor(power) {
  11. this._power = power;
  12. }
  13. }
  14. // 创建咖啡机
  15. let coffeeMachine = new CoffeeMachine(100);
  16. // 加水
  17. coffeeMachine.waterAmount = -10; // Error: Negative water

参考资料

  1. 现代 JavaScript 教程