可枚举属性
可枚举对象的一个定义特征是,当我们通过赋值运算符将属性赋值给对象时,我们将内部可枚举标志(enumerable)设置为 true。这是默认值。
但是,我们可以通过将其设置为 false 来更改此行为。
经验法则是,可枚举属性总是出现在 for...in 循环中。
可枚举属性和不可枚举属性是什么
所谓的可枚举就是可遍历的意思,也就是说对象的属性是否能够通过遍历得到。即通过for...in循环遍历到。
怎么判断属性是否可枚举
对象的属性是否具有可枚举属性是由enumerable值决定;
可以通过obj.propertyIsEnumerable(prop);来判断obj对象的prop属性是否能够枚举,该方法返回的是一个布尔值
哪些对象的属性是不可枚举的
js中基本包装类型的原型属性是不可枚举的,如Object, Array, Number
属性是否可枚举有什么用
引入可枚举性这个概念的最初目的是为了让某些属性可以规避掉for...in操作,不然内部属性和方法都会遍历到
那些方法可遍历到可枚举属性
- for...in;遍历自身和继承的可枚举属性;(for i in obj),console.log(i)//返回数组组成的键名
- Object.keys(obj);返回一个包括对象自身可枚举属性的健名的数组
Object.keys() (ES6对象的拓展)
用于获取对象自身所有的可枚举的属性值,但不包括原型中的属性,然后返回一个由属性名组成的数组。注意它同for..in一样不能保证属性按对象原来的顺序输出。
// 遍历数组
var colors = ['red', 'green', 'blue'];
colors.length = 10;
colors.push('yellow');
Array.prototype.demo = function () {};
Object.keys(colors); // ["0", "1", "2", "10"]
// 遍历对象
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.demo = function() {};
var jenemy = new Person('jenemy', 25);
Object.keys(jenemy); // ["name", "age"]
可迭代对象
如果一个对象定义了它的迭代行为,那么它是可迭代的。在本例中,将在 for...of 构造中循环的值将定义其迭代行为。可迭代的内置类型包括 Array、String、Set 和 Map、特殊类数组对象(arguments、dom节点等), 对象不可迭代,因为它没有指定 iterator 方法。可以为其对象添加迭代方法,对象才可迭代。有迭代行为才可使用for...of
可迭代对象的含义:
- 可迭代(Iterable) 对象是数组的泛化,几乎所有对象可以作为在for…of 循环中的对象。
- 可迭代对象必须有迭代器 iterator
- 迭代器中必须有next()方法
// 获取迭代对象
let arr=[1,2];
let i=arr.keys() //因为keys()返回的是数组,所以获取迭代器对象;
//由于set集合没有key,所以与values⽅法返回结果⼀致,set的keys与values键值一样
console.log(i)//Object [Array Iterator] {} 返回迭代器对象
// console.log(i.next())//{ value: 0, done: false }
// console.log(i.next())//{ value: 1, done: false }
// console.log(i.next())//{ value: undefined, done: true }
let item;
// 对迭代器 键 值的遍历,与使用for-in结果一样
while(!(item=i.next()).done){
// done为true时不执行
// console.log(item.value);//0 1
var key=item.value;
console.log(key,arr[key])//0 1 1 2
}
// Object.keys(obj);返回一个包括对象自身可枚举属性的健名的数组
let w=Object.keys(arr) //keys获取键
console.log(w)//[ '0', '1']
let obt={name:'we',age:13}
let w2=Object.keys(obt)
console.log(w2)//[ 'name', 'age' ]
let obj={name:'ww'}
let obj2={age:14}
// console.log(...obj) //报错,obj不是迭代对象
//正确写法
console.log({...obj})//{ name: 'ww' } ...扩展运算符
console.log({...obj,...obj2})//{ name: 'ww', age: 14 }
let arr=[1,2,4]
let arr2=[3,4,5]
// 合并,...与concat区别
console.log(...arr,...arr2);//1 2 4 3 4 5
console.log(...arr)//1 2 4
console.log(arr.concat(arr2))//[ 1, 2, 4, 3, 4, 5 ]
console.log(arr)//[1 2 4]
基本上,在 JavaScript 中,所有可迭代对象都是可枚举对象,但并非所有可枚举对象都是可迭代对象。
Array.from() 我们可以对可迭代对象和类数组对象进行数组化,进行数组化后就可以使用原生数组的方法进行操作。
类数组对象和可迭代对象的相似点:都可以进行迭代操作。如字符串就是类数组对象,当然数组也可以进行迭代操作(可以用for...of
进行迭代)。
类数组对象定义:只包含使用从零开始,且自然递增的整数做键名,并且定义了length表示元素个数的对象,我们就认为他是类数组对象!
判断是否拥有可迭代能力
- 当一个数据具备
Symbol.iterator
属性的时候,才可以用for...of
进行迭代。
// hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性
console.log(Array.prototype.hasOwnProperty(Symbol.iterator));//true
console.log(Set.prototype.hasOwnProperty(Symbol.iterator));//true
- Symbol.iterator
可迭代对象具有Symbol.iterator
属性,即具有Symbol.iterator
属性的对象都有默认迭代器
为什么数据是可迭代对象,却不能使用next()
可迭代对象不是迭代器,迭代器才有next()方法。
迭代器 (Iterator)
JavaScript 原有的表示“集合”的数据结构,主要是数组(Array
)和对象(Object
),ES6 又添加了Map
和Set
。
定义:遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是 ES6 创造了一种新的遍历命令
for...of
循环,Iterator 接口主要供for...of
消费。
Iterator 的遍历过程是这样的
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next
方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next
方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next
方法,直到它指向数据结构的结束位置。
有迭代属性的基础用法:
- (字符串,数组,set,map、Array,大多数类数组对象(DOM ,节点对象(NodeList),arguments)等有迭代属性。
- 类数组对象、普通对象并没有迭代属性。
- 可以让有迭代的元素赋值使类数组对象拥有迭代属性(对象不能赋值拥有)。对象需要自定义添加构造迭代方法才拥有迭代属性,或者使用generator生成器函数生成迭代对象。
let arr=[1,2,3]
console.log(arr[Symbol.iterator])//获取迭代器方法 [Function: values]
console.log(arr[Symbol.iterator]())//获取迭代器对象 Object [Array Iterator] {}
// console.log(arr[Symbol.iterator]().next())//迭代器对象 { value: 1, done: false }
// console.log(arr[Symbol.iterator]().next())//迭代器对象 { value: 1, done: false }
let i=arr[Symbol.iterator]()
console.log(i.next())// {value:1,done:false}
console.log(i.next())// {value:2,done:false}
console.log(i.next())// {value:3,done:false}
console.log(i.next())// { value: undefined, done: true }
let str='he';
// for of 用于可迭代元素 字符串,数组,set,map、Array、大多数类数组对象(DOM获取到的类数组对象 ,节点对象(NodeList),arguments)等都可用,有迭代对象
for (let item of str){
console.log(item)
}
//设置内置symbol Symbol.iterator
let r=str[Symbol.iterator]//获取迭代器方法
console.log(r)//[Function: [Symbol.iterator]]
let r2=str[Symbol.iterator]()//获取迭代对象
console.log(r2,r2.next())//Object [String Iterator] {} { value: 'h', done: false }
将一个类数组对象转化为可迭代对象:
// 将一个类数组对象转化为可迭代对象 类数组对象有内置的arguments有迭代属性,本身并没有
let typeObj={
"0":"tom",
"1":"terry",
length:2,
}
console.log(typeObj[Symbol.iterator])//undefined,没有迭代属性
typeObj[Symbol.iterator]=[][Symbol.iterator] //获取迭代属性
let tt=new Set(typeObj)//有迭代属性才可以用set获取属性值
console.log(tt)//Set(2) { 'tom', 'terry' }
// 或者用for of获取属性值
for(let i of typeObj){
console.log(i);//tom terry
}
将一个对象转化为可迭代对象:
let typeObj2={
"0":'tom',
"1":'terry',
"2":'lerry',
}//对象 非迭代器对象
// 使用generator函数方式添加迭代器 让一个对象变为可迭代对象
typeObj2[Symbol.iterator]=function * gen(){
// yield 'terry'
// yield 'lerry'
// yield 'tom'
console.log(this)
// 遍历
for( let key in this){
let v=this[key];
yield v
}
}
// console.log(typeObj[Symbol.iterator]);//[GeneratorFunction: gen]
// let r=typeObj[Symbol.iterator]();//迭代器对象
// console.log(r.next())//{ value: 'tom', done: false }
for(let i of typeObj2){
console.log(i)//tom terry lerry
}
构造迭代器的实例:
let range ={
from:0;
to:5;
}
// 运行for...of时吗,首先执行的就是Symbol.iterator
range[Symbol.iterator] = function() {
return {
// 返回当前元素,和目标元素
current:this.from,
last:this.to, // 当last = infinity 时,迭代器将永远执行下去,我们可以使用break跳出循环
// 必须有next方法,迭代器才能继续下去
next(){
// done 为true 时迭代结束
if (this.current <= this.last){
return {done:flase,value:this.current++};
else return {done:true};
}
}
}
}
for-in
for-in循环用来遍历数据-------例如数组中的键名。
- 1.一般用于遍历对象的可枚举属性。以及对象从构造函数原型中继承的属性。对于每个不同的属性,语句都会被执行。
- 2.不建议使用 for in 遍历数组,因为输出的顺序是不固定的。
- 3.如果迭代的对象的变量值是 null 或者 undefined, for in 不执行循环体,建议在使用 for in 循环之前,先检查该对象的值是不是 null 或者 undefined
for-of
for-of循环用来遍历数据-------例如数组中的元素值。
一个数据结构只要部署了
Symbol.iterator
属性,就被视为具有 iterator (迭代器)接口,就可以用for...of
循环遍历它的成员。也就是说,for...of
循环内部调用的是数据结构的Symbol.iterator
方法。
for...of
循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments
对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。
let str=' hello world ';
// for of => 对字符串的遍历操作
for(let s of str){
console.log(s)
}
let obj={
name:'ww',
age:13
}
// 对象的遍历
for(let s in obj){
console.log(s,obj[s]);//name ww,age 13
}
// JavaScript 原有的for...in循环,只能获得对象的键名,不能直接获取键值。
// ES6 提供for...of循环,允许遍历获得键值。但for...of不能访问对象,需要和迭代器配用
// for(let s2 of obj){
// console.log(s2) //报错
// }
var arr = ['a', 'b', 'c', 'd'];
// 键名
for (let a in arr) {
console.log(a); // 0 1 2 3
}
// 键值
for (let a of arr) {
console.log(a); // a b c d
}
for-of循环不仅支持数组,还支持大多数类数组对象,例如DOM ,节点对象(NodeList),arguments。(因为这种类数组对象拥有迭代属性)
for-of循环也支持字符串遍历,它将字符串视为一系列的Unicode字符来进行遍历。
for-of循环同样支持Map和Set对象遍历。
for-of循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用for-in循环(这也是它的本职工作)或内建的Object.keys()方法, 或设置对象拥有Symbol内置属性 symbol.iterator(迭代器)
// for in 与for of对对象的操作
var obj={
name:'ewew',
age:14,
}
console.log(obj.hasOwnProperty(Symbol.iterator))//false
// for (var key of obj) {
// console.log(key );//报错,对象中没有Symbol.iterator,没有迭代能力,不能直接遍历对象
// }
// 对对象的正确用法
for (var key of Object.keys(obj)) {
console.log(key + ": " + obj[key]);//name: ewew,age: 14
}
// for in
for(var key in obj){
console.log(key+":"+obj[key]);//name: ewew,age: 14
}
Object.keys(obj)方法返回一个由给定对象的自身可枚举属性组成的属性名的数组。与for...in遍历返回的顺序一样。