语言基础

DOM、BOM、Window、Document #

DOM 是操作文档的 API,Document 是其一个对象。

BOM 是操作浏览器的 API,Window 是其一个对象。

function component(s) {
  const element = document.createElement('div');
  element.innerHTML = s;
  return element;
}

document.body.appendChild(component("hello"));

SSR vs CSR #

服务器渲染和客户端渲染

var 和 let #

var 在全局级别(函数外部)进行声明会自动变成 Window对象的一个属性:

var name = "John";
window.name === name // true

let 或者 const 则不会:

let name = "John";
window.name === name // false

var 存在变量提升现象:闭包或函数内部重新声明后会对外部相同变量进行覆盖。

浅谈 JavaScript 变量提升

this 作用域 #

数据类型 #

undefined #

一个没有被赋值的变量就会有个默认值 undefined

Boolean #

true or false

Number #

JavaScript 中只有一种数字类型,基于双精度 64 位二进制格式的值($-(2^{53})-1$ 到 $2^{53}-1$)。

除了能够表示浮点数外,还有一些带符号的值:+Infinity (正无穷),-Infinity (负无穷)和 NaN (非数值,Not-a-Number),

要检查值是否大于或小于 +/-Infinity,可以使用常量 Number.MAX_VALUENumber.MIN_VALUE。另外在 ECMAScript 6 中,也可以通过 Number.isSafeInteger() 方法还有 Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER 来检查值是否在双精度浮点数的取值范围内。 超出这个范围,JavaScript 中的数字就不再安全了。

数字类型中只有一个整数有两种表示方法: 0 可表示为 -0 和 +0(“0” 是 +0 的简写)。 在实践中,这也几乎没有影响。 例如 +0 === -0 为真。 但是,可能要注意除以0的时候:

42 / +0; // Infinity
42 / -0; // -Infinity

String #

BigInt #

Symbol #

null #

Object 对象 #

判断 key 是否在对象中。 #

function main() {
    a = {hello: "world"}

    if ("hello" in a) {
        console.log("has 1")
    }

    if (a.hasOwnProperty("hello")) {
        console.log("has 2")
    }
}
// output:
// has 1
// has 2

解构赋值 #

解构赋值语法是一种 Javascript 表达式。通过解构赋值, 可以将属性/值从对象/数组中取出,赋值给其他变量。

function main() {
  l = [1, 2, 3];
  let [a, b, ...rest] = l;
  d = { hello: "world", foo: "bar", other: "other" };
  let { hello, foo, ...other } = d;
  console.log(a);
  console.log(b);
  console.log(rest);
  console.log(hello);
  console.log(foo);
  console.log(other);
}

/*
1
2
[ 3 ]
world
bar
{ other: 'other' }
*/

正则表达式 #

var re = /ab+c/;
// 等价于

var re = new RegExp("ab+c");

程序控制 #

循环控制 #

JS 支持传统的 for 语法,此外还有以下 for 的用法

for … of #

for (const element of ['a', 'b', 'c']) {
  console.log(element);
}

for … in #

for...in 是为遍历对象属性而构建的,不建议与数组一起使用,数组可以用for...of

for...in 得到的是属性名称。

var obj = {a:1, b:2, c:3};

for (var prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}

// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"

do…while #

function main(params) {
    let i = 0
    do {
        console.log(i)
        i += 1
    } while (i < 10);
}

statement 执行至少一次,并在每次 condition 值为真时重新执行。

with #

严格模式下不允许使用 with 语句,否则将视为语法错误(所以能看懂就行了,尽量别用。。)

var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;

// 等效于

with(location){
  var qs = search.substring(1);
  var hostName = hostname;
  var url = href;
}

switch #

这里有坑:省略 break 关键字会执行下一个 case

switch (expression) {
    case value: statement
	    break;
    case value: statement
	    break;
    case value: statement
    	break;
    default:
		...

throw #

???

try…catch #

函数 #

普通函数 #

function showMessage() {
  alert( 'Hello everyone!' );
}

showMessage(); // 函数调用

匿名函数 #

function main(params) {
    params = {hello: "world"}
    return function f() {
        console.log("hello", params)
    }
}

箭头函数 #

在 js 中区分函数的引用和函数的执行,箭头函数返回的是函数的引用,而不是函数的执行结果。

箭头函数中 this 指的是外部的 this,因为箭头函数没有 this,匿名函数则是内部的 this。

  • 箭头函数没有 this
  • 箭头函数没有 arguments 变量
  • 不能用 new
  • 不能用 super

深入理解箭头函数

生成器函数 #

调用一个生成器函数并不会马上执行它里面的语句,而是返回一个这个生成器的 迭代器 iterator )对象。当这个迭代器的 next() 方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现yield的位置为止,yield 后紧跟迭代器要返回的值。或者如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)。

typeof #

typeof 的对象一般是

instanceof #

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

arguments对象 #

example1:

(function() {
  for (let argument of arguments) {
    console.log(argument);
  }
})(1, 2, 3);

// 1
// 2
// 3

example2:

function foo(n) {
  // 隐式绑定 foo 函数的 arguments 对象. arguments[0] 是 n,即传给foo函数的第一个参数
  var f = () => arguments[0] + n;
  console.log(f());
}

foo(1); // 2
foo(2); // 4
foo(3); // 6
foo(3,2);//6

严格模式 #

参考 Javascript 严格模式详解

  1. 针对整个脚本文件

在脚本文件的第一行添加

"use strict";
  1. 针对单个函数

将"use strict"放在函数体的第一行,则整个函数以"严格模式"运行。

function strict(){
	"use strict";
	return "这是严格模式。";
}
  1. 将整个脚本文件放在一个立即执行的匿名函数之中。
(function (){
	"use strict";
	// some code here
})();

类(class) #

ES6 引入了面向对象的概念(class)

static

extends

getter 和 setter #

**get**语法将对象属性绑定到查询该属性时将被调用的函数。

const obj = {
  log: ['a', 'b', 'c'],
  get latest() {
    if (this.log.length === 0) {
      return undefined;
    }
    return this.log[this.log.length - 1];
  },
  set latest(value) {
    console.log(value)
  }
};

console.log(obj.latest);
// expected output: "c"
obj.latest = 1
// expected output: 1

prototype 原型链 #

JavaScript 是基于原型的语言。

当我们调用一个对象的属性时,如果对象没有该属性,JavaScript 解释器就会从对象的原型对象上去找该属性,如果原型上也没有该属性,那就去找原型的原型,直到最后返回null为止,null没有原型。这种属性查找的方式被称为原型链(prototype chain)。

准确地说,这些属性和方法定义在Object的构造器函数(constructor functions)之上的prototype属性上,而非对象实例本身。

__proto__(隐式原型)与 prototype(显式原型)

var o = new Foo();

// 等价于

var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);

Promise,async/await #

let promise = new Promise(function(resolve, reject) {
  // executor(生产者代码)
});

Promise #

new Promise 构造器返回的 promise 对象具有以下内部属性:

  • state — 最初是 "pending",然后在 resolve 被调用时变为 "fulfilled",或者在 reject 被调用时变为 "rejected"
  • result — 最初是 undefined,然后在 resolve(value) 被调用时变为 value,或者在 reject(error) 被调用时变为 error

与最初的 “pending” promise 相反,一个 resolved 或 rejected 的 promise 都会被称为 “settled”。

then/catch/finally #

async/await #

async function f() {
  return 1;
}

在函数前面的 “async” 这个单词表达了一个简单的事情:即这个函数总是返回一个 promise。其他值将自动被包装在一个 resolved 的 promise 中。

await 只在 async 函数内工作。await 实际上会暂停函数的执行,直到 promise 状态变为 settled,然后以 promise 的结果继续执行。这个行为不会耗费任何 CPU 资源,因为 JavaScript 引擎可以同时处理其他任务:执行其他脚本,处理事件等。相比于 promise.then,它只是获取 promise 的结果的一个更优雅的语法,同时也更易于读写。

module 模块 #

export+import (ES6) #

  1. 命名导出(每个模块包含任意数量)
  2. 默认导出(每个模块包含一个)

export

// 命名导出
export function hello (s) {
  return `hello, ${s}`;
}

import xxx from ‘xxx’

<!doctype html>
<script type="module">
  import {hello} from './hello.js'
  alert(hello("js"));
</script>

<script nomodule>
    alert("no module");
</script>

也可以

import * as hello from './hello.js'

nomodule

旧时的浏览器不理解 type="module", 会运行nomodule的代码块,支持 module 的浏览器不会运行该代码块。

与 Node.js 中 exports 和 required 区别

Node.js 如何处理 ES6 模块

exports+require (node.js) #

非空判断 #

null 和 undefined 的关系 #

null 是一个空对象指针,表示“空值”,使用 typeof 得到 object,所以是一个特殊的对象值。

undefined 表示的是一个变量为初始化时,得到的就是 undefined。

仅判断是否为 null #

变量需要赋值为 null,typeof 为 object,所以可以使用排除法,去掉其他类型,当然也可以直接与 null 做比较。

var a = null;

if (a === null) {
  console.log('a is null');
}

// 排除法
if(!a && typeof(a) !== 'undefined' && a != 0) {
  console.log('a 是 null');
}else{
  console.log('a 不是 null');
}

仅判断是否为 undefined #

var b

if(typeof b === 'undefined') {
    console.log("b is undefined")
} else{
    console.log("b is not undefined")
}

非空判断 #

function main(params) {
    let a = 0 // 或 null 或 undefined
    if (!a) {
        console.log("空")
    } else {
        console.log("非空")
    }
}

非空非 0 判断 #

也就是 null 或 undefined 判断

var a

if(!a && a !== 0) {
    console.log("a is empty")
} else{
    console.log("a is not empty")
}

两个等号与三个等号的区别 #

严格相等:===,非严格相等:==

使用 == 时,如果两边的类型不一样,会先进行类型转换。

=== 是直接进行比较,不会进行类型转换。

一般不推荐使用 == 了。

void 运算符 #

void 运算符 对给定的表达式进行求值,然后返回 undefined

void (async function () {
  console.log('hello void')
})();

“+” 加号运算符 #

参考 https://www.cnblogs.com/polk6/p/js-adv-addopr.html

一元运算符 #

语法: + Expression

说明: + 号运算符作为一元运算符时,Expression将进行 ToNumber 操作。

二元运算符 #

  1. 值进行 GetValue 操作。

  2. 值进行 ToPrimitive 操作,

  3. 若一方为 String 类型,2个值都进行 ToString 转换,最后进行字符串连接操作。

  4. 若都不是 String 类型,2个值都进行 ToNumber 转换,最后进行算数加法运算。

自增++ #

++i 先给 i 自加1,再计算表达式的值 i++ 先计算表达式的值,再给 i 自加1

内存管理 #

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory_Management

localStorage 和 sessionStorage #

sessionStorage:页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话。

localStorage 类似 sessionStorage,但其区别在于:存储在 localStorage 的数据可以长期保留;而当页面会话结束——也就是说,当页面被关闭时,存储在 sessionStorage 的数据会被清除 。

参考 #