什么是JavaScript?
-
高级解释语言
- 高级:很多抽象。
- 解释:不是直接执行,不需通过编译器运行,是被解释的脚本语言。
-
遵从ECMAScript规范
-
多范式(多种编写代码方式)
- 面向对象编程
- 函数式编程
-
运行在浏览器和客户端的语言,也可以运行服务器端(Node.js)
为何要学JavaScript?
- 运行在浏览器的编程语言
- 可以使用 JS 框架构建交互式页面
- 用于构建非常快速的服务器端和全栈应用程序
- 用于移动开发(React Native、NativeScript、lonic)
- 用于桌面应用开发(Electron JS)
变量
var - 全局变量
全局作用域。
let - 局部变量
可以重新赋值。
const - 常量
不能重新赋值
数据类型
String - 字符串
const name = 'Kevin';
字符串拼接
// 可以用 ‘+’ 拼接
console.log('My name is ' + name + ' and I am ' + age);
模板字符串(ES6)
// 可以用反引号 ``、$符号和大括号写入变量名
console.log(`My name is ${name} and I am ${age}`);
字符串属性和方法
-
属性(没有括号)
- 长度:s.length
-
方法(有括号)
- 变大/小写:s.toUpperCase() / s.toLowerCase()
- 子字符串:s.substring(0, 5)
- 分割到数组:s.split(',')
- 获取指定值的索引:s.indexOf('.')
- 提取某个字符串的一部分: s.slice(BeginIndex[, endIndex])
nubmer - 数值
const age = 30;
const rating = 4.5;
boolean - 布尔
const isCool = true;
null - 空
const x = null;
undefined - 未定义
const y = undefined;
let z;
typeof:可以判断变量的数值类型
数组
包含多个变量值。
JS中同一个数组可以保存不同类型值。
创建数组
直接声明创建
// 数组结构体创建方式
const numbers = new Array(1,2,3,4,5);
// 变量创建方式
const fruits = ['apples', 'oranges', 'pears', 10, true];
数组方法创建
- Array.fill(value[, start[, end]]): 用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。
- Array.from(arrayLike[, mapFn]) : 对一个类似数组或可迭代对象创建一个新的、浅拷贝的数组实例。如
Array.from([1, 2, 3], x => x + x));
数组元素操作 ☆
访问: 通过下标访问。
添加元素:
-
可以直接通过下标在末尾添加,
fruits[5] = 'grapes'。 -
可以通过push方法添加
- 在末尾:
fruits.push('mangos')。 - 在开头:
fruits.unshift('strawberries')。
- 在末尾:
删除元素: 可以通过pop方法在末尾删除,fruits.pop()。
获取某个元素的索引: fruits.indexOf。
将数组元素连接成一个字符串: Array.join('')
删除或替换现有元素或者原地添加新的元素:Array.splice(start[, deleteCount[, item1[, ...]]])
const类型的数组可以在数组中添加元素,可以操作它,也可以使用方法,唯一不能做的是初始化为空(
fruits = [])
数组操作
判断是否为数组:Array.isArray(fruits)。
获取数组的子串: Array.slice([begin[, end]]) 。
slice()返回一个新的数组对象,这一对象是一个由begin和end决定的原数组的浅拷贝(包括begin,不包括end)不改变原数组。
数组排序: Array.sort([compareFunction]) 。
sort()默认将数组元素转换为字符串比较,可以通过比较函数来定义排序,如sort((a, b) => { return a-b; })数组将会比较数字并升序排列。
对象字面量
对象就是键值对。
创建对象中可以有变量、数组、对象。
const person = {
firstName: 'Kevin',
lastName: 'Zhang',
hobbies: ['music', 'movies', 'sports'],
address: {
street: '50 main st',
city: 'WenZhou',
country: 'China'
}
}
对象可以解构赋值,重新定义新的变量去取出对象中属性值。(ES6)
const { firstName, lastName, address: { city } } = person;
// console.log(firstName);
// console.log(city);
添加新的属性,可以直接添加。
person.email = 'zkc@example.com';
对象数组
对象数组也是很常用的形式。
const todos = [
{
id: 1,
text: 'Take out trash',
inCompleted: true
},
{
id: 2,
text: 'Meeting with boss',
inCompleted: true
},
{
id: 3,
text: 'Dentist appt',
inCompleted: false
}
]
JSON
JSON是一种数据格式,与对象语法很相似。在全栈开发中广泛使用,像服务发送数据会用到JSON格式,也是接受JSON格式。
JSON与对象语法很相似,并且可以直接用 JSON.stringify(object name)直接转换。
const todoJSON = JSON.stringify(todos);
/*
属性名和字符串都为双引号!
const todoJSON = [
{
"id": 1,
"text": "Take out trash",
"inCompleted": true
},
{
"id": 2,
"text": "Meeting with boss",
inCompleted: true
},
{
"id": 3,
"text": "Dentist appt",
"inCompleted": false
}
]
*/
循环
For循环
for (let i = 0; i < 10; i++) {
// code
}
While循环
// 在循环外设置变量
let i = 0;
while (i < 10) {
// code
i++;
}
数组循环
// for循环
for (let i = 0; i < todos.length; i++) {
// todos[i].text ...
}
// for of 循环
for(let todo of todos) {
// todo ...
}
高阶数组方法
用于迭代数组,参数是函数(可以用箭头函数)。
不同方法可以链接起来用,利用函数式编程的思想,可以更强大。
// forEach 循环
todos.forEach(function(todo) {
// todo ...
})
todos.forEach(todo => {
// todo ...
})
// map 从数组中创建新数组
const todoText = todos.map(function(todo) {
return todo.text;
})
// filter 根据条件创建新数组
const todoCompleted = todos.filter(function(todo) {
return todo.isCompleted === true;
})
条件
if else语句
if (x == 10) {
// code ...
} else if(x > 10){
// code ...
} else {
// code ...
}
三元操作符
const x = 10;
const color = x > 10 ? 'red' : 'blue';
switch语句
switch(color) {
case 'red':
// code...
break;
case 'blue':
// code...
break;
default:
// code...
break;
}
函数
function addNums(num1, num2) {
// num1 + num2
}
addNums(5, 4); // 9
addNums(); // NaN
可以给函数默认参数设置值,在有传参的情况下也会重写默认值。
function addNums(num1 = 1, num2 = 2) {
// num1 + num2
}
addNums(); // 3
addNums(5, 5) // 10
箭头函数
const addNums = (num1 = 1, num2 = 2) => {
return num1 + num2;
}
// 不写{} ,就不写return
const addNums = (num1 = 1, num2 = 2) => num1 + num2;
// 若一个参数可以不写()
const addNums = num1 => num1 + 5;
面向对象编程OOP
可以通过构造函数来构造对象。
- 用原型实现构造函数
// 构造函数
function Person (firstName, lastName, dob) {
this.firstName = firstName;
this.lastName = lastName;
this.dob = new Date(dob);
// 给对象添加函数
this.getBirthYear = function(){
return this.dob.getFullYear();
}
this.getFullName = function() {
return `${this.firstName} ${this.lastName}`;
}
}
// 可以在原型中添加方法和属性,适合不是每个对象都添加的方法和属性
// 在原型中给对象添加函数
Person.prototype.getBirthYear1 = function() {
return this.dob.getFullYear();
}
Person.prototype.getFullName1 = function() {
return `${this.firstName} ${this.lastName}`;
}
// 实例化对象
const person1 = new Person('Kevin', 'Zhang', '4-18-1998');
const person2 = new Person('Kevin2', 'Zhang2', '4-18-1999');
- ES6的类
class Person {
constructor(firstName, lastName, dob) {
this.firstName = firstName;
this.lastName = lastName;
this.dob = new Date(dob);
}
getBirthYear(){
return this.dob.getFullYear();
}
getFullName() {
return `${this.firstName} ${this.lastName}`;
}
}
ES6中的
class可以看作一个语法糖,它的绝大部分的功能,ES5都可以做到,新的class写法让原型的写法更加的清晰、更像面向对象编程的语法。
原型和继承
原型链和原型对象是js的核心,js以原型链的形式,保证函数或对象中的方法、属性可以让向下传递,使用了构造函数来实现继承机制。按照面向对象的说法,这就是继承,而js通过原型链才得以实现函数或对象的继承。
- 每一个
构造函数都有一个原型属性prototype指向他的原型对象; - 每一个
原型对象中都有一个constructor属性指向他的构造函数; - 每一个
实例(除了null)都有一个属性叫__proto__指向原型对象; - 写在
构造函数内的属性和方法只有构造函数本身可以调用,原型对象与实例都不可以调用; - 写在原型对象的属性和方法称为实例属性和实例方法,既可以
通过实例调用,也可以通过原型对象调用; 构造函数可以创建多个实例,每个实例都有自己的属性和方法,实例也可以继承原型对象中的属性和方法;- 所有实例对象都会从它的原型上继承一个
constructor,指向原型对象
相关Object 和 Function
Object.__proto__、Object.prototype、Object.prototype.__proto__
Object是构造函数,所有的函数都是通过new Function创建了,因此Object相当于Function的实例,即Object.__proto__ --> Function.prototype。Object.prototype是Object构造函数的原型,处于原型链的顶端,Object.prototype.__proto__已经没有可以指向的上层原型,因此其值为null
Object.__proto__ --> Function.prototype
Object.prototype.__proto__ --> null
Function.__proto__、Function.prototype、Function.prototype.__proto__
Function.prototype是Function的原型,是所有函数实例的原型,例如上面讲的Object.__proto__Function.prototype是一个普通对象,因此Function.prototype.__proto__ --> Object.prototypeFunction.__proto__:__proto__指向创造它的构造函数的原型,Function.__proto__ --> Function.prototype
Function函数不通过任何东西创建,JS引擎启动时,添加到内存中
Funtion.__proto__ === Object.__proto__//f() { [native code] }
总结如下:
- 所有函数(包括
Function)的__proto__指向Function.prototype - 自定义对象实例的
__proto__指向构造函数的原型 - 函数的
prototype的__proto__指向Object.prototype Object.prototype.__proto__ --> null
原型链
实例对象在查找属性时,如果查找不到,就会沿着__proto__去与对象关联的原型上查找,如果还查找不到,就去找原型的原型,直至查到最顶层,这也就是原型链的概念。
所有的原型对象的__proto__属性都是指向function Object的原型对象。 而function Object的原型对象在上图中我们可以得知是不存在__proto__这个属性的,它指向了null。我们就得知了原型链的尽头是null。
DOM文档树结构
window对象:是浏览器的父对象,一般也可以不写。
单元素选择器
-
getElementById: 通过ID来获取元素
-
querySelector: 可以通过类、标签本身等等选中元素。是单元素选择器,如果有多个元素符合,只选中第一个。
- 选择id用 '#'
- 选择类名用 '.'
document.getElementById('my-form');
document.querySelector('.container');
document.querySelector('#my-form');
多元素选择器
- querySelectorAll:选择所有符合的元素。
- getElementsByTagName:通过标签来获取元素。
- getElementsByClassName:通过类名来获取元素。
document.querySelectorAll('.item');
document.getElementsByTagName('li');
document.getElementsByClassName('item');
query... 选择器得到的结果为NodeList,可以使用数组方法。
getElements... 选择器得到的结果为HTMLCollection,不能使用数组方法,需要转换为数组后使用。
循环遍历
const items = document.querySelectorAll('.item');
items.forEach(item => console.log(item));
操控和修改DOM
HTML页面操控
- remove():删除元素
- textContent:文本内容
- children[index]:通过索引在节点列表中获取节点
- innerHTML:动态修改HTML内容
const ul = document.querySelector('items');
// ul.remove();
// ul.lastElementChild.remove(); // 删除最后一个子项
ul.firstElementChild.textContent = 'Hello'; // 设置第一个子项文本内容为'Hello'
ul.children[1].innerText = 'Kevin';
ul.lastElementChild.innerHTML = '<h1>Hello</h1>'
CSS样式操控
可以写在事件和函数里,使其动态处理样式,在页面中实时操控。
const btn = document.querySelector('.btn');
// btn.style.background = 'red';
事件
addEventListener('event', e => {...}
- event是发生的事件(click、input、mouseover、mouseout、submit)
- e是事件发生时运行的函数。
const btn = document.querySelector('.btn');
// 点击事件
btn.addEventListener('click', e => {
e.preventDefault();
console.log(e.target.className); // btn
document.getElementById('my-form').style.background = '#ccc';
document.querySelector('body').classList.add('bg-dark');
ul.lastElementChild.innerHTML = '<h1>Changed</h1>';
});
// 键盘事件
const nameInput = document.querySelector('#name');
nameInput.addEventListener('input', e => {
document.querySelector('.container').append(nameInput.value);
});