day2_js

本文详细讲解了JavaScript中的对象操作,包括const声明、点取值与方括号取值、计算属性、属性存在性测试、对象循环、删除属性、对象比较、克隆、垃圾回收机制、this指向、构造函数、可选链、Symbol的使用、数据类型(如数字和数组)以及JSON的序列化与反序列化等内容。
摘要由CSDN通过智能技术生成

Day2

对象

const声明的对象也可以被修改

(1)点取值和方括号取值的区别?

点取值在里面的键一般是没有引号的,比如:

let user = {
  name: "John",
  age: 30, // 这个就可以使用点进行取值
  "likes birds": true  // 多词属性名必须加引号,使用方括号进行取值
};

但是请注意方括号适用于所有情况,推荐使用方括号进行取值。

(2)计算属性

let fruit = prompt("Which fruit to buy?", "apple");
let bag = {
  [fruit]: 5, // 属性名是从 fruit 变量中得到的
};
alert( bag.apple ); // 5 如果 fruit="apple"

// 实际上上述的方式与bag[fruit] = 5; 所实现的是一致的

(3)简写

function makeUser(name, age) {
  return {
    name, // 与 name: name 相同
    age,  // 与 age: age 相同
    // ...
  };
}

当属性名与变量名一致时就可以使用对象的简写方式。

(4)属性存在性测试

JavaScript 的对象有一个需要注意的特性:能够被访问任何属性。即使属性不存在也不会报错!

语法:"key" in object

(5)对象的循环

for xxx in xxx

这里循环的都是对象里面的键。

(6)删除属性

delete obj.prop

对象比较

(1)比较

请注意在js的对象是引用数据类型,因此其保存的是内存的地址,当两个对象变量比较的时候,需要看其引用的地址是否一致

let a = {};
let b = {}; // 两个独立的对象

alert( a == b ); // false
//----------------------------
let a = {};
let b = a; // 复制引用

alert( a == b ); // true,都引用同一对象
alert( a === b ); // true

(2)如何克隆一个对象【引用的地址不相同】

使用Object.assign()方法

//Object.assign(dest, [src1, src2, src3...])语法
let user = { name: "John" };

let permissions1 = { canView: true };
let permissions2 = { canEdit: true };

// 将 permissions1 和 permissions2 中的所有属性都拷贝到 user 中
Object.assign(user, permissions1, permissions2);

// 现在 user = { name: "John", canView: true, canEdit: true }

(3)深拷贝

内容:即对象里面还有对象,如果使用刚才的克隆方法,最后其里面的对象又可以彼此访问,不满足拷贝形式

// 例如
let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50
  }
};

let clone = Object.assign({}, user);

alert( user.sizes === clone.sizes ); // true,同一个对象

// user 和 clone 分享同一个 sizes
user.sizes.width++;       // 通过其中一个改变属性值
alert(clone.sizes.width); // 51,能从另外一个获取到变更后的结果

解决办法:我们可以使用递归来实现它。或者为了不重复造轮子,采用现有的实现,例如 lodash 库的 _.cloneDeep(obj)

垃圾回收机制

其实就是引用不到的数据,内存进行释。实现过程就是从全局的根(全局变量)开始沿着引用递归地进行标记,没标记的就被释放

优化:(1)分代收集【新老】 (2)增量收集【大小】 (3)闲时收集

this

this 总结:
--------------------
method 里的 this 返回的是 obj,
function 里的 this 非严格模式下返回的是 global or window ,严格模式下返回 undefined,
箭头函数里没有自己的 this

构造函数

理论上任何函数都可以做为构造函数(除去箭头函数)【因为要使用this】,一般的规则要求构造函数首字母大写,同时使用new关键字后传一个函数作为构造函数。

function Fun(str) {
    this.name = str
    this.a = false
}
let obj = new Fun("hello")

当一个函数被使用 new 操作符执行时,它按照以下步骤:

  1. 一个新的空对象被创建并分配给 this
  2. 函数体执行。通常它会修改 this,为其添加新的属性。
  3. 返回 this 的值。
可选链

格式: ?. 这个其实与 ?? 运算符很像

let user = null;
alert( user.address ); // 直接报错,因为null不能有对应的属性

// 这样就不会报错了
let user = null;
alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
Symbol

symbol 是唯一标识符的基本类型

symbol 是使用带有可选描述(name)的 Symbol() 调用创建的。

symbol 总是不同的值,即使它们有相同的名字。如果我们希望同名的 symbol 相等,那么我们应该使用全局注册表:Symbol.for(key) 返回(如果需要的话则创建)一个以 key 作为名字的全局 symbol。使用 Symbol.for 多次调用 key 相同的 symbol 时,返回的就是同一个 symbol。

symbol 有两个主要的使用场景:

  1. “隐藏” 对象属性。

    如果我们想要向“属于”另一个脚本或者库的对象添加一个属性,我们可以创建一个 symbol 并使用它作为属性的键。symbol 属性不会出现在 for..in 中,因此它不会意外地被与其他属性一起处理。并且,它不会被直接访问,因为另一个脚本没有我们的 symbol。因此,该属性将受到保护,防止被意外使用或重写。

    因此我们可以使用 symbol 属性“秘密地”将一些东西隐藏到我们需要的对象中,但其他地方看不到它。

  2. JavaScript 使用了许多系统 symbol,这些 symbol 可以作为 Symbol.* 访问。我们可以使用它们来改变一些内建行为。例如,在本教程的后面部分,我们将使用 Symbol.iterator 来进行 迭代 操作,使用 Symbol.toPrimitive 来设置 对象原始值的转换 等等。

从技术上说,symbol 不是 100% 隐藏的。有一个内建方法 Object.getOwnPropertySymbols(obj) 允许我们获取所有的 symbol。还有一个名为 Reflect.ownKeys(obj) 的方法可以返回一个对象的 所有 键,包括 symbol。但大多数库、内建方法和语法结构都没有使用这些方法。

数据类型

数字

  1. 二进制 八进制 十六进制如何表示(0b, 0o, 0x)
  2. toString可以将数字转化为对应进制的字符串
  3. toFixed()保留小数点后几位[规则为四舍五入]
  4. isFinite()和isNaN()
isFinite(value)` 将其参数转换为数字,如果是常规数字而不是 `NaN/Infinity/-Infinity`,则返回 `true
  1. parseInt(xxx,xxx)可以实现进制的字符串的转化

    alert( parseInt('0xff', 16) ); // 255
    alert( parseInt('ff', 16) ); // 255,没有 0x 仍然有效
    alert( parseInt('2n9c', 36) ); // 123456
    

    同时主要的还是转化为数字,但是规则不太一样

    alert( parseInt('100px') ); // 100
    alert( parseFloat('12.5em') ); // 12.5
       
    alert( parseInt('12.3') ); // 12,只有整数部分被返回了
    alert( parseFloat('12.3.4') ); // 12.3,在第二个点出停止了读取
    
    1. JS中小数除非是二进制可以表示的小数,其他小数都会有精度损失

数组

  1. unshift、shift分别为开头添加与移除元素

    且都支持多元素操作

  2. 关于length

    当我们修改数组的时候,length 属性会自动更新。准确来说,它实际上不是数组里元素的个数,而是最大的数字索引值加一。

    let fruits = [];
    fruits[123] = "Apple";
    
    alert( fruits.length ); // 124
    
    // --------------------------
    let arr = [1, 2, 3, 4, 5];
    arr.length = 2; // 截断到只剩 2 个元素
    alert( arr ); // [1, 2]
    arr.length = 5; // 又把 length 加回来
    alert( arr[3] ); // undefined:被截断的那些数值并没有回来
    // 因此清空数组最简单的办法就是arr.length = 0 
    
  3. toString问题

    数组没有 Symbol.toPrimitive,也没有 valueOf,它们只能执行 toString 进行转换,所以这里 [] 就变成了一个空字符串,[1] 变成了 "1"[1,2] 变成了 "1,2"

    alert( [] + 1 ); // "1"
    alert( [1] + 1 ); // "11"
    alert( [1,2] + 1 ); // "1,21"
    
  4. 万能函数 splice()

    // 删除
    let arr = ["I", "study", "JavaScript"];
    arr.splice(1, 1); // 从索引 1 开始删除 1 个元素
    alert( arr ); // ["I", "JavaScript"]
    
    // 删除并替换
    let arr = ["I", "study", "JavaScript", "right", "now"];
    // 删除数组的前三项,并使用其他内容代替它们
    arr.splice(0, 3, "Let's", "dance");
    alert( arr ) // 现在 ["Let's", "dance", "right", "now"]
    
    // 截取
    let arr = ["I", "study", "JavaScript", "right", "now"];
    // 删除前两个元素
    let removed = arr.splice(0, 2);
    alert( removed ); // "I", "study" <-- 被从数组中删除了的元素
    
    // 插入,就是将删除的参数设置为0即可
    let arr = ["I", "study", "JavaScript"];
    
    // 从索引 2 开始
    
    // 删除 0 个元素
    // 然后插入 "complex" 和 "language"
    arr.splice(2, 0, "complex", "language");
    alert( arr ); // "I", "study", "complex", "language", "JavaScript"
    
  5. 截取slice

    let arr = ["t", "e", "s", "t"];
    
    alert( arr.slice(1, 3) ); // e,s(复制从位置 1 到位置 3 的元素)
    
    alert( arr.slice(-2) ); // s,t(复制从位置 -2 到尾端的元素)
    
  6. 拼接为 concat()

  7. 重点forEach(方法允许为数组的每个元素都运行一个函数。)

    arr.forEach(function(item, index, array) {
      // ... do something with item
    });
    
    // 例如
    ["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => {
      alert(`${item} is at index ${index} in ${array}`);
    });
    
  8. 查找

    方法 includes 可以正确的处理 NaN

    const arr = [NaN];
    alert( arr.indexOf(NaN) ); // -1(错,应该为 0)
    alert( arr.includes(NaN) );// true(正确)
    
  9. 对象数组的处理arr.find方法与filter()方法

    let result = arr.find(function(item, index, array) {
      // 如果返回 true,则返回 item 并停止迭代
      // 对于假值(false)的情况,则返回 undefined
    });
    
    例子
    let users = [
      {id: 1, name: "John"},
      {id: 2, name: "Pete"},
      {id: 3, name: "Mary"}
    ];
    let user = users.find(item => item.id == 1);
    alert(user.name); // John
    

    这里的find一般都是查找的单个元素,但如果有很多满足要求的元素就是用过filter函数进行实现

    let users = [
      {id: 1, name: "John"},
      {id: 2, name: "Pete"},
      {id: 3, name: "Mary"}
    ];
    // 返回前两个用户的数组
    let someUsers = users.filter(item => item.id < 3);
    alert(someUsers.length); // 2
    
    
  10. map

    arr.map 方法是最有用和经常使用的方法之一。

    它对数组的每个元素都调用函数,并返回结果数组。

    let result = arr.map(function(item, index, array) {
      // 返回新值而不是当前元素
    })
    
    let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);
    alert(lengths); // 5,7,6
    会把原来数组直接替换了
    
  11. 排序

    arr.sort(fn) 方法实现了通用的排序算法。我们不需要关心它的内部工作原理(大多数情况下都是经过 快速排序Timsort 算法优化的)。它将遍历数组,使用提供的函数比较其元素并对其重新排序,我们所需要的就是提供执行比较的函数 fn

    function compareNumeric(a, b) {
      if (a > b) return 1;
      if (a == b) return 0;
      if (a < b) return -1;
    }
    let arr = [ 1, 2, 15 ];
    arr.sort(compareNumeric);
    alert(arr);  // 1, 2, 15
    
  12. 字符串与数组 拆分与合并

    let names = 'Bilbo, Gandalf, Nazgul';
    
    let arr = names.split(', ');
    
    for (let name of arr) {
      alert( `A message to ${name}.` ); // A message to Bilbo(和其他名字)
    }
    ------------------------
    let arr = ['Bilbo', 'Gandalf', 'Nazgul'];
    
    let str = arr.join(';'); // 使用分号 ; 将数组粘合成字符串
    
    alert( str ); // Bilbo;Gandalf;Nazgul
    
  13. 判断是否为数组

    alert(Array.isArray({})); // false
    
    alert(Array.isArray([])); // true
    
  14. Array.from()将可迭代对象转化为数组

Map

基本操作
在这里插入图片描述

let map = new Map();

map.set('1', 'str1');   // 字符串键
map.set(1, 'num1');     // 数字键
map.set(true, 'bool1'); // 布尔值键

// 还记得普通的 Object 吗? 它会将键转化为字符串
// Map 则会保留键的类型,所以下面这两个结果不同:
alert( map.get(1)   ); // 'num1'
alert( map.get('1') ); // 'str1'

alert( map.size ); // 3

遍历方式

let recipeMap = new Map([
  ['cucumber', 500],
  ['tomatoes', 350],
  ['onion',    50]
]);

// 遍历所有的键(vegetables)
for (let vegetable of recipeMap.keys()) {
  alert(vegetable); // cucumber, tomatoes, onion
}

// 遍历所有的值(amounts)
for (let amount of recipeMap.values()) {
  alert(amount); // 500, 350, 50
}

// 遍历所有的实体 [key, value]
for (let entry of recipeMap) { // 与 recipeMap.entries() 相同
  alert(entry); // cucumber,500 (and so on)
}

Set(不会重复)

在这里插入图片描述

其他类型

img

解构

解构赋值可以简洁地将一个对象或数组拆开赋值到多个变量上。

let user = {
 name: "John",
 age: 30
};

// 使用循环遍历键—值对
for (let [key, value] of Object.entries(user)) {
 alert(`${key}:${value}`); // name:John, then age:30
}

JSON的序列化与反序列化
  1. JSON.stringify 将对象转换为 JSON。

  2. JSON.parse 将 JSON 转换回对象。

  3. 在stringify中还有一个参数可以确定那些key进行转化为字符串

let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  participants: [{name: "John"}, {name: "Alice"}],
  place: room // meetup 引用了 room
};
room.occupiedBy = meetup; // room 引用了 meetup
alert( JSON.stringify(meetup, ['title', 'participants']) );
// {"title":"Conference","participants":[{},{}]}
这是因为name没有传进来,name也算是键值

同理在parse也可以传一个参数叫reviver

let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';

let meetup = JSON.parse(str, function(key, value) {
  if (key == 'date') return new Date(value);
  return value;
});

alert( meetup.date.getDate() ); // 现在正常运行了!
  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值