JavaScript - MuxiaoWF跳到主要内容
JavaScript

JavaScript

周二 6月 28 2005
5845 字 · 37 分钟

JavaScript

Javascript脚本代码须位于<script></script>标签内或单独创建一个js文件并通过<script src="SomeFile.js"></script>导入使用。

与Java相同,JS使用分号分割语句

基础

基础中的基础

使用var关键字来定义一个变量:var a = 0;var a, b;;没有使用var关键词定义的变量,默认为全局变量,全局变量可以在任何地方访问。

也可以通过let声明变量,该变量只在let命令所在的代码块{}内有效。

常量定义符const,同cpp

函数定义:function myFunction(a, b) { return a * b; },非常简单,但是要注意前面有一个function关键词

注:有一种特殊的自调用函数形式,(function () {})(); 函数闭包 解决计数器难题等 可以自行搜索,反正俺应该也许大概可能不会用

数据类型的定义和python差不太多,都是拥有动态类型(相同的变量可用作不同的类型),不同的是数字没有int或者float等区分而是只用number一个;可以通过 typeof关键字查看变量类型,例如typeof a查看a变量的类型。

注:var myFunction = function (a, b) {return a * b}; var x = myFunction(4, 3);函数和python一样可以直接赋值给变量,并通过变量调用。

JS的对象定义也非常简单:var person = { name: "Muxiao", hobby: "playing games" }; ,类似于Python中的字典或者Java中的HashMap;里面也可以加对象方法,定义:methodName : function() { // 代码 }。更多详情参考下面的《类》章节

JS可以在html事件触发时调用,但是html事件非常多,种类也分的五花八门。常见的就是鼠标的onclickonmouseoveronmouseout ;页面的onloadonunloadonchangeonresizeonpagehideonpageshow;键盘的onkeydownonkeyup;表单的onsubmitonresetonselect

字符串的方法也非常多,但是大部分和python的语法一致。

各种基础语句如if (condition) { // 代码 } else { // 代码 }switch (expression) { case value1: // 代码 break; default: // 逻辑代码 }for (initialization; condition; increment) { // 代码 }while (condition) { // 代码 }do { // 代码 } while ()try { // 代码 } catch (error) { // 错误处理代码 } finally { // 代码 }等等都不再赘述

void()在其他语言里也算是非常常见了,同样的它在JS里仅仅是代表不返回任何值,但是括号内的表达式还是要运行。语法如: void func()javascript:void func()或者void(func())javascript:void(func())

因此,有一个非常常见的语句javascript:void(0),也就是什么都不做,通常用在链接的href中中,如 <a href="javascript:void(0)"">点击我</a>代表什么都不执行。

输出

JS输出可通过:

  • 使用window.alert()弹出警告框。
  • 使用document.write()方法将内容写到 HTML 文档中。(废弃)
  • 使用innerHTML写入到 HTML 元素。想要获取一个元素,可以使用document.getElementById()方法。
  • 使用console.log()写入到浏览器的控制台。
<button onclick="showAlert()">window.alert()弹出警告框</button>
<button onclick="writeToElement()">innerHTML写入元素</button>
<button onclick="writeToConsole()">console.log()写入控制台</button>
<div id="outputElement" class="output-container">
这里是innerHTML输出的目标元素
</div>
<div id="consoleOutput" class="output-container">
控制台输出内容请查看浏览器开发者工具
</div>
<script>
function showAlert() {
window.alert("这是通过window.alert()显示的消息!");
}
function writeToElement() {
const element = document.getElementById('outputElement');
element.innerHTML = "<strong>这是通过innerHTML写入的内容 - " + new Date().toLocaleTimeString() + "</strong>";
}
function writeToConsole() {
console.log("这是通过console.log()输出的内容 - " + new Date().toLocaleTimeString());
document.getElementById('consoleOutput').innerHTML = "<strong>已向控制台输出消息,请打开开发者工具(右键检查)查看</strong>";
}
</script>

document.getElementById('consoleOutput').innerHTML = "xxx"这句就是在找页面里id叫consoleOutput的元素,然后把内容写入到里面。

这里是innerHTML输出的目标元素
控制台输出内容请查看浏览器开发者工具

类即class,定义为class ClassName { constructor() { // 构造函数 } funcName(){ //正常方法 } } 注意到在类里的方法都是直接funcName来构造的,而不用像在类外还要加function关键字

同样的,类也有继承:

class ChildClass extends ParentClass {
constructor(/* 参数 */) {
super(/* 父类构造函数参数 */);
// 子类初始化代码
}
}

getter和setter:

class MyClass {
constructor(value) {
this._value = value;
}
get value() {
return this._value;
}
set value(newValue) {
this._value = newValue;
}
}
let myClass = new MyClass(42);
// 使用getter
console.log(myClass.value);
// 使用setter
myClass.value = 99;

注意:即使getter/setter是一个方法,当你想获取属性值时也不要使用括号,getter/setter 方法的名称不能与属性的名称相同,在其之中一切的 return 都是无效的。

私有:使用#来定义私有属性和私有方法。

class MyClass {
#privateField = '私有字段';
getPrivateField() {
return this.#privateField; // 只能在类内部访问
}
}

静态方法&属性:使用static关键字定义静态方法或属性。与Java相同,静态方法或属性不能访问实例属性或实例方法,需要通过类名访问。

class MyClass {
static staticMethod() {
// 静态方法
}
static staticProperty = '静态属性';
}

原型Prototype

每个JavaScript对象都有一个内部属性 [[Prototype]](通常通过 __proto__访问),它指向另一个对象,这个对象就是原型。简单的来说,原型就是对象继承。

与Java相同,JS的所有对象都默认继承自Object.prototype,Object.prototype是所有对象的原型。

我们可以通过原型来添加原本对象里没有的方法:

// 给所有数组添加自定义方法
Array.prototype.customPush = function (element) {
this[this.length] = element;
return this.length;
};
const numbers = [1, 2, 3];
numbers.customPush(4);
console.log(numbers); // [1, 2, 3, 4]
// 给所有字符串添加方法
String.prototype.reverse = function () {
return this.split('').reverse().join('');
};
console.log('hello'.reverse()); // 'olleh'

原型链继承:

// 父类构造函数(非类class形式)
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
return `${this.name} 发出了声音`;
};
// 子类构造函数
function Dog(name, breed) {
// 调用父类构造函数
Animal.call(this, name);
this.breed = breed;
}
// 设置原型链继承 Dog类的原型为Animal
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 添加子类特有方法
Dog.prototype.bark = function () {
return `${this.name} 在汪汪叫`;
};
// 重写父类方法
Dog.prototype.speak = function () {
return `${this.name} 在说话`;
};
const dog = new Dog('旺财', '金毛');
console.log(dog.speak()); // 旺财 在说话
console.log(dog.bark()); // 旺财 在汪汪叫
const animal = new Animal('动物');
console.log(animal.speak()); // 动物 发出了声音
//console.log(animal.bark()) // undefined

在上面我们用到了Object.create(proto, propertiesObject)方法,这个方法可以创建一个对象,并设置它的原型为指定的对象。proto:新对象的原型对象(必需参数); propertiesObject:可选参数,用于添加到新对象的属性描述符

// 创建一个原型对象
const person = {
name: '张三',
age: 25,
introduce: function () {
return `我是 ${this.name},今年 ${this.age} 岁`;
}
};
// 使用 Object.create() 创建新对象,并将 person 对象设置为新对象的原型
const student = Object.create(person);
student.grade = '大三';
student.school = '清华大学';
console.log(student.name); // '张三' (继承自原型)
console.log(student.introduce()); // '我是 张三,今年 25 岁' (继承自原型)
console.log(student.grade); // '大三' (自有属性)

DOM

DOM即Document Object Model,即文档对象模型,是HTML文档的API,用于操作HTML文档,即可以修改HTML文档。

在上面章节《输出》里俺们已经介绍过一点,这里写的更详细一点

通过JS操作HTML内元素可通过以下方式:

  • id: document.getElementById('id')
  • class: document.getElementsByClassName('class')
  • tagName: document.getElementsByTagName('tagName')
  • 全选某元素: document.querySelectorAll('元素种类名')

注1:使用document.getElementsByTagName('tagName')获取的元素是一个HTMLCollection对象,所以要使用[0]来获取第一个元素

注2:使用document.querySelectorAll('元素种类名')获取的元素是一个NodeList对象,所以也要使用[0]来获取第一个元素

上面通过innerHTML写入内容,JS还可以修改属性:document.getElementById(id).attribute=新属性值,如: document.getElementById("image").src="NEW.jpg";将图片的src属性改为NEW.jpg

不仅如此,JS还可以修改样式:document.getElementById(id).style.属性=新属性值,如: document.getElementById("ID").style.color="blue";将文本颜色改为蓝色

同样的,在《基础的基础》中俺们已经看到过一系列的html事件,除了通过事件直接调用外,俺们可以给一些元素添加事件监听器,如: document.getElementById("ID").addEventListener("click", function); 注意,这里的事件监听器没有on,函数也没有小括号

添加了事件监听器,自然也有删除事件监听器的方法:document.getElementById("ID").removeEventListener("click", function);

还可以使用.appendChild(newElement)添加元素:下面是添加一个段落

var para = document.createElement("p");
var node = document.createTextNode("这是一个新的段落。");
para.appendChild(node);
var element = document.getElementById("id");
element.appendChild(para);

注:当然还有.insertBefore(newElement, oldElement)添加到某个元素之前,这里略过

删除元素:要移除一个元素,一定需要知道该元素的父元素,下面删除一个元素:

var parent = document.getElementById("div1");
var child = document.getElementById("p1");
parent.removeChild(child);

替换元素:使用.replaceChild(newElement, oldElement)同样,要替换一个元素,也需要知道该元素的父元素,

var para = document.createElement("p");
var node = document.createTextNode("这是一个新的段落。");
para.appendChild(node);
var parent = document.getElementById("div1");
var child = document.getElementById("p1");
parent.replaceChild(para, child);

BOM

BOM即Browser Object Model,即浏览器对象模型,是浏览器的API,用于操作浏览器,比如:

  • 窗口:window
  • URL:location
  • 屏幕:screen
  • 浏览器:navigator
  • 历史记录:history

对于window,常用的属性和方法:

  • window.innerHeight - 浏览器窗口的内部高度(包括滚动条)
  • window.innerWidth - 浏览器窗口的内部宽度(包括滚动条)
  • window.open() - 打开新窗口
  • window.close() - 关闭当前窗口 可惜的是,似乎大部分浏览器都限制了此操作
  • window.moveTo() - 移动当前窗口 可惜的是,似乎大部分浏览器都限制了此操作
  • window.resizeTo() - 调整当前窗口的尺寸 可惜的是,似乎大部分浏览器都限制了此操作

location:

  • location.hostname 返回 web 主机的域名
  • location.pathname 返回当前页面的路径和文件名
  • location.port 返回 web 主机的端口 (80 或 443)
  • location.protocol 返回所使用的 web 协议(http: 或 https:)
  • location.href 属性返回当前页面的 URL
  • location.assign(url) 加载新的文档
  • location.replace(url) 替换当前文档

注:location.assign() 和 location.replace() 都会加载新的文档,但是它们之间有区别: location.assign() 会创建一个新的窗口可回退,location.replace() 会替换当前窗口没有回退。

screen:

  • screen.availWidth - 可用的屏幕宽度
  • screen.availHeight - 可用的屏幕高度

navigator:

  • navigator.appName - 浏览器名称
  • navigator.appVersion - 浏览器版本
  • navigator.userAgent - 浏览器信息
  • navigator.platform - 运行浏览器的操作系统
  • navigator.language - 浏览器语言
  • navigator.cookieEnabled - 返回浏览器是否启用 Cookie
  • navigator.appCodeName - 浏览器代号

注:navigator的属性可能会被用户改变,所以最好不要依赖这些属性

history:

  • history.back() - 与在浏览器点击后退按钮相同
  • history.forward() - 与在浏览器中点击向前按钮相同
  • history.go() - 参数为正数时,表示前进指定页面数;参数为负数时,表示后退指定页面数;参数为 0 时,表示刷新当前页面。

注意小心使用

除此之外还有各种弹窗:

// 警告框
alert("警告!");
// 确认框
confirm("确定吗?");
// 输入框
var x = prompt("输入你的名字:", "Harry Potter");

操作结果将显示在这里。

计时事件:setInterval(function, milliseconds)会间隔指定的毫秒数不停地执行指定的代码; setTimeout(function, milliseconds)会等待指定的毫秒数后执行指定的代码;与他们相对的是clearInterval(var)clearTimeout(var),但是停止的必须是全局变量

// 一个展示实时时间的功能
const week = ['天', '一', '二', '三', '四', '五', '六'],
dom = document.getElementById("dateTxt"),
checkTime = i => (i < 10) ? `0${i}` : i; // 在小于10的数字前加一个‘0’
let today;
function startTime() {
today = new Date();
dom.innerHTML = `星期${week[today.getDay()]}${checkTime(today.getHours())}:${checkTime(today.getMinutes())}:${checkTime(today.getSeconds())}`;
}
setInterval(startTime, 500); // 循环计时, 500毫秒执行一次
// 一个计时器功能
const start = new Date();
function timer(start) {
const now = new Date();
const det = now - start;
now.setTime(det);
now.setHours(0);
document.getElementById("id").innerHTML = now.toLocaleTimeString();
}
setInterval(function () {
timer(start)
}, 1000);

也可以通过JS来创建/读取 Cookie:

// 创建 Cookie 可加expires设置过期时间
document.cookie = "username=MuxiaoWF";
// 读取 Cookie ,将以字符串的方式返回所有的 cookie
var x = document.cookie;

由于cookie只能在尾后追加,所以想要删除可以利用到提到过的expires设置一个删除时间expires=Thu, 01 Jan 1970 00:00:00 GMT

异步

异步即多线程,JS是单线程的,但是可以通过回调函数或者Promise实现异步。

回调函数

回调函数又分为

  1. 同步回调函数 :函数执行时,会先执行同步代码,再执行回调函数。
  2. 异步回调函数 :函数执行时,会先执行异步代码,再执行回调函数。
// 数组的 forEach 方法使用同步回调
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(function (num) {
console.log(num * 2);
});
console.log('这行会在回调执行完后输出');
// setTimeout 是典型的异步回调使用场景
console.log('开始');
setTimeout(function () {
console.log('3秒后执行');
}, 3000);
console.log('这行会先执行');
同步回调
异步回调

Promise

Promise是JavaScript中用于处理异步操作的对象,它表示将来某个时间点会返回一个结果(可能是成功的结果,也可能是失败的原因)。

Promise 有三种状态:

  • pending:初始状态,既不是成功,也不是失败状态
  • fulfilled:操作成功完成
  • rejected:操作失败

promise有几个方法:

  1. .then(): 当 Promise 成功时调用的函数。
  2. .catch(): 当 Promise 失败时调用的函数。
  3. .finally(): 当 Promise 完成时调用的函数,无论成功与否。

基本用法如下:

// 创建一个 Promise
const myPromise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true;
if (success) {
resolve('操作成功完成');
} else {
reject('操作失败');
}
}, 1000);
});
// 使用 Promise
myPromise.then(result => {
console.log(result); // 输出: 操作成功完成
}).catch(error => {
console.log(error); // 输出: 操作失败
}).finally(() => {
console.log('清理工作');
});

promise有几个static的方法:

.resolve()创建一个Promise,并立即解决:

const resolvedPromise = Promise.resolve('成功');
resolvedPromise.then(value => {
console.log(value); // 输出: 成功
});

.reject()创建一个Promise,并立即拒绝:

const rejectedPromise = Promise.reject('失败');
rejectedPromise.catch(error => {
console.log(error); // 输出: 失败
});

.all()等待所有Promise都解决或拒绝:

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
// 等待所有Promise都解决
Promise.all([promise1, promise2, promise3]).then(values => {
console.log(values); // 输出: [3, 42, "foo"]
});

.race()等待所有Promise都解决或拒绝,并返回第一个解决或拒绝的Promise的结果:

const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
// 返回最先完成的 Promise
Promise.race([promise1, promise2]).then(value => {
console.log(value); // 输出: "two"
});

promise的错误处理&链式调用:链式调用中的返回值都会留给下一个链式

Promise.resolve()
.then(() => {
throw new Error('出错了');
})
.catch(error => {
console.log(error.message); // 输出: 出错了
return '恢复值';
})
.then(value => {
console.log(value); // 输出: 恢复值
});

Promise也有局限性:

  1. 无法取消:一旦创建,Promise无法取消
  2. 内存占用:未处理的Promise可能导致内存泄漏
  3. 错误处理复杂:在复杂的链式调用中,错误处理可能变得复杂

示例:

// 示例1: 基础 Promise
document.getElementById('basicPromise').addEventListener('click', () => {
const resultDiv = document.getElementById('basicResult');
resultDiv.innerHTML = '<div class="loading">执行中...</div>';
delay(2000, '基础 Promise 执行成功!')
.then(result => {
resultDiv.innerHTML = `<div class="success">${result}</div>`;
})
.catch(error => {
resultDiv.innerHTML = `<div class="error">错误: ${error.message}</div>`;
});
});
// 示例2: 链式调用
document.getElementById('chainPromise').addEventListener('click', () => {
const resultDiv = document.getElementById('chainResult');
resultDiv.innerHTML = '<div class="loading">执行链式调用...</div>';
delay(500, '第一步完成')
.then(result => {
resultDiv.innerHTML = `<div>${result}</div>`;
return delay(500, '第二步完成');
})
.then(result => {
resultDiv.innerHTML += `<div>${result}</div>`;
return delay(500, '第三步完成');
})
.then(result => {
resultDiv.innerHTML += `<div>${result}</div>`;
resultDiv.innerHTML += '<div class="success">链式调用全部完成!</div>';
})
.catch(error => {
resultDiv.innerHTML += `<div class="error">错误: ${error.message}</div>`;
});
});
// 示例3: 错误处理
document.getElementById('successPromise').addEventListener('click', () => {
const resultDiv = document.getElementById('errorResult');
resultDiv.innerHTML = '<div class="loading">执行成功操作...</div>';
delay(1000, '操作成功!', false)
.then(result => {
resultDiv.innerHTML = `<div class="success">${result}</div>`;
})
.catch(error => {
resultDiv.innerHTML = `<div class="error">错误: ${error.message}</div>`;
});
});
document.getElementById('errorPromise').addEventListener('click', () => {
const resultDiv = document.getElementById('errorResult');
resultDiv.innerHTML = '<div class="loading">执行失败操作...</div>';
delay(1000, '操作失败!', true)
.then(result => {
resultDiv.innerHTML = `<div class="success">${result}</div>`;
})
.catch(error => {
resultDiv.innerHTML = `<div class="error">错误: ${error.message}</div>`;
});
});
// 示例4: 并行处理
document.getElementById('parallelPromise').addEventListener('click', () => {
const resultDiv = document.getElementById('parallelResult');
resultDiv.innerHTML = '<div class="loading">并行执行多个操作...</div>';
const promises = [
delay(1000, '任务A完成'),
delay(2000, '任务B完成'),
delay(1500, '任务C完成'),
delay(800, '任务D完成')
];
Promise.all(promises)
.then(results => {
resultDiv.innerHTML = '<div class="success">所有任务完成:</div>';
results.forEach((result, index) => {
resultDiv.innerHTML += `<div>-${result}</div>`;
});
})
.catch(error => {
resultDiv.innerHTML = `<div class="error">错误: ${error.message}</div>`;
});
});
// 示例5: 竞速
document.getElementById('racePromise').addEventListener('click', () => {
const resultDiv = document.getElementById('raceResult');
resultDiv.innerHTML = '<div class="loading">执行竞速操作...</div>';
const promises = [
delay(2000, '慢速操作'),
delay(1000, '快速操作'),
delay(3000, '超慢速操作')
];
Promise.race(promises)
.then(result => {
resultDiv.innerHTML = `<div class="success">最先完成: ${result}</div>`;
})
.catch(error => {
resultDiv.innerHTML = `<div class="error">错误: ${error.message}</div>`;
});
});

async/await

async/await建立在 Promise 之上,让异步代码看起来像同步代码一样

在函数声明前添加 async 关键字,表示该函数是异步的:async function fetchData() { // 函数体 }

async函数总是返回一个 Promise: 如果返回值不是Promise,会自动包装成resolved Promise ;如果抛出异常,会返回rejected Promise

await只能在 async 函数内部使用:const result = await somePromise;表示暂停 async 函数的执行,等待 Promise 完成: 如果 Promise 被 resolve,返回 resolve 的值 ;如果 Promise 被 reject,抛出错误(可以用 try/catch 捕获)

示例:如同我们一开始说的,async/await建立在 Promise 之上,因此使用Promise完成的需求,也可以使用async/await完成

// 基础示例
document.getElementById('asyncAwaitBasic').addEventListener('click', async () => {
const resultDiv = document.getElementById('asyncAwaitResult');
resultDiv.innerHTML = '<div class="loading">执行 async/await 操作...</div>';
try {
// 使用 async/await 代替 .then()
const step1 = await delay(500, '第一步完成 (async/await)');
resultDiv.innerHTML = `<div>${step1}</div>`;
const step2 = await delay(500, '第二步完成 (async/await)');
resultDiv.innerHTML += `<div>${step2}</div>`;
const step3 = await delay(500, '第三步完成 (async/await)');
resultDiv.innerHTML += `<div>${step3}</div>`;
resultDiv.innerHTML += '<div class="success">async/await 全部完成!</div>';
} catch (error) {
resultDiv.innerHTML += `<div class="error">错误: ${error.message}</div>`;
}
});

AJAX

AJAX即Asynchronous JavaScript And XML(异步的 JavaScript 和 XML)

不写了 懒的 没啥好写的(


感谢您的阅读!如果可以,给俺点些关注吧~

JavaScript

周二 6月 28 2005
5845 · 37 分钟
封面
示例歌曲
示例艺术家
封面
示例歌曲
示例艺术家
0:00 / 0:00