
JavaScript
继HTML和CSS后的JavaScript记录
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事件非常多,种类也分的五花八门。常见的就是鼠标的onclick,onmouseover,onmouseout ;页面的onload,onunload,onchange,onresize,onpagehide,onpageshow;键盘的onkeydown,onkeyup;表单的onsubmit, onreset,onselect,
字符串的方法也非常多,但是大部分和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的元素,然后把内容写入到里面。
类
类即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);// 使用getterconsole.log(myClass.value);// 使用settermyClass.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类的原型为AnimalDog.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 ,将以字符串的方式返回所有的 cookievar x = document.cookie;由于cookie只能在尾后追加,所以想要删除可以利用到提到过的expires设置一个删除时间expires=Thu, 01 Jan 1970 00:00:00 GMT
异步
异步即多线程,JS是单线程的,但是可以通过回调函数或者Promise实现异步。
回调函数
回调函数又分为
- 同步回调函数 :函数执行时,会先执行同步代码,再执行回调函数。
- 异步回调函数 :函数执行时,会先执行异步代码,再执行回调函数。
// 数组的 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有几个方法:
.then(): 当 Promise 成功时调用的函数。.catch(): 当 Promise 失败时调用的函数。.finally(): 当 Promise 完成时调用的函数,无论成功与否。
基本用法如下:
// 创建一个 Promiseconst myPromise = new Promise((resolve, reject) => { // 异步操作 setTimeout(() => { const success = true; if (success) { resolve('操作成功完成'); } else { reject('操作失败'); } }, 1000);});
// 使用 PromisemyPromise.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');});
// 返回最先完成的 PromisePromise.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也有局限性:
- 无法取消:一旦创建,Promise无法取消
- 内存占用:未处理的Promise可能导致内存泄漏
- 错误处理复杂:在复杂的链式调用中,错误处理可能变得复杂
示例:
// 示例1: 基础 Promisedocument.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)
不写了 懒的 没啥好写的(