尼采般地抒情

尼采般地抒情

尼采般地抒情

音乐盒

站点信息

文章总数目: 321
已运行时间: 1782

前言:JavaScript语言基本知识

  1. JavaScript编程语言
  2. 变量
  3. 基本数据类型
  4. 运算符和表达式
  5. 语句结构
  6. 异常与捕获

一、JavaScript编程语言

解释型语言和编译型语言

计算机不能直接理解任何除机器语言以外的语言,所以必须要把程序员所写的程序语言翻译成机器语言才能执行程序。程序语言翻译成机器语言的工具,被称为翻译器。

  • 翻译器翻译的方式有两种:一个是编译,另外一个是解释。两种方式之间的区别在于翻译的时间点不同
  • 编译器是在代码执行之前进行编译,生成中间代码文件
  • 解释器是在运行时进行及时解释,并立即执行(当编译器以解释方式运行的时候,也称之为解释器)

执行过程

是什么

  • JavaScript 是世界上最流行的语言之一,是一种运行在客户端的脚本语言 (Script == 脚本)
  • 脚本语言:不需要编译,运行过程中由 js 解释器( js 引擎)逐行来进行解释并执行
  • 现在也可以基于 Node.js 技术进行服务器端编程

用途

  • 表单动态校验(密码强度检测) ( JS 产生最初的目的 )
  • 网页特效
  • 服务端开发(Node.js)
  • 桌面程序(Electron)
  • App(Cordova)
  • 控制硬件-物联网(Ruff)
  • 游戏开发(cocos2d-js)

浏览器执行JS简介

浏览器分成两部分:

  1. 渲染引擎:用来解析HTML和CSS,俗称内核。比如Chrome浏览器是blink,老版本是webkit。
  2. JS引擎:也称JS解释器,用来读取网页中的JavaScript代码,对其处理后运行。比如Chrome的V8引擎。

浏览器本身并不会执行JS代码,而是通过内置 JavaScript 引擎(解释器) 来执行 JS 代码 。JS 引擎执行代码时逐行解释每一句源码(转换为机器语言),然后由计算机去执行,所以 JavaScript 语言归为脚本语言,会逐行解释执行。

JavaScript的组成

  1. ECMAScript

ECMAScript 是由ECMA 国际( 原欧洲计算机制造商协会)进行标准化的一门编程语言,这种语言在万维网上应用广泛,它往往被称为 JavaScript或 JScript,但实际上后两者是 ECMAScript 语言的实现和扩展。

ECMAScript:规定了JS的编程语法和基础核心知识,是所有浏览器厂商共同遵守的一套JS语法工业标准。

  1. DOM——文档对象模型

文档对象模型(DocumentObject Model,简称DOM),是W3C组织推荐的处理可扩展标记语言的标准编程接口。通过 DOM 提供的接口可以对页面上的各种元素进行操作(大小、位置、颜色等)

  1. BOM——浏览器对象模型

浏览器对象模型(Browser Object Model,简称BOM) 是指浏览器对象模型,它提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。通过BOM可以操作浏览器窗口,比如弹出框、控制浏览器跳转、获取分辨率等。

二、变量

“行为怪异的var所造成的各种问题。”

《JavaScript高级程序设计》

var和let和const

【var】

  • 在全局区域声明是全局变量
  • 函数里面用var声明变量的时候是局部变量,但是省略var的时候,就变成全局变量了(坑……),好在严格模式下如果在函数里面这样定义会报错

  • 会变量提升,把所有变量声明都拉到函数作用域的顶部,所以在同一个域里面,var定义的变量,先使用在定义也是可以的。
  • 可以多次定义赋值
var i = 0;
var i = 0;

编写代码不建议使用var来定义变量了

【let】

  • 作用范围:块作用域
  • 会有冗余声明(不可以多次定义赋值)
  • 不会变量提升,俗称“暂时性死区”

【const】

  • 作用范围:块作用域
  • 声明变量必须同时初始化变量
  • 不允许重复声明
  • 尝试修改变量的值报错,但对对象操作就不一样了

使用建议:参照《JavaScript高级程序设计》书中所说的,“不使用var,const优先,let次之”。

作用域链

因为var可以多次声明这一个特点,就产生出了一个问题,在函数里面声明一个变量,在函数外也声明一个同名的变量,就会导致接下来的代码使用哪一个变量的情况。分析是哪个其实也好办,就是看调用的这个函数离哪个声明的变量最近,哪个最近取哪个。


作用域链有其解决的办法,那就是利用作用域链,但是使用let和const更为方便


比如:循环迭代过程中

预解析和代码执行

浏览器的js解析器当中,会将js代码分两步执行,分别为,预解析和执行代码

  • 预解析:在当前作用域下, JS 代码执行之前,浏览器会默认把带有 var 和 function 声明的变量在内存中进行提前声明或者定义,这个机制也叫hoisting(变量提升)
  • 代码执行: 从上到下执行JS语句

三、基本数据类型

8种数据类型

最新的 ECMAScript 标准定义了 8 种数据类型:

七种基本数据类型

  1. 数字(Number),整数或浮点数,例如: 42 或者 3.14159。
  2. 字符串(String),字符串是一串表示文本值的字符序列,例如:"Howdy" 。
  3. 布尔值(Boolean),有 2 个值分别是:true 和 false.
  4. undefined,和 null 一样是一个特殊的关键字,undefined 表示变量未赋值时的属性。
  5. null,一个表明 null 值的特殊关键字。JavaScript 是大小写敏感的,因此 null 与 Null、NULL或变体完全不同。
  6. 任意精度的整数 (BigInt) ,可以安全地存储和操作大整数,甚至可以超过数字的安全整数限制。
  7. 代表(Symbol)( 在 ECMAScript 6 中新添加的类型).。一种实例是唯一且不可改变的数据类型。
  8. 以及对象(Object)。

自动确定类型机制

JavaScript 是一种弱类型或者说动态语言。这意味着不用提前声明变量的类型,在程序运行过程中,类型会被自动确定,这是和Java基本类型不同的一点。

let age = 10;        // 这是一个数字型
let forsome= '是的';   // 这是一个字符串

在代码运行时,变量的数据类型是由 JS引擎 根据 = 右边变量值的数据类型来判断 的,运行完毕之后, 变量就确定了数据类型。JavaScript 拥有动态类型,同时也意味着相同的变量可用作不同的类型

number

  1. Infinity和NaN是JavaScript语言的全局属性(JavaScript 标准内置对象 - JavaScript | MDN
  2. JavaScript 数字类型既可以保存整数,也可以保存小数(浮点数)

Number.MAX_VALUE

最大值

Number.MIN_VALUE

最小值

Infinity

正无穷大

Number.MAX_VALUE < Infinity

-Infinity

负无穷大

NaN

非数值

string

  1. 字符串长度:通过字符串的 length 属性可以获取整个字符串的长度。

Java里面基本数据类型比如定义了一串字符,是没有相应长度length属性一说的,除非new一个字符的对象。JavaScript里面也有相应的机制,称之为——“包装对象”,可以直接使用length属性,指的是与数值、字符串、布尔值分别相对应的Number、String、Boolean三个原生对象。这三个原生对象可以把原始类型的值变成(包装成)对象,和Java里面一样。

  1. 转义符

转义符

解释说明

\n

换行符,n 是 newline 的意思

\ \

斜杠 \

'

' 单引号

"

”双引号

\t

tab 缩进

\b

空格 ,b 是 blank 的意思

boolean

  1. 布尔类型有两个值:true 和 false ,其中 true 表示真(对),而 false 表示假(错)。
  2. 布尔型和数字型相加的时候, true 的值为 1 ,false 的值为 0。
console.log(true + 1);  // 2
console.log(false + 1); // 1

undefined

一个声明未定义的变量的初始值,或没有实际参数的形式参数。

null

  1. null是一个表示“空”的对象,转为数值时为0
  2. undefined是一个表示”此处无定义”的原始值,转为数值时为NaN

symbol

使用场景:

  1. 定义一组常量来代表一种业务逻辑下的几个不同类型。
  2. 如果做为对象的键,那么这个的keys遍历键是会略过symbol类型的键的,这一点可以用来构造一些复杂数据结构,比如设置不太想让外部用到的键遍历。

🤖数据类型判断及转换

  • 数据类型的几种判断方法

判断方法

缺点

typeof

  • 不能判断null
  • 区分array/Date/RegExp等

instanceof
(判断的是否处于原型链上)

  • 无法检测null和undefined
  • 未必准确,无法判断字面量方式创建的基本数据类型

constructor

无法检测null和undefined,未必准确

Object.prototype.toString.call()

测试代码

const values = {
  b: false,
  s: 'str',
  n: 1,
  u: undefined,
  l: null,
  sy: Symbol('sy'),
  bi: BigInt(1),
  o: {},
  f: () => {},
  a: [],
  p: Promise.resolve(),
  r: /abc/,
  d: new Date(),
  e: new Error(),
  m: new Map(),
  set: new Set(),
  nan: NaN,
};
const logTyppeof = (values: any) => {
  console.info('[typeof]');
  Object.entries(values).forEach(([key, value]) => {
    console.log(key, typeof value);
  });
};
const toString = (values: any) => {
  console.info('[toString]');
  Object.entries(values).forEach(([key, value]) => {
    console.info(key, Object.prototype.toString.call(value));
  });
};

// logTyppeof(values);
// toString(values);
  • 数据类型的转换:使用表单、prompt 获取过来的数据默认是字符串类型的,此时就不能直接简单的进行加法运算,而需要转换变量的数据类型
  • 当参数为: ''、0、NaN、null、undefined,转换为false,其他都转换称true

四、运算符和表达式

基本运算符

  • +-*/%:数学运算符
  • ++:自增运算符(注意i++和++i区别)
  • ==、===:区别就是后者要求值和数据类型都相等,前者只要求值
  • &&、||、!:与或非
  • !!:将值转换为Boolean类型

位移运算符和二进制位运算符

  • <<:按位左移
  • >>:按位右移
  • >>>:按位无符号右移


  • &:按位与
  • |:按位或

两边转换为二进制进行相加得到后的结果

技巧:Number|0 将Number取整

Number为正数的时候,相当于向下取整,Math.floor()。console.log(1.1|0) // 1

Number为负数的时候,相当于向上取整,Math.ceil()。console.log(-5.22|0) // -5

  • ~:按位非

按位取反(eg:00000001 -> 11111110)

  • ^:按位异或

💥位运算符实际应用

  • 判断奇偶
console.info(2 & 1); // 偶数 -> 0
console.info(3 & 1); // 奇数 -> 1
  • 取整
console.info('[~~]');
console.log(~~6.66);
console.log(~~6.33);
console.log(~~-6.66);
console.log(~~-6.33);

console.info('[>>]');
console.log(6.66 >> 0);
console.log(6.33 >> 0);
console.log(-6.66 >> 0);
console.log(-6.33 >> 0);

console.info('[<<]');
console.log(6.66 << 0);
console.log(6.33 << 0);
console.log(-6.66 << 0);
console.log(-6.33 << 0);

console.info('[|]');
console.log(6.66 | 0);
console.log(6.33 | 0);
console.log(-6.66 | 0);
console.log(-6.33 | 0);

// >>>不可对负数取整
console.info('[>>>]');
console.log(6.66 >>> 0);
console.log(6.33 >>> 0);
console.log(-6.66 >>> 0);
console.log(-6.33 >>> 0);

  • 交换值(不定义第三个变量)
let a = 5;
let b = 8;
a ^= b;
b ^= a;
a ^= b;
console.log(a); // 8
console.log(b); // 5
  • 16进制颜色值和RGB颜色值互换
/**
 * 16进制颜色值转RGB
 * @param  {String} hex 16进制颜色字符串
 * @return {String}     RGB颜色字符串
 */
function hexToRGB(hex: string) {
  const hexx = parseInt(hex.replace('#', '0x'), 16);
  const r = hexx >> 16;
  const g = (hexx >> 8) & 0xff;
  const b = hexx & 0xff;
  return `rgb(${r}, ${g}, ${b})`;
}

/**
 * RGB颜色转16进制颜色
 * @param  {String} rgb RGB进制颜色字符串
 * @return {String}     16进制颜色字符串
 */
function RGBToHex(rgb: string) {
  const rgbArr = rgb.split(/[^\d]+/);
  const color = (Number(rgbArr[1]) << 16) | (Number(rgbArr[2]) << 8) | Number(rgbArr[3]);
  return '#' + color.toString(16);
}

console.log(hexToRGB('#6424be')); // 'rgb(100,36,190)'
console.log(RGBToHex('rgb(100,36,190)')); // '#6424be'
  • 一个数除以2并向下取整(求中位数)【等同于:Math.floor(n / 2)
let n = 10  // 二进制:1010
n >> 1      // 二进制:0101 = 5

let n = 7   // 二进制:0111
n >> 1      // 二进制:0011 = 3
  • 判断是否为2的整数幂
// n & (n - 1)
console.info(64 & (64 - 1)); // 0

🎨逗号运算符

逗号(,)运算符对它的每个操作数从左到右求值,并返回最后一个操作数的值。这让你可以创建一个复合表达式,其中多个表达式被评估,复合表达式的最终值是其成员表达式中最右边的值。这通常用于为 for 循环提供多个参数。

逗号赋值

let x, y, z = 1;
console.info(x, y, z)
undefined undefined 1
const [x, y, z] = [, 1, 2];
console.info(x, y, z);
undefined 1 2

逗号运算

let a = 1;
const b = ((a += 1), a * 2);
console.info('b', b);
// 箭头函数只返回最有一个表达式的值
const c = (x => (x + 1, x * 2))(1);
console.info('c', c);
const d = (x => (x += 1, x *= 2))(1);
console.info('d', d);
4
2
4

可选链运算符

  • 可选链?.:类似判断条件是否存在
const obj = {
  a: 1
}
console.info(obj?.a)
console.info(obj?.b)
1
undefined

💙二元逻辑运算符

  • && 逻辑与:存在假则返回假的值,否则返回最后一个值
console.log( 123 && 456 );        
console.log( 0 && 456 );        
console.log( 123 && 456&& 789 ); 
456
0
789
  • || 逻辑或:返回最先为真的值
console.log( 123 || 456 );     
console.log( 0 ||  456 );        
console.log( 123 || 456 || 789 );  
123
456
123
  • ?? 空值合并运算符:当前面的值为null或者undefined时,取后面的值,否则取前面的值
console.info(null ?? 1)
console.info(undefined ?? 1)
console.info(0 ?? 1)
console.info(false ?? 1)
console.info(true ?? 1)
1
1
0
false
true

🧡赋值运算符

  • ??=
let x = null;
let y = false;
let z = 1
x ??= 1;
y ??= 1;
z ??= 2;
console.info(x, y, z)
1 false 1
  • ||=
let x = null;
let y = false;
let z = 1
x ||= 1;
y ||= 1;
z ||= 2;
console.info(x, y, z)
1 1 1
  • &&=
let x = null;
let y = false;
let z = 1
x &&= 1;
y &&= 1;
z &&= 2;
console.info(x, y, z)
null false 2
  • >>=:右位移复制
  • ……

❤️赋值运算符和二元逻辑运算符相结合

Vue源码中应用到较多改表达式:

let a1 = 1,
  a2 = 2,
  a3 = false,
  a4 = null;
const b = 4;
console.info('>>> 1 >>>', (a1 &&= b === 4));
console.info('>>> 2 >>>', (a2 ||= b === 4));
console.info('>>> 3 >>>', (a3 ??= b === 4));
console.info('>>> 4 >>>', (a4 ??= b === 4));
true
2
false
true

五、语句结构

顺序结构

浏览器引擎对js代码是从上往下依次执行的

所以js代码的位置,以及代码与代码相互依赖的关系顺序都要注意

同时注意异步逻辑代码的执行执行机制

选择结构

  • if...else
  • switch 语句
switch( 表达式 ){ 
  case value1:
    // 表达式 等于 value1 时要执行的代码
    break;
  case value2:
    // 表达式 等于 value2 时要执行的代码
    break;
  default:
    // 表达式 不等于任何一个 value 时要执行的代码
}
const status = isStatus ? 1 : 2

循环结构

for、while、do while

和Java一样都有,都有for(){}、while(){}、do{}while()三种结构,以及continue、break两个关键字,用法完全一样,在java里面有增强的for循环一说,js也有相应的增强的for循环。

  • for
  • while
  • do...while

for in, for of

  • for in不能保证执行顺序
  • 如果对null或者undefined执行for in表现不会执行,执行for of会报错
const obj = {
  a: 111,
  b: 222,
}
//  [synchronous]
// for in
for(let i in obj) {
  console.log('for in: ', i, obj[i]);
}
// for of
for(let [i, j] of Object.entries(obj)) {
  console.log('for of: ', i, j);
}
for in:  a 111
for in:  b 222
for of:  a 111
for of:  b 222

🔴循环里面执行异步逻辑

(async function () {
  const asyncFunction = (delay) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(`Result from async function ${delay}`);
      }, delay * 1000);
    });
  }

  // [asynchronous]
  // map
  const list = await Promise.all([3, 2, 1].map(async item => `[map][${item}s] ${await asyncFunction(item)}`))
  console.info('>>> map list >>>', list)
  // forEach
  try {
    await Promise.all([3, 2, 1].forEach(async item => console.info(`[forEach][${item}s] ${await asyncFunction(item)}`)))
  } catch (error) {
    console.info(`[forEach] catch error: `, error)
  }
  
  // for of
  for (let item of [3, 2, 1]) {
    console.info(`[for of][${item}s] ${await asyncFunction(item)}`)
  }
  // for
  for (let i = 3; i > 0; i--) {
    console.info(`[for][${i}s] ${await asyncFunction(i)}`)
  }
  //  for await of
  const runForAwait = async () => {
    for await (let item of [3, 2, 1].map(item => asyncFunction(item))) {
      console.info(`[for await] ${item}`)
    }
  }
  runForAwait()
})();
>>> map list >>> (3) ['[map][3s] Result from async function 3', '[map][2s] Result from async function 2', '[map][1s] Result from async function 1']
  [forEach] catch error:  TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
at Function.all (<anonymous>)
at xxx
  [forEach][1s] Result from async function 1
  [forEach][2s] Result from async function 2
  [forEach][3s] Result from async function 3
  [for of][3s] Result from async function 3
  [for of][2s] Result from async function 2
  [for of][1s] Result from async function 1
  [for][3s] Result from async function 3
  [for][2s] Result from async function 2
  [for][1s] Result from async function 1
  [for await] Result from async function 3
  [for await] Result from async function 2
  [for await] Result from async function 1

forEach的表现形式不会等待执行

六、异常与捕获

throw

抛出一个用户定义的异常。

throw('xxxxxxxxxxxxxx')
throw new EvalError('xxxxxxxxxxxxxx')

try...catch...finally

标记一个语句块,并指定一个应该抛出异常的反馈。

评论区

什么都不舍弃,什么也改变不了