JavaScript详解(一)
回顾
前端三要素
- HTML(结构):超文本标记语言(Hyper Text Markup Language)决定网页结构和内容
- CSS(表现):层叠样式表(Cascading Style Sheets),设定网页的表现形式
- JavaScript(行为):是一种弱类型脚本类型语言,其源代码不需要经过编译,而是由浏览器解释运行,用于控制网页的行为
表现层(CSS)
CSS层叠样式表是一门标记语言,并不是编程语言,因此不可以自定义变量,不可以引用等,换句话说就说不具备任何语法支持,它主要缺陷如下:
- 语法不够强大,比如无法嵌套书写,导致模块化开发中需要书写很多重复的选择器;
- 没有变量和合理的样式复用机制,使得逻辑上相关的属性值必须以字面量的形式重复输出,导致难以维护;
这就导致了我们在工作中无端增加了许多工作量。为了解决这个问题,前端开发人员会使用一种称之为CSS预处理器的工具,提供CSS缺失的样式层复用机制、减少代码冗余,提高样式代码的可维护性,大大提高了前端在样式上的开发效率。
什么是CSS预处理器
CSS预处理器定义了一种新的语言,其基本思想是,用一种专门的编程语言,为CSS增加了一些编程的特性,将CSS作为目标生成文件,然后开发者就只要使用这种语言进行CSS的编码工作。转化成通熟易懂的话就是“用一种专门的编程语言,进行Web页面样式设计,再通过编译器转化成正常的CSS文件,以供项目使用。”
行为层(JavaScript)
JavaScript 一门弱类型脚本语言,其源代码在发往客户端运行之前不需经过编译,而是将文本格式的字符代码发送给浏览器由浏览器解释运行。
Native 原生JS开发
原生JS开发,也就是让我们按照【ECMAScript】标准的开发方式,简称是ES,特点是所有浏览器都支持。截止到当前博客发布时间,ES标准已发布如下版本:
- ES3
- ES4(内部。未正式发布)
- ES5(全浏览器支持)
- ES6(常用,当前主流版本:webpack打包成为ES5支持!)
JavaScript框架
- jQuery:大家熟知的JavaScript框架,优点是简化了DOM操作,缺点是DOM操作太频繁,影响前端性能;在前端眼里使用它仅仅是为了兼容IE6、7、8
- Angular: Google收购的前端框架,由一群Java程序员开发,其特点是将后台的MVC模式搬到了前端并增加了模块化开发的理念,与微软合作,采用TypeScript语法开发;对后台程序员友好,对前端程序员不太友好;最大的缺点是版本迭代不合理(如:1代 > 2代,除了名字,基本就是两个东西;截止发表博客时已推出了 Angular6)
- React:Facebook 出品,一款高性能的JS前端框架;特点是提出了新概念【虚拟DOM】用于减少DOM操作,在内存中模拟DOM操作,有效的提升了前端渲染效率;缺点是使用复杂,因为需要学习一门【JSX】语言
- Vue:一款渐进式JavaSript框架,所谓渐进式就是逐步实现新特性的意思,如实现模块化开发、路由、状态等新特性。其特点是综合了Angular(模块化)和React(虚拟DOM)的优点
- Axios:前端通信框架;因为Vue的边界很明确,就是为了处理DOM,所以并不具备通信能力,此时就需要额外使用一个通信框架与服务器交互;当然也可以直接选择使用jQuery提供的AJAX通信功能
UI框架
- Ant-Design: 阿里巴巴出品,基于React的UI框架
- ElementUI、iview、ice:饿了么出品,基于Vue的UI框架
- Bootstrap:Twitter退出的一个用于前端开发的开源工具包
- AmazeUI:又叫“妹子UI”,一款HTML5跨屏前端框架
JavaScript构建工具
- Babel:JS编译工具,主要用于浏览器不支持的ES新特性,比如用于编译TypeScript
- WebPack:模块打包器,主要作用是打包、压缩、合并及按序加载
JavaScript入门
什么是JavaScript
JavaScript是一门世界上最流行的脚本语言
一个合格的后端人员,必须要精通JavaScript
ECMAScript它可以理解为是JavaScript的一个标准
最新版本已经到es6
但是大部分浏览器还只停留在支持 es5 代码上
基本使用
- 内部标签使用
<script>
//......
</script>
- 外部引入
abc.js
alert("hello,world!")
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--外部引入-->
<!--注意:script标签,必须成对出现-->
<script src="./js/qj.js"></script>
</head>
<body>
</body>
</html>
基本语法入门
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--javascript 严格区分大小写-->
<script>
// 1、定义变量 变量类型 变量名 = 变量值;
var score = 71;
alert(score);
// 2、条件控制
if (score>60 && score<70){
alert("60~70");
}else if(score>70 && score<80){
alert("70~80");
}else {
alert("other")
}
// console.log(score); 在浏览器的控制台打印变量
</script>
</head>
<body>
</body>
</html>
浏览器控制台常用语法
# 弹窗
alert(score);
# 打印日志
console.log(score);
数据类型
数值,文本,图形,音频,视频......
变量
var 王者荣耀 = "王者荣耀";
number
js不区分小数和整数,Number
123 // 整数123
123.1 // 浮点数123.1
1.123e3 // 科学计数法
-99 // 负数
NaN // not a number
Infinity // 表示无限大
字符串
‘abc’ "abc"
布尔值
true,false
逻辑运算
&& 两个都为真,结果为真
|| 一个为真,结果为真
! 真即假,假即真
比较运算符
=
== 等于(类型不一样,值一样,也会判断为true)
=== 绝对等于(类型一样,值一样,结果为true)
⚠️注:这是JS的一个缺陷,坚持不使用==
须知
- NaN==NaN,这个与所有的数值都不相等,包括自己
- 只能通过isNaN(NaN)来判断这个数值是否是NaN
console.log((1/3) === (1-2/3))
console.log(Math.abs((1/3)-(1-2/3))<0.00000001)
false
true
尽量避免使用浮点数进行运算,存在精度问题!
null和undefined
- null 空
- undefined 未定义
数组
java的数值必须是相同类型的对象,js中不需要这样
// 保证代码的可读性,尽量使用 []
var arr = [1,2,3,4,5,'hello',true];
new Array(1,12,3,4,5,'hello');
取数组下标:如果越界了,就会undefined
undefined
对象
对象是大括号,数组是中括号
每个属性之间使用逗号隔开,最后一个不需要添加
// Person person new Person(1,2,3,4,5);
var person = {
name: "binzaza",
age: 3,
tags: ['js','java','web','...']
}
取对象的值
person.name
> binzaza
person.age
> 3
严格检查模式
// 全局变量
i= 1;
//严格检查模式,预防javascript的随意性导致产生的一些问题
// 局部变量建议都使用 let 去定义~
'use strict';
let j = 3;
字符串类型详解
- 正常字符串我们使用单引号,或者双引号包裹
- 注意转译字符
\'
\n
\t
\u4e2d \u#### unicode字符
\x41 Ascll字符
- 多行字符串编写
//tab 上面 esc下面
let msg =
`hello
world
你好ya
你好`
console.log(msg);
hello
world
你好ya
你好
- 模板字符串
let name = "binzaza";
let age = 3;
let message = `你好呀,${name}`+`你今年${age}岁`;
console.log(message);
你好呀,binzaza你今年3岁
- 字符串长度
str.length
- 字符串的可变性,不可变
var student = "student";
student[0]=1;
Uncaught TypeError
7. 大小写转换
let student = "sTuDent";
// 注意,这里是方法,不是属性
console.log(student.toUpperCase());
console.log(student.toLowerCase());
STUDENT
student
- 获取字符串下标
let student = "sTudent001";
console.log(student.indexOf("d"))
3
- 截取字符串
let student = "sTudent001";
// 从第一个字符串截取到最后一个字符串
console.log(student.substring(1));
console.log(student.substring(1,3));
Tudent001
Tu
数组类型详解
Array可以包含任意的数据类型
var arr = [1,2,3,4,5] //通过下标取值和赋值
arr[0]
arr[0] = 1
- 长度
arr.length
注意:假如给arr.length赋值,数组就会发生变化~,如果赋值过小,元素就会丢失
2. 通过元素获得下标索引
arr.indexof(2)
1
字符串的“1”和数字1是不同的
- slice()截取Array的一部分,返回一个新数组
arr = [1,2,3,4,5,6,"1","2"]
arr.slice(3)
> (5) [4,5,6,"1","2"]
arr.slice(1,5)
> (4) [2,3,4,5]
类似于String中的substring
4. 尾部push、pop
// push: 压入到尾部
arr = [1,2,3,4,5,6,"1","2"]
arr.push("a","b");
> (9) [1, 2, 3, 4, 5, '1', '2', 'a', 'b']
// pop: 弹出尾部的一个元素
arr.pop()
> 'b'
arr.pop()
> 'a'
- 头部unshift(),shift()
// unshift头部添加元素
arr.unshift("a","b");
> (9) ['a', 'b', 1, 2, 3, 4, 5, '1', '2'];
// shift 弹出头部一个元素
arr.shift();
> 'a'
- 排序sort()
arr.sort()
> (7) [1, '1', 2, '2', 3, 4, 5]
- 元素反转reverse()
arr = ["A","B","C"];
arr.reverse();
> (3) ['C', 'B', 'A']
- 拼接concat()
arr = ["A","B","C"];
arr.concat([1,2,3])
> (6) ['A', 'B', 'C', 1, 2, 3]
arr;
> (3) ['A', 'B', 'C']
注意:concat() 并没有修改数组,只是会返回一个新的数组
9. 连接符join
arr = ["A","B","C"];
arr.join('-');
> 'A-B-C'
- 多维数组
arr = [[1,2],[3,4],[5,6]];
arr[1][1];
> 4
数组:存储数据,方法可以自己实现
对象
若干个键值对
var = 对象名 = {
属性名: 属性值,
属性名: 属性值,
属性名: 属性值
}
// 定义了一个person对象,它有4个属性
var person = {
name: "binzaza",
age: 3,
email: "binzichen@126.com",
score: 0
}
js中的对象,键值队描述属性 xxx:xxx, 多个属性用逗号隔开,最后一个属性不加逗号!
- 对象赋值
person.name = "xiaoming";
> "xiaoming"
person.name;
> "xiaoming"
- 使用一个不存在的对象属性,不会报错!
person.haha
> undefined
- 动态的删减属性
delete person.name;
> true
person;
> {age: 3, email: 'binzichen@126.com', score: 0}
- 动态的添加,直接给新的属性添加值即可
person.haha = "haha";
> "haha"
person;
> {age: 3, email: 'binzichen@126.com', score: 0, haha: 'haha'}
- 判断属性值是否存在这个对象中! xxx in xxx!
'age' in person;
> true
// 继承
'toString' in person;
> true
- 判断一个属性是否是这个对象自身拥有的 hasOwnProperty()
person.hasOwnProperty('toString');
> false
person.hasOwnProperty('age');
> true
流程控制
- if 判断
let age = 3;
if (age>3){
alert("haha");
} else {
alert("kuwa~")
}
- while 循环
let age = 3;
while (age<100){
age = age +1;
console.log(age)
}
- for 循环
for (let i = 0; i < 100; i++){
console.log(i)
}
- forEach 循环
var age = [12,3,12,3,12,3,12,31,23,123];
// 函数
age.forEach(function (value){
console.log(value)
})
Map和Set
ES6新特性
map
// 学生成绩,学生的名字
// var names = ["tom","jack","haha"];
// var scores = [100,80,90];
var map = new Map([["tom",100],["jack",80],["haha",90]]);
var score = map.get("tom"); // 通过key获得value
console.log(score);
map.set("admin",70);
var score2 = map.get("admin");
console.log(score2)
> 100
> 70
set: 无序不重复的集合
var set = new Set([3,1,3,3,7,"a"]);
set.add(5);
set.delete(3);
console.log(set);
iterator迭代
使用iterator来遍历迭代我们的map、set
遍历数组
var arr = [3,4,5];
for (var x of arr){
console.log(x)
}
遍历map
var map = new Map([["tom",100],["jack",80],["haha",90]]);
for (let x of map){
console.log(x)
}
遍历set
var set = new Set([4,5,6,7]);
for (let x of set){
console.log(x)
}
函数
定义函数
定义方式一
function abs(x){
if (x>=0){
return x;
}else{
return -x;
}
}
abs(-10);
> 10
abs(20);
> 20
一旦执行了return,代表函数结束,返回结果!
如果没有执行return,函数执行完也会返回结果,结果就是undefined
定义方式二
var abs = function(x){
if(x>=0){
return x;
}else{
return -x;
}
}
abs(-30);
> 30
abs(40);
> 40
function(x){ ... } 这是一个匿名函数。但是可以把结果赋值给abs,通过abs就可以调用函数!
调用函数
abs(10) // 10
abs(-10) // 10
参数问题
javaScript可以传任意个参数,也可以不传参数
参数进来是否存在的问题,假设不存在参数怎么做
var abs = function(x){
if (typeof x!== "number"){
throw 'Not a Number';
}
if(x>=0){
return x;
}else{
return -x;
}
}
arguments
arguments
是一个js免费赠送的关键字
代表,传递进来的所有参数,是一个数组
var abs = function(x){
console.log('x=>'+x);
for (var i = 0; i<arguments.length;i++){
console.log(arguments[i]);
}
if(x>=0){
return x;
}else{
return -x;
}
}
问题:arguments包含所有的参数,我们有时候想使用多余的参数来进行附加操作,需要排除已有的参数
rest
以前
function aaa(a,b){
console.log("a=>"+a);
console.log("b=>"+b);
if (arguments.length>2){
for (var i=2;i<arguments.length;i++){
// ...
}
}
}
ES6引入的新特性,获取除了已经定义的参数之外所有参数
function aaa(a,b,...rest){
console.log("a=>"+a);
console.log("b=>"+b);
console.log(rest);
}
aaa(5,6,7,8,9);
> a=>5
> b=>6
> (3) [7, 8, 9]
变量的作用域
在javascript中,var定义的变量实际是有作用域的
假设在函数体中声明,则在函数体外不可以使用~ (闭包
)
function qj(){
var x = 1;
x = x + 1;
}
x = x + 2; // Uncaught ReferenceError: x is not defined
如果两个函数使用了相同的变量名,只要在函数内部,就不冲突
funtction qj(){
var x = 1;
x = x + 1;
}
function qj2(){
var x = 'A';
x = x + 1;
}
内部函数可以访问内部函数的成员,反之则不行
function qj(){
var x = 1;
// 内部函数可以访问内部函数的成员,反之则不行
function qj2(){
var y = x + 1;
}
var z = y + 1; // Uncaught ReferenceError: y is not defined
}
假设,内部函数变量和外部函数变量重名,则用内部变量
function qj(){
var x = 1;
function qj2(){
var x = 'A';
console.log('inner'+x); // innerA
}
console.log('outer'+x); // outer1
qj2()
}
qj()
函数查找变量从自身函数开始,由内向外查找
提升变量的作用域
function qj(){
var x = "x" + y;
console.log(x);
var y = 'y';
}
// 结果
> xundefined
说明,js执行引擎,自动提升了y的声明,但是不会提升y的值
function qj2(){
var y;
var x = 'x' + y;
console.log(x);
y = 'y';
}
这个是在javascript建立之初就存在的特性,养成规范,所有的变量定义都放在函数的头部,不要乱放,便于代码维护。
function qj2(){
var x = 1,
y = x + 1,
z,i; //undefined
// 之后随意用
}
全局函数
//全局变量
x = 1;
function f(){
console.log(x);
}
f();
console.log(x);
全局对象 window
var x = 'xxx';
alert(x);
alert(window.x); // 默认所有的全局变量,都会绑定在window对象下
alert()函数本身也是一个window
变量
var x = 'xxx';
window.alert(x);
var old_alert = window.alert;
window.alert = function (){};
// 发现 alert() 失效了
window.alert(123);
// 恢复
window.alert = old_alert;
window.alert(456);
javascript 实际上只有一个全局作用域,任何变量(函数也可以视为变量),假设没有在函数作用范围内找到,就会向外查找,如果在全局作用域都没有找到,报错RefrenceError
规范
由于我们所有的全局变量都会绑定在我们的window上。如果不同的js文件,使用了相同的全局变量,冲突 -> 如何能减少冲突
// 唯一全局变量
var MyAPP = {};
// 定义全局变量
MyAPP.name = "binzaza";
MyAPP.add = function (a,b) {
return a+b;
}
把自己的代码全部放入自己定义的唯一空间名字中,降低全局命名冲突的问题~
局部作用域let
function aaa(){
for (var i = 0; i < 100; i++) {
console.log(i)
}
console.log(i+1); // 问题 -> i出了这个作用域还可以使用
}
ES6 let关键字,解决了局部作用域冲突问题
function aaa(){
for (let i = 0; i < 100; i++) {
console.log(i)
}
console.log(i+1); // Uncaught ReferenceError: i is not defined
}
建议大家使用let
去定义局部作用域的变量;
常量 const
在ES6之前,怎么定义常量:只用全部大写字母命名的就是常量;建议不要修改这样的值
var PI = '3.1415';
console.log(PI);
PI = '926.54';
console.log(PI);
在ES6引入了常量关键字 const
const PI = '3.1415'; // 只读变量
console.log(PI);
方法
方法就是把函数放在对象的里面,对象只有两个东西:属性和方法
var binzaza = {
name: "bin",
birth: 2002,
// 方法
age: function (){
// 今年 - 出生年
var now = new Date().getFullYear();
return now - this.birth;
}
}
// 调用方法
binzaza.age();
> 20
this.代表什么
function getAge(){
// 今年 - 出生年
var now = new Date().getFullYear();
return now - this.birth;
}
var binzaza = {
name: "bin",
birth: 2002,
// 方法
age: getAge
}
this是无法指向的,是默认指向调用它的那个对象
apply
在js中可以控制this指向
function getAge(){
// 今年 - 出生年
var now = new Date().getFullYear();
return now - this.birth;
}
var binzaza = {
name: "bin",
birth: 2002,
age: getAge
};
getAge.apply(binzaza,[]);
> 20
js中可以控制this指向
function getAge(){
// 今年 - 出生年
var now = new Date().getFullYear();
return now - this.birth;
}
var binzaza = {
name: "bin",
birth: 2002,
age: getAge
};
var xiaoming = {
name: "小明",
birth: 2008,
age: getAge
};
getAge.apply(xiaoming,[]);
> 14
内部对象
标准对象
typeof 123
> 'number'
typeof '123';
> 'string'
typeof true;
> 'boolean'
typeof NaN;
> 'number'
typeof [];
> 'object'
typeof {};
> 'object'
typeof Math.abs;
> 'function'
typeof undefined;
> 'undefined'
Date
基本使用
var now = new Date();
now.getFullYear(); // 年
now.getMonth(); // 月 0~11 代表月
now.getDate(); // 天
now.getDay(); // 星期
now.getHours(); // 时
now.getMinutes(); // 分
now.getSeconds(); // 秒
now.getTime(); // 时间戳 全世界统一
转换
console.log(new Date(1661144775292));
> Mon Aug 22 2022 13:06:15 GMT+0800 (中国标准时间)
now.toLocaleString();
> '2022/8/22 13:06:15'
now.toGMTString();
> 'Mon, 22 Aug 2022 05:06:15 GMT'
Json
json是什么
早期,所有数据传输习惯使用xml
- json是一种轻量级的数据交换格式
- 简洁和清晰的层次结构使得JSON 成为理想的数据交换语言
- 易于阅读和编写,同时也易于机器解析和生成,并有效地提升网络的传输效率
在JavaScript中一切皆为对象,任何js支持的类型都可以用json来表示
格式:
- 对象都用 {}
- 数组都用 []
- 所有的键值对 都是用key:value
var user = {
name: "binzaza",
age: 3,
sex: "男"
}
// 对象转化为json字符串 {"name":"binzaza","age":3,"sex":"男"}
var jsonUser = JSON.stringify(user);
// json字符串转化为对象
var obj = JSON.parse('{"name":"binzaza","age":3,"sex":"男"}')