JavaScript---Web学习总结——目录
1 何为JavaScript
- JavaScript 是一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。
现在JavaScript再也不是简单的脚本语言了,无论前端还是后台,JavaScript都有良好的表现! - 基础的JavaScript学习可以参见:
W3school-领先的Web技术教程
W3C菜鸟教程 - 运行JavaScript的方式:
可以直接在浏览器的控制台编写运行
也可以编写一个独立的js文件,然后在Html文件中引入
也可以用编辑软件如Webstrom或VSCode编写独立的js文件,由安装好的Node.js解释执行运行。
2 基本语法
2.1 JavaScript 对大小写敏感
所有 JavaScript 标识符对大小写敏感。
如:变量 Name
和 name
,是两个不同的变量。
2.2 标识符
- 标识符是名称,指变量、函数、属性的名字,或者函数的参数。
- 在 JavaScript 中,首字符必须是
字母
、下划线(-)
或美元符号($)
。
连串的字符可以是字母
、数字
、下划线
或美元符号
。 - 按照惯例,ECMAScript(European Computer Manufacturers Association) 标识符采用 驼峰大小写格式,也就是第一个字母小写,剩下的每个单词的首字母大写,如 firstName, lastName, masterCard, interCity
- 注意:数值不可以作为首字符。这样,JavaScript 就能轻松区分标识符和数值。
2.3 注释
JavaScript 注释用于解释 JavaScript 代码,增强其可读性。
JavaScript 注释也可以用于在测试替代代码时阻止执行。
- 单行注释
单行注释以//
开头。
任何位于//
与行末之间的文本都会被 JavaScript 忽略(不会执行)。 - 多行注释
多行注释以/*
开头,以*/
结尾。
任何位于/*
和*/
之间的文本都会被 JavaScript 忽略。
注意:使用单行注释最为常见。 - 使用注释来阻止执行
使用注释来防止代码执行很适合代码测试。
在代码行之前添加//
会把可执行的代码行更改为注释。
2.4 语句
在 HTML 中,JavaScript 语句是由 web 浏览器“执行”的“指令”。
- 构成
JavaScript 语句由以下构成:
值、运算符、表达式、关键词和注释。 - 分号 ;
分号
分隔 JavaScript 语句。
在每条可执行的语句之后添加分号,以分号结束语句不是必需的。
2.5 关键字/保留字
在 JavaScript 中,一些标识符是保留关键字,不能用作变量名或函数名。
- 常见的关键字/保留字
break | do | instanceof | typeof | case |
---|---|---|---|---|
else | new | var | catch | finally |
void | continue | for | switch | while |
debugger | function | this | with | default |
if | throw | delete | in | try |
详细内容可见:JavaScript 保留词
2.6 变量
JavaScript 变量是存储数据值的容器。
- ECMAScript 的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。换句话说,每个变量仅仅是一个用于保存值的占位符而已。
- 定义变量
定义变量时要使用var
操作符
注意:即用 var 操作符定义的变量将成为定义该变量的作用域中的局部变量。也就是说,如果在函数中使用 var 定义一个变量,那么这个变量在函数退出后就会被销毁。 - 重复声明 JavaScript 变量
如果再次声明某个 JavaScript 变量,将不会丢它的值。 - Value = undefined
在计算机程序中,被声明的变量经常是不带值的。值可以是需被计算的内容,或是之后被提供的数据,比如数据输入。
不带有值的变量,它的值将是undefined
。
例如:
- 一条语句,多个变量
可以在一条语句中声明许多变量。
以var
作为语句的开头,并以逗号分隔变量。
例如:
2.7 操作符
- 一元操作符
运算符 | 描述 |
---|---|
++ | 递加 |
– | 递减 |
一元运算符,只有一个操作数
可以写在变量前和变量后面,位置不同可能导致程序运行结果不同
- 布尔操作符(逻辑运算符)
&&:逻辑与
||:逻辑或
!:逻辑非- 注意:&&和||都属于 短路操作!
- 除下列值为假外其余皆为真: false、null、undefined、‘’、0、NaN
- 算术操作符
运算符 | 描述 |
---|---|
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 取模(余数) |
++ | 递加 |
– | 递减 |
- 关系操作符
运算符 | 描述 |
---|---|
== | 等于 |
=== | 等值等型 |
!= | 不相等 |
!== | 不等值或不等型 |
> | 大于 |
< | 小于 |
>= | 大于或等于 |
<= | 小于或等于 |
注意: ===称为全等(值和类型相等)
- 条件(问号)操作符
条件(问号)操作符? :
能有效的简洁代码 - 赋值操作符
常见赋值操作符:= += -+ *= /= %=
2.8 语句
- JavaScript 代码块
JavaScript 语句可以用花括号({...}
)组合在代码块中。
代码块的作用是定义一同执行的语句。 - 关键词
大部分与常见的c语言关键词类似:if do-while while for for-in for-of break continue switch
其中,需要注意for-of
、forEach
能简洁的遍历集合中的元素。
- for…in
作用:
for…in 语句用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作),其所遍历的为对象的属性名(键),而非属性值。
语法:
for (variable index in object){
//...
}
- for…of
作用:
for…of语法是ES6新引入的语法,for…of语法用于遍历可迭代(iterable)对象,js中的可迭代对象包括字符串String、数组Array、集合Set、字典Map、arguments 对象、DOM NodeList 对象等等,for…of语法用于遍历这些对象本身的元素。
语法:
for (variable element of iterable){
//...
}
- forEach
作用:
forEach作用于数组对象,用于遍历数组对象的每一个元素,并对每一个元素执行回调(callback)函数。
语法:
ArrayObject.forEach(callback(currentValue, index, arr), thisValue))
//其中currentValue为遍历时数组中每次进行输入到回调函数的当前元素,为必需参数;
//index为当前元素的索引值,为可选参数;
//array为当前元素所属的数组对象,为可选参数。
//thisValue为传递给回调函数的"this"值,可选,如果这个参数为空,则"undefined"则会传递给"this"值。
2.9 函数(function)
JavaScript 函数是被设计为执行特定任务的代码块。
JavaScript 函数会在某代码调用它时被执行。
- 函数语法
function name(参数 1, 参数 2, 参数 3) {
要执行的代码
}
//在函数中,参数是局部变量
- 函数调用
函数中的代码将在其他代码调用该函数时执行:- 当事件发生时(当用户点击按钮时)
- 当 JavaScript 代码调用时
- 自动的(自调用)
调用栈
JS引擎在调用一个函数前,需要把函数所在的环境push到一个数组里,这个数组叫做调用栈,等函数执行完了,就会把环境弹出(pop)出来,然后return到之前的环境,继续执行后续代码
- 函数返回
当 JavaScript 到达 return 语句,函数将停止执行。
如果函数被某条语句调用,JavaScript 将在调用语句之后“返回”执行代码。- ECMAScript 中的函数在定义时不必指定是否返回值
- ECMAScript 函数不介意传递进来多少个参数,传递的参数与定义时的个数可以不一致。
原因: ECMAScript 中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。如果这个数组中不包含任何元素,无所谓;如果包含多个元素,也没有问题。 - ECMAScript中定义了两个名字相同的函数,则该名字只属于后定义的函数
2.10 对象Object
常将数据和方法封装在对象中。
创建对象有如下两种方式,我们常用第二种。
//方式一new
var person = new Object();//生成空对象
person.name = 'Elon Musk';//设置对象的属性
person.age = 46;
person.job = 'SpaceX Rocket';
person.sayName = function(){ //设置对象的方法/函数,注意此处
console.log(this.name);
};
//方式二字面量
var person = {
name: 'Lary Page',
age: 47,
job: 'Software Engineer',
sayName: function(){ //注意此处
console.log(this.name);
}
};
console.log(person.job);
person.sayName();
注意:空格和折行都是允许的。对象定义可横跨多
之后,进一步改进简化:
var person = {firstName:"Bill", lastName:"Gates", age:62, eyeColor:"blue"};
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
};
return o;
}
var person1 = createPerson('Steve Jobs',56 , 'Inventor');
var person2 = createPerson('Linus Torvalds', 49, 'Software Engineer');
var person2 = createPerson('Julian Assange', 47, 'Ethical Hacker');
- this 关键词
在函数定义中,this 引用该函数的“拥有者”。
在上面的例子中,this 指的是“拥有” fullName 函数的 person 对象。
换言之,this.firstName 的意思是 this 对象的 firstName 属性。
2.11 数组Array
JavaScript 数组用于在单一变量中存储多个值。
- 与其他语言不同的是,ECMAScript 数组的每一项 可以保存任何类型的数据(不建议!)
- ECMAScript 数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据。
创建数组
- 使用 JavaScript 关键词
new
- 使用
数组文本
是创建 JavaScript 数组最简单的方法。(通常使用)
//方式一new
var cars = new Array("Saab", "Volvo", "BMW");
//方式二字面量
var cars = ["Saab", "Volvo", "BMW"];
//访问数组元素
var name = cars[0];
//修改数组元素
cars[0] = "Opel";
//访问完整数组
var cars = ["Saab", "Volvo", "BMW"];
document.getElementById("demo").innerHTML = cars;
// 创建一个空数组
var names = [];
// 将一组值转换成数组
var arr = Array.of(element0, element1, ..., elementn)
var hyBird = [1, 2, 'haha', {firstName: 'Yong', lastName: 'Wang'}]; //不推荐!
console.log(hyBird[3].firstName);
常用的数组方法
- 元素联合
var colors = ['red', 'green', 'blue'];
console.log(colors.join(',')); //red,green,blue
console.log(colors.join('||')); //red||green||blue
- 堆栈方法
栈是一种 LIFO(Last-In-First-Out,后进先出)的数据结构,也就是最新添加的项最早被移除。而栈中项的插入(叫做推入)和移除(叫做弹出),只发生在一个位置——栈的顶部。
ECMAScript 为数组专门提供了 push()
和 pop()
方法,以便实现类似栈的行为。
push()
可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度
let arr = [1, 2, 3];
arr.push(4);
console.log(arr); // [ 1, 2, 3, 4 ]
console.log(arr.length); // 4
pop()
从数组末尾移除最后一项,减少数组的length值,然后返回移除的项
let arr = [1, 2, 3];
let delVal = arr.pop();
console.log(arr); // [ 1, 2]
console.log(delVal); // 3
- 队列方法
栈数据结构的访问规则是 LIFO(后进先出),而队列数据结构的访问规则是 FIFO(First-In-First-Out,先进先出)。队列在列表的末端添加项,从列表的前端移除项。
由于 push() 是向数组末端添加项的方法,因此要模拟队列只需一个从数组前端取得项的方法。实现这一操作的数组方法就是shift()
,它能够移除数组中的第一个项并返回该项,同时将数组长度减1。
shift()
移除数组中的第一个项并返回该项,同时将数组长度减1
let arr = [1, 2, 3];
let delVal = arr.shift();
console.log(delVal); // 1
console.log(arr); // [ 2, 3 ]
console.log(arr.length); // 2
unshift()
在数组前端添加任意个项并返回新数组的长度
let arr = [1, 2, 3];
let arrLength = arr.unshift(0);
console.log(arrLength); // 4
console.log(arr); // [ 0, 1, 2, 3 ]
总结: push
、pop
操作在数组末,而 unshift
、shift
操作在数组头;push
、unshift
压入,而pop
、shift
弹出。
- 反转数组项
var values = [1, 2, 3, 4, 5];
values.reverse();
console.log(values); //5,4,3,2,1
- 链接方法
var colors1 = ['red', 'green', 'blue'];
var colors2 = ['yellow', 'black'];
console.log(colors1.concat(colors2));
console.log(colors2.concat(colors1));
console.log(colors2.concat('brown'));
console.log(color2)//注意:concat返回一个新数组,原数组没改变
- 分片方法
slice()
它能够基于当前数组中的一或多个项创建一个新数组。 slice()
方法可以接受一或两个参数,即要返回项的起始和结束位置- 在只有一个参数的情况下,
slice()
方法返回从该参数指定位置开始到当前数组末尾的所有项。 - 如果有两个参数,该方法返回起始和结束位置之间的项——但不包括结束位置的项。
- 注意,
slice()
方法不会影响原始数组
let arr = [1, 2, 3, 4];
let newArr = arr.slice(1, 2);
console.log(newArr); // [ 2 ]
let newArr2 = arr.slice(1);
console.log(newArr2); // [ 2, 3, 4 ]
- splice方法
注意:splice()
方法直接更改原始数组。
splice() 方法对数组如下3种操作:- 删除:
可以删除任意数量的项,只需指定2个参数:要删除的第一项的位置和要删除的项数。
例如,splice(0,2)
会删除数组中的前两项。 - 插入
可以向指定位置插入任意数量的项,只需提供 3 个参数:起始位置、0(要删除的项数) 和要插入的项。如果要插入多个项,可以再传入第四、第五,以至任意多个项。
例如,splice(2,0,'red','green')
会从当前数组的位置 2 开始插入字符串'red'
和'green'
。 - 替换
可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定3个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。
例如,splice (2,1,“red”,“green”)
会删除当前数组位置2的项,然后再从位置2开始插入字符串"red"
和"green"
。
- 删除:
2.12 链式语法
javascript的链式编程的核心就是 return this;
链式语法只需要让每个函数返回 this代表包含该函数的对象,这样其他函数就可以立即被调用。
//链式语法
var bird = {//定义对象字面量
catapult: function() {
console.log( 'Yippeeeeee!' );
return this;//返回bird对象自身
},
destroy: function() {
console.log( "That'll teach you... you dirty pig!" );
return this;
}
};
bird.catapult().destroy();//destroy()后还可以再链接吗?
2.13 闭包
- 闭包是
Closure
,这是静态语言所不具有的一个新特性。闭包就是:函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。
闭包就是就是函数的“堆栈”在函数返回后并不释放,也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配。
当在一个函数内定义另外一个函数就会产生闭包。
function greeting(name) {
var text = 'Hello ' + name; // local variable
// 每次调用时,产生闭包,并返回内部函数对象给调用者
return function() { console.log(text); }//注意该函数无名称,称为匿名函数
}
var sayHello = greeting('Closure');//调用greeting()返回了什么?
sayHello(); // 注意此处的使用方法。通过闭包访问到了局部变量text
上述代码的执行结果是:Hello Closure
因为sayHello指向了greeting函数对象,sayHello()则对其进行调用,greeting函数执行完毕后将返回greeting函数内定义的匿名函数对象,而该匿名函数仍然可以访问到了定义在greeting之内的局部变量text。
注意此时我们已从greeting函数中退出了(但请留意,也只有该内部匿名函数能访问,其它任何代码都不能访问)
var scope = 'global scope'; //全局变量
function checkScope(){
var scope = 'local scope'; //局部变量
function f(){
return scope;
}
return f;
}
checkScope()(); //注意此处的使用方法。返回值为local scope而非global scope
- 闭包的几种表现形式
- 返回一个函数
var a = 1;
function foo(){
var a = 2;
// 这就是闭包
return function(){
console.log(a);
}
}
var bar = foo();
// 输出2,而不是1
bar();
- 作为函数参数传递
无论通过何种手段将内部函数传递到它所在词法作用域之外,它都会持有对原始作用域的引用,无论在何处执行这个函数,都会产生闭包。
var a = 1;
function foo(){
var a = 2;
function baz(){
console.log(a);
}
bar(baz);
}
function bar(fn){
// 这就是闭包
fn();
}
// 输出2,而不是1
foo();
- 回调函数
在定时器、事件监听、Ajax请求、跨窗口通信、Web Workers或者任何异步中,只要使用了回调函数,实际上就是在使用闭包。
// 定时器
setTimeout(function timeHandler(){
console.log('timer');
},100)
// 事件监听
$('#container').click(function(){
console.log('DOM Listener');
})
- 非典型闭包IIFE(立即执行函数表达式)
IIFE(立即执行函数表达式)并不是一个典型的闭包,但它确实创建了一个闭包。
var a = 2;
(function IIFE(){
// 输出2
console.log(a);
})();