js学习1

js学习1

date: 2024/5/17

基础

ES5 规范为了保证旧的功能能够使用,大部分的修改是默认不生效的,"use strict" 来明确地激活这些特性。当你的代码全都写在了 class 和 module 中时,则可以将 "use strict"; 这行代码省略掉。

"use strict";// 代码以现代模式工作,出现在脚本的最顶部,否则严格模式可能无法启用。
let hello='hello world!'//声明变量
const myBirthday = '18.04.1982';//声明常量

类型:Number、BigInt、String、Boolean、null、undefined、Object 和 Symbol (typeof 运算符返回参数的类型)。显式地调用 String(value)进行类型转换

交互:alert、prompt 和 confirm

比较字符串的大小时,是按照字典顺序逐个比较;不同类型的值比较会转换为数字在进行比较,那么就存在一个问题:普通的相等性检查 == 存在一个问题,它不能区分出 0false;也同样无法区分空字符串和 false;严格相等运算符 === 在进行比较时不会做任何的类型转换。相等性检查 == 和普通比较符 > < >= <= 的代码逻辑是相互独立的;进行值的比较时,null 会被转化为数字0;undefinednull 在相等性检查 == 中不会进行任何的类型转换。undefined 不应该被与其他值进行比较,undefined在比较中被转换为了NaN。

当一个值既不是 null 也不是 undefined 时,我们将其称为“已定义的(defined)”。a ?? b 的结果是:

  • 如果 a 是已定义的,则结果为 a
  • 如果 a 不是已定义的,则结果为 b

函数支持默认值:function showMessage(from, text = "no text given")

function sayHi() {//函数声明
  alert( "Hello" );
}
let sayHi = function() {// 函数表达式
  alert( "Hello" );
};

回调函数:ask 的两个参数值 showOk 和 showCancel 可以被称为 回调函数 或简称 回调。主要思想是我们传递一个函数,并期望在稍后必要时将其“回调”。

function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}

ask(
  "Do you agree?",
  function() { alert("You agreed."); },
  function() { alert("You canceled the execution."); }
);

箭头函数:只有一个参数,还可以省略掉参数外的圆括号;如果没有参数,括号则是空的(但括号必须保留)

let func = (arg1, arg2, ..., argN) => expression;//它是下面这段代码的更短的版本
let func = function(arg1, arg2, ..., argN) {
  return expression;
};
//多行箭头函数
let sum = (a, b) => {  // 花括号表示开始一个多行函数
  let result = a + b;
  return result; // 如果我们使用了花括号,那么我们需要一个显式的 “return”
};

debugger; // <-- 调试器会在这停止

使用以下 JavaScript 库进行测试:

  • Mocha —— 核心框架:提供了包括通用型测试函数 describeit,以及用于运行测试的主函数。
  • Chai —— 提供很多断言(assertion)支持的库。它提供了很多不同的断言,现在我们只需要用 assert.equal
  • Sinon —— 用于监视函数、模拟内建函数和其他函数的库,我们在后面才会用到它。

转译器(Transpilers):以解析(“阅读和理解”)现代代码,并使用旧的语法结构对其进行重写,进而使其也可以在旧的引擎中工作。Babel 是最著名的转译器之一。

垫片(Polyfills):新的语言特性可能不仅包括语法结构和运算符,还可能包括内建函数。在一些(非常过时的)JavaScript 引擎中没有 Math.trunc 函数,更新/添加新函数的脚本被称为“polyfill”。它“填补”了空白并添加了缺失的实现。

对象

基础

对象用来存储键值对和更复杂的实体。通过使用带有可选 属性列表 的花括号 {…} 来创建对象。一个属性就是一个键值对(“key: value”),其中键(key)是一个字符串(也叫做属性名),值(value)可以是任何值。

//创建空对象
let user = new Object(); // “构造函数” 的语法
let user = {};  // “字面量” 的语法

let user = {     // 一个对象
  name: "John",  // 键 "name",值 "John"
  age: 30        // 键 "age",值 30
};
alert( user.age ); // 30,读取值
user.isAdmin = true;//添加值
delete user.age;//删除值
//多字词语来作为属性名
"likes birds": true  // 多词属性名必须加引号

对于多词属性,点操作就不能用了,有另一种方法,就是使用方括号,可用于任何字符串user["likes birds"] = true;

计算属性:如果一个用户输入 "apple",bag 将变为 {apple: 5}。

let fruit = prompt("Which fruit to buy?", "apple");

let bag = {
  [fruit]: 5, // 属性名是从 fruit 变量中得到的
};

alert( bag.apple ); // 5 如果 fruit="apple"

属性简写

function makeUser(name, age) {
  return {
    name: name,
    age: age,
    // ……其他的属性
  };
}

let user = makeUser("John", 30);
alert(user.name); // John

可以用 name 来代替 name:name 像下面那样:

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

属性命名没有限制。属性名可以是任何字符串或者 symbol。其他类型会被自动地转换为字符串。一个名为 __proto__ 的属性。我们不能将它设置为一个非对象的值。能够被访问任何属性。即使属性不存在也不会报错!

读取不存在的属性只会得到 undefined。检查属性是否存在的操作符 "in":"key" in object。遍历属性,整数属性会被进行排序,其他属性则按照创建的顺序显示。

for (key in object) {
  // 对此对象属性中的每个键执行的代码
}

引用、复制、this、new、转换

对象是“通过引用”存储和复制的,而原始类型:字符串、数字、布尔值等 —— 总是“作为一个整体”复制。

赋值了对象的变量存储的不是对象本身,而是该对象“在内存中的地址” —— 换句话说就是对该对象的“引用”。

复制一个对象:创建一个新对象,通过遍历已有对象的属性,并在原始类型值的层面复制它们;可以使用 Object.assign 方法。Object.assign(dest, [src1, src2, src3...])。深层克隆:我们可以使用递归来实现它。采用现有的实现,例如 lodash 库的 _.cloneDeep(obj)。

作为对象属性的函数被称为 方法为了访问该对象,方法中可以使用 this 关键字。 this 可以用于任何函数,即使它不是对象的方法。this 的值是在代码运行时计算出来的,它取决于代码上下文。箭头函数有些特别:它们没有自己的 this。如果我们在这样的函数中引用 this,this 值取决于外部“正常的”函数。

user = {
  sayHi: function() {
    alert("Hello");
  }
};

// 方法简写看起来更好,对吧?
let user = {
  sayHi() { // 与 "sayHi: function(){...}" 一样
    alert("Hello");
  }
};

new User(...) 做的就是类似的事情,从技术上讲,任何函数(除了箭头函数,它没有自己的 this)都可以用作构造器。可以使用 new.target 属性来检查它是否被使用 new 进行调用了。对于常规调用,它为 undefined,对于使用 new 的调用,则等于该函数。带有对象的 return 返回该对象,在所有其他情况下返回 this。

function User(name) {
  // this = {};(隐式创建)
  // 添加属性到 this
  this.name = name;
  this.isAdmin = false;
  // return this;(隐式返回)
}

可选链?. 前面的值为 undefined 或者 null,它会停止运算并返回 undefined。 ?.() 用于调用一个可能不存在的函数。语法 ?.[] 也可以使用。

let user = {}; // user 没有 address 属性
alert( user?.address?.street ); // undefined(不报错)
delete user?.name; // 如果 user 存在,则删除 user.name

“symbol” 值表示唯一的标识符。可以使用 Symbol() 来创建这种类型的值,对象字面量 {...} 中使用 symbol,则需要使用方括号把它括起来。symbol 属性不参与 for..in 循环。全局 symbol,该调用会检查全局注册表,如果有一个描述为 key 的 symbol,则返回该 symbol,否则将创建一个新 symbol(Symbol(key)),并通过给定的 key 将其存储在注册表中。所有 symbol 都具有 description 属性。JavaScript 内部有很多“系统” symbol,我们可以使用它们来微调对象的各个方面。

// id 是描述为 "id" 的 symbol
let id = Symbol("id");
// 从全局注册表中读取
let id = Symbol.for("id"); // 如果该 symbol 不存在,则创建它
// 再次读取(可能是在代码中的另一个位置)
let idAgain = Symbol.for("id");
// 相同的 symbol
alert( id === idAgain ); // true
// 通过 name 获取 symbol
let sym = Symbol.for("name");
// 通过 symbol 获取 name
alert( Symbol.keyFor(sym) ); // name,适用于全局symbol

对象转换:单独对象在全局中都为true,不存在为false。只有字符串和数字转换。数字转换发生在对象相减或应用数学函数时。字符串转换 —— 通常发生在我们像 alert(obj) 这样输出一个对象和类似的上下文中。

hint:string,number,default

为了进行转换,JavaScript 尝试查找并调用三个对象方法:

  1. 调用 obj[Symbol.toPrimitive](hint) —— 带有 symbol 键 Symbol.toPrimitive(系统 symbol)的方法,如果这个方法存在的话,
  2. 否则,如果 hint 是 "string" —— 尝试调用 obj.toString()obj.valueOf(),无论哪个存在。
  3. 否则,如果 hint 是 "number""default" —— 尝试调用 obj.valueOf()obj.toString(),无论哪个存在。