【JavaScript 简明入门教程】为了Screeps服务的纯JS入门教程

news2025/4/1 12:51:04

0 前言

0-1 Screeps: World

请添加图片描述

  • 众所不周知,​Screeps: World是一款面向编程爱好者的开源大型多人在线即时战略(MMORTS)沙盒游戏,其核心机制是通过编写JavaScript代码来控制游戏中的单位(称为“Creep”),以建立和发展自己的帝国。
  • 在游戏中,玩家需要编写代码来自动化单位的行为,包括采集资源、建造建筑、扩张领土等。​这些代码会在游戏服务器上持续运行,即使玩家离线,单位也会按照预设的指令执行任务。
  • 那为了我们愉快的进入游戏玩耍之前,我们今天来速通一下JavaScript的基础语法
0-2 JavaScript

请添加图片描述

  • JavaScript(简称JS)是一种高级、解释型、动态的编程语言,主要用于==网页开发==。它最早由网景(Netscape)公司的布兰登·艾奇(Brendan Eich)在1995年开发,用于增强网页的交互性。如今,JavaScript已经发展成为一种功能强大的通用编程语言,广泛应用于前端、后端、移动端、游戏开发等领域
  • JavaScript的特点:
  1. 解释性语言:无需编译,浏览器或Node.js可以直接执行。
  2. 支持动态类型:变量的类型可以在运行时动态变化。
  3. 弱类型:允许不同类型的数据进行运算。
  4. 基于对象:JavaScript中的几乎所有内容(如数组、函数等)都是对象。
  5. 事件驱动和异步:使用事件监听和回调函数,使网页能响应用户操作。通过Promiseasync/await支持异步编程,提高性能。
  6. 跨平台:适用于Windows、Mac、Linux等各种操作系统,并且支持大多数浏览器(Chrome、Firefox、Edge等)。
0-3 食用本文前须知!!!
  • 通常大部分的JS教程都会和HTMLCSS绑定(毕竟是网页开发捏),但由于本教程的后续目的咱们是为了愉快的玩耍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 的 aptbrew)。
  • 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 语言的一次重大更新,引入了 letconst、箭头函数、类(class)、模块(import/export)等重要特性。
版本发布时间重要特性
ES6 (ES2015)2015let / const、箭头函数、类(class)、模板字符串、解构赋值、默认参数、import/export
ES7 (ES2016)2016Array.prototype.includes()、指数运算符 (**)
ES8 (ES2017)2017async/awaitObject.entries()Object.values()
ES9 (ES2018)2018Promise.finally()、正则表达式改进(命名捕获组等)
ES10 (ES2019)2019Array.prototype.flat(), Object.fromEntries()
ES11 (ES2020)2020BigIntPromise.allSettled()、可选链 ?.
ES12 (ES2021)2021String.replaceAll()、逻辑赋值运算符 (`&&=,
1-5 JavaScript 严格模式 ("use strict")
  • 严格模式("use strict")是 ES5 引入的一种 JavaScript 执行模式,旨在 消除 JavaScript 的不安全或错误用法,提高代码质量和执行效率
  • 开启严格模式后,JavaScript 代码会 抛出更多错误,阻止一些潜在的 Bug,同时提高 JS 引擎的优化效率。
  • 如何开启严格模式?
  1. 在代码的第一行添加
"use strict";

console.log("严格模式开启!");
  1. 严格模式也可以局部应用于某个函数
function strictFunction() {
    "use strict";
    let x = 10;
    console.log(x);
}
strictFunction();

  • 严格模式总结:
规则解释
必须显式声明变量x = 10; 会报错
禁止重复变量声明var a = 10; var a = 20; 会报错
禁止删除变量/函数delete x; 会报错
普通函数的 thisundefinedfunction 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 提供了 varletconst 三种方式来声明变量
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(数字类型)
  • 包括整数和浮点数
  • 例如:103.14-5
  • 还有特殊值:Infinity-InfinityNaN
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(布尔类型)
  • 只有两个值:truefalse
  • 用于逻辑判断
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 访问对象的属性(一):使用 . 访问(常用)
  • . 访问方式只能用于符合变量命名规则的键(如 nameage)。
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 检查对象是否包含某个属性
  1. 使用 in 关键字
console.log("name" in person);  // true
console.log("gender" in person); // false
  1. 使用 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)); // 25

function 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 有三种主要作用域
  1. 全局作用域(Global Scope)**: 声明在函数外部的变量,在整个脚本或浏览器窗口中可访问
  2. 函数作用域(Function Scope):在函数内部声明的变量,只能在该函数内部访问
  3. 块级作用域(Block Scope,ES6):letconst 具有块级作用域,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)。
  • 严格模式下,thisundefined
"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' == 5true
===严格相等(值相等,类型也必须相同)'5' === 5false
!=宽松不等(值不相等,类型可转换)'5' != 5false
!==严格不等(值或类型不同)'5' !== 5true
>大于10 > 5true
<小于10 < 5false
>=大于等于10 >= 10true
<=小于等于10 <= 5false
4-2 算数运算符
运算符描述示例结果
+加法5 + 38
-减法5 - 32
*乘法5 * 315
/除法5 / 22.5
%取模(取余数)5 % 21
**指数(幂运算)2 ** 38
  • 然后是自增自减老规矩~
形式说明示例结果
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 + yx += 2;
-=x = x - yx -= 2;
*=x = x * yx *= 2;
/=x = x / yx /= 2;
**=x = x ** yx **= 2;
%=x = x % yx %= 2;

5 分支语句(条件语句) 和 循环语句

  • 本节同样基础,我们飞速略过
5-1 if-else 语句
  • 无需多言
if (/*condition1*/)  
{  
    
}  
else if (/*condition2*/)  
{  
   
}  
else  
{  
  
}
5-2 switch 语句
  • 需要注意的是,js的switch 语句可以用于 任何类型stringnumberboolean,甚至 objectfunction)。
  • 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...infor...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 breakcontinue
  • 同理:
    • 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 来管理逻辑,而不是 setTimeoutPromise
  • 因此本节对Screeps编程毫无作用,不感兴趣的朋友们可以直接跳过~
7-1 异步机制
  • JavaScript 是 单线程 语言,为了防止长时间执行的任务(如网络请求、文件读写)阻塞主线程,采用 异步编程 方式,使代码可以在等待操作完成的同时执行其他任务。
  • 常见的异步操作:
    • setTimeout / setInterval(定时器)
    • DOM 事件监听 (addEventListener)
    • AJAX / Fetch 请求(HTTP 请求)
    • Promiseasync/await
7-2 setTimeout / setInterval(定时器)
  • setTimeoutsetInterval 是 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(必填):延迟时间(单位:毫秒 ms1000ms = 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/awaitES8 引入的一种更直观的异步编程方式,它是 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
  • BirdRobot 没有继承同一个父类,但都实现了 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,将JSCSSHTML的教程分离,专注于讲解JS的原生基础语法。

  • 完成上述基础知识学习,你就可以正式开始学习玩耍Screeps咯,事不宜迟那就从教程开始吧:Screeps官方教程请添加图片描述

  • 如有错误,欢迎指出!!!!!

  • 感谢大家的支持!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2322440.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Prometheus stack命令行接入springboot服务metrics

使用Prometheus Stack监控SpringBoot应用 本文将详细介绍如何使用Prometheus Stack监控SpringBoot应用的metrics。假设你已经安装了Kubernetes集群&#xff0c;并使用Helm安装了Prometheus Stack全家桶。SpringBoot应用已经配置好&#xff0c;暴露了相应的metrics端点。 Sprin…

Git Bash 设置Notepad++作为默认编辑器

网上搜的时候发现别人搞得有点复杂 &#xff08;绝对正确的方法&#xff09;Git Bash 设置Notepad作为默认编辑器_git 通过notpad 编辑器-CSDN博客 最简单的方式就是重新安装git&#xff0c;然后在选择编辑器的时候&#xff0c;勾选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); }// 生成随机多边形…

【数据结构】二叉树 — 经典OJ面试题剖析!!!

目录 二叉树相关oj题 1. 检查两颗树是否相同 2. 另一棵树的子树 3. 翻转二叉树 4. 判断一颗二叉树是否是平衡二叉树 5. 对称二叉树 6. 二叉树的构建及遍历 7. 二叉树的层序遍历 8. 判断一棵树是不是完全二叉树 9. 二叉树的最近公共祖先 10. 根据前序与中序遍历序列构…

【MySQL】用户账户、角色、口令、PAM

目录 查看用户账户设置 连接 1.本地连接 2.远程连接 账户 角色 操作用户账户和角色 配置口令和账户有效期限 手工使口令过期 配置口令有效期限 PAM身份验证插件 客户端连接&#xff1a;使用 PAM 账户登录 在连接到MySQL服务器并执行查询时&#xff0c;会验证你的身…

SpringBoot 3+ Lombok日志框架从logback改为Log4j2

r要将Spring Boot 3项目中的日志框架从Logback切换到Log4j2&#xff0c;并配置按日期滚动文件和控制台输出&#xff0c;请按照以下步骤操作&#xff1a; 步骤 1&#xff1a;排除Logback并添加Log4j2依赖 在pom.xml中修改依赖&#xff1a; <dependencies><!-- 排除默…

【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——安装及…

二叉树相关算法实现:判断子树与单值二叉树

目录 一、判断一棵树是否为另一棵树的子树 &#xff08;一&#xff09;核心思路 &#xff08;二&#xff09;代码实现 &#xff08;三&#xff09;注意要点 二、判断一棵树是否为单值二叉树 &#xff08;一&#xff09;核心思路 &#xff08;二&#xff09;代码实现…

CSS 美化页面(一)

一、CSS概念 CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式表&#xff09;是一种用于描述 HTML 或 XML&#xff08;如 SVG、XHTML&#xff09;文档 样式 的样式表语言。它控制网页的 外观和布局&#xff0c;包括字体、颜色、间距、背景、动画等视觉效果。 二、CS…

23种设计模式-组合(Composite)设计模式

组合设计模式 &#x1f6a9;什么是组合设计模式&#xff1f;&#x1f6a9;组合设计模式的特点&#x1f6a9;组合设计模式的结构&#x1f6a9;组合设计模式的优缺点&#x1f6a9;组合设计模式的Java实现&#x1f6a9;代码总结&#x1f6a9;总结 &#x1f6a9;什么是组合设计模式…

LSTM创新点不足?LSTM + Transformer融合模型引领Nature新突破

LSTM创新点不足&#xff1f;LSTM Transformer融合模型引领Nature新突破 2024年LSTM真的没有创新空间了吗&#xff1f; 最新研究表明&#xff0c;通过将LSTM与Transformer巧妙融合&#xff0c;依然能创造出Nature级别的突破性成果。LSTM擅长处理短期时序模式&#xff0c;但在…

【区块链安全 | 第六篇】NFT概念详解

文章目录 NFTNFT&#xff08;非同质化代币&#xff09;FT&#xff08;可替代代币&#xff09; 以太坊 NFT 标准ERC-721&#xff08;单一资产&#xff09;ERC-1155&#xff08;多资产&#xff09; NFT 市场版税机制NFT 借贷NFT 安全 NFT NFT&#xff08;Non-Fungible Token&…

iOS常见网络框架

URLSession、Alamofire 和 Moya 1. URLSession 1.1 核心概念 URLSession 是 Apple 官方提供的网络请求 API&#xff0c;封装在 Foundation 框架中。它支持 HTTP、HTTPS、FTP 等协议&#xff0c;可用于&#xff1a; ​ • 普通网络请求&#xff08;GET/POST&#xff09; ​ …

蓝桥杯备考---->激光炸弹(二维前缀和)

本题我们可以构造二维矩阵&#xff0c;然后根据题意&#xff0c;枚举所有边长为m的正方形&#xff0c;找到消灭价值最多的炸弹 #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…

数据结构 --树和森林

树和森林 树的存储结构 树的逻辑结构 树是一种递归定义的数据结构 树是n(n≥0)个结点的有限集。当n0时&#xff0c;称为空树。在任意一棵非空树中应满足&#xff1a; 1)有且仅有一个特定的称为根的结点。 2)当n>1时&#xff0c;其余结点可分为m(m>0)个互不相交的有…

QOpenGLWidget视频画面上绘制矩形框

一、QPainter绘制 在QOpenGLWidget中可以绘制&#xff0c;并且和OpenGL的内容叠在一起。paintGL里面绘制完视频后&#xff0c;解锁资源&#xff0c;再用QPainter绘制矩形框。这种方式灵活性最好。 void VideoGLWidget::paintGL() {glClear(GL_COLOR_BUFFER_BIT);m_program.bi…

Linux系统加固笔记

检查口令为空的账户 判断依据&#xff1a;存在则不符合 特殊的shell a./bin/false:将用户的shell设置为/bin/false&#xff0c;用户会无法登录&#xff0c;并且不会有任何提示信息b./sbib/nologin&#xff1a;nologin会礼貌的向用户发送一条消息&#xff0c;并且拒绝用户登录…

【Go万字洗髓经】Golang中sync.Mutex的单机锁:实现原理与底层源码

本章目录 1. sync.Mutex锁的基本用法2. sync.Mutex的核心原理自旋到阻塞的升级过程自旋CAS 饥饿模式 3. sync.Mutex底层源码Mutex结构定义全局常量Mutex.Lock()方法第一次CAS加锁能够成功的前提是&#xff1f;竞态检测 Mutex.lockSlow()lockSlow的局部变量自旋空转state新值构造…

Django REST framework 源码剖析-认证器详解(Authentication)

Django REST framework 源码剖析-认证器详解(Authentication) 身份验证始终在视图的最开始运行&#xff0c;在权限和限制检查发生之前&#xff0c;以及在允许任何其他代码继续之前。request.user属性通常设置为contrib.auth包的user类的实例。request.auth属性用于任何其他身份…

TCP/IP三次握手的过程,为什么要3次?

一&#xff1a;过程 第一次&#xff08;SYN&#xff09;&#xff1a; 客户端发送一个带有SYN标志的TCP报文段给服务器&#xff0c;设置SYN1&#xff0c;并携带初始序列号Seqx&#xff08;随机值&#xff09;&#xff0c;进入SYN_SENT状态。等待服务器相应。 第二次&#xff08…