参考: JS中8种数据类型 - 不知名前端李小白 - 博客园
值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol、BigInt。
引用数据类型(对象类型):对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)。
其中Object是个大类,function函数、array数组、date日期…等都归属于Object。
- 在ES5的时候,我们认知的数据类型确实是 6种:Number、String、Boolean、undefined、object、Null
- ES6 中新增了一种 Symbol 。这种类型的对象永不相等,即始创建的时候传入相同的值,可以解决属性名冲突的问题,做为标记。
- 谷歌67版本中还出现了一种 bigInt。是指安全存储、操作大整数。
数组存储了一系列同一种数据类型的值,虽然在JavaScript中,也可以在数组中保存不同类的值,但我们还是需要遵守最佳实践,避免这样做。
new Array(7) // 创建一个长度为7的数组
new Array(1, 2, 3) // 创建一个数组,其元素为1, 2, 3
let arr2 = [1, 2, 3]
numbers[numbers.length] = 10;
numbers.push(10); // 数组尾添加
numbers.unshift(0); // 数组头添加 (所有的往后移动一位)
numbers.pop(); //从末尾删除元素
numbers.shift(); // 从数组头删除元素
使用pop()方法,而通过push()方法和pop()方法,就能用数组来模拟栈结构。
使用shift()方法和unshift()方法可以让数组模拟基本的队列数据结构。
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
numbers.splice(0, 1) // 表示:在索引为0处,删除一个元素
console.log(numbers) // 结果:[2, 3, 4, 5, 6, 7, 8, 9, 10]
numbers.splice(3, 2) // 表示:在索引为3处,删除两个元素
console.log(numbers) // 结果:[2, 3, 4, 7, 8, 9, 10]
numbers.splice(5, 0, 0, 1) // 表示:在索引为5处,添加0和1这两个元素
console.log(numbers) // 结果:[2, 3, 4, 7, 8, 0,1,9, 10]
注意:const使用时必须要初始化且不可以更改变量。【只可以添加数值】
以下为 ES5
方法:
方法 | 描述 |
---|---|
concat | 连接2个或者更多数组,并返回结果 |
every | 对数组中的每一个元素运行给定的函数,如果该函数对每一个元素都返回true,则返回true |
filter | 对数组中的每一个元素运行给定的函数,返回该函数会返回true的元素组成的数组 |
forEach | 对数组中的每一个元素运行给定的函数 |
join | 将所有的数组元素以指定的字符链接成一个字符串 |
indexOf | 返回第一个与给定参数相等的数组元素的索引,没有找到则返回-1 |
lastIndexOf | 从数组末尾开始搜索,并返回第一个与给定参数相等的数组元素的索引,没有找到则返回-1 |
map | 对数组中的每一个元素运行给定的函数,返回每次函数调用的结果组成的数组 |
reverse | 颠倒数组中元素的顺序 |
slice | 传入索引值,将数组里对应索引范围内的元素作为新数组返回 |
some | 对数组中的每个元素运行给定的函数,如果任一元素返回true,则返回true |
sort | 按照元素的ASCII值进行排序 |
reduce | 返回数组中所以元素值的合计 |
toString | 将数组作为字符串返回 |
valueOf | 和toString类似,将数组作为字符串返回 |
以下为 ES6
新增方法:
方法 | 描述 |
---|---|
@@iterator | 返回一个包含数组键值对的迭代器对象,可以通过同步调用的方式得到数组元素的键值对 |
copyWhthin | 复制数组中的一系列元素到同一数组指定的起始位置 |
entries | 返回包含数组所有键值对的@@iterator |
find | 根据回调函数给定的条件从数组中查找元素,如果找到则返回该元素 |
findIndex | 根据回调函数给定的条件从数组中查找元素,如果找到则返回该元素的索引 |
fill | 用静态值填充数组 |
from | 将一个类数组转换为一个真正的数组 |
of | 根据传入的参数创建一个新数组 |
values | 返回包含数组中所以值的@@iterator |
ES7
中新增数组方法:
方法 | 描述 |
---|---|
includes | 如果数组中存在某个元素,则返回true,否则返回false |
数组扩展方法使用:
扩展运算符的使用
深拷贝一维数组
const list = [1, 2, 3, 4, 5];
const list1 = [...list];
console.log(list1);
分割数组
const list = [1, 2, 3, 4, 5];
const [, ...list1] = list;
console.log(list1);
//[2, 3, 4, 5]
将数组转化成参数传递给函数
const list = [1, 2];
function xd(a, b) {
console.log(a + b);
}
xd(...list);
新增的常用方法
fill(会改变原数组)
const list = [1, 2, 3, 4, 5];
const list1 = [...list].fill(6);
const list2 = [...list].fill(6, 1, 4);
console.log(list1);
console.log(list2);
// output: (5) [6, 6, 6, 6, 6]
// (5) [1, 6, 6, 6, 5]
find/findIndex
const list = [
{ hobby: '吃饭', id: 1 },
{ hobby: '睡觉', id: 1 },
{ hobby: '敲代码', id: 1 },
{ hobby: '吃饭', id: 2 },
];
let str = '';
for (let i = 0; i < list.length; i++) {
if (list[i].hobby === '吃饭') {
str = list[i];
}
}
console.log(str);
const result = list.find(function (item) {
return item.hobby === '吃饭';
});
const result1 = list.findIndex(function (item) {
return item.hobby === '吃饭';
});
console.log(result, result1);
// {hobby: '吃饭', id: 2}
// {hobby: '吃饭', id: 1} 0
flat
const list = [1, 2, 3, ['2nd', 5, 6, ['3rd', 7, 8]]];
const list1 = [].concat(...list);
console.log(list1);
const list2 = list.flat(2);
console.log(list2);
// 展开 铺平
// (7) [1, 2, 3, '2nd', 5, 6, Array(3)]
// (9) [1, 2, 3, '2nd', 5, 6, '3rd', 7, 8]
filter(改变长度过滤,返回数组)
const list = [
{ hobby: '吃饭', id: 1 },
{ hobby: '睡觉', id: 1 },
{ hobby: '敲代码', id: 1 },
{ hobby: '吃饭', id: 2 },
];
const result = list.filter(function (item) {
return item.hobby === '吃饭';
});
console.log(result); // [{ hobby: '吃饭', id: 1 },{ hobby: '吃饭', id: 2 }]
const list = [
{ hobby: '吃饭', id: 1 },
{ hobby: '睡觉', id: 1 },
{ hobby: '敲代码', id: 2 },
];
const list1 = list.map(function (i) {
// return {
// action: i.hobby,
// state: i.id === 1 ? '喜欢' : '沉迷',
// };
let obj = {};
Object.assign(obj, i);
obj.state = i.id === 1 ? '喜欢' : '沉迷';
return obj;
});
console.log(list);
console.log(list1);
const posts = [
{id: 1, title: 'Title 1'},
{id: 2, title: 'Title 2'}
];
// 找出id为1的posts
// find返回数组或类似结构中满足条件的第一个元素
const title = posts.find(p => p.id === 1).title;
some找到数组中符合条件的一项就不会在找,类似于find只找第一项。
[1,2,3,4,5].some(v=>v>4) // true 符合有某一项满足条件
every数组中每个条件都为真才会返回真。
[1,2,3,4,5].every(v=>v>1) // false 数组中每一项都大于1才会返回true
当你想要将多个数据放进一个实例中时,你可以使用一个reducer.
传给reduce的第一个参数函数还可以增加2个参数:
第三个参数:每个元素在原数据结构中的位置,比如数组下标。
第四个参数:调用reduce方法的数据集合,比如例子中的posts
const posts = [
{id: 1, upVotes: 2},
{id: 2, upVotes: 89},
{id: 3, upVotes: 1}
];
const totalUpvotes = posts.reduce((totalUpvotes, currentPost) =>
totalUpvotes + currentPost.upVotes, //reducer函数
0 // 初始化投票数为0
);
console.log(totalUpvotes)//输出投票总数:92
简介:详细介绍Map与WeakMap结构的特点
背景
JavaScript中的对象,实质就是键值对的集合,但是在对象里却只能用字符串作为键名。在一些特殊的场景里就满足不了我们的需求了,正因为此,Map这一数据提出了,它是JavaScript中的一种更完善Hash结构。
Map对象
用于保存键值对,任何值(对象或者原始值)都可以作为一个键名或一个值。
使用介绍
// 通过构造函数创建一个Map
let m = new Map();
m.set([1,2],'张三')
构造函数
静态属性
实例属性
实例方法
内置API
属性/方法 | 作用 | 例子 |
---|---|---|
size | 返回键值对的数量 | m.size |
clear() | 清除所有键值对 | m.clear() |
has(key) | 判断键值对中是否有指定的键名,返回值是布尔值 | m.has(key) |
get(key) | 获取指定键名的键值,如不存在则返回undefined | m.get(key) |
set(key, value) | 添加键值对,如键名已存在,则更新键值对 | m.set(key, value) |
delete(key) | 删除指定键名的键值对 | m.delete(key) |
myMap.forEach((value, key) => {
console.log(`${key} = ${value}`);
});
// 0 = zero
// 1 = one
WeakMap
只接受对象作为键名
const weakMap = new WeakMap([[{ name: 1 }, '张三']]);
console.log(weakMap);
介绍
Set是ES6给开发者提供的一种类似数组的数据结构,可以理解为值的集合。它和数组的最大的区别就在于: 它的值不会有重复项。
基本使用
// 创建
let set = new Set();
let set2 = new Set([1,2,3])
// 添加元素
set.add(1)
特点
属性及方法
属性/方法 | 作用 | 例子 |
---|---|---|
size | 返回成员个数 | s.size |
clear() | 清除所有成员 | s.clear() |
has(value) | 判断键值对中是否有指定的值,返回值是布尔值 | s.has(key) |
delete(value) | 删除指定值 | s.delete(key) |
// 注意
const a = { aa: 1 };
const list = new Set([a]);
console.log(list.has(a));
key和value相等
const list = new Set([1, 2, 3, 4]);
console.log(list.keys());
console.log(list.values());
用途
// 去重
let arr = [1,2,2,3,4,4,4];
let s = new Set(arr);
//结果:Set {1,2,3,4}
let newArr = Array.from(s);
//结果:[1,2,3,4],完成去重
WeakSet
扩展的API
方法 | 描述 |
---|---|
includes(string, index) | 判断字符串中是否包含指定字符串,返回值是布尔值 |
startsWith(string, index) | 判断字符串的开头是否包含指定字符串,返回值是布尔值 |
endsWith(string, index) | 判断字符串的尾部是否包含指定字符串,返回值是布尔值 |
repeat(n) | repeat() 方法返回一个新字符串,表示将原字符串重复n 次。 |
字符串补全 | 第一个参数是补全后的字符串长度,第二个参数是用于补全的字符串 |
padStart(length, str) | 用于头部补全 |
padEnd(length, str) | 用于尾部补全 |
模板字符串
// 语法
const name = "张三"
const age = 18
const hobbies = "打篮球"
// ES5写法
const str1 = '我的名字是'+name+',我今年'+age+'岁,我喜欢'+hobbies
console.log(str1)
// ES6写法
const str2 = `我的名字是${name},我今年${age}岁,我喜欢${hobbies}`
console.log(str2)
使用模板字符串的注意事项
请注意区分 JavaScript 字符串对象和基本字符串值 . ( 对于 Boolean 和Numbers 也同样如此.)
字符串字面量 (通过单引号或双引号定义) 和 直接调用 String 方法 (没有通过 new 生成字符串对象实例) 的字符串都是基本字符串。JavaScript 会自动将基本字符串转换为字符串对象,只有将基本字符串转化为字符串对象之后才可以使用字符串对象的方法。当基本字符串需要调用一个字符串对象才有的方法或者查询值的时候 (基本字符串是没有这些方法的),JavaScript 会自动将基本字符串转化为字符串对象并且调用相应的方法或者执行查询。
var s_prim = "foo";
var s_obj = new String(s_prim);
console.log(typeof s_prim); // Logs "string"
console.log(typeof s_obj); // Logs "object"
当使用 eval时,基本字符串和字符串对象也会产生不同的结果。eval 会将基本字符串作为源代码处理; 而字符串对象则被看作对象处理,返回对象。例如:
s1 = "2 + 2"; // creates a string primitive
s2 = new String("2 + 2"); // creates a String object
console.log(eval(s1)); // returns the number 4
console.log(eval(s2)); // returns the string "2 + 2"
由于上述原因,当一段代码在需要使用基本字符串的时候却使用了字符串对象就会导致执行失败 (虽然一般情况下程序员们并不需要考虑这样的问题)。
利用 valueOf 方法,我们可以将字符串对象转换为其对应的基本字符串。
console.log(eval(s2.valueOf())); // returns the number 4
Object.is
let res = Object.is(NaN, NaN);
console.log(res, NaN === NaN); // 比较相等,最佳用法~相比于=== 一是+0不等于-0,二是NaN等于自身。
Object.assign
let a = { aa: 1, bb: 2 };
let b = { aa: '1' };
let c = Object.assign(b, a); // (目标值, 源值) 源值会替换目标值
console.log(c);
// {aa:1, bb:2}
Object.keys
let xd = {
hobby1: '吃饭',
hobby2: '睡觉',
hobby3: '敲代码',
};
console.log(Object.keys(xd));
// ['hobby1', 'hobby2', 'hobby3']
Object.values
let xd = {
hobby1: '吃饭',
hobby2: '睡觉',
hobby3: '敲代码',
};
console.log(Object.keys(xd));
// 获取对应keys ['吃饭', '睡觉', '敲代码']
Object.entries
let xd = {
hobby1: '吃饭',
hobby2: '睡觉',
hobby3: '敲代码',
};
console.log(Object.keys(xd));
// 对象变为数组值 —— 3行2列 的数组!
Object 和 Map 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 Map 使用。
不过 Map 和 Object 有一些重要的区别,在下列情况中使用 Map 会是更好的选择:
Map | Object | |
---|---|---|
意外的键 | Map 默认情况不包含任何键。只包含显式插入的键。 | 一个 Object 有一个原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。【备注:虽然可以用 Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。】 |
键的类型 | 一个 Map 的键可以是任意值,包括函数、对象或任意基本类型。 | 一个 Object 的键必须是一个 String 或是 Symbol。 |
键的顺序 | Map 中的键是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。 | 虽然 Object 的键目前是有序的,但并不总是这样,而且这个顺序是复杂的。因此,最好不要依赖属性的顺序。【自 ECMAScript 2015 规范以来,对象的属性被定义为是有序的;ECMAScript 2020 则额外定义了继承属性的顺序。参见 OrdinaryOwnPropertyKeys 和 EnumerateObjectProperties 抽象规范说明。但是,请注意没有可以迭代对象所有属性的机制,每一种机制只包含了属性的不同子集。(for-in 仅包含了以字符串为键的属性;Object.keys 仅包含了对象自身的、可枚举的、以字符串为键的属性;Object.getOwnPropertyNames 包含了所有以字符串为键的属性,即使是不可枚举的;Object.getOwnPropertySymbols 与前者类似,但其包含的是以 Symbol 为键的属性,等等。) 】 |
Size | Map 的键值对个数可以轻易地通过 size 属性获取。 | Object 的键值对个数只能手动计算。 |
迭代 | Map 是 可迭代的 的,所以可以直接被迭代。 | Object 没有实现 迭代协议,所以使用 JavaSctipt 的 for…of 表达式并不能直接迭代对象。【备注:对象可以实现迭代协议,或者你可以使用 Object.keys 或 Object.entries。】for…in 表达式允许你迭代一个对象的可枚举属性。 |
性能 | 在频繁增删键值对的场景下表现更好。 | 在频繁添加和删除键值对的场景下未作出优化。 |
序列化和解析 | 没有元素的序列化和解析的支持。(但是你可以使用携带 replacer 参数的 JSON.stringify() 创建一个自己的对 Map 的序列化和解析支持。参见 Stack Overflow 上的提问:How do you JSON.stringify an ES6 Map?) | 原生的由 Object 到 JSON 的序列化支持,使用 JSON.stringify()。 原生的由 JSON 到 Object 的解析支持,使用 JSON.parse()。 |
const books = [];
const newBook = {title: 'Alice in wonderland', id: 1};
const updatedBooks = [...books, newBook];
//updatedBooks的值为[{title: 'Alice in wonderland', id: 1}]
如果需要实现用户从购物车中删除物品,但是又不想破坏原来的购物车列表,可以使用filter方法
const myId = 6;
const userIds = [1, 5, 7, 3, 6];
const allButMe = userIds.filter(id => id !== myId);
// allButMe is [1, 5, 7, 3]
const books = ['Positioning by Trout', 'War by Green'];
const newBooks = [...books, 'HWFIF by Carnegie'];
// newBooks are now ['Positioning by Trout', 'War by Green', 'HWFIF // by Carnegie']
const user = {name: 'Shivek Khurana'};
const updatedUser = {...user, age: 23};
//updatedUser的值为:{name: 'Shivek Khurana', age: 23}
const dynamicKey = 'wearsSpectacles';
const user = {name: 'Shivek Khurana'};
const updatedUser = {...user, [dynamicKey]: true};
// updatedUser is {name: 'Shivek Khurana', wearsSpectacles: true}
const posts = [
{id: 1, title: 'Title 1'},
{id: 2, title: 'Title 2'}
];
const updatedPosts = posts.map(p => p.id !== 1 ?
p : {...p, title: 'Updated Title 1'}
);
/*
updatedPosts is now
[
{id: 1, title: 'Updated Title 1'},
{id: 2, title: 'Title 2'}
];
*/
const posts = [
{id: 1, title: 'Title 1'},
{id: 2, title: 'Title 2'}
];
const postInQuestion = posts.find(p => p.id === 2);
// postInQuestion now holds {id: 2, title: 'Title 2'}
//方法一
const user = {name: 'Shivek Khurana', age: 23, password: 'SantaCl@use'};
const userWithoutPassword = Object.keys(user)
.filter(key => key !== 'password')
.map(key => {[key]: user[key]})
.reduce((accumulator, current) =>
({...accumulator, ...current}),
{}
)
;
// 方法二
const user = {name: 'Shivek Khurana', age: 23, password: 'SantaCl@use'};
const userWithoutPassword = (({name, age}) => ({name, age}))(user);
// userWithoutPassword becomes {name: 'Shivek Khurana', age: 23}
const params = {color: 'red', minPrice: 8000, maxPrice: 10000};
const query = '?' + Object.keys(params)
.map(k =>
encodeURIComponent(k) + '=' + encodeURIComponent(params[k])
)
.join('&')
;
// encodeURIComponent将对特殊字符进行编码。
// query is now "color=red&minPrice=8000&maxPrice=10000"
const posts = [
{id: 13, title: 'Title 221'},
{id: 5, title: 'Title 102'},
{id: 131, title: 'Title 18'},
{id: 55, title: 'Title 234'}
];
// 找到id为131的元素
const requiredIndex = posts.findIndex(obj=>obj.id===131);
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。常用来合并对象。
const obj1 = { a: 1, b: 2 }
const obj2 = { b: 4, c: 5 }
const obj3 = Object.assign(obj1, obj2)
const obj4 = Object.assign({}, obj1) // 克隆了obj1对象
console.log(obj1) // { a: 1, b: 4, c: 5 } 对同名属性b进行了替换 obj1发生改变是因为obj2赋给了obj1
console.log(obj2) // { b: 4, c: 5 }
console.log(obj3) // { a: 1, b: 4, c: 5 }
console.log(obj4) // { a: 1, b: 4, c: 5 }
Object.assign(target, ...sources)
注意
也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。同名属性会替换。
const obj5 = {
name: 'dengke',
a: 10,
fn: {
sum: 10
}
}
const obj6 = Object.assign(obj1, obj5)
console.log(obj6) // { a: 10, b: 2, name: 'dengke', fn: {…}}
console.log(obj1) // {a: 10, b: 2, name: 'dengke', fn: {…}} 对同名属性a进行了替换
上边枚举对象属性时有用到了Object.keys(),在这里就具体为大家介绍一下它。
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。与Object.values()相似,区别在于这个返回的是数据的属性就是key。接下来就会介绍Object.values(),不要着急。😊
const arr = ['a', 'b', 'c']
console.log(Object.keys(arr)) // ['0', '1', '2']
const obj = { 0: 'a', 1: 'b', 2: 'c' }
console.log(Object.keys(obj)) // ['0', '1', '2']
const obj2 = { 100: 'a', 2: 'b', 7: 'c' }
console.log(Object.keys(obj2)) // ['2', '7', '100']
Object.keys(obj)
Object.keys("foo") // TypeError: "foo" is not an object (ES5 code)
Object.keys("foo") // ["0", "1", "2"] (ES2015 code)
Object.values() 方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for…in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。与Object.keys()相似,区别在于这个返回的是数据的值也就是value
const obj1 = { foo: 'bar', baz: 42 }
console.log(Object.values(obj1)) // ['bar', 42]
const obj2 = { 0: 'a', 1: 'b', 2: 'c' }
console.log(Object.values(obj2)) // ['a', 'b', 'c']
Object.values(obj)
参数:obj被返回可枚举属性值的对象。
返回值:一个包含对象自身的所有可枚举属性值的数组。
注意
对象key为number的话,会从升序枚举返回。
const obj3 = { 100: 'a', 2: 'b', 7: 'c' }
console.log(Object.values(obj3)) // ['b', 'c', 'a']
Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组。可使用Object.fromEntries()方法,相当于反转了Object.entries()方法返回的数据结构。接下来也会介绍Object.fromEntries()
const obj1 = {
name: 'dengke',
age: 18
};
for (const [key, value] of Object.entries(obj1)) {
console.log(`${key}: ${value}`);
}
// "name: dengke"
// "age: 18"
const obj2 = { foo: 'bar', baz: 42 }
console.log(Object.entries(obj2)) // [ ['foo', 'bar'], ['baz', 42] ]
const obj3 = { 0: 'a', 1: 'b', 2: 'c' }
console.log(Object.entries(obj3)) // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
Object.entries(obj)
将Object转换为Map,new Map()构造函数接受一个可迭代的entries。借助Object.entries方法你可以很容易的将 Object转换为Map:
const obj = { foo: "bar", baz: 42 }
const map = new Map(Object.entries(obj))
console.log(map) // Map { foo: "bar", baz: 42 }
Object.fromEntries() 方法把键值对列表转换为一个对象。与Object.entries()相反。相当于反转了Object.entries()方法返回的数据结构。(下面补充里有具体的演示)
const entries = new Map([
['foo', 'bar'],
['baz', 42]
]);
const obj = Object.fromEntries(entries);
console.log(obj);
// Object { foo: "bar", baz: 42 }
Object.fromEntries(iterable)
参数:iterable类似Array、Map或者其它实现了可迭代协议的可迭代对象。
返回值:一个由该迭代对象条目提供对应属性的新对象。
补充
Map 转化为 Object
通过 Object.fromEntries, 可以将Map转换为Object:
const map = new Map([ ['foo', 'bar'], ['baz', 42] ])
const obj = Object.fromEntries(map)
console.log(obj)
// { foo: "bar", baz: 42 }
Array 转化为 Object
通过 Object.fromEntries, 可以将Array转换为Object:
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
const obj = Object.fromEntries(arr)
console.log(obj)
// { 0: "a", 1: "b", 2: "c" }
对象转换
Object.fromEntries 是与 Object.entries()相反的方法,用 数组处理函数 可以像下面这样转换对象:
const object1 = { a: 1, b: 2, c: 3 }
const object2 = Object.fromEntries(
Object.entries(object1)
.map(([ key, val ]) => [ key, val * 2 ])
)
// Object.entries(object1) >>> [["a",1],["b",2],["c",3]]
console.log(object2) // { a: 2, b: 4, c: 6 }
上边枚举对象属性时为了避免for…in遍历继承来的属性,给大家补充了可以借助Object.prototype.hasOwnProperty()方法进行判断,在这里也具体为大家介绍一下它。
hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
const obj1 = {};
obj1.property1 = 42
console.log(obj1.hasOwnProperty('property1')) // true
console.log(obj1.hasOwnProperty('toString')) // false
console.log(obj1.hasOwnProperty('hasOwnProperty')) // false
obj.hasOwnProperty(prop)
参数:prop 要检测的属性的String字符串形式表示的名称,或者Symbol。
返回值:用来判断某个对象是否含有指定的属性的布尔值Boolean。
注意
只会对自身属性进行判断,继承来的一律返回false。配合for…in使用,可以避免其遍历继承来的属性。
const o = new Object()
o.prop = 'exists'
console.log(o.hasOwnProperty('prop')) // true
console.log(o.hasOwnProperty('toString')) // false
console.log(o.hasOwnProperty('hasOwnProperty')) // false
即使属性的值是 null 或 undefined,只要属性存在,hasOwnProperty 依旧会返回 true。
const o = new Object();
o.propOne = null
o.propTwo = undefined
console.log(o.hasOwnProperty('propOne')) // true
console.log(o.hasOwnProperty('propTwo')) // true
上边枚举对象属性时也有用到该方法,在这里也具体为大家介绍一下它。
Object.getOwnPropertyNames() 返回一个数组,该数组对元素是 obj自身拥有的枚举或不可枚举属性名称字符串。 数组中枚举属性的顺序与通过for…in循环Object.keys迭代该对象属性时一致。数组中不可枚举属性的顺序未定义。
const arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()) // ["0", "1", "2", "length"]
// 类数组对象
const obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()) // ["0", "1", "2"]
// 使用Array.forEach输出属性名和属性值
Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) {
console.log(val + " -> " + obj[val]);
})
// 0 -> a
// 1 -> b
// 2 -> c
// 不可枚举属性
const my_obj = Object.create({}, {
getFoo: {
value: function() { return this.foo; },
enumerable: false
}
});
my_obj.foo = 1;
console.log(Object.getOwnPropertyNames(my_obj).sort())
// ["foo", "getFoo"]
obj.getOwnPropertyNames(obj)
参数:obj一个对象,其自身的可枚举和不可枚举属性的名称被返回。
返回值:在给定对象上找到的自身属性对应的字符串数组。
补充
Object.getOwnPropertyNames和Object.keys的区别:Object.keys只适用于可枚举的属性,而Object.getOwnPropertyNames返回对象的全部属性名称(包括不可枚举的)。
'use strict'
(function () {
// 人类的构造函数
const person = function (name, age, sex) {
this.name = name
this.age = age
this.sex = sex
this.sing = () => {
console.log('sing');
}
}
// new 一个ladygaga
const gaga = new person('ladygaga', 26, 'girl')
// 给嘎嘎发放一个不可枚举的身份证
Object.defineProperty(gaga, 'id', {
value: '1234567890',
enumerable: false
})
//查看gaga的个人信息
const arr = Object.getOwnPropertyNames(gaga)
console.log(arr) // name, age, sex, sing, id
// 注意和getOwnPropertyNames的区别,不可枚举的id没有输出
const arr1 = Object.keys(gaga)
console.log(arr1) // name, age, sex, sing
})()
如果你只要获取到可枚举属性,可以用Object.keys或用for…in循环(for…in会获取到原型链上的可枚举属性,可以使用hasOwnProperty()方法过滤掉)。
获取不可枚举的属性,可以使用Array.prototype.filter()方法,从所有的属性名数组(使用Object.getOwnPropertyNames()方法获得)中去除可枚举的属性(使用Object.keys()方法获得),剩余的属性便是不可枚举的属性了:
const target = myObject;
const enum_and_nonenum = Object.getOwnPropertyNames(target);
const enum_only = Object.keys(target);
const nonenum_only = enum_and_nonenum.filter(function(key) {
const indexInEnum = enum_only.indexOf(key);
if (indexInEnum == -1) {
// 没有发现在enum_only健集中意味着这个健是不可枚举的,
// 因此返回true 以便让它保持在过滤结果中
return true;
} else {
return false;
}
});
console.log(nonenum_only);
在 ES5 中,如果参数不是一个原始对象类型,将抛出一个 TypeError异常。在 ES2015 中,非对象参数被强制转换为对象。
Object.getOwnPropertyNames('foo') // TypeError: "foo" is not an object (ES5 code)
Object.getOwnPropertyNames('foo') // ['length', '0', '1', '2'] (ES2015 code)
Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。
const obj = {
prop: 42
}
Object.freeze(obj)
obj.prop = 33
console.log(obj.prop)
// 42
obj.freeze(obj)
被冻结的对象是不可变的。但也不总是这样。下例展示了冻结对象不是常量对象(浅冻结)。
const obj1 = {
internal: {}
}
Object.freeze(obj1)
obj1.internal.a = 'aValue'
console.log(obj1.internal.a) // 'aValue'
要使对象不可变,需要递归冻结每个类型为对象的属性(深冻结)。
// 深冻结函数.
function deepFreeze(obj) {
// 取回定义在obj上的属性名
const propNames = Object.getOwnPropertyNames(obj)
// 在冻结自身之前冻结属性
propNames.forEach(function(name) {
const prop = obj[name]
// 如果prop是个对象,冻结它
if (typeof prop == 'object' && prop !== null)
deepFreeze(prop)
})
// 冻结自身
return Object.freeze(obj);
}
const obj2 = {
internal: {}
}
deepFreeze(obj2)
obj2.internal.a = 'anotherValue'
obj2.internal.a // undefined
Object.isFrozen() 方法判断一个对象是否被冻结。
// 一个对象默认是可扩展的, 所以它也是非冻结的。
Object.isFrozen({}) // false
// 一个不可扩展的空对象同时也是一个冻结对象。
var vacuouslyFrozen = Object.preventExtensions({})
Object.isFrozen(vacuouslyFrozen) // true
var frozen = { 1: 81 }
Object.isFrozen(frozen) // false
// 使用Object.freeze是冻结一个对象最方便的方法.
Object.freeze(frozen)
Object.isFrozen(frozen) // true
obj.isFrozen(obj)
参数:obj被检测的对象。
返回值:表示给定对象是否被冻结的Boolean。
注意
在 ES5 中,如果参数不是一个对象类型,将抛出一个TypeError异常。在 ES2015 中,非对象参数将被视为一个冻结的普通对象,因此会返回true。
Object.isFrozen(1) // (ES5 code)
// TypeError: 1 is not an object
Object.isFrozen(1) // (ES2015 code)
// true