2023.10.22: 更新了js对象内容(ver1.2)
2023.10.21: 优化了排版,更新至ver1.1
2023.10.19:更新js ver1.0
JavaScript学习(MDN)
在MDN上的javascript基础学习。
JavaScript第一步
什么是javaScript?
js简要
javascript是轻量级解释型语言,使用即时编译:会编译为二进制的格式。
js使用
使用内部js(在</head>
标签结束前插入<script>
元素)
或者直接外联(推荐): <script src="script.js" defer></script>
不建议内联(太乱了)
案例:使用addEventListener
使用querySelectorAll()函数选择按钮,使用addEventListener()为每个按钮分配一个处理器。
const buttons = document.querySelectorAll("button");
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", createParagraph);
}
脚本调用策略
HTML是按其出现在页面中的次序调用的,若js加载在欲操作的元素之前会发生错误。
1.内部:
document.addEventListener("DOMContentLoaded", () => {
// …
});
原理:这个事件监听器能监听浏览器的DOMContentLoaded
事件,标志着HTML文档体完全加载和解析,而在该代码需在事件触发后才进行,从而避免了错误。
2.外部:
<script src="script.js" defer></script>
在<script>
最后加入defer解决问题。
原理:defer告知浏览器在遇到<script>
元素时继续下载HTML内容。
async 和 defer 的区别
- 使用
async
脚本时不会阻塞页面渲染,而是直接下载然后运行。但是,一旦下载完成,脚本就会执行,从而阻止页面渲染。脚本的运行次序无法控制。当页面的脚本之间彼此独立,且不依赖于本页面的其他任何脚本时,async
是最理想的选择。 - 使用
defer
属性加载的脚本将按照它们在页面上出现的顺序加载。在页面内容全部加载完毕之前,脚本不会运行,如果脚本依赖于 DOM 的存在(例如,脚本修改了页面上的一个或多个元素),这一点非常有用。
例:
<script async src="js/vendor/jquery.js"></script>
<script async src="js/script2.js"></script>
<script async src="js/script3.js"></script>
三者的调用顺序是不确定的。
async
应该在有大量后台脚本需要加载,并且只想尽快加载到位的情况下使用
<script defer src="js/vendor/jquery.js"></script>
<script defer src="js/script2.js"></script>
<script defer src="js/script3.js"></script>
这将会按顺序以此加载。
注释
// 注释
/* 注释 */
javaScript初体验
感觉没什么要写的,很多都是在java中学过的了。
这篇文章更多是在讲第一个程序,贴个链接:[JavaScript 初体验](JavaScript 初体验 - 学习 Web 开发 | MDN (mozilla.org))
事件
侦听事件的结构称为事件监听器(Event Listener), 响应事件触发而运行的代码块称为事件处理器(Event Handler)。
for of循环
const fruits = ["apples", "bananas", "cherries"];
for (const fruit of fruits) {
console.log(fruit);
}
结果:
'apples','bananas','cherries'
for of在这里干了什么:获取fruits的第一个元素赋值给fruit,运行代码块,再重复这两个操作一直到fruits的末尾。
变量
验证变量存在
可以使用变量的方式验证变量是否存在:
myName;
myAge; //若没数值会返回undefined,若不存在会产生报错信息
初始化变量
var myName = "Chris";
var myAge = 37;
更新变量
myName = "Bob";
myAge = 40;
var和let的区别
let
是在现代版本中的 JavaScript 创建的一个新的关键字,用于创建与var
工作方式有些不同的变量,解决了过程中的问题。
D1:
myName = "Chris";
function logName() {
console.log(myName);
}
logName();
var myName; //变量提升
但let将不再允许该操作,并产生错误。
D2:
var myName = "Chris";
var myName = "Bob";
但let不能多次声明相同名称的变量。
注:Internet Explorer 直到第 11 版才支持 let
!
变量类型
- Number
- String
- Boolean
- Array
- Object
注:js的变量为动态类型
数字与操作符
typeof:返回数据类型
**: 幂, 5 ** 5
=3125
===:严格等于,要求数据类型相等
字符串
js的字符串用单双引号都可以,但不能在字符串中包含相同的引号。
转义字符
let bigmouth = 'I\'ve got no right to take my place...'; // 使用\'转义
数字与字符串
数值与字符串相互转换,用Number() 和 toString()方法。
let myNum = Number(myString); //转换为数值
let myString = myNum.toString(); //转换为字符串
字符串方法
length方法返回长度:
browserType.length;
indexOf方法查找子字符串:
browserType.indexOf("zilla"); //找不到返回-1
slice方法提取字符串:
browserType.slice(0, 3); //范围不包含第二个参数
browserType.slice(3); //返回自第三个字符后的字符串
toLowerCase、toUpperCase大小写转换:
let radData = "mY";
radData.toLowerCase(); //“my”
radData.toUpperCase(); //"MY"
replace进行替换:
let browserType = mozilia;
browserType.replace("moz", "van"); //vanilia
数组
创建数组
let shopping = ["bread", "milk", "cheese", "hummus", "noodles"];
let random = ["tree", 795, [0, 1, 2]]; //不一定同一种类
访问数组
shopping[0] = "tahini";
shopping;
// shopping will now return [ "tahini", "milk", "cheese", "hummus", "noodles" ]
获取数组长度
shopping.length;
字符串和数组的转换
split方法:
let myData = "Manchester,London,Liverpool,Birmingham,Leeds,Carlisle";
let myArray = myData.split(",");
myArray; //Manchester London Liverpool Birmingham Leeds Carlisle"
join方法:
let myNewString = myArray.join(",");
myNewString; //Manchester,London,Liverpool,Birmingham,Leeds,Carlisle
toString方法:
myArray.toString();
添加或删除数组项
let myArray = [
"Manchester",
"London",
"Liverpool",
"Birmingham",
"Leeds",
"Carlisle",
];
myArray.push("Cardiff", "Bradford"); //末尾添加Cardiff、Bradford
myArray.pop() //删除最后一个元素
myArray.unshift("Edinburgh") //首部添加Edinburgh
myArray.shift() //删除第一个元素
JavaScript基础
条件语句
if (condition) {
} else {
}
例子:
<label for="weather">选择今天的天气:</label>
<select id="weather">
<option value="">--作出选择--</option>
<option value="sunny">晴天</option>
<option value="rainy">雨天</option>
<option value="snowing">雪天</option>
<option value="overcast">阴天</option>
</select>
<p></p>
const select = document.querySelector("select");
const para = document.querySelector("p");
select.addEventListener("change", setWeather);
function setWeather() {
const choice = select.value;
if (choice === "sunny") { //进行判断
para.textContent = "阳光明媚。穿上短裤吧!去海滩,或公园,吃个冰淇淋。";
} else if (choice === "rainy") {
para.textContent = "外面下着雨;带上雨衣和雨伞,不要在外面呆太久。";
} else if (choice === "snowing") {
para.textContent =
"大雪纷飞,天寒地冻!最好呆在家里喝杯热巧克力,或者去堆个雪人。";
} else if (choice === "overcast") {
para.textContent =
"虽然没有下雨,但天空灰蒙蒙的,随时都可能变天,所以要带一件雨衣以防万一。";
} else {
para.textContent = "";
}
}
循环语句
同java,不再写笔记了。
函数
自定义函数
function myFunction() {
alert("hello world!");
}
myFunction();
匿名函数
也称函数表达式,没有函数名的函数,经常与事件处理程序一起使用。
function() {
alert("hello world!");
}
例:
var Button = document.querySelector("button");
Button.onclick = function()
{
alert("hello World!");
}
又如:
var test = function () {
alert("hello world!");
};
test();
事件介绍
事件是发生在你正在编程的系统中的事情——当事件发生时,系统产生(或“触发”)某种信号,并提供一种机制,当事件发生时,可以自动采取某种行动(即运行一些代码)。事件是在浏览器窗口内触发的,并倾向于附加到驻留在其中的特定项目。这可能是一个单一的元素,一组元素,当前标签中加载的 HTML 文档,或整个浏览器窗口。有许多不同类型的事件可以发生。
为了对一个事件做出反应,你要给它附加一个事件处理器
实例:处理点击事件
<button>
元素将在用户点击按钮时触发一个事件(定义了一个addEventListener函数)
<button>改变颜色</button>
const btn = document.querySelector("button");//引用
function random(number) {
return Math.floor(Math.random() * (number + 1));
}
//click可以监听点击事件
btn.addEventListener("click", () => {
const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
document.body.style.backgroundColor = rndCol;
});
不仅仅可以监听点击事件,addEventListener还可以:
- focus:聚焦(按Tab)
- blur:失焦(再次按下Tab)
- dblclick:双击按键
- mouseover:鼠标指针在按钮上悬停
- mouseout:鼠标指针移出按钮
移除监听器
用removeEventListener()方法
btn.removeEventListener("click", changeBackground);
也可以传递AbortSignal到addEventListener(),在拥有AbortSignal的控制器上调用abort()来删除。
const controller = new AbortController();
btn.addEventListener("click",() => {
const rndCol = `rgb(${random(255)},${random(255)}, ${random(255)})`;
document.body.style.backgroundColor = rndCol;
},
{ signal: controller.signal } // 向该处理器传递 AbortSignal
);
然后
controller.abort(); // 移除所有与该控制器相关的事件处理器
在单个事件上添加多个监听器
myElement.addEventListener("click", functionA);
myElement.addEventListener("click", functionB);
其他事件监听器机制
事件处理器属性:可以触发事件的对象通常有on+事件名称
的属性,例如 onclick
。
所以也可以这样写:
btn.onclick = () => {
const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
document.body.style.backgroundColor = rndCol;
};
或者btn.onclick = functionA
;
事件处理器属性不能添加多个处理程序。
内联事件处理器
<button onclick="bgChange()">按下我</button>
function bgChange() {
const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
document.body.style.backgroundColor = rndCol;
}
许多常见的服务器配置将禁止内联 JavaScript。
你永远不应该使用 HTML 事件处理器属性——那些已经过时了,使用它们是不好的做法。
事件对象
在事件处理函数内部可能会看到一个固定指定名称的参数,例如 event
、evt
或 e
,这就是事件对象。
//e.target指的是按钮本身。事件对象 e 的 target 属性始终是事件刚刚发生的元素的引用
const btn = document.querySelector("button");
function bgChange(e) {
const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
e.target.style.backgroundColor = rndCol;
console.log(e);
}
btn.addEventListener("click", bgChange);
事件对象的额外属性
keydown
事件在用户按下一个键时发生。它的事件对象是 KeyboardEvent
,它是一个专门的 Event
对象,有一个 key
属性,告诉你哪个键被按下。
例:
<input id="textBox" type="text" />
<div id="output"></div>
const textBox = document.querySelector("#textBox");
const output = document.querySelector("#output");
textBox.addEventListener("keydown", (event) => {
output.textContent = `You pressed "${event.key}".`;
});
运行后,每当你在输入框内敲下字母,将会显示你刚敲下的内容。
阻止默认行为
以下代码创造表单,要求填入名和姓,并有提交按钮。
<form>
<div>
<label for="fname">First name: </label>
<input id="fname" type="text" />
</div>
<div>
<label for="lname">Last name: </label>
<input id="lname" type="text" />
</div>
<div>
<input id="submit" type="submit" />
</div>
</form>
<p></p>
在submit事件中实行简单检查,如果文本字段为空,调用preventDefault()
停止提交并报告错误信息,否则继续提交。
const form = document.querySelector("form");
const fname = document.getElementById("fname");
const lname = document.getElementById("lname");
const para = document.querySelector("p");
form.addEventListener("submit", (e) => {
if (fname.value === "" || lname.value === "") {
e.preventDefault();
para.textContent = "You need to fill in both names!";
}
});
事件冒泡
<body>
<div id="container">
<button>点我!</button>
</div>
<pre id="output"></pre>
</body>
在父元素上附加事件处理器,也会触发单击事件:
const output = document.querySelector("#output");
function handleClick(e) {
output.textContent += `你在 ${e.currentTarget.tagName} 元素上进行了点击\n`;
}
const container = document.querySelector("#container");
container.addEventListener("click", handleClick);
在父元素与按钮上同时附加事件处理器,都会触发:
const output = document.querySelector("#output");
function handleClick(e) {
output.textContent += `你在 ${e.currentTarget.tagName} 元素上进行了点击\n`;
}
const container = document.querySelector("#container");
const button = document.querySelector("button");
document.body.addEventListener("click", handleClick);
container.addEventListener("click", handleClick);
button.addEventListener("click", handleClick);
/*Result:
你在BUTTON元素上进行了点击
你在DIV元素上进行了点击
你在BODY元素上进行了点击
*/
实例:视频播放器
- 单击显示”视频按钮“时,显示包含视频的盒子但不开始播放
- 单击视频时播放
- 单击盒子内视频以外的任何区域时隐藏盒子
<button>显示视频</button>
<div class="hidden">
<video>
<source
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm" type="video/webm" />
<p>
你的浏览器不支持 HTML 视频,这里有视频的<a href="rabbit320.mp4">替代链接</a>。
</p>
</video>
</div>
const btn = document.querySelector("button");
const box = document.querySelector("div");
const video = document.querySelector("video");
btn.addEventListener("click", () => box.classList.remove("hidden"));
video.addEventListener("click", () => video.play());
box.addEventListener("click", () => box.classList.add("hidden"));
当点击按钮后,因为div包含着video,盒子和视频都会同时显示,当点击视频时,会同时运行两个事件处理器,所以视频播放但盒子又消失了。
使用stopPropagation()修复问题
Event对象有一个stopPropagation()
函数,可以在当一个事件处理器调用时防止事件向其他元素传递。
将video的addEventListener修改即可解决问题:
video.addEventListener("click", (event) => {
event.stopPropagation();
video.play();
});
事件捕获
先在最小嵌套元素上发生,然后在连续更多的嵌套元素上发生,直到达到目标
事件捕获默认禁用,需要在addEventListener()
中的capture
选项中启用
接事件冒泡中的代码:
const output = document.querySelector("#output");
function handleClick(e) {
output.textContent += `你在 ${e.currentTarget.tagName} 元素上进行了点击\n`;
}
const container = document.querySelector("#container");
const button = document.querySelector("button");
//设置事件捕获
document.body.addEventListener("click", handleClick, { capture: true });
container.addEventListener("click", handleClick, { capture: true });
button.addEventListener("click", handleClick);
/* Result:
你在BODY元素上进行了点击
你在DIV元素上进行了点击
你在BUTTON元素上进行了点击
*/
事件委托
可以在父元素上设置监听器,让发生在它们身上的事件冒泡到父元素上。
当用户点击一个按钮时页面的区域设置成随机颜色
<div id="container">
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
</div>
//设置区域尺寸
.tile {
height: 100px;
width: 25%;
float: left;
}
function random(number) {
return Math.floor(Math.random() * number);
}
function bgChange() {
const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
return rndCol;
}
const container = document.querySelector("#container");
container.addEventListener("click", (event) =>{
event.target.style.backgroundColor = bgChange();
});
javaScript对象介绍
java对象基础
构造对象以及表示法
/*构造一个空对象*/
const person = {};
/*用对象字面量创造对象*/
const person = {
name: ["Bob", "Smith"],
age: 32,
bio() {
console.log(`${this.name[0]} ${this.name[1]} 现在 ${this.age} 岁了。`);
},
introduceSelf() {
console.log(`你好!我是 ${this.name[0]}。`);
},
};
/* 调用对象方法和属性
1.点表示法 */
person.name;
person.name[0];
person.age;
person.bio();
// "Bob Smith 现在 32 岁了。"
person.introduceSelf();
// "你好!我是 Bob。"
/* 2,括号表示法 */
person["age"];
person["name"]["first"];
多数情况下用点表示法,但某些情况下你必须用括号表示法。
/*当对象属性名称保存在变量中,只能用括号表示法*/
const person = {
name: ["Bob", "Smith"];
age:32;
};
function logProperty(propertyName){
console.log(person[propertyName]);
}
logProperty("name");
//["Bob", "Smith"]
logProperty("age");
// 32
子命令空间
/*用一个对象来做另一个对象成员的值*/
const person = {
name:{
first:"Bob",
last:"Smith",
}
};
//访问
person.name.first;
person.name.last;
设置对象成员
括号表示法可以动态创建成员名字,而点表示法不行。
/*设置对象成员*/
person.age = 45;
person["name"]["last"] = "Cratchit";
/*创建对象成员*/
person["eyes"] = "hazel";
person.farewell = function(){
console.log("再见!");
};
person["eyes"];
person.farewell();
构造函数
/*创造person的构造函数*/
function Person(name){
this.name = name;
this.introduceSelf = function(
console.log(`你好!我是$(this.name)。`);
);
}
/*调用构造函数*/
const salva = new Person("Salva");
salva.name;
salva.introduceSelf();
// "你好!我是 Salva。"
对象原型
原型链
const myObject = {
city: "Madrid",
greet(){
console.log(`来自$(this.city)的问候`);
},
};
myObject.greet();
//来自 Madrid 的问候
调用myObject.时,出现以下信息
__defineGetter__
__defineSetter__
__lookupGetter__
__lookupSetter__
__proto__
city
constructor
greet
hasOwnProperty
isPrototypeOf
propertyIsEnumerable
toLocaleString
toString
valueOf
//调用其中的toString
myObject.toString(); //"[object Object]"
javaScript中所有对象都有一个内置属性,称作原型,原型对象也会有自己的原型,逐渐构成了原型链,原型链终止于拥有null作为其原型的对象上。
当访问一个对象的属性时,如果在其本身找不到该属性,就会在原型中搜索,仍然找不到则继续搜索原型的原型,直到链的末端(返回undefined)。
Object.prototype
是最基础的原型,所有对象默认都拥有它,
它的原型是null
,故其位于原型链的终点。
一个对象的原型不一定就是Object.prototype
const myDate = new Date();
let object = myDate;
do {
object = Object.getPrototypeOf(object);
console.log(object);
} while (object);
// Date.prototype
// Object { }
// null
属性遮蔽
如果在对象中定义属性,而在对象原型中也定义了一个同名属性会发生什么?
const myDate = new Date(1995, 11, 17);
console.log(myDate.getYear()); // 95
myDate.getYear = function () {
console.log("别的东西!");
};
myDate.getYear(); // '别的东西!'
/*调用 getYear时,会先在myDate中寻找。这叫做属性遮蔽 */
设置原型
/*方式一:使用Object.create()*/
const personPrototype = {
greet() {
console.log("hello!");
},
};
//创造一个以personPrototype为原型的新对象
const carl = Object.create(personPrototype);
//原型为对象提供了greet方法
carl.greet();
/*方式二:使用构造函数*/
//创造personPrototype对象,该对象具有方法greet。
const personPrototype = {
greet() {
console.log(`你好,我的名字是 ${this.name}!`);
},
};
//创造Person的构造函数
function Person(name) {
this.name = name;
}
//将personPrototype中的方法绑定到Person的prototype属性上
Object.assign(Person.prototype, personPrototype);
// 或
// Person.prototype.greet = personPrototype.greet;
//调用
const reuben = new Person("Reuben");
reuben.greet(); // 你好,我的名字是 Reuben!
自有属性
直接在对象中定义的属性,被称为自有属性。
使用静态方法 Object.hasOwn()
检查一个属性是否是自有属性。
const irma = new Person("Irma");
console.log(Object.hasOwn(irma, "name")); // true
console.log(Object.hasOwn(irma, "greet")); // false
面向对象编程基本概念
java方面知识,不再赘述。
javaScript中的原型与对象很像java中的类与对象,但两者依旧有区别。
javaScript中的类
基本使用
//用class声明一个类
class Person{
name;
//构造函数
constructor(name){
this.name = name;
}
introduceSelf(){
console.log(`Hi! I'm ${this.name}`);
}
}
//创建并使用
const giles = new Person("Giles");
giles.introduceSelf(); // Hi! I'm Giles
省略构造函数
/*不需要初始化时,默认构造函数将自动生成*/
class Animal {
sleep() {
console.log("zzzzzzz");
}
}
const spot = new Animal();
spot.sleep(); //'zzzzzzz'
继承
class Professor extends Person{
teaches;
constructor(name, teaches){
super(name);
this.teaches = teaches;
}
introduceSelf() {
console.log(
`My name is ${this.name}, and I will be your ${this.teaches} professor.`,
)};
grade(paper) {
const grade = Math.floor(Math.random() * (5 - 1) + 1);
console.log(grade);
}
}
//使用
const walsh = new Professor("Walsh", "Psychology");
walsh.introduceSelf();
// 'My name is Walsh, and I will be your Psychology professor'
walsh.grade("my paper"); // some random grade
封装
class Student extends Person {
//#year是私有数据属性,'用#'表示。
#year;
constructor(name, year) {
super(name);
this.#year = year;
}
introduceSelf() {
console.log(`Hi! I'm ${this.name}, and I'm in year ${this.#year}.`);
}
canStudyArchery() {
return this.#year > 1;
}
}
//在类的外部尝试访问#year将会出错
const summers = new Student("Summers", 2);
summers.#year; // SyntaxError
私有方法
//与私有属性一样
class Example {
somePublicMethod() {
this.#somePrivateMethod();
}
#somePrivateMethod() {
console.log("You called me?");
}
}
const myExample = new Example();
myExample.somePublicMethod(); // 'You called me?'
myExample.#somePrivateMethod(); // SyntaxError
使用JSON
JSON简介
javaScript对象表示法(JSON)是用于将结构化数据表示为 JavaScript 对象的标准格式,通常用于在网站上表示和传输数据。
JSON可以作为一个对象或者字符串存在,前者用于解读JSON中的数据,后者用于通过网络传输JSON数据。
将字符串转换为原生对象称为反序列化,原生对象转换为字符串称为序列化。
JSON存储在.json
中。
JSON结构
JSON类似于对象字面量。
{
"squadName": "Super hero squad",
"homeTown": "Metro City",
"formed": 2016,
"secretBase": "Super tower",
"active": true,
"members": [
{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": ["Radiation resistance", "Turning tiny", "Radiation blast"]
},
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
},
{
"name": "Eternal Flame",
"age": 1000000,
"secretIdentity": "Unknown",
"powers": [
"Immortality",
"Heat Immunity",
"Inferno",
"Teleportation",
"Interdimensional travel"
]
}
]
}
将字符串加载入js程序中,并解析到名叫superHeroes
变量中,可以用该变量访问数据。
superHeroes.hometown;
superHeroes["active"];
//访问members数组的第二个元素,其中的powers数组的第三个元素
superHeroes["members"][1]["powers"][2];
JSON数组
数组与JSON可以相互转换,比如这样写依然有效。
[
{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": ["Radiation resistance", "Turning tiny", "Radiation blast"]
},
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
}
]
//调用
[0]["powers"][0];
JSON注意事项
- 是纯数据格式,只有属性,没有方法。
- 要求在字符串和属性名称周围用双引号。
- JSON文件很容易出错,可以用JSONLint验证JSON。
- JSON中只有带引号的字符串可以用作属性。