0 前言
0-1 Screeps: World
- 众所不周知,
Screeps: World
是一款面向编程爱好者的开源大型多人在线即时战略(MMORTS)沙盒游戏,其核心机制是通过编写JavaScript
代码来控制游戏中的单位(称为“Creep”),以建立和发展自己的帝国。 - 在游戏中,玩家需要编写代码来自动化单位的行为,包括采集资源、建造建筑、扩张领土等。这些代码会在游戏服务器上持续运行,即使玩家离线,单位也会按照预设的指令执行任务。
- 那为了我们愉快的进入游戏玩耍之前,我们今天来速通一下
JavaScript
的基础语法
0-2 JavaScript
JavaScript
(简称JS)是一种高级、解释型、动态的编程语言,主要用于==网页开发==。它最早由网景(Netscape)公司的布兰登·艾奇(Brendan Eich)在1995年开发,用于增强网页的交互性。如今,JavaScript
已经发展成为一种功能强大的通用编程语言,广泛应用于前端、后端、移动端、游戏开发等领域。- JavaScript的特点:
解释性语言
:无需编译,浏览器或Node.js可以直接执行。支持动态类型
:变量的类型可以在运行时动态变化。弱类型
:允许不同类型的数据进行运算。基于对象
:JavaScript中的几乎所有内容(如数组、函数等)都是对象。事件驱动和异步
:使用事件监听和回调函数,使网页能响应用户操作。通过Promise和async/await支持异步编程,提高性能。跨平台
:适用于Windows、Mac、Linux等各种操作系统,并且支持大多数浏览器(Chrome、Firefox、Edge等)。
0-3 食用本文前须知!!!
- 通常大部分的
JS
教程都会和HTML
和CSS
绑定(毕竟是网页开发捏),但由于本教程的后续目的咱们是为了愉快的玩耍Screeps: World
,故本教程将专注于JS 纯逻辑编程 为主,涵盖变量、函数、对象、异步编程等核心内容。 - 本教程目录:
- 编程环境搭建和HelloWorld
- 变量声明和数据类型
- 函数、作用域与闭包
- 运算符
- 异常处理机制
- 异步机制、回调函数与Promise(Screeps用不上)
- 类、继承与prototype
- 多态
- 数组(Array)
1 编程环境搭建和HelloWorld
1-1 Node.js
简介
- 通常传统的
JavaScript·
只能在浏览器环境中运行,无法直接访问文件系统、操作系统资源或数据库。为了解决上述局限性,我们引入Node.js
~ Node.js
是一个基于Chrome V8 引擎
的JavaScript
运行环境,允许 JS 在服务器端运行。
1-2 Node.js
的安装
-
官网:Download Node.js
-
如官网所述,
windows
端的下载打开powershell
:
# Download and install fnm:
winget install Schniz.fnm# Download and install Node.js:
fnm install 22# 如果输入node -v 出现报错请输入下列代码
# 记得是在poweshell中输出
fnm env --use-on-cd | Out-String | Invoke-Expression# Verify the Node.js version:
node -v # Should print "v22.14.0".# Verify npm version:
npm -v # Should print "10.9.2".
winget
是 Windows 10/11自带的 包管理器(类似于 Linux 的apt
或brew
)。fnm(Fast Node Manager)
,是一个 Node.js 版本管理工具。方便管理多个 Node.js 版本并且可以快速进行切换。npm
(Node Package Manager)是 Node.js 自带的包管理工具,用于安装和管理 JavaScript 库。- 值得一提的是,上述代码在下载完成中验证
Node.js
的版本时候也许会出现下述报错:
fnm env --use-on-cd | Out-String | Invoke-Expression
- 上述代码将
fnm
的环境变量加载到当前的 PowerShell 会话中,使得 Node.js 的相关命令(如node -v
)能够在当前终端会话中正常使用。
1-3 HelloWorld与你的第一个js程序
- IDE的话这里大家就随便自己选咯,我这里用的是
VSCode
- 我们新建一个文件命名为
Hello.js
console.log("Hello World!");
- 然后在终端直接使用
Node.js
运行js文件
node .\hellojs.js
-
至此我们的第一个
JS
程序就完成辣 -
当然可以考虑安装一些扩展(当然你也可以纯记事本进行编程~)
1-4 (补充)ESX是啥
- ESX 通常是一个非正式的表达,泛指 ECMAScript 的各种版本(ES6 及后续版本),即 ES6、ES7、ES8、ES9 等等。
- ES6(ECMAScript 2015) 是 JavaScript 语言的一次重大更新,引入了
let
、const
、箭头函数、类(class)、模块(import/export)等重要特性。
版本 | 发布时间 | 重要特性 |
---|---|---|
ES6 (ES2015) | 2015 | let / const 、箭头函数、类(class)、模板字符串、解构赋值、默认参数、import/export |
ES7 (ES2016) | 2016 | Array.prototype.includes() 、指数运算符 (** ) |
ES8 (ES2017) | 2017 | async/await 、Object.entries() 、Object.values() |
ES9 (ES2018) | 2018 | Promise.finally() 、正则表达式改进(命名捕获组等) |
ES10 (ES2019) | 2019 | Array.prototype.flat() , Object.fromEntries() |
ES11 (ES2020) | 2020 | BigInt 、Promise.allSettled() 、可选链 ?. |
ES12 (ES2021) | 2021 | String.replaceAll() 、逻辑赋值运算符 (`&&=, |
1-5 JavaScript 严格模式 ("use strict"
)
- 严格模式(
"use strict"
)是 ES5 引入的一种 JavaScript 执行模式,旨在 消除 JavaScript 的不安全或错误用法,提高代码质量和执行效率。 - 开启严格模式后,JavaScript 代码会 抛出更多错误,阻止一些潜在的 Bug,同时提高 JS 引擎的优化效率。
- 如何开启严格模式?
- 在代码的第一行添加
"use strict";console.log("严格模式开启!");
- 严格模式也可以局部应用于某个函数:
function strictFunction() {"use strict";let x = 10;console.log(x);
}
strictFunction();
- 严格模式总结:
规则 | 解释 |
---|---|
必须显式声明变量 | x = 10; 会报错 |
禁止重复变量声明 | var a = 10; var a = 20; 会报错 |
禁止删除变量/函数 | delete x; 会报错 |
普通函数的 this 变 undefined | function test() { console.log(this); } |
禁止 with 语句 | with (obj) { console.log(x); } 会报错 |
禁止 eval() 影响作用域 | eval("var x = 10;"); console.log(x); |
禁止八进制数 | var num = 010; |
2 变量声明和数据类型
2-1 变量声明
- JavaScript 提供了
var
、let
和const
三种方式来声明变量
2-1-1 var
(已过时,不推荐)
- 变量可以在声明前使用(变量提升)。
- 作用域是函数作用域(function scope)。
- 可以重复声明同名变量。
console.log(a); // undefined(变量提升)
var a = 10;
var a = 20; // 允许重复声明
console.log(a); // 20
2-1-2 let
(推荐使用)
- 作用域是块作用域(block scope)。
- 不能在声明前使用(不会变量提升)。
- 不能重复声明同名变量。
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 10;
let b = 20; // ❌ SyntaxError: Identifier 'b' has already been declared
2-1-3 const
(推荐用于常量)
- 作用域是块作用域(block scope)。
- 必须在声明时初始化,且值不能更改(不可变)。
- 不能重复声明。
const PI = 3.14159;
PI = 3.14; // ❌ TypeError: Assignment to constant variable
2-2 原始数据类型
- JavaScript 主要有七种原始数据类型和一种对象类型
2-2-1 number
(数字类型)
- 包括整数和浮点数
- 例如:
10
、3.14
、-5
- 还有特殊值:
Infinity
、-Infinity
和NaN
let num1 = 42;
let num2 = 3.14;
let notANumber = NaN;
2-2-2 string
(字符串类型)
- 由字符组成,使用单引号
'
、双引号"
或反引号`
(模板字符串)。 - 例如:
"hello"
、'world'
、`Hello, ${name}`
let str1 = "Hello";
let str2 = 'World';
let name = "Alice";
let str3 = `Hello, ${name}`; // 模板字符串
2-2-3 boolean
(布尔类型)
- 只有两个值:
true
和false
- 用于逻辑判断
let isOnline = true;
let hasError = false;
2-2-4 undefined
(未定义类型)
- 变量声明但未赋值时的默认值
let x;
console.log(x); // undefined
2-2-5 null
(空值)
- 表示“空值”或“无值”
- 通常用于手动赋值,表示变量为空
let y = null;
2-2-6 symbol
(符号类型,ES6 新增)
- 创建独一无二的值,通常用于对象属性键
let sym = Symbol("unique");
2-2-7 bigint
(大整数类型,ES11/ES2020 新增)
- 适用于处理比
Number.MAX_SAFE_INTEGER
更大的整数
let bigNum = 9007199254740991n;
2-3 对象类型
JavaScript 只有一种复杂数据类型——对象(Object),包括:
- 普通对象
{ key: value }
- 数组
[1, 2, 3]
- 函数
function() {}
- 日期
new Date()
- 正则表达式
/abc/
let person = {name: "Alice",age: 25
};let arr = [1, 2, 3];function sayHello() {console.log("Hello!");
}
- 可以使用
typeof
来检查变量类型:
console.log(typeof 42); // "number"
console.log(typeof "hello"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object"(JavaScript 的历史遗留问题)
console.log(typeof {}); // "object"
console.log(typeof []); // "object"
console.log(typeof function() {}); // "function"
console.log(typeof Symbol("id")); // "symbol"
console.log(typeof 9007199254740991n); // "bigint"
2-4 对象类型的创建,访问,属性引用
- 创建对象
- (下述内容部分超出范围还没说,但是相信熟悉其他编程语言的朋友们可以看懂)
- (如果没看懂的话可以先往后看后面再回来看这一部分)
const person = {name: "Alice",age: 25,"home city": "New York", greet: function () {console.log("Hello, I'm " + this.name);}
};
2-4-1 访问对象的属性(一):使用 .
访问(常用)
.
访问方式只能用于符合变量命名规则的键(如name
和age
)。
console.log(person.name); // 输出: Alice
console.log(person.age); // 输出: 25
2-4-2 访问对象的属性(二):使用 []
访问(适用于动态键)
- 可以访问特殊字符(如空格、
-
等)的属性(如"home city"
)。
- 可以用变量作为键名,适用于动态访问。
console.log(person["name"]); // 输出: Alice
console.log(person["home city"]); // 输出: New York
- 动态键访问
const key = "age";
console.log(person[key]); // 输出: 25
2-4-4 访问对象的方法
person.greet(); // 输出: Hello, I'm Alice
2-4-5 检查对象是否包含某个属性
- 使用
in
关键字
console.log("name" in person); // true
console.log("gender" in person); // false
- 使用
hasOwnProperty()
方法
console.log(person.hasOwnProperty("age")); // true
console.log(person.hasOwnProperty("gender")); // false
2-4-6 遍历对象的属性
for (let key in person) {console.log(key + ": " + person[key]);
}
2-4-7 删除对象属性
- 访问对象中不存在的属性时,返回
undefined
,不会报错。
delete person.age;
console.log(person.age); // 输出: undefined
3 函数、作用域与闭包
- JavaScript 中,函数(Function) 是代码的可复用块,允许封装逻辑并在需要时调用。同时,作用域(Scope) 决定了变量的可访问性。
- JavaScript 提供了三种常见方式来定义函数:
3-1 函数声明(Function Declaration)
- 关键字
function
开头
- 支持函数提升(Hoisting),可以在定义前调用
console.log(square(5)); // 25function square(num) {return num * num;
}console.log(square(5)); // 25
3-2 函数表达式(Function Expression)
- 没有函数名(匿名函数)
- 赋值给变量后使用
- 不支持函数提升(只能在定义后调用)
const greet = function(name) {return "Hello, " + name;
};
console.log(greet("Bob")); // Hello, Bob
3-3 箭头函数(Arrow Function,ES6)
- 语法更简洁
- 没有
this
绑定(this 取决于外部作用域) - 适合回调函数 & 简单逻辑
const add = (a, b) => {return a + b;
};
console.log(add(3, 4)); // 7
- 单行省略
{}
和return
:
const multiply = (a, b) => a * b;
console.log(multiply(2, 3)); // 6
3-4 作用域(Scope)
- 作用域决定了变量的可访问范围。JavaScript 有三种主要作用域:
全局作用域
(Global Scope)**: 声明在函数外部的变量,在整个脚本或浏览器窗口中可访问函数作用域
(Function Scope):在函数内部声明的变量,只能在该函数内部访问块级作用域
(Block Scope,ES6):let
和const
具有块级作用域,var
没有块级作用域
{let x = 10;const y = 20;var z = 30; // ⚠️ var 例外
}
console.log(z); // ✅ 30
console.log(x); // ❌ ReferenceError
console.log(y); // ❌ ReferenceError
3-5 闭包(Closure)
- 闭包是指内部函数可以访问外部函数的变量,即使外部函数已经执行完毕。4
- 闭包的作用:
- 数据私有化
- 模拟
static
变量
function outer() {let count = 0;return function inner() {count++;console.log("Count:", count);};
}const counter = outer();
counter(); // Count: 1
counter(); // Count: 2
- 关于闭包的调用方式及其注意点:
调用方式 | 是否形成闭包? | 变量是否累积? | 原因 |
---|---|---|---|
outer(); | ❌ 否 | ❌ 否 | inner 从未被执行 |
outer().inner(); | ❌ 否 | ❌ 否 | count 在每次 outer() 调用后重置 |
const counter = outer(); counter(); counter(); | ✅ 是 | ✅ 是 | counter 持有 count 的引用,形成闭包 |
3-6 this
关键字
this
是 JavaScript 中的特殊关键字,它的值取决于函数的调用方式。
1️⃣ 全局作用域- 在非严格模式下,
this
指向window
(浏览器)或global
(Node.js)。 - 在严格模式下,
this
是undefined
"use strict";
console.log(this); // 输出:undefined
2️⃣ 作为对象方法调用
- 此时
this
指向对象本身
const obj = {name: "Alice",sayHi: function() {console.log(this.name);}
};obj.sayHi(); // 输出:"Alice"
3️⃣作为构造函数调用
- 在构造函数中,
this
指向新创建的对象。
function Person(name) {this.name = name;
}const p = new Person("Bob");
console.log(p.name); // 输出:"Bob"
4️⃣call
/ apply
/ bind
显式绑定
- JavaScript 允许手动设置
this
,可以使用:call()
传递参数列表apply()
传递参数数组bind()
返回一个新的绑定函数
const user = { name: "Charlie" };function sayHello() {console.log("Hello, " + this.name);
}sayHello.call(user); // 输出:"Hello, Charlie"
sayHello.apply(user); // 输出:"Hello, Charlie"const boundFunc = sayHello.bind(user);
boundFunc(); // 输出:"Hello, Charlie"
5️⃣ 箭头函数中的 this
- 箭头函数不会创建自己的
this
,而是继承外部作用域的this
。
const obj = {name: "Alice",sayHi: function() {const inner = () => {console.log(this.name);};inner();}
};obj.sayHi(); // 输出:"Alice"
4 运算符
- 本节内容基础就给出表格大家自行参阅!
4-1 比较运算符
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
== | 宽松相等(值相等,类型可转换) | '5' == 5 | true |
=== | 严格相等(值相等,类型也必须相同) | '5' === 5 | false |
!= | 宽松不等(值不相等,类型可转换) | '5' != 5 | false |
!== | 严格不等(值或类型不同) | '5' !== 5 | true |
> | 大于 | 10 > 5 | true |
< | 小于 | 10 < 5 | false |
>= | 大于等于 | 10 >= 10 | true |
<= | 小于等于 | 10 <= 5 | false |
4-2 算数运算符
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
+ | 加法 | 5 + 3 | 8 |
- | 减法 | 5 - 3 | 2 |
* | 乘法 | 5 * 3 | 15 |
/ | 除法 | 5 / 2 | 2.5 |
% | 取模(取余数) | 5 % 2 | 1 |
** | 指数(幂运算) | 2 ** 3 | 8 |
- 然后是自增自减老规矩~
形式 | 说明 | 示例 | 结果 |
---|---|---|---|
x++ | 后置自增(先返回值,再加 1) | let a = 5; let b = a++; | b = 5, a = 6 |
++x | 前置自增(先加 1,再返回值) | let a = 5; let b = ++a; | b = 6, a = 6 |
x-- | 后置自减(先返回值,再减 1) | let a = 5; let b = a--; | b = 5, a = 4 |
--x | 前置自减(先减 1,再返回值) | let a = 5; let b = --a; | b = 4, a = 4 |
4-3 赋值运算符
运算符 | 等价于 | 示例 |
---|---|---|
+= | x = x + y | x += 2; |
-= | x = x - y | x -= 2; |
*= | x = x * y | x *= 2; |
/= | x = x / y | x /= 2; |
**= | x = x ** y | x **= 2; |
%= | x = x % y | x %= 2; |
5 分支语句(条件语句) 和 循环语句
- 本节同样基础,我们飞速略过
5-1 if-else 语句
- 无需多言
if (/*condition1*/)
{ }
else if (/*condition2*/)
{ }
else
{ }
5-2 switch 语句
- 需要注意的是,js的
switch
语句可以用于 任何类型(string
、number
、boolean
,甚至object
和function
)。 - js的
switch
语句支持嵌套(不建议你这么干) - 其他部分和其他语言相同:
case
语句后必须是具体的值,不能是范围或条件表达式。break
语句用于阻止穿透(fall-through),否则会继续执行下一个case
代码块。
default
是可选的,但建议写上,以防所有case
都不匹配。
let color = "红色";
switch (color) {case "红色":console.log("你选择了红色");break;case "蓝色":console.log("你选择了蓝色");break;case "绿色":console.log("你选择了绿色");break;default:console.log("未知颜色");
}
5-3 for 循环
for (let i = 1; i <= 5; i++) {console.log(i);
}
- 注意区分:
for...in
和for...of
语句 | 适用对象 | 作用 |
---|---|---|
for...in | 对象 | 遍历对象的属性名 |
for...of | 数组、字符串 | 遍历数组的值 |
for...in
:
let person = { name: "张三", age: 25, city: "北京" };
for (let key in person) {console.log(key + ": " + person[key]);
}
for...of
let numbers = [10, 20, 30];
for (let num of numbers) {console.log(num);
}
5-4 while 循环
let count = 1;
while (count <= 3) {console.log(count);count++;
}
5-5 do-while循环
let num = 1;
do {console.log(num);num++;
} while (num <= 3);
5-6 break
和 continue
- 同理:
break
终止循环continue
跳过当前循环的剩余代码,直接进入下一次循环,不会终止整个循环。
6 异常处理机制
- 在 JavaScript 开发中,程序运行时可能会遇到错误(比如变量未定义、JSON 解析失败、网络请求错误等)。为了防止这些错误导致程序崩溃,我们可以使用 异常处理机制 来捕获和处理错误。
6-1 异常处理机制 try-catch-finally
try {// 可能会出错的代码
} catch (error) {// 处理错误console.log("捕获错误:", error.message);
} finally{// 无论是否发生错误都会执行,适用于资源释放、日志记录等场景。
}
6-2 throw
关键字(手动抛出异常)
throw new Error("你的错误信息");
7 异步机制、回调函数与Promise
- 值得一提的是,Screeps 的代码是 Tick 驱动的,而不是时间驱动的,所以要用
Game.time
来管理逻辑,而不是setTimeout
或Promise
! - 因此本节对
Screeps
编程毫无作用,不感兴趣的朋友们可以直接跳过~
7-1 异步机制
- JavaScript 是 单线程 语言,为了防止长时间执行的任务(如网络请求、文件读写)阻塞主线程,采用 异步编程 方式,使代码可以在等待操作完成的同时执行其他任务。
- 常见的异步操作:
setTimeout
/setInterval
(定时器)- DOM 事件监听 (
addEventListener
) - AJAX / Fetch 请求(HTTP 请求)
Promise
和async/await
7-2 setTimeout
/ setInterval
(定时器)
setTimeout
和setInterval
是 JavaScript 的内置函数。它属于 Web API,由 浏览器或 Node.js 提供,并不直接属于 JavaScript 语言核心。
方法 | 用途 | 特点 |
---|---|---|
setTimeout(fn, delay) | 延迟执行 fn 一次 | 仅执行一次,需要手动递归调用 |
setInterval(fn, delay) | 间隔执行 fn | 会一直执行,直到调用 clearInterval() |
- 下面我们来看二者具体的区别:
7-2-1 setTimeout
setTimeout
是 JavaScript 的内置函数,用于延迟执行代码。
setTimeout(callback, delay, param1, param2, ...);
callback
(必填):延迟执行的 函数(匿名函数或函数引用)。delay
(必填):延迟时间(单位:毫秒 ms,1000ms = 1秒
)。param1, param2, ...
(可选):传递给callback
的参数。
- 注意可以使用
clearTimeout(timer)
; // 取消定时器
let timer = setTimeout(() => {console.log("不会执行");
}, 5000);clearTimeout(timer); // 取消定时器
7-2-2 ``setInterval`
setInterval
是 JavaScript 内置的定时器函数,用于按指定时间间隔重复执行某个函数,直到调用clearInterval()
停止。
setInterval(callback, delay, param1, param2, ...);
callback
要执行的函数(可以是匿名函数或函数引用)delay
时间间隔(毫秒),1000ms = 1秒param1
,param2
, … 传递给 callback 的参数(可选)
setInterval(() => {console.log("每 2 秒执行一次");
}, 2000);
- 使用
clearInterval(intervalID)
停止setInterval
。
let count = 0;
let intervalID = setInterval(() => {count++;console.log("执行次数:" + count);if (count === 5) {clearInterval(intervalID); // 停止定时器console.log("定时器已停止");}
}, 1000);
7-3 回调函数
- 回调函数 是一种最基本的异步处理方式,即将一个函数作为参数传递,待操作完成后调用该函数。
function fetchData(callback) {setTimeout(() => {console.log("数据获取成功");callback("数据内容");}, 2000);
}fetchData((data) => {console.log("回调函数接收数据:", data);
});
缺点:
- 回调地狱(Callback Hell):多个回调嵌套使代码变得难以维护。
- 错误处理不方便:错误需要通过回调手动传递,容易遗漏。
7-4 Promise
Promise
是 ES6 引入的一种异步编程解决方案,它可以更优雅地处理异步操作,避免回调地狱。- *Promise 三种状态:
pending
(进行中)fulfilled
(已成功)rejected
(已失败)
function fetchData() {return new Promise((resolve, reject) => {setTimeout(() => {let success = true; // 模拟成功或失败if (success) {resolve("数据获取成功");} else {reject("数据获取失败");}}, 2000);});
}fetchData().then((data) => console.log("成功:", data)) // 处理成功.catch((error) => console.log("失败:", error)) // 处理失败.finally(() => console.log("请求完成")); // 无论成功失败都会执行
优势:
- 避免回调地狱,使代码更易读。
- 提供
.then()
、.catch()
、.finally()
结构,方便管理异步操作。
7-5 async/await(基于 Promise)
async/await
是 ES8 引入的一种更直观的异步编程方式,它是Promise
的语法糖,使异步代码更接近同步写法。
async function fetchData() {try {let data = await new Promise((resolve) => setTimeout(() => resolve("数据获取成功"), 2000));console.log(data);} catch (error) {console.log("错误:", error);} finally {console.log("请求完成");}
}fetchData();
特点:
await
关键字会等待Promise
处理完成后再执行后续代码。try/catch
可用于错误处理,比.catch()
更直观。
8 类、继承与prototype
- 在 JavaScript 中,类(Class) 和 原型(Prototype) 是实现面向对象编程(OOP)的核心。ES6 之前,JavaScript 使用 原型继承(Prototype Inheritance),而在 ES6 引入了
class
语法,使面向对象编程更直观。
8-1 class
(类)
- ES6 引入
class
关键字,使 JavaScript 的面向对象代码更加清晰,但本质上它仍然是 基于原型的继承。
class Person {constructor(name, age) {this.name = name;this.age = age;}// 定义方法(自动添加到原型上)sayHello() {console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old.`);}
}// 创建对象
const person1 = new Person("Alice", 25);
person1.sayHello(); // 输出: Hi, I'm Alice and I'm 25 years old.
constructor()
:构造函数,在创建对象时自动执行。sayHello()
:实例方法,所有Person
对象共享。
8-2 静态方法 (Static Methods)
- 在 JavaScript 中,静态方法是定义在类本身而不是类的实例上的方法。静态方法可以通过类名直接访问,而不能通过类的实例来调用。
- 使用
static
关键字定义静态方法。
class MyClass {// 静态方法static staticMethod() {console.log("This is a static method");}// 实例方法instanceMethod() {console.log("This is an instance method");}
}// 调用静态方法
MyClass.staticMethod(); // 输出: This is a static method// 创建类的实例
const myInstance = new MyClass();// 调用实例方法
myInstance.instanceMethod(); // 输出: This is an instance method// 无法通过实例调用静态方法
// myInstance.staticMethod(); // TypeError: myInstance.staticMethod is not a function
8-3 继承(Inheritance)
- 在 ES6 中,可以使用
extends
关键字实现 类继承,并使用super()
调用父类的构造函数。
class Student extends Person {constructor(name, age, grade) {super(name, age); // 调用父类的构造函数this.grade = grade;}study() {console.log(`${this.name} is studying in grade ${this.grade}.`);}
}const student1 = new Student("Bob", 20, "10th");
student1.sayHello(); // 继承父类的方法
student1.study(); // 输出: Bob is studying in grade 10th.
extends
让子类继承父类。super()
允许调用 父类的构造函数,必须在constructor
里第一行执行。- 子类可以扩展自己的方法。
- 静态方法也可以被继承,子类可以继承父类的静态方法,或者重写静态方法。
class Animal {static type() {console.log("I am an animal");}
}class Dog extends Animal {static type() {console.log("I am a dog");}
}// 调用父类静态方法
Animal.type(); // 输出: I am an animal// 调用子类重写的静态方法
Dog.type(); // 输出: I am a dog
8-4 prototype
(原型)
- JavaScript 是基于 原型继承 的语言,每个对象都有一个
__proto__
属性,指向其 原型(prototype)。 - JavaScript 本质上仍然是基于原型的继承,
class
只是语法糖。 - 类的方法实际上是添加到
prototype
上的。
function Animal(name) {this.name = name;
}// 添加方法到原型
Animal.prototype.makeSound = function () {console.log(`${this.name} makes a sound.`);
};const dog = new Animal("Dog");
dog.makeSound(); // 输出: Dog makes a sound.
console.log(Person.prototype.sayHello === person1.sayHello); // true
prototype
让所有实例共享方法,减少内存占用。- 直接操作
prototype
可实现手动继承。
9 多态
多态(Polymorphism)
指的是相同的方法在不同对象上具有不同的行为。在 JavaScript 中,多态主要通过**方法重写(Method Overriding)**
和**鸭子类型(Duck Typing)**
实现。- JavaScript 是支持多态的,但它不像 Java、C++ 那样有严格的类型系统,而是依赖于其动态特性和原型继承来实现多态。
9-1 方法重写(Method Overriding)
- 子类可以重写父类的方法,实现不同的功能。
class Animal {makeSound() {console.log("Some generic animal sound");}
}class Dog extends Animal {makeSound() {console.log("Woof! Woof!"); // 重写父类的方法}
}class Cat extends Animal {makeSound() {console.log("Meow~"); // 重写父类的方法}
}// 统一调用
const animals = [new Dog(), new Cat()];//不关心 `animal` 的具体类型,只调用 `makeSound()`。
animals.forEach(animal => animal.makeSound());
// 输出:
// Woof! Woof!
// Meow~
9-2 鸭子类型(Duck Typing)
- “如果它会游泳、嘎嘎叫,那么它就是一只鸭子。” —— 鸭子类型 JavaScript 是动态语言,只要对象实现了相同的方法,就可以被当作同一种类型使用,而不关心它的类继承关系。
class Bird {speak() {console.log("Chirp chirp");}
}class Robot {speak() {console.log("Beep boop");}
}// 统一处理不同对象
const entities = [new Bird(), new Robot()];
entities.forEach(entity => entity.speak());// 输出:
// Chirp chirp
// Beep boop
Bird
和Robot
没有继承同一个父类,但都实现了speak()
方法。
entities.forEach(entity => entity.speak());
实现了多态,因为 JS 只在运行时检查speak()
是否存在,而不检查对象的类型。
9-3 函数多态(参数多态)
- JavaScript 的函数可以接收不同类型的参数,表现出函数多态(Function Polymorphism)。
function printMessage(msg) {if (typeof msg === "string") {console.log(`Text: ${msg}`);} else if (typeof msg === "number") {console.log(`Number: ${msg}`);} else {console.log("Unknown type");}
}printMessage("Hello"); // Text: Hello
printMessage(123); // Number: 123
printMessage(true); // Unknown type
10 数组(Array)
- 在JavaScript中,数组是存储一组数据的对象。数组的元素可以是任何类型,包括其他对象和函数等。
10-1 数组的基本创建和访问
- 有两种常见的方式来创建数组:
// 使用数组字面量创建数组
let numbers = [1, 2, 3, 4]; // 数字数组
let names = ['Alice', 'Bob', 'Charlie']; // 字符串数组// 使用Array构造函数
let emptyArray = new Array(); // 创建一个空数组
let anotherArray = new Array(10); // 创建一个包含10个空位的数组
let filledArray = new Array(1, 2, 3); // 创建并初始化数组
10-2 元素访问
- 数组的索引是从
0
开始的。我们可以通过索引来访问数组的元素。
let arr = [10, 20, 30, 40];
console.log(arr[0]); // 输出:10
console.log(arr[2]); // 输出:30
indexOf()
:查找元素的索引位置。
let arr = [10, 20, 30, 40];
console.log(arr.indexOf(30)); // 输出:2
10-3 数组内建方法
push()
: 向数组末尾添加一个或多个元素,返回新数组的长度。
let arr = [1, 2, 3];
arr.push(4); // arr变为 [1, 2, 3, 4]
console.log(arr); // 输出:[1, 2, 3, 4]
pop()
: 删除数组末尾的元素,返回删除的元素。
let arr = [1, 2, 3, 4];
let poppedElement = arr.pop(); // poppedElement = 4
console.log(arr); // 输出:[1, 2, 3]
shift()
: 删除数组开头的元素,返回删除的元素。
let arr = [1, 2, 3, 4];
let shiftedElement = arr.shift(); // shiftedElement = 1
console.log(arr); // 输出:[2, 3, 4]
unshift()
: 向数组的开头添加一个或多个元素。
let arr = [1, 2, 3];
arr.unshift(0); // arr变为 [0, 1, 2, 3]
console.log(arr); // 输出:[0, 1, 2, 3]
splice()
: 在任意位置添加或删除元素。
let arr = [1, 2, 3, 4];
arr.splice(2, 1, 5); // 从索引2删除1个元素,插入5
console.log(arr); // 输出:[1, 2, 5, 4]
map()
:对数组中的每个元素执行一个函数并返回一个新数组。
let arr = [1, 2, 3];
let squared = arr.map(x => x * x); // [1, 4, 9]
console.log(squared);
filter()
:根据条件过滤数组元素,返回一个新数组。
let arr = [1, 2, 3, 4];
let evenNumbers = arr.filter(x => x % 2 === 0); // [2, 4]
console.log(evenNumbers);
reduce()
:将数组元素通过某个函数累积成一个单一的值。
let arr = [1, 2, 3, 4];
let sum = arr.reduce((acc, current) => acc + current, 0); // 10
console.log(sum);
10-4 数组遍历
for
循环:传统的遍历方式。
let arr = [10, 20, 30];
for (let i = 0; i < arr.length; i++) {console.log(arr[i]);
}
forEach()
:对每个元素执行一个函数。
let arr = [10, 20, 30];
arr.forEach((element, index) => {console.log(`Index: ${index}, Value: ${element}`);
});
map()
:创建一个新数组,数组的每个元素是通过提供的函数对原数组每个元素进行处理的结果。
let arr = [1, 2, 3];
let doubled = arr.map(x => x * 2); // [2, 4, 6]
console.log(doubled);
10-5 注意点
- 数组大小:在JavaScript中,数组实际上是对象,且键名是数字(数组的索引)。因此,处理大型数组时,性能可能会成为问题。尤其是使用
shift()
和unshift()
操作时,数组的所有元素会被重新索引,可能导致性能下降。
11 总结
-
本教程我们从
Screeps: World
必须的JavaScript
,将JS
和CSS
与HTML
的教程分离,专注于讲解JS
的原生基础语法。 -
完成上述基础知识学习,你就可以正式开始学习玩耍
Screeps
咯,事不宜迟那就从教程开始吧:Screeps官方教程 -
如有错误,欢迎指出!!!!!
-
感谢大家的支持!!!