对象
__proto__
属性
let obj = {};
obj.__proto__ = 5; // 分配一个数字
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
```javascript
let john = { name: "John" };
let jo = { age: 23 };
let visitsCountObj = {}; // 尝试使用对象
visitsCountObj[john] = 123; // 尝试将 john 对象作为键
visitsCountObj[jo] = "dd";
console.log(visitsCountObj[john]);
// 是写成了这样!
alert( visitsCountObj["[object Object]"] );
原型、继承
设置 原型的限制:
- 引用不能形成闭环。如果我们试图在一个闭环中分配proto,JavaScript 会抛出错误。
- proto的值可以是对象,也可以是null。而其他的类型都会被忽略。
for … in 循环
for..in循环也会迭代继承的属性
let animal = {
eats: true
};
let rabbit = {
jumps: true,
__proto__: animal
};
// Object.keys 只返回自己的 key
alert(Object.keys(rabbit)); // jumps
// for..in 会遍历自己以及继承的键
for(let prop in rabbit) alert(prop); // jumps,然后是 eats
使用 obj.hasOwnProperty(key) 过滤掉继承的属性
let animal = {
eats: true
};
let rabbit = {
jumps: true,
__proto__: animal
};
for(let prop in rabbit) {
let isOwn = rabbit.hasOwnProperty(prop);
if (isOwn) {
alert(`Our: ${prop}`); // Our: jumps
} else {
alert(`Inherited: ${prop}`); // Inherited: eats
}
}
F.prototype
默认的 ‘prototype’ 是一个只有属性的 constructor 的对象,属性 constructor 指向函数自身
function Rabbit() {}
// by default:
// Rabbit.prototype = { constructor: Rabbit }
alert( Rabbit.prototype.constructor == Rabbit ); // true
如果F.prototype是一个对象,那么new操作符会使用它为新对象设置[[Prototype]]
let animal = {
eats: true
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal;
let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal
alert( rabbit.eats ); // true
类
class User {…}构造实际上做了如下的事儿:
- 创建一个名为User的函数,该函数成为类声明的结果。该函数的代码来自于constructor方法(如果我们不编写这种方法,那么它就被假定为空)。
- 存储类中的方法,例如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
![微信截图_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)
```typescript
// 用纯函数重写 class User
// 1. 创建构造器函数
function User(name) {
this.name = name;
}
// 函数的原型(prototype)默认具有 "constructor" 属性,
// 所以,我们不需要创建它
// 2. 将方法添加到原型
User.prototype.sayHi = function() {
alert(this.name);
};
// 用法:
let user = new User("John");
user.sayHi();
私有的和受保护的属性和方法
通过设置 get 和 set
class CoffeeMachine {
_waterAmount = 0;
set waterAmount(value) {
if (value < 0) throw new Error("Negative water");
this._waterAmount = value;
}
get waterAmount() {
return this._waterAmount;
}
constructor(power) {
this._power = power;
}
}
// 创建咖啡机
let coffeeMachine = new CoffeeMachine(100);
// 加水
coffeeMachine.waterAmount = -10; // Error: Negative water