写点什么

JavaScript 基础

作者:无人之路
  • 2022-12-22
    浙江
  • 本文字数:7334 字

    阅读完需:约 24 分钟

JavaScript基础

最近工作中会涉及到前端工作,因此要用到 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 中原生类型:


  • Number:除了非常非常大的整数,Number 可以表达任意数字(整数、浮点数均可)。

  • Bigint:非常非常大的整数。

  • String:字符串。

  • Boolean:布尔型,true/false。

  • Symbol:类似 uuid,creating unique identifiers that won't collide。

  • Undefined:没有被赋值的变量。

  • Null:non-value


其他的数据都属于Object,包括:


  • Function:函数不是一种特殊的数据结构,它只是可以被调用的一种特殊的 object。

  • Array

  • Date

  • RegExp

  • Error


疑问❓:没有 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
复制代码


1.5
复制代码


// 浮点数可以存在不精确的情况console.log(0.1+0.2)
复制代码


0.30000000000000004
复制代码


// Number literals can also have prefixes to indicate the base // (binary, octal, decimal, or hexadecimal), or an exponent suffix.
console.log(0b111110111); // 503console.log(0o767); // 503console.log(0x1f7); // 503console.log(5.03e2); // 503
复制代码


503503503503
复制代码


Bigint 用来指定数值是整数,它是在数字后面跟一个后缀:n


console.log(-3n)
复制代码


-3n
复制代码


// console.log(-3.1n)
复制代码


console.log(-3n/2n)
复制代码


-1n
复制代码


// Bigint与Number不能混合运算// console.log(-3n + 2); //TypeError: Cannot mix BigInt and other types, use explicit conversions
复制代码


Math 是一个提供标准数据运算的 Object。


Math.sin(3.5);
复制代码


-0.35078322768961984
复制代码


var r0 = 2;const circumference0 = 2 * Math.PI * r0;
复制代码


circumference0
复制代码


12.566370614359172
复制代码


可以使用下面的方式做数字和字符串之间的转换:


  • parseInt():把字符串解析成整数。

  • parseFloat():将字符串解析成一个浮点数。

  • Number():将表示 Number 数值的字符串解析成 Number 类型的值。也可以使用+/-符号来代替 Number()函数。


parseInt('123');
复制代码


123
复制代码


parseInt('123n');
复制代码


123
复制代码


parseInt('-123');
复制代码


-123
复制代码


parseInt('123.4'); // 可以解析小数,只是得到其整数部分
复制代码


123
复制代码


parseFloat('123.4'); 
复制代码


123.4
复制代码


parseInt('0b111110111'); // 不能正确解析其他进制的数
复制代码


0
复制代码


Number('0b111110111'); 
复制代码


503
复制代码


Number('0o767'); 
复制代码


503
复制代码


+'0o767'
复制代码


503
复制代码


-'0b111110111'
复制代码


-503
复制代码


NaN表示 Not a Number,比如解析一个非数字表达;传入NaN做运算,会返回NaN


Infinity表示无穷大,除以 0 会产生这个值。它有正负之分。


parseInt('Not a Number');
复制代码


NaN
复制代码


NaN + 1
复制代码


NaN
复制代码


1/0
复制代码


Infinity
复制代码


-1/0
复制代码


-Infinity
复制代码

String

JavaScript 中的字符串就是 Unicode(准确说是 UTF-16 编码)字符的序列。


console.log('Hello, world');console.log('你好,世界!');
复制代码


Hello, world你好,世界!
复制代码


// 单引号和双引号都OKconsole.log("Hello, world");console.log("你好,世界!");
复制代码


Hello, world你好,世界!
复制代码


// 字符和字符串之间也没有差别:字符就是长度为1的字符串'Hello'[1] === 'e'
复制代码


true
复制代码


// 字符串长度:length属性'Hello'.length
复制代码


5
复制代码


// 字符串相加const age = 25;console.log('I am ' + age + ' years old.') // String concatenation
复制代码


I am 25 years old.
复制代码


// 也可用字符串模板:使用反引号 `` + ${}console.log(`I am ${age} years old.`)
复制代码


I am 25 years old.
复制代码

其他类型

在 JavaScript 中,null表示 deliberate non-value(故意的空值),undefined表示 absence of value(没有定义的值)。null只能通过 null 关键字获取,而undefined可以通过下面的多种方式获取:


  • return 语句不带值(return;)

  • 访问 object 一个并不存在的属性(obj.iDontExist)

  • 申明一个变量但不赋值(let x;)


function returnNothing() {    return}
console.log(returnNothing())
复制代码


undefined
复制代码


var arr = [1, 2, 3]
console.log(arr.iDontExist)
复制代码


undefined
复制代码


let xxx;console.log(xxx)
复制代码


undefined
复制代码


JavaScript 的布尔值truefalse,任何值都可以转换成布尔值,其规则如下:


  • false, 0, 空字符串"", NaN, null以及undefined被转换成false

  • 其他值都被转换成true


Boolean("")
复制代码


false
复制代码


Boolean(0)
复制代码


false
复制代码


Boolean(0.0)
复制代码


false
复制代码


Boolean(undefined)
复制代码


false
复制代码


Boolean('Hello')
复制代码


true
复制代码

Variables: 变量

JavaScript 中,变量可以由下面的三个关键字申明:let, const, var。他们之间的差别可以参考:


JavaScript 中的 Var、Let 和 Const 有什么区别


总结其异同点:


  • var声明是全局作用域或函数作用域,而letconst是块作用域。

  • var变量可以在其范围内更新和重新声明; let变量可以被更新但不能重新声明; const变量既不能更新也不能重新声明。

  • 它们都被提升到其作用域的顶端。但是,虽然使用变量 undefined 初始化了var变量,但未初始化letconst变量。

  • 尽管可以在不初始化的情况下声明varlet,但是在声明期间必须初始化const


现在let是主流的申明方式。


下面通过几个例子来说明。


// var的作用域是全局或者函数范围
var greeter = 'hi';
function newFunc() { var hello = 'hello';}
console.log(greeter);// console.log(hello); // ReferenceError: hello is not defined
复制代码


hi
复制代码


// var变量可以重新申明和修改var greeter = 'hi';greeter = 'hello';console.log(greeter);
var greeter = 'hi hi hi';console.log(greeter);
复制代码


hellohi hi hi
复制代码


// var 的变量提升// 变量提升是 JavaScript 的一种机制:在执行代码之前,变量和函数声明会移至其作用域的顶部。这意味着如果我们这样做:
console.log(greeter0);var greeter0 = 'hi 0';
复制代码


undefined
复制代码


// 上面的代码会被解释为:var greeter0;console.log(greeter0);greeter0 = 'hi 0';
复制代码


hi 0




'hi 0'
复制代码


// var的问题:因为全局作用域造成的不希望发生的赋值和引用
var greeter1 = 'hi';var times = 4;
if(times > 3) { var greeter1 = 'Hello'}
console.log(greeter1)
复制代码


Hello
复制代码


上例中,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);
复制代码


Hellohi
复制代码


// let变量能被修改,但不能被重新申明let greeter5 = 'hi';greeter5 = 'hello';
console.log(greeter5);
复制代码


hello
复制代码


// let greeter5 = 'hi'; //SyntaxError: Identifier 'greeter5' has already been declared
复制代码


constlet类似,只不过其申明的变量必须保持常量,不能被修改。


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 是动态类型,意味着同一个变量名可以指向不同类型的数据。


let a = 1;a = 'foo';
复制代码


'foo'
复制代码

Operators:运算符

JavaScript 支持的运算符包括:


  • +, -, *, /, %, **: 数学运算

  • =, +=, -=: 赋值

  • ++, --: 自加自减

  • +: 字符串拼接

  • >, <, >=, <=: 不等比较

  • ==: 双等号,不同类型会强制转换(type coercion), 对应的不等号是!=

  • ===: 三等号,不同类型不会强制转换, 对应的不等号是!==

  • &&, ||, !: 逻辑运算:与或非

  • &, |, ~: 位运算:与或非


更详尽的运算符说明,见链接


下面是一些示例。


// 求余数123 % (3.5)
复制代码


0.5
复制代码


// 注意顺序"3" + 4 + 5;
复制代码


'345'
复制代码


3 + 4 + '5';
复制代码


'75'
复制代码


// 双等号 vs 三等号
console.log(123 == '123');console.log(1 == true);
console.log(123 === '123');console.log(1 === true);
复制代码


truetruefalsefalse
复制代码


// 逻辑运算const a1 = 0 && 'Hello'; // 0 because 0 is "falsy"console.log(a1);
复制代码


0
复制代码


const b1 = "Hello" || "world"; // "Hello" because both "Hello" and "world" are "truthy"console.log(b1);
复制代码


Hello
复制代码

Grammar: 语法风格

JavaScript 的语法风格接近于 C 语言。有下面几点值得注意:


  • 注释:单行用//,多行用/* */

  • 表达式的结尾用';':但这个分号也是可选的

Control Structure: 控制结构

和大多数语言一样,JavaScript 包括下面的控制结构:


  • if, else: 条件

  • while, do...while: 循环

  • for, 除了常规的 for 循环,还衍生出两种特殊的遍历:

  • for...of: 遍历数组的各个元素

  • for...in: 遍历 object 的各个属性

  • switch: 分支

  • try...catch, throw: 异常

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'])
复制代码


CarrotCarrot
复制代码


// 用变量来标识一个keylet 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']);
复制代码


orange12
复制代码


// object是按照引用传值const obj1 = {}
function doSth(o) { o.x = 1;}
doSth(obj1);
obj1
复制代码


{ x: 1 }
复制代码


// 引用const obj2 = obj1;obj1.y = 2;
obj2
复制代码


{ x: 1, y: 2 }
复制代码

Arrays: 数组

JavaScript 中的数组是一种特殊的 object,通过[index]来访问元素。数组长度通过.length来获取。


const arr1 = ['dog', 'cat', 'hen'];a.length
复制代码


3
复制代码


// 可以给任意非负整数的下标赋值arr1[10] = 'fox'
arr1
复制代码


[ 'dog', 'cat', 'hen', <7 empty items>, 'fox' ]
复制代码


// 数组访问越界是不会报错的,只会放回一个undefinedconsole.log(arr1[100])
复制代码


undefined
复制代码


// 数组元素可以是任意类型的arr1.pusharr1.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...offor(const a of arr1) {    console.log(a);}
复制代码


dogcathenundefinedundefinedundefinedundefinedundefinedundefinedundefinedfoxfalsenull101{ x: 1 }[ 'ok' ]
复制代码


Array 有一系列的方法,比如 cancat, map, filter, slice 等,详见Array


// mapconst 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;}
复制代码


// 函数传参个数可以少于定义的参数个数,缺少的参数会被定义成undefinedadd(); // Equivalent to add(undefined, undefined)
复制代码


NaN
复制代码


// 如果传入的参数个数多余定义的,多余的参数被直接忽略。总之,函数不会因为参数的多少而报语法错add(1, 2, 3, 4);
复制代码


3
复制代码


函数的参数可以是 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)
复制代码


3
复制代码


// 还可以这么调用const arr5 = [5, 6, 7, 8]avg(...arr5)
复制代码


6.5
复制代码


虽然 JavaScript 的函数不支持**kwargs这样的命名参数,但是可以通过传入 object 类型参数,通过object destructuringpack/unpack


// Note the { } braces: this is destructuring an objectfunction area({ width, height }) {  return width * height;}
// The { } braces here create a new objectconsole.log(area({ width: 2, height: 3 }));
复制代码


6
复制代码


console.log(area({ width1: 2, height1: 3 }));
复制代码


NaN
复制代码


// 与其他语言一样,JavaScript的函数支持参数的默认值function avg3(v1, v2, v3=0) {    return (v1 + v2 + v3)/3}
avg3(1, 2)
复制代码


1
复制代码

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);
复制代码


2.3333333333333335
复制代码


另一种定义的方式是通过 arrow function expression,也就是=>:


const avgFunc2 = (...args) => {    let sum = 0;    for (const item of args) {        sum += item;    }    return sum / args.length;  };
avgFunc2(1,2,4);
复制代码


2.3333333333333335
复制代码


// 对简单的表达式,可以不用returnconst sumFunc = (a, b, c) => a + b + c;sumFunc(1, 2, 3);
复制代码


6
复制代码


const sumFunc1 = (a, b, c) => {return a + b + c;};sumFunc1(1, 2, 3);
复制代码


6
复制代码

Functions are first-class objects: 函数是头等公民

函数像其他类型一样,可以被赋值给其他参数,或者传参给其他函数,以及被其他函数作为返回值返回。


const addxy = (x) => (y) => x + y;
复制代码


addxy(1)
复制代码


[Function (anonymous)]
复制代码


addxy(1)(2)
复制代码


3
复制代码


// Function accepting functionconst 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();
复制代码


5
复制代码

Classes: 类

JavaScript 的类定义类似 Java:


class Person {    constructor(name) {        this.name = name;    };        sayHello() {        return `Hello, I am ${this.name}!`;    };};
const p = new Person('Maria');p.sayHello();
复制代码


'Hello, I am Maria!'
复制代码

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:


  • Callback-based (such as setTimeout())

  • Promise-based

  • async/await, which is a syntactic sugar for Promises


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 moduleconst b = 2;
export const a = 1;
*/
复制代码


这就是 JavaScript 的基本语法知识。Keep going!


发布于: 刚刚阅读数: 5
用户头像

无人之路

关注

无人之路,自己领航。 2018-04-25 加入

喜欢写点东西的数据人,自动驾驶从业者。公众号同名,欢迎加微信yangls06交流。

评论

发布
暂无评论
JavaScript基础_JavaScript_无人之路_InfoQ写作社区