【JavaScript 简明入门教程】为了Screeps服务的纯JS入门教程
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官方教程
-
如有错误,欢迎指出!!!!!
-
感谢大家的支持!!!
相关文章:
【JavaScript 简明入门教程】为了Screeps服务的纯JS入门教程
0 前言 0-1 Screeps: World 众所不周知,Screeps: World是一款面向编程爱好者的开源大型多人在线即时战略(MMORTS)沙盒游戏,其核心机制是通过编写JavaScript代码来控制游戏中的单位(称为“Creep”)&#…...
Prometheus stack命令行接入springboot服务metrics
使用Prometheus Stack监控SpringBoot应用 本文将详细介绍如何使用Prometheus Stack监控SpringBoot应用的metrics。假设你已经安装了Kubernetes集群,并使用Helm安装了Prometheus Stack全家桶。SpringBoot应用已经配置好,暴露了相应的metrics端点。 Sprin…...
Git Bash 设置Notepad++作为默认编辑器
网上搜的时候发现别人搞得有点复杂 (绝对正确的方法)Git Bash 设置Notepad作为默认编辑器_git 通过notpad 编辑器-CSDN博客 最简单的方式就是重新安装git,然后在选择编辑器的时候,勾选notepad即可...
Qt 制作验证码
Qt 制作验证码 #include <QRandomGenerator> #include <QPainterPath> #include <QPainter>// 生成随机数 int r(int a,int b0){return b ? QRandomGenerator::global()->bounded(a, b): QRandomGenerator::global()->bounded(a); }// 生成随机多边形…...
WPF InkCanvas 控件详解
1. InkCanvas 是什么? InkCanvas 是 WPF 提供的一个手写绘图控件,它允许用户使用鼠标、触摸屏或手写笔在界面上进行绘图、标注等操作。 核心特点: ✅ 具备笔迹存储和管理功能。 ✅ 提供 Children 和 Strokes 两个集合,分别用于管理子控件和绘制的笔迹。 ✅ 通过 EditingM…...
【数据结构】二叉树 — 经典OJ面试题剖析!!!
目录 二叉树相关oj题 1. 检查两颗树是否相同 2. 另一棵树的子树 3. 翻转二叉树 4. 判断一颗二叉树是否是平衡二叉树 5. 对称二叉树 6. 二叉树的构建及遍历 7. 二叉树的层序遍历 8. 判断一棵树是不是完全二叉树 9. 二叉树的最近公共祖先 10. 根据前序与中序遍历序列构…...
【MySQL】用户账户、角色、口令、PAM
目录 查看用户账户设置 连接 1.本地连接 2.远程连接 账户 角色 操作用户账户和角色 配置口令和账户有效期限 手工使口令过期 配置口令有效期限 PAM身份验证插件 客户端连接:使用 PAM 账户登录 在连接到MySQL服务器并执行查询时,会验证你的身…...
Linux之 权限提升(Linux Privilege Escalation)
Linux 之权限提升 系统信息 1.获取操作系统信息 2.检查PATH,是否有任何可写的文件夹? 3.检查环境变量,有任何敏感细节吗? 4.使用脚本(DirtyCow?)搜索内核漏洞 5.检查sudo 版本是否存在漏洞…...
贪心算法经典应用:最优答疑调度策略详解与Python实现
目录 引言:从现实场景到算法设计 一、问题背景与数学建模 1.1 现实场景抽象 1.2 时间线分析 二、贪心策略的数学证明与选择依据 2.1 贪心选择性质 2.2 证明过程 三、算法实现与代码解析 3.1 算法步骤分解 3.2 代码亮点解析 四、测试案例与结果验证 4.1 …...
SpringBoot 3+ Lombok日志框架从logback改为Log4j2
r要将Spring Boot 3项目中的日志框架从Logback切换到Log4j2,并配置按日期滚动文件和控制台输出,请按照以下步骤操作: 步骤 1:排除Logback并添加Log4j2依赖 在pom.xml中修改依赖: <dependencies><!-- 排除默…...
【bug】[42000][1067] Invalid default value for ‘xxx_time‘
MySQL错误解决:Invalid default value for xxx_time’问题分析与修复方案 问题描述 在MySQL数据库操作中,当尝试创建或修改表结构时,可能会遇到以下错误信息: [bug] [42000][1067] Invalid default value for xxx_time这个错误…...
网站安全专栏-------浅谈CC攻击和DDoS攻击的区别
CC攻击和DDoS攻击都是网络攻击的类型,但它们在攻击方式、目标和效果上有所不同。以下是它们之间的一些主要区别: ### 1. 定义 - **DDoS攻击(分布式拒绝服务攻击)**: DDoS攻击是指攻击者通过大量的分布式计算机&#x…...
【Tauri2】002——Cargo.toml和入口文件
目录 前言 正文 toml文件的基础 注释——# Comment 键值对——Key/Value 表——[table] 内联表——Inline Table 数组——Array package和crate Cargo.toml文件 Cargo.toml——dependencies Cargo.toml——lib crate-type main.rs 前言 【Tauri2】001——安装及…...
二叉树相关算法实现:判断子树与单值二叉树
目录 一、判断一棵树是否为另一棵树的子树 (一)核心思路 (二)代码实现 (三)注意要点 二、判断一棵树是否为单值二叉树 (一)核心思路 (二)代码实现…...
CSS 美化页面(一)
一、CSS概念 CSS(Cascading Style Sheets,层叠样式表)是一种用于描述 HTML 或 XML(如 SVG、XHTML)文档 样式 的样式表语言。它控制网页的 外观和布局,包括字体、颜色、间距、背景、动画等视觉效果。 二、CS…...
23种设计模式-组合(Composite)设计模式
组合设计模式 🚩什么是组合设计模式?🚩组合设计模式的特点🚩组合设计模式的结构🚩组合设计模式的优缺点🚩组合设计模式的Java实现🚩代码总结🚩总结 🚩什么是组合设计模式…...
LSTM创新点不足?LSTM + Transformer融合模型引领Nature新突破
LSTM创新点不足?LSTM Transformer融合模型引领Nature新突破 2024年LSTM真的没有创新空间了吗? 最新研究表明,通过将LSTM与Transformer巧妙融合,依然能创造出Nature级别的突破性成果。LSTM擅长处理短期时序模式,但在…...
【区块链安全 | 第六篇】NFT概念详解
文章目录 NFTNFT(非同质化代币)FT(可替代代币) 以太坊 NFT 标准ERC-721(单一资产)ERC-1155(多资产) NFT 市场版税机制NFT 借贷NFT 安全 NFT NFT(Non-Fungible Token&…...
React组件简介
组件 在 React 中,组件(Component) 是 UI 的基本构建块。可以把它理解为一个独立的、可复用的 UI 单元,类似于函数,它接受输入(props),然后返回 React 元素来描述 UI。 组件的简单…...
iOS常见网络框架
URLSession、Alamofire 和 Moya 1. URLSession 1.1 核心概念 URLSession 是 Apple 官方提供的网络请求 API,封装在 Foundation 框架中。它支持 HTTP、HTTPS、FTP 等协议,可用于: • 普通网络请求(GET/POST) …...
蓝桥杯备考---->激光炸弹(二维前缀和)
本题我们可以构造二维矩阵,然后根据题意,枚举所有边长为m的正方形,找到消灭价值最多的炸弹 #include <iostream> using namespace std; const int N 1e4; int a[N][N]; int n,m; int f[N][N]; int main() {cin >> n >> m…...
python笔记之判断月份有多少天
1、通过随机数作为目标月份 import random month random.randint(1,12)2、判断对应的年份是闰年还是平年 因为2月这个特殊月份,闰年有29天,而平年是28天,所以需要判断对应的年份属于闰年还是平年,代码如下 # 判断年份是闰年还…...
数据结构 --树和森林
树和森林 树的存储结构 树的逻辑结构 树是一种递归定义的数据结构 树是n(n≥0)个结点的有限集。当n0时,称为空树。在任意一棵非空树中应满足: 1)有且仅有一个特定的称为根的结点。 2)当n>1时,其余结点可分为m(m>0)个互不相交的有…...
QOpenGLWidget视频画面上绘制矩形框
一、QPainter绘制 在QOpenGLWidget中可以绘制,并且和OpenGL的内容叠在一起。paintGL里面绘制完视频后,解锁资源,再用QPainter绘制矩形框。这种方式灵活性最好。 void VideoGLWidget::paintGL() {glClear(GL_COLOR_BUFFER_BIT);m_program.bi…...
Linux系统加固笔记
检查口令为空的账户 判断依据:存在则不符合 特殊的shell a./bin/false:将用户的shell设置为/bin/false,用户会无法登录,并且不会有任何提示信息b./sbib/nologin:nologin会礼貌的向用户发送一条消息,并且拒绝用户登录…...
【Go万字洗髓经】Golang中sync.Mutex的单机锁:实现原理与底层源码
本章目录 1. sync.Mutex锁的基本用法2. sync.Mutex的核心原理自旋到阻塞的升级过程自旋CAS 饥饿模式 3. sync.Mutex底层源码Mutex结构定义全局常量Mutex.Lock()方法第一次CAS加锁能够成功的前提是?竞态检测 Mutex.lockSlow()lockSlow的局部变量自旋空转state新值构造…...
npm前端模块化编程
1. 代码编写 使用npm和Webpack进行前端模块化编程的完整案例。这个案例将创建一个简单的网页,该网页显示一个标题和一个按钮,点击按钮会在控制台中打印一条消息。 步骤 1: 初始化项目 创建项目目录并初始化npm: mkdir my-modular-fronten…...
Django REST framework 源码剖析-认证器详解(Authentication)
Django REST framework 源码剖析-认证器详解(Authentication) 身份验证始终在视图的最开始运行,在权限和限制检查发生之前,以及在允许任何其他代码继续之前。request.user属性通常设置为contrib.auth包的user类的实例。request.auth属性用于任何其他身份…...
TCP/IP三次握手的过程,为什么要3次?
一:过程 第一次(SYN): 客户端发送一个带有SYN标志的TCP报文段给服务器,设置SYN1,并携带初始序列号Seqx(随机值),进入SYN_SENT状态。等待服务器相应。 第二次(…...
Centos6安装nerdctl容器运行时
Centos6安装nerdctl容器运行时 前言Centos6安装docker---失败--不可拉取镜像docker配置国内镜像加速 Centos6安装nerdctl-full容器管理工具为Centos6配置containerd服务开机自启动设置nerdctl自动补全 前言 本文写于2025年3月22日,因一些特殊业务需要用到Centos6Docker,但Cent…...
