什么是JS
JavaScript 是一种基于对象和事件驱动的简单描述性语言。它从服务器端被下载到客户端,由浏览器执行。
JavaScript 可用于 HTML 和 Web,更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。
JavaScript主要有以下三个特点
- JavaScript是一种解释性语言,通常是边执行边解释。
- JavaScript一般还是用来编写客户端的脚本。
- JavaScript还是用来在HTML页面上实现交互行为。
变量与数据类型
JS里面的数据类型分为两种:
- 基本数据类型:字符串(string),数字(number),布尔(boolean),空(null),未定义(undefined)。
- 引用数据类型:对象(object)和数组(array)。
1.变量的使用
我们知道JS里面的数据类型通常用var、const和let来声明变量,但是他们三个的区别我们必须明白。
var赋值的变量是全局作用域或函数作用域,而且它的赋值是可以改变的。如下就是函数作用域:
function exampleFunction() {
var x = 10;
if (true) {
var x = 20; // 这里的x与上面的x是同一个变量
console.log(x); // 输出20
}
console.log(x); // 输出20,因为x的作用域是整个函数
}
exampleFunction();
- 那么
const和let则都是块级作用域,不同的是const还有一个额外的限制就是一旦变量被赋值就不能再改变其引用,但是如果变量引用的是一个对象或者数组,那么是可以修改对象和数组的内容的,但是还是不能让变量去引用一个新的对象和数组。而后者则没有。块级作用域代码如下:
function exampleFunction() {
let x = 10;
if (true) {
let x = 20; // 这里的x是一个新的块级作用域变量
console.log(x); // 输出20
}
console.log(x); // 输出10,因为内部的x和外部的x是不同的变量
}
exampleFunction();
从上面的块级作用域可以看到,它极大地减少了变量冲突,因为限制了变量的可见性,并使得变量的生命周期更加明确。这也就是现在用const和let的更多。
2.数组的使用
JS里面创建数组和数组的命名格式非常简单,如下所示:
var array = new Array('元素1','元素2');
var array = [1,2,3];
运算符与表达式
- 算术运算符就非常简单了,如:加
+减-乘*除/取余%自增++自减--。 - 赋值运算符则有:
=,+=,-=,*=,/=。 - 比较运算符有:
- 逻辑运算符有下列四种:与
&&或||非!
这里我需要举个例子,就是广义或和广义与,直接看代码吧:
//如果A为true,那么返回B。
return A && B;
//如果A为false,那么返回B。
return A || B;
- 条件运算符:
条件表达式 ? 表达式1 : 表达式2
流程控制语句
1.首先就是分支结构,有单分支if(成员表达式){},还有双分支if...else if...else,以及多分支if...else-if...else-if。最后还有switch语句,如下所示:
switch (key) {
case value:
//语句表达式
break;
//中间还可以有无数个case语句。
default:
//语句表达式
break;
}
2.其次就是循环结构,首先有while (条件) {}语句,然后是do {} while (条件)语句,最后有for(初始化表达式;条件表达式;循环后的操作) {}。
需要注意的就是for..in语句,我们直接看例子非常容易理解。
var arr = ["小红", "小蓝", "小白", "小黑"];
for (i in arr) {
r = "欢迎" + arr[i] + "来到德莱联盟。" + "</br>";
//上面的</br>标签的作用是换行。
document.write(r);
}
函数
函数的使用标准格式即为:
function Sum(a, b) {
sum = a + b;
document.write(a + "+" + b + "=" + sum);
}
Sum(4, 5);
变量作用域
1.全局作用域直接看图:
//在函数外部上明的变量即为全局变量,所有网页的脚本和函数都能访问。
var str = "www.lanqiao.cn";
document.write("函数外部:" + str + "<br>");
function Test() {
document.write("函数内部:" + str);
}
Test();
2.局部作用域也看图吧:
function Test() {
//在函数内部声明的变量为局部变量,即只有函数内部可以访问。
var str = "www.lanqiao.cn";
document.write("我最喜欢的网站:" + str + "<br>");
}
Test();
var str;
document.write("函数外部:" + str);
那些常用的全局函数
- parseInt(String ,int radix) 这个是解析一个字符串,并返回指定基数的十进制数。
- parseFloat(String) 也是解析字符串并返回number类型,如果不能解析则返回NaN。
- isNaN(value) 如果参数为数字则返回 true ,反之则返回 false。
- encodeURI(URL)则是编码URL,将里面的一些特殊字符转换成转义序列。
- decodeURI(URL)与encodeURL()函数的作用相反。
- eval(string) 如果里面的参数是一个或多个表达式或JavaScript语句,则函数会分别计算和执行它们。
另外,我得再提一下就是计时器,这个内置计时器(内置就是我们需要在计时器里面定义函数)目前有两种,第一种就是
setTimeout,第二种则就是setInterval。
计时器
首先我们看一段代码,各位就显而易见的理解了这两种计时器的区别了。
// 首先定义计时总秒数,单位 s
let i = 60;
// 定义变量用来储存定时器的编号
let timerId;
// 写一个函数,这个函数即每次要执行的代码,能够完成上述的 1、2、3
function count() {
console.log(i);
i--;
if (i > 0) {
timerId = setTimeout(count, 1000);
} else {
// 清除计时器
clearTimeout(timerId);
}
}
// 首次调用该函数,开始第一次计时
count();
从上面可以看出,setTimeout本身就是睡眠1s,也就是说只会执行一次。而我们制作了一个回调函数count,就是不断的去调用、递归,这样子就很容易的陷入回调地狱之中。
我们不妨再来看一下setInterval,它与上面不同的就是可以无限调用,因此我们不必再像上面弄那么麻烦,我们只需要在函数里面写一个条件语句即可,如下所示:
let i = 60;
print();
let timer = setInterval(print, 1000);
function print() {
console.log(i);
i--;
if (i < 1) {
//下面的clearInterval()函数就是清除计时器。
clearInterval(timer);
}
}
JS对象
对象的继承
在JavaScript里面也有对象得继承,就是prototype关键字。这里我们看代码,简单理解一下就行。
// 字面量
let o1 = {
name: 'alice',
};
// 构造函数
let o2 = new Object();
let o3 = new Object();
// 继承
funtion O4() {
};
O4.prototype = o1;
let o4 = new O4();
从上面就可以看出,o4继承了o1的属性和方法,那么o1就是o4原型。
数学对象
数学对象即为Math对象,里面的函数较多,但我们记住常用的即可。
剩下常用的就非常好记了,直接看如下图所示即可。
啥,你不知道欧拉常数是什么,其实就是e,没错,就是它,约等于2.71828。上面的exp()函数就是可以求它的任意次方。
扩展小知识
除了Math对象还有Storage对象,这个接口主要用于在浏览器存储数据,如window.sessionStorage()和windowLocalStorage()两个接口,前者是为了存储一次会话,后者则是存储在本地浏览器的数据。
写入数据非常简单,用setItem()即可,如下所示:
const obj = {
name: 'henry',
age: 18
}
JSON的stringify()方法可以将JvaSript对象转化为字符串。
const value = JSON.stringify(obj);
window.localStorage.setItem('myLocalStorage', value);
查看方式如下图所示,我们也可以获得数据,即getItem()。这个Js语句我们可以在下图中的console里面去写。
清除缓存更加简单,使用window.localStorage.clear()即可清除本地缓存。
日期对象
日期对象很简单,看代码吧:
另外还有get和set方法,如下:
数组对象和字符串对象
数组对象和字符串对象的格式也相当多,我放在代码里面了,看吧:
我们在数组里面遍历的话一般都是用的for循环遍历,但是JS给我们提供了两个更靠谱的遍历方式——map遍历和forEach遍历。
有返回值的遍历——map
//以下就是JS代码
let arr = [
{name : "大佛",age : 25},
{name : "岳山",age : 38},
{name : "火狗",age : 40}
]
//elem代表依次传入的数组成员。
//index代表依次传入的成员下表。
//
const handledArr = arr.map(function (elem,index,a) {
elem.age++;
console.log(elem,index,a);
return elem.name;
})
document.write(JSON.stringify(arr));
document.write(JSON.stringify(handledArr));
结果如图所示:
无返回值的遍历——forEach
这个和map遍历方式一样,只不过没有返回值,如下:
const handledArr = arr.forEach(function (elem, index, a) {
elem.age += 1;
console.log(elem, index, a);
return elem.name;
});
console.log(handledArr);//输出undefined,因为foEach并没有返回值。
DOM和BOM
1.DOM
首先我们需要知道什么是DOM,DOM全称是document object model(文档对象模型)。它是浏览器为每个HTML页面创建的一个document对象来对页面元素进行操作。
DOM映射
例如下面,其实HTML并不是平面的,而是和XML一样都是树状结构,对应数据结构里面的树。
<html>
<head>
<title>youkeda</title>
</head>
<body>
<div>
<h1>掘金</h1>
<p>挖掘知识只为黄金。</p>
</div>
</body>
</html>
上面的树根是DOCUMENT,也可以称为整个页面文档。<html>标签是DOM节点,而head标签和body标签均是html标签的儿子节点,他俩又是兄弟节点。h1和p标签是body的孙子节点,所有p和h1的长辈均是他们的祖先节点。
DOM的属性
DOM里面的属性很多,但是大致也就这些:
DOM类别
- 元素节点的
nodeType为1 - 特性节点的
nodeType为2 - 文本节点的
nodeType为3
代码如下:
DOM内容
OuterHTML指的是整个DOM的HTMLinnerHTML指的是DOM内部的HTMLinnerText指DOM内部的纯文本
代码如下:
//DOM内容
let divdom = document.querySelector('div#test');
//里面的outerHTML会输出所有DOM的HTML代码
/*<div id="test">
<p>掘金</p>
<p>只为开发矿石</p>
</div>
*/
//里面的innerHTML则会输出div里面的所有元素节点,即:
/*
<p>掘金</p>
<p>只为开发矿石</p>
*/
//innerText则会输出里面的所有文本节点,即:
/*
掘金
只为开发矿石
*/
console.log(divdom.outerHTML,divdom.innerHTML,divdom.innerText);
DOM亲属
firstChild指的是节点的第一个子节点。lastChild指的是节点的最后一个子节点。childNodes指的是节点的所有子节点的集合。parentNode指的是节点在DOM树中的父节点。
DOM样式
我们通过DOM也可以访问到CSS中的特性。我让大家看个例子就懂了,代码:
<!DOCTYPE html>
<head>
<meta charset="UTF-8" />
<title>优课达</title>
</head>
<body>
<h1 class="test youkeda" style="color: #FF3300; font-size: 24px;">优课达</h1>
<script src="./index.js"></script>
</body>
const h1Dom = document.querySelector('h1');
console.log(h1Dom.classList);//classList数组存储所有的class类名
console.log(h1Dom.style);//对象或字典方法存储的style
console.log(h1Dom.style.color);//可以访问style的CSS特性
DOM数据属性
我们都知道,前端网页就是使得数据和HTML标签相结合,我们看到的都是HTML标签渲染的结果,但是数据肯定不止这么少,有些数据不是我们字眼能够看到的,如我们就可以用data-*去存储数据,而是需要用JS去获取的,获取方法就如下所示了:
//HTML
<article data-parts="3" data-words="1314" data-category="python"></article>
//JavaScript
const article = document.querySelector('article');
console.log(article.dataset);
结果相信大家也看到了,dataset是个对象,而data-*里面的*是个key-value对象。
DOM方法
常用的DOM方法其实也就下面这些:
我们举个简单的例子你们就能明白了。看如下代码:
2.BOM
我们要知道我们访问网页时要输入网站,然后就会出现页面,而这些页面渲染效果就是BOM有关的,全称叫做浏览器对象模型(Brower Object Model)。
而BOM里面重要的对象也恰好有五个,分别是为:
window:即窗口,是每个网页的整个的框架,每个网页的内容都是装载在window中的,在每个网页中都是唯一的。
window是默认对象,每个函数甚至是顶层函数都是挂载在window下面的,但是一般都可以省略,我们如果用*console.log()*打印window会发现里面有很多方法和属性对象。
navigator:即浏览器,里面存储的都是浏览器的基础信息,在每个浏览器里面也是唯一的。screen:即显示屏幕,在每个电脑中都是唯一的,显示屏幕信息,即硬件信息。location:即网站,显示当前访问的网站信息,即也是唯一的。history:显示浏览历史,即存储整个网页栈,这个也是唯一的。
history两个重要的方法需要记住,一个是前进方法forward(),一个是后退方法back()。
以下则是BOM的常用方法,我们看下代码吧。
代码:
JavaScript事件
1. 点击事件
鼠标点击事件分为好几种,但是记起来一点都不难,如下图所示:
还是老样子,看例子代码即可:
2. 键盘事件
有了鼠标了,不得有个键盘?,其实键盘事件说白了就两个,一个是键盘按下时触发的onkeydown事件,另一个就是键盘松开时触发的onkeyup事件。
里面有个东西我忘说了,就是键盘监听事件addEventListener(enentName,callBack),这个叫做DOM 2级事件,而我们之前写的都是DOM 0级事件。注意哈,里面的eventName是不需要加on的,例如我们往里面写onkeydown只需要写个keydown即可。这个绑定事件操作是官方默认的,相比于我写的value.onkeydown而它可以添加多个事件,而后者一旦下面出现了onclick就会发生时间替换,总之,前者是要大大好于后者的。
上面的DOM级别事件不理解不着急,我在后面会讲。
3. 表单事件
表单事件相较于上面要多一点,即分别为焦点事件和内容值变化。
焦点事件
焦点事件无非就两种,分别为聚焦事件focus和失焦事件blur。
const nick = document.querySelector('input.nick');
nick.addEventListener('focus', function() {
//打开浏览器工具我们则可以看到,当我们把光标放到这个input输入框内后控制台就会打印"获取焦点"。
console.log('获取焦点');
});
nick.addEventListener('blur', function() {
//当我们移开光标即将鼠标点击其他处就会打印"失去焦点"。
console.log('失去焦点');
});
内容值变化
内容值变化则有两个事件,一个是输入即触发的input事件,另一个则是变化即触发的change事件。
说的有点懵,change事件如果是作用于input的text类型则是当光标移开时才会触发,而如果是chechbox或者select类型则是发生变化就会立即触发。
nick.addEventListener('input', function() {
console.log(nick.value);
});
nick.addEventListener('change', function() {
console.log(nick.value);
});
看下列图片更容易理解
4. 事件对象
事件对象就是通过事件对象的属性让我们去了解一个事件的详情情况。
上面的keyCode已经从web中弃用了,这里只做下了解即可,因为我们要尽量的去避免过度依赖键值码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>type 的使用</title>
<style></style>
<script>
window.onload = function () {
var value = document.getElementById("btn");
value.onclick = function (e) {
console.log("这是一个" + e.type + "事件"); // 控制台打印"这是一个click事件"
};
};
</script>
</head>
<body>
<input type="button" value="点击" id="btn" />
</body>
</html>
5.滚动事件
滚动在页面中还是很常见的哈,事件scroll就是最常见的,如下:
window.addEventListener('scroll', function () {
console.log(window.scrollY);//控制台会打印竖直滚动的值,即1,2,3...等。
});
无尽滚动
上面的只是事件描述,最常用之一就是无尽滚动,我们直接看例子就好了。
6. 冒泡、捕获、委托
这个是全篇文章最大的重点,这节你可能不经常遇到,但是如果遇到解答不了,那会是巨大的折磨。
- 冒泡事件:当我们触发了一个监听事件时,它会冒泡找到父亲节点并触发父亲节点,然后一直冒泡到html根元素为止。
要是想解决它也非常简单,我们写入以下代码即可:
// ......省略
likeBtn.addEventListener('click', function(e) {
// 阻止冒泡事件
e.stopPropagation()
// ......省略
- 捕获事件:它和冒泡事件完全相反,它是从html根元素依次移动到当前元素并触发所过之处的监听事件。
我们想要在捕获阶段监听事件只需要输入以下即可:
//第三个元素为true则代表事件句柄在捕获阶段执行,而默认是false,即冒泡阶段执行。
dom.addEventListener('click', function() {}, true);
- 委托事件:委托的话是冒泡事件里面的一种应用,如果我们想在很多子节点上设置一个共同的方法,那么不如直接设置在父节点上,即我们触发子节点事件则会冒泡到父节点,避免了很多的代码冗余。
网络请求
在学习网络请求我们需要先知道一个概念,就是协议和URL,直接看简介吧:
- 协议:全称是网络协议,这是计算机双方必须遵守的约定,即怎么样建立连接,怎样互相识别,只有遵守约定,才可以正常访问交流。目前协议分为
HTTP和HTTPS两种,前者不安全,后者是安全的,因为站长购买了身份认证的证书,地址经过了身份认证且数据传输也得到了加密。 - URL:全称是
Uniform Resource Locator(统一资源标识符),标准格式如下所示:
注意哈,如果路径不是以/开头的,那么这就是相对路径,具体相对于什么就不知道了,而默认路径则是以/开头,这种是安全的,可以打开。
API+GET请求
老版的AJAX我就不说了,先是写一大段代码,然后通过addEventListener监听load函数,然后触发后面的function回调函数,但如果后面的回调函数里面又添加了诸如setTimeOut和addEventListener监听代码,那么会出现多层嵌套,专业名叫做回调地狱。但是呢,有了promise对象,一切都好办了。
promise
promise是异步编程的一种方案,我们可以把它理解成一个装填异步操作的容器,而且一旦promise(pending进行中,fulfilled已成功,rejected已失败)的状态确定后,就不会再改变,并且promise对象还可以通过.then触发回调函数,而then的中文意思就是下一步,可见非常的人性化。我们看一个例子加强理解一下:
//这里的fetch不指定类型,默认都是发起Get请求。
fetch('https://mock.youkeda.com/api/m/f4-11-1-1')
.then(function (response) {
//这里判断响应状态是否OK,如果不是则会抛出一个错误。
if (!response.ok) {
throw new Error('Network response was not ok.');
}
return response.json();
})
.then(function (myJson) {
console.log(myJson);
}) //上面抛出的Error被catch块捕获并处理。
.catch(function (error) {
console.error('There has been a problem with your fetch operation:', error);
});
上面response.json()返回的也是一个promise对象,所以后续可以继续使用.then触发后续回调。
把嵌套型回调调整为平整型回调,这样就完美的避免了回调地狱的问题。
POST请求
懂了上面的get请求,那么post请求就更容易理解了,看代码吧:
// 把JSON数据序列化成字符串,下面这其实就是创建了个匿名对象。
const data = JSON.stringify({
username: 'admin',
password: '123456'
});
fetch(
'https://mock.youkeda.com/api/m/f4-11-4-1',
{
//指定请求为POST
method: 'POST',
//指定请求正文
body: data,
//设置请求头,下面指定了发送到服务器的数据类型是JSON格式的。
headers: {
'content-type': 'application/json'
}
}
)
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson);
});//最后控制台会打印 isSuccess:true 的字段。