<!-- 整数数组,只有一个数字出现一次,其余数字都出现两次,如何找到这个数字 -->
<script>
// 哈希表
function findSingleNumber(nums) {
let count = {};
// 遍历 并记录次数 for of获取值
for (let num of nums) {
if (count[num]) {
// 键的值加1
count[num]++
} else {
count[num] = 1;
}
}
// zhao for in 获取键名 这块用for of会报错
for (let num in count) {
if (count[num] === 1) {
return parseInt(num);
}
}
}
const nums = [4, 2, 4, 2, 3, 1, 3];
console.log(findSingleNumber(nums))
// 为啥第一次用for of 第二次用for in 以及这个count【numb】拿的是索引值嘛?
// 当使用 for...in 循环遍历对象时,循环变量获取的是对象的键名(即属性名)。数组的索引 对象的属性
// for of 是数组的元素值字符串的值 只能遍历有可迭代接口的对象
for of值; for in 是键
for in的话拿到的是自身及原型链上的key
-
例1:遍历Object{}对象时
const obj = { a: 1, b: 2, c: 3 } for (let i in obj) { console.log(i) // a // b // c } for (let i of obj) { console.log(i) // Uncaught TypeError: obj is not iterable 报错了 // 一个数据结构只要部署了 Symbol.iterator 属性, 就被视为具有 iterator接口, 就可以使用 for //of循环。没有 Symbol.iterator这个属性,所以使用 for of会报 obj is not iterable }
-
例2: 遍历数组时
const arr = 'abc'//['a','b','c'] // for in 循环 for (let i in arr) { console.log(i) // 0 // 1 // 2 } // for of for (let i of arr) { console.log(i) // a // b // c }
总结
for in
-
遍历时候:对象就是key,数组就是下标,字符串就是引用地址。
-
for ... in 循环返回的值都是数据结构的 键值名。 遍历对象返回的对象的key,遍历数组返回的数组的下标(key)。 for ... in 循环不仅可以遍历数字键名,还会遍历原型上的值和手动添加的其他键。
for of
-
遍历时候:对象就是键值value,数组就是数组的值,字符串就是字符。
-
for of 循环用来获取一对键值对中的值,而 for in 获取的是 键名。贱人
补充:for of 不同与 forEach, 它可以与 break、continue和return 配合使用,也就是说 for of 循环可以随时退出循环。
在函数中使用 for...of 循环时,可以使用 return 语句返回某个值并退出函数
function findFirstPositive(nums) {
for (let num of nums) {
if (num > 0) {
return num; // 返回第一个正数并退出函数
}
}
}
=========
function logPositiveNumbers(nums) {
nums.forEach(num => {
if (num <= 0) {
return; // 这里的 return 仅退出当前的回调函数执行,不影响 forEach 循环的继续执行
}
console.log(num);
});
}
举这些是一些常见的例子,表明了具有可迭代接口的对象和不具有可迭代接口的对象之间的区别。具有可迭代接口的对象可以使用 `for...of` 循环来遍历,而不具有可迭代接口的对象则不能直接使用 `for...of` 循环来遍历。
可迭代接口的对象:
1. **数组(Array):** 数组是 JavaScript 中最常见的可迭代对象。例如:
const myArray = [1, 2, 3];
2. **字符串(String):** 字符串也是 JavaScript 中的可迭代对象,每个字符都可以被迭代。例如:
const myString = "Hello";
3. **Map:** Map 对象是一种以键值对存储数据的集合,也是可迭代对象。例如:
const myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);
```
4. **Set:** Set 对象是一种存储不重复值的集合,同样是可迭代对象。例如:
const mySet = new Set([1, 2, 3]);
```
### 不具有可迭代接口的对象:
1. **普通对象(Plain Object):** 普通对象通常不具有可迭代接口,因为它们并没有实现 `Symbol.iterator` 方法。例如:
const myObject = { a: 1, b: 2, c: 3 };
```
2. **函数(Function):** 函数对象也通常不具有可迭代接口。例如:
function myFunction() {
console.log('Hello');
}
```
3. **数字(Number):** 数字也不是可迭代对象,因为它们不是集合或者序列。例如:
const myNumber = 123;
```
补充:
for...in
循环用于遍历一个对象的所有可枚举属性,包括它继承的属性。如果你只想遍历对象自身的属性,而不包括从原型链上继承来的属性,你可以使用 Object.hasOwnProperty()
方法来判断当前属性是否为对象自身的属性。
Object.hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(不考虑原型链中的属性)。
### 注意
虽然 `Object.hasOwnProperty()` 是一种常见的检查对象自身属性的方法,但在使用时需要小心。如果对象可能会覆盖 `hasOwnProperty` 属性,或者你不确定对象是不是 `null`,可能需要使用 `Object.prototype.hasOwnProperty.call(myObject, key)` 来代替,以确保正确性和安全性:
### 示例
假设有一个对象 `myObject`,你想遍历这个对象的所有自身属性(不包括原型链上的属性):
```javascript
let myObject = {
property1: 'value1',
property2: 'value2'
};
// 假设 myObject 的原型有一个属性
Object.prototype.inheritedProperty = 'value3';
for (let key in myObject) {
if (myObject.hasOwnProperty(key)) {
console.log(key + ": " + myObject[key]);
// 输出:
// property1: value1
// property2: value2
}
}
```
在这个示例中,`for...in` 循环遍历 `myObject` 的所有可枚举属性,
包括原型链上的属性。通过在循环体内使用 `myObject.hasOwnProperty(key)` 检查,
我们确保只处理对象自身的属性,`inheritedProperty` 属性因为是从原型链继承的,所以不会被打印出来。
```javascript
for (let key in myObject) {
if (Object.prototype.hasOwnProperty.call(myObject, key)) {
console.log(key + ": " + myObject[key]);
// 安全地输出 myObject 自身的属性
}
}
```
这种方法避免了直接在对象上调用 `hasOwnProperty` 的潜在问题,
尤其是当对象可能没有继承自 `Object.prototype` 或者 `hasOwnProperty` 方法被覆盖时。
因为在JavaScript中,对象是可以自定义属性的,包括内置方法名如 `hasOwnProperty`。如果某个对象定义了自己的 `hasOwnProperty` 属性,那么这个属性可能会覆盖继承自 `Object.prototype` 的同名方法。这样,当你试图调用对象的 `hasOwnProperty()` 方法来检查是否存在某个属性时,实际上可能调用的是对象自定义的 `hasOwnProperty` 属性,而非原本期望的那个用于检测属性的方法。这可能导致预期之外的行为或错误。
另一方面,尝试在 `null` 或 `undefined` 上调用方法或访问属性会导致运行时错误。如果你有一个变量,但不确定它是否是 `null` 或 `undefined`(或任何其他值),直接调用这个变量的 `hasOwnProperty()` 方法可能会抛出异常,因为这些值并不是对象,也没有 `hasOwnProperty` 方法。
### 覆盖 `hasOwnProperty`
考虑下面的示例:
```javascript
let obj = {
hasOwnProperty: function() {
return false;
},
prop: 'hello'
};
console.log(obj.hasOwnProperty('prop')); // 这里会调用obj自定义的方法,返回 false
```
在这个示例中,`obj` 对象自定义了 `hasOwnProperty` 方法,
这意味着当你尝试检查 `obj` 对象是否有某个属性时,
实际上调用的是对象自身的 `hasOwnProperty` 方法,而不是 `Object.prototype` 上的方法。
### 避免这些问题
为了避免这些潜在问题,可以使用 `Object.prototype.hasOwnProperty.call()` 方法。
这种方式通过直接从 `Object.prototype` 调用 `hasOwnProperty` 方法,
并将要检查的对象作为第一个参数传递(使用 `call` 方法改变 `this` 的指向),
可以安全地检查对象是否拥有某个属性,即使对象覆盖了 `hasOwnProperty` 方法或者是 `null`/`undefined`。
```javascript
let obj = {
hasOwnProperty: function() {
return false;
},
prop: 'hello'
};
// 安全检查方式
console.log(Object.prototype.hasOwnProperty.call(obj, 'prop')); // true
```
这种方法能够确保即使在对象可能没有继承自 `Object.prototype` 或 `hasOwnProperty` 方法被覆盖的情况下,也能正确地检查属性。