最近工作中会涉及到前端工作,因此要用到 JavaScript,故而学习一波。参考:
环境准备:Jupyter Notebook + IJavaScript
打算在 Jupyter Notebook 中使用 JavaScript,便于交互式的学习和探查。于是找到了 IJavascript 这个 Jupyter Notebook 的 JavaScript Kernel. 其安装和使用参考主页:http://n-riesco.github.io/ijavascript/
注意📢:在安装的过程中(brew install pkg-config node zeromq),可能会出现“Error: No such file or directory @ rb_sysopen”的错误,可以参考:https://blog.csdn.net/weixin_43770545/article/details/127715990, 做相应的问题排查和修复。
安装好 IJavaScript 之后,可以直接通过 jupyter-lab(或者 jupyter notebook)命令启动,然后选择 JavaScript 内核的 Notebook。
然后就能在其中测试和探索 JavaScript 的各种功能。
下面是跟随https://developer.mozilla.org/en-US/docs/Web/JavaScript/Language_Overview 教程一路 run 下来的结果。
Data types:数据类型
JavaScript 中有 7 中原生类型:
其他的数据都属于Object
,包括:
疑问❓:没有 dict 或者 map 吗?
Numbers
JavaScript 有两种数字类型:Number 和 Bigint。
Number 既能表示整数(-(2^53-1)~2^53-1),又能表示浮点数(最大值:1.79*10^308)
console.log(3/2); // 1.5, not 1
复制代码
// 浮点数可以存在不精确的情况
console.log(0.1+0.2)
复制代码
// Number literals can also have prefixes to indicate the base
// (binary, octal, decimal, or hexadecimal), or an exponent suffix.
console.log(0b111110111); // 503
console.log(0o767); // 503
console.log(0x1f7); // 503
console.log(5.03e2); // 503
复制代码
Bigint 用来指定数值是整数,它是在数字后面跟一个后缀:n
// Bigint与Number不能混合运算
// console.log(-3n + 2); //TypeError: Cannot mix BigInt and other types, use explicit conversions
复制代码
Math 是一个提供标准数据运算的 Object。
var r0 = 2;
const circumference0 = 2 * Math.PI * r0;
复制代码
可以使用下面的方式做数字和字符串之间的转换:
parseInt('123.4'); // 可以解析小数,只是得到其整数部分
复制代码
parseInt('0b111110111'); // 不能正确解析其他进制的数
复制代码
NaN
表示 Not a Number,比如解析一个非数字表达;传入NaN
做运算,会返回NaN
。
Infinity
表示无穷大,除以 0 会产生这个值。它有正负之分。
parseInt('Not a Number');
复制代码
String
JavaScript 中的字符串就是 Unicode(准确说是 UTF-16 编码)字符的序列。
console.log('Hello, world');
console.log('你好,世界!');
复制代码
// 单引号和双引号都OK
console.log("Hello, world");
console.log("你好,世界!");
复制代码
// 字符和字符串之间也没有差别:字符就是长度为1的字符串
'Hello'[1] === 'e'
复制代码
// 字符串长度:length属性
'Hello'.length
复制代码
// 字符串相加
const age = 25;
console.log('I am ' + age + ' years old.') // String concatenation
复制代码
// 也可用字符串模板:使用反引号 `` + ${}
console.log(`I am ${age} years old.`)
复制代码
其他类型
在 JavaScript 中,null
表示 deliberate non-value(故意的空值),undefined
表示 absence of value(没有定义的值)。null
只能通过 null 关键字获取,而undefined
可以通过下面的多种方式获取:
function returnNothing() {
return
}
console.log(returnNothing())
复制代码
var arr = [1, 2, 3]
console.log(arr.iDontExist)
复制代码
JavaScript 的布尔值true
和false
,任何值都可以转换成布尔值,其规则如下:
false
, 0
, 空字符串""
, NaN
, null
以及undefined
被转换成false
其他值都被转换成true
Variables: 变量
JavaScript 中,变量可以由下面的三个关键字申明:let
, const
, var
。他们之间的差别可以参考:
JavaScript 中的 Var、Let 和 Const 有什么区别。
总结其异同点:
var
声明是全局作用域或函数作用域,而let
和const
是块作用域。
var
变量可以在其范围内更新和重新声明; let
变量可以被更新但不能重新声明; const
变量既不能更新也不能重新声明。
它们都被提升到其作用域的顶端。但是,虽然使用变量 undefined 初始化了var
变量,但未初始化let
和const
变量。
尽管可以在不初始化的情况下声明var
和let
,但是在声明期间必须初始化const
。
现在let
是主流的申明方式。
下面通过几个例子来说明。
// var的作用域是全局或者函数范围
var greeter = 'hi';
function newFunc() {
var hello = 'hello';
}
console.log(greeter);
// console.log(hello); // ReferenceError: hello is not defined
复制代码
// var变量可以重新申明和修改
var greeter = 'hi';
greeter = 'hello';
console.log(greeter);
var greeter = 'hi hi hi';
console.log(greeter);
复制代码
// var 的变量提升
// 变量提升是 JavaScript 的一种机制:在执行代码之前,变量和函数声明会移至其作用域的顶部。这意味着如果我们这样做:
console.log(greeter0);
var greeter0 = 'hi 0';
复制代码
// 上面的代码会被解释为:
var greeter0;
console.log(greeter0);
greeter0 = 'hi 0';
复制代码
// var的问题:因为全局作用域造成的不希望发生的赋值和引用
var greeter1 = 'hi';
var times = 4;
if(times > 3) {
var greeter1 = 'Hello'
}
console.log(greeter1)
复制代码
上例中,if block 中的 greeter1 影响了全局 greeter1 的值。这有可能是我们不希望看到的。这就是使用 let 的原因:
// let的作用域是block {} 级别的
let greeter3 = 'hi';
let times1 = 4;
if(times1 > 3) {
let greeter3 = 'Hello';
console.log(greeter3);
}
console.log(greeter3);
复制代码
// let变量能被修改,但不能被重新申明
let greeter5 = 'hi';
greeter5 = 'hello';
console.log(greeter5);
复制代码
// let greeter5 = 'hi'; //SyntaxError: Identifier 'greeter5' has already been declared
复制代码
const
与let
类似,只不过其申明的变量必须保持常量,不能被修改。
const greeter6 = 'hi';
// greeter6 = 'hello'; //TypeError: Assignment to constant variable.
复制代码
不过其申明的 object 类型的变量,其属性是可以被改变的。
const obj_greeter = {
message: 'hi',
times: 4
}
obj_greeter.message = 'hello'
console.log(obj_greeter)
复制代码
{ message: 'hello', times: 4 }
复制代码
obj_greeter.receiver = 'Jack'
console.log(obj_greeter)
复制代码
{ message: 'hello', times: 4, receiver: 'Jack' }
复制代码
JavaScript 是动态类型,意味着同一个变量名可以指向不同类型的数据。
Operators:运算符
JavaScript 支持的运算符包括:
+
, -
, *
, /
, %
, **
: 数学运算
=
, +=
, -=
: 赋值
++
, --
: 自加自减
+
: 字符串拼接
>
, <
, >=
, <=
: 不等比较
==
: 双等号,不同类型会强制转换(type coercion), 对应的不等号是!=
===
: 三等号,不同类型不会强制转换, 对应的不等号是!==
&&
, ||
, !
: 逻辑运算:与或非
&
, |
, ~
: 位运算:与或非
更详尽的运算符说明,见链接。
下面是一些示例。
// 双等号 vs 三等号
console.log(123 == '123');
console.log(1 == true);
console.log(123 === '123');
console.log(1 === true);
复制代码
// 逻辑运算
const a1 = 0 && 'Hello'; // 0 because 0 is "falsy"
console.log(a1);
复制代码
const b1 = "Hello" || "world"; // "Hello" because both "Hello" and "world" are "truthy"
console.log(b1);
复制代码
Grammar: 语法风格
JavaScript 的语法风格接近于 C 语言。有下面几点值得注意:
注释:单行用//
,多行用/* */
表达式的结尾用';':但这个分号也是可选的
Control Structure: 控制结构
和大多数语言一样,JavaScript 包括下面的控制结构:
Objects: 对象类型
JavaScript 的 object 类型用来放键值对 key-value 数据,相当于 Python 中的 dict. object 是非常动态的, 它的属性可以随时被添加、删除、重排、突变 mutated。object 的 key 总是 string 或者 symbol。
const obj = {
name: 'Carrot',
for: 'Max',
details: {
color: 'orange',
size: 12
}
}
obj
复制代码
{ name: 'Carrot', for: 'Max', details: { color: 'orange', size: 12 } }
复制代码
// dot notation: 只能是一个静态的标识符
console.log(obj.name);
// bracket notation:可以是一个动态的变量
console.log(obj['name'])
复制代码
// 用变量来标识一个key
let userName1 = 'nick';
obj[userName1] = 'Catty';
obj
复制代码
{
name: 'Carrot',
for: 'Max',
details: { color: 'orange', size: 12 },
nick: 'Catty'
}
复制代码
// 链式访问
console.log(obj.details.color);
console.log(obj['details']['size']);
复制代码
// object是按照引用传值
const obj1 = {}
function doSth(o) {
o.x = 1;
}
doSth(obj1);
obj1
复制代码
// 引用
const obj2 = obj1;
obj1.y = 2;
obj2
复制代码
Arrays: 数组
JavaScript 中的数组是一种特殊的 object,通过[index]
来访问元素。数组长度通过.length
来获取。
const arr1 = ['dog', 'cat', 'hen'];
a.length
复制代码
// 可以给任意非负整数的下标赋值
arr1[10] = 'fox'
arr1
复制代码
[ 'dog', 'cat', 'hen', <7 empty items>, 'fox' ]
复制代码
// 数组访问越界是不会报错的,只会放回一个undefined
console.log(arr1[100])
复制代码
// 数组元素可以是任意类型的
arr1.push
arr1.push(false);
arr1.push(null);
arr1.push(101);
arr1.push({});
arr1.push([]);
arr1
复制代码
[
'dog',
'cat',
'hen',
<7 empty items>,
'fox',
false,
null,
101,
{},
[]
]
复制代码
arr1[14].x = 1;
arr1[15].push('ok');
arr1
复制代码
[
'dog',
'cat',
'hen',
<7 empty items>,
'fox',
false,
null,
101,
{ x: 1 },
[ 'ok' ]
]
复制代码
// 遍历: 通过for
for(let i = 0; i < arr1.length; i++) {
console.log('#' + i + ' : ' + arr1[i])
}
复制代码
#0 : dog
#1 : cat
#2 : hen
#3 : undefined
#4 : undefined
#5 : undefined
#6 : undefined
#7 : undefined
#8 : undefined
#9 : undefined
#10 : fox
#11 : false
#12 : null
#13 : 101
#14 : [object Object]
#15 : ok
复制代码
// 遍历: for...of
for(const a of arr1) {
console.log(a);
}
复制代码
dog
cat
hen
undefined
undefined
undefined
undefined
undefined
undefined
undefined
fox
false
null
101
{ x: 1 }
[ 'ok' ]
复制代码
Array 有一系列的方法,比如 cancat, map, filter, slice 等,详见Array。
// map
const babies = ['dog', 'cat', 'hen', 'fox'].map((name) => 'baby ' + name);
babies
复制代码
[ 'baby dog', 'baby cat', 'baby hen', 'baby fox' ]
复制代码
// concat: 数组拼接
const arr2 = ['a', 'b', 'c'];
const arr3 = ['d', 'e', 'f'];
const arr4 = arr2.concat(arr3);
arr4
复制代码
[ 'a', 'b', 'c', 'd', 'e', 'f' ]
复制代码
Functions: 函数
函数在 JavaScript 中非常重要,是其核心组成部分。下面是一个基础的函数声明:
function add(x, y) {
const total = x + y;
return total;
}
复制代码
// 函数传参个数可以少于定义的参数个数,缺少的参数会被定义成undefined
add(); // Equivalent to add(undefined, undefined)
复制代码
// 如果传入的参数个数多余定义的,多余的参数被直接忽略。总之,函数不会因为参数的多少而报语法错
add(1, 2, 3, 4);
复制代码
函数的参数可以是 rest parameter 语法,通过一个数组来存储未明确指定的参数值,类似 Python 的*args
(注:在语法层面没有**kwargs
)。
function avg(...args) {
let sum = 0;
for (const item of args) {
sum += item;
}
return sum / args.length;
}
avg(1, 2, 4, 5)
复制代码
// 还可以这么调用
const arr5 = [5, 6, 7, 8]
avg(...arr5)
复制代码
虽然 JavaScript 的函数不支持**kwargs
这样的命名参数,但是可以通过传入 object 类型参数,通过object destructuring
来pack/unpack
。
// Note the { } braces: this is destructuring an object
function area({ width, height }) {
return width * height;
}
// The { } braces here create a new object
console.log(area({ width: 2, height: 3 }));
复制代码
console.log(area({ width1: 2, height1: 3 }));
复制代码
// 与其他语言一样,JavaScript的函数支持参数的默认值
function avg3(v1, v2, v3=0) {
return (v1 + v2 + v3)/3
}
avg3(1, 2)
复制代码
Anonymous functions: 匿名函数
匿名函数就是没有名字的函数。在实践中,匿名函数常常会作为参数传递给其他函数,或者立刻传入参数让函数立刻被调用(通常只被调用一次),抑或被另一个函数作为返回值返回。
下面是一个匿名函数的定义方式:function 后面不带函数名。
const avgFunc = function (...args) {
let sum = 0;
for (const item of args) {
sum += item;
}
return sum / args.length;
};
avgFunc(1,2,4);
复制代码
另一种定义的方式是通过 arrow function expression,也就是=>
:
const avgFunc2 = (...args) => {
let sum = 0;
for (const item of args) {
sum += item;
}
return sum / args.length;
};
avgFunc2(1,2,4);
复制代码
// 对简单的表达式,可以不用return
const sumFunc = (a, b, c) => a + b + c;
sumFunc(1, 2, 3);
复制代码
const sumFunc1 = (a, b, c) => {return a + b + c;};
sumFunc1(1, 2, 3);
复制代码
Functions are first-class objects: 函数是头等公民
函数像其他类型一样,可以被赋值给其他参数,或者传参给其他函数,以及被其他函数作为返回值返回。
const addxy = (x) => (y) => x + y;
复制代码
// Function accepting function
const babies2 = ["dog", "cat", "hen"].map((name) => `baby ${name}`);
babies2
复制代码
[ 'baby dog', 'baby cat', 'baby hen' ]
复制代码
Inner functions: 内部函数
在函数内部定义的函数,内部函数可以访问上层函数作用域内的变量。这个特性可以在内部函数间共享上层函数的变量,从而避免污染全局变量。
function parentFunc() {
const a = 1;
function innerFunc() {
const b = 4;
return a + b;
};
return innerFunc();
};
parentFunc();
复制代码
Classes: 类
JavaScript 的类定义类似 Java:
class Person {
constructor(name) {
this.name = name;
};
sayHello() {
return `Hello, I am ${this.name}!`;
};
};
const p = new Person('Maria');
p.sayHello();
复制代码
Asynchronous programming: 异步编程
JavaScript is single-threaded by nature. There's no paralleling; only concurrency. Asynchronous programming is powered by an event loop, which allows a set of tasks to be queued and polled for completion.
There are three idiomatic ways to write asynchronous code in JavaScript:
For example, here's how a file-read operation may look like in JavaScript:
// Callback-based
/*
fs.readFile(filename, (err, content) => {
// This callback is invoked when the file is read, which could be after a while
if (err) {
throw err;
}
console.log(content);
});
// Code here will be executed while the file is waiting to be read
*/
// Promise-based
/*
fs.readFile(filename)
.then((content) => {
// What to do when the file is read
console.log(content);
}).catch((err) => {
throw err;
});
// Code here will be executed while the file is waiting to be read
*/
// Async/await
/*
async function readFile(filename) {
const content = await fs.readFile(filename);
console.log(content);
}
*/
复制代码
Modules: 模块
模块通常是一个 js 文件,可以被一个文件路径或者 URL 指定。可以通过 import 或者 export 在 module 之间交换数据。
/*
import { foo } from "./foo.js";
// Unexported variables are local to the module
const b = 2;
export const a = 1;
*/
复制代码
这就是 JavaScript 的基本语法知识。Keep going!
评论