JavaScript基础知识(二)

一、ES2015 基础语法

1.变量

使用 let 代替 var
let 的优点:

  • 块级作用域;
{  var str = 'hello world';}
console.log(str);     //  hello world
{  let str = 'hello world';}
console.log(str);     //  报错:str is not defined
  • 不存在变量提升;
console.log(str);
var str = 'hello world';    // undefined

console.log(str);
let str = 'hello world';    // 报错:Cannot access 'str' before initialization
  • 不允许重复声明。
var num = 10;
var num = 20;
console.log(num);     // 20

let num = 10;
let num = 20;
console.log(num);     // 报错:Identifier 'num' has already been declared\n

总的来说,let 会让变量声明更加规范。

2.常量

使用 const 定义 常量(即不变的值)。
定义之后不可以修改:

const num = 10;
num = 20;
console.log(num);    //   报错:Assignment to constant variable.

使用 const 声明的几种情况:

  • 不变的值;
const PI = 3.14;\nconsole.log(PI);    //   3.14
  • 函数表达式;
const fun = function (a, b) {
    return a + b;
}

fun(1, 1);
  • 对象;
function getStundent() {
	return {
    	name: 'Lulu',
         age: 20
	}
}
// 对象声明可以使用常量
const student = getStundent();
// 虽然是常量,但是对象里面的属性可以改变
student.name = 'Mary';
console.log(student.name);    // 'Mary'
  • 引入外部模块。
const express = require('express');

3.模板字符串

语法:

// 反引号 ``
let str = `hello world`;
console.log(str);      // hello world

优点:

  • 支持换行:
let str = `hello
world`;
console.log(str);

在这里插入图片描述

  • 支持嵌入变量,${} 连接字符串:
let year = "2023";
let month = "08";
let date = "08";
// 拼接成 "2023年08月08日" 
let result = `${year}${month}${date}`
console.log(result);     // 2023年08月08日

4.结构赋值

  • 数组的解构赋值:
// let n = 10;
// let m = 20;
let [n, m] = [10, 20];
console.log(n);   // 10
console.log(m);  // 20

示例:交换 n 和 m 的值。

let n = 10;
let m = 20;
// 交换
[n, m] = [m, n];
console.log(n);   // 20
console.log(m);   // 10
  • 对象的结构赋值:
// let obj = {name: "xiaoming", age: 10};
// let name = obj.name;
// let age = obj.name;
let { name, age } = { name: "xiaoming", age: 10 };
// 如果颠倒 name,age的顺序并不会影响结果:
// let { age, name } = { name: "xiaoming", age: 10 };  
console.log(name);   // xiaoming
console.log(age);    // 10
  • 通过解构赋值传递参数:
// function getName(obj) {
//    return obj.name
//}
function getName({name, age}) {
	// name  ==> obj.name
	// age  ==> obj.age
	return name
}

let result = getName({name: 'xiaoming', age: 10});
console.log(result);     // xiaoming

二、函数进阶

1. 设置默认参数值

ES2015 的语法可以为函数的参数设置默认值:

function fun(x = 10, y = 20) {
  return x + y;
}
  
fun();    		// 30
fun(1);   		// 21
fun(1020);   // 30
  • 未传递参数和未设置默认参数值时:
function fun(x, y) {
	console.log(x); // undefined
    console.log(y); // undefined
    return x + y;
}

let result = fun();
console.log(result);   // NaN

2. 立即执行函数

(function () {
	let a = 10;
	let b = 20;
	console.log(a + b);
})();
// 30

功能:封装代码。

特点:

  • 声明之后可以直接调用;
  • 不可以多次调用;
  • 某些第三方库实现封装。

作用域链:

  • 每一个函数都会创建一个新的作用域;
  • 函数外部无法访问函数内部的值;
  • 函数内部的值可以访问函数外部的值(如果内部找不到值就去外部一层一层找)。
    示例:树状图。
|-window
| |-str
| |-fun1
| | |-str
| | |-num
| | |-fun2
| | | |-str
| | | |-num
let str = "hello";
function fun1() {
	let str = "world";
	let num = 10; 
  	function fun2() {
    	let str = 'fun2';
        let num = 20;
        console.log(str); // fun2
         console.log(num); // 20
	}
    fun2();
    console.log(str); // world
    console.log(num); // 10
}
fun1();  
console.log(str); // hello

3. 闭包

  • 闭包函数:声明在一个函数中的函数,叫做闭包函数。
  • 闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回之后。
  • 闭包的特性: 内部函数未执行完,外部函数即使执行完成,外部函数中的变量也不会被销毁。

示例1:想要在 fun1 的外部打印 fun2 的结果。

function fun1() {
  function fun2() {
      console.log("I'm fun2");
  }
}

利用 return在函数外部调用 fun2。

function fun1() {
	function fun2() {
		console.log("I'm fun2");
	}
	// 闭包的精华:return
	return fun2;
}

const f = fun1(); 
f();    // I'm fun2

示例2:想要在 fun1 的外部求和。

function fun1() {
	let n = 10;
    let m = 20;
    function fun2() {
		return n + m;
     }
     return fun2;
}

const f = fun1(); // fun1的运行结果是fun2
let result = f();
console.log(result);  // 30

代码封装:
ES5 的一个模块化的语法。

const module = (function () {
	let a = 10;
    let b = 20;
    function add() {
    	return a + b;
    }
    return add;
})();

4. 箭头函数

作用: 简化写法。

const add = function (x) {
	return x * x;
};
// 简化
const add = (x) => {
	return x * x;
};
// 简化
//         参数  返回值
const fun = x => x * x;

示例:每秒输出一次名字。

const cat = {
	name: "miaomiao",
	sayName() {
		let self = thissetInterval(function () {
		 // window调用的setInterval
		 // this 指向 window,所以需要提前保存
		 	console.log(self.name);
		 }, 1000)
	},
	...
	
	// 使用箭头函数:
	sayName() {    
	// 箭头函数:在哪里定义,this 就指向谁
	setInterval(() => {
		// this 指向 cat
		console.log(this.name);
		}, 1000);
	},
	...
};

cat.sayName();  // miaomiao
  • 使用 function 定义的函数, this 取决于调用的函数;
  • 使用箭头函数, this 取决于函数定义的位置;

箭头函数和普通函数的 this 指向不同:
- 普通函数指向的是 调用该函数的对象
- 箭头函数是 在哪里定义,this 就指向谁

三、面向对象

1. 面向对象概述

面向对象是一种编程思想,这种编程思想可以当做一个学科来研究。

除了 JavaScript,例如 C++、Java、Python、PHP 等等编程语言都可以使用这种面向对象的编程思想来开发应用程序。

2. 基本概念

  • :类型、模板、统称(例如:狗类、鸟类);
  • 对象:是类的一个实例,会具体到某一个事物上(天上飞的那只鸟,我加的那只猫);
  • 继承:狗类继承至哺乳动物类,猫类也继承至哺乳动物类,继承后,子类可以使用父类的属性和方法。

3. 新语法 与 旧语法

  • ES5 面向对象语法:prototype
  • ES6(2015) 面向对象语法:class

3.1 ES5 面向对象的知识

  • 构造函数:用于创建对象的函数;
  • 原型对象:prototype;
  • 原型链:实现继承;
ES5构造函数

构造函数的函数名,首字母大写;

构造函数是用来创建对象用的。

语法:function Dog(){}

// 构造函数Dog
function Dog(name, age) {
  this.name = name;
  this.age = age;
}
let dog = new Dog("wangwang", 2);  // 创建了一个对象,狗类的实例:wangwang
console.log(dog.name);    // wangwang
原型对象

通过设置构造函数的 prototype 属性,可以扩展构造函数生成的对象。
通过原型对象,为构造函数生成的对象赋予新的方法。

function Dog(name, age) {
  this.name = name;
  this.age = age;
}
// 给 Dog.prototype 添加方法
Dog.prototype.sayName = function () {
	console.log(this.name);
}; 

let dog1 = new Dog("wangwang", 2);
let dog2 = new Dog("jimmy", 6);
// 所有 Dog 的实例都可以使用 sayName 方法
dog1.sayName();		// wangwang
dog2.sayName();     // jimmy
原型链(继承)

将该构造函数 Dog 的原型指向另一个构造函数 Animal 的实例,则 所有 Dog 的实例都可以使用 Animal 上的变量和方法。
Dog.prototype = new Animal()

function Animal(name) {
  this.name = name;
}
Animal.prototype.sayName = function () {
  console.log(`你好,我是${this.name}`);
};

function Dog(name) {
  this.name = name;
}
// 继承的精华 => 就在这句
Dog.prototype = new Animal();

var dog = new Dog("wangwang");
dog.sayName();  // 你好,我是wangwang

3.2 ES5 面向对象的知识

Class关键字

语法:
class 构造函数名字 {
// 使用constructor 声明属性
constructor() {…}
}

class Dog {
  // constructor 声明属性:
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  // 方法在 constructor 下面写:
  sayName() {
    console.log(`我是${this.name}`);
  }
}

let dog = new Dog("wangwang", 2);
dog.sayName();  // 我是wangwang
继承

语法:extends 关键字 + super

示例1:让 Dog 继承 Animal

class Animal {
  constructor(name) {
    this.name = name;
  }
  sayName() {
    console.log(`我是${this.name}`);
  }
}
// 使用 extends关键字:让 Dog 继承 Animal
class Dog extends Animal {}

let dog = new Dog("wangwang");
dog.sayName();  // 我是wangwang

示例2:让 Dog 继承 Animal 中的一些属性,然后再声明自身所需要的属性。

class Animal {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  sayName() {
    console.log(`我是${this.name}`);
  }
}
// 使用 extends关键字:让 Dog 继承 Animal
class Dog extends Animal {
  constructor(name, age, id) {
    // 父级的属性: super关键字
    super(name, age);
    // 子级的属性就用 this 关键字声明
    this.id = id;
  }

  // 方法:
  getId() {
    console.log(`我的编码是${this.id}`);
  }	
}

let dog = new Dog("wangwang", 2, 1234567);
console.log(dog.name, dog.age, dog.id); // wangwang, 2, 1234567
dog.sayName(); // 我是wangwang
dog. getId(); // 我的编码是1234567 

四、DOM 基础

1. 什么是DOM

DOM:是一套标准编程接口。
我们通过 DOM 这套接口来操作HTML元素。

2. 节点类型

  • 元素节点;
  • 属性节点;
  • 文本节点;

网页效果:操作元素节点、属性节点、文本节点,以及修改元素的样式。

3. document 对象

DOM 通过 document 对象,为开发者提供了大量的接口(API)来操作 DOM 树。

3.1 获取节点

  • document.getElementById() :通过 ID 来获取元素,其返回值是一个 DOM 节点;
<h1 id="title">hello world</h1>
let h1 = document.getElementById("title");
console.log(h1);
  • document.getElementByClassName() :通过类名来获取元素,其返回值是一个 DOM 节点的集合,需要遍历;
<button>123</button>
<button>456</button>
<button>789</button>
let btns = document.getElementsByClassName("btn");
console.log(btns);
  • document.querySelector() :参数是 css 选择器 获取一个节点,不管能找到多少个,返回值都是一个 DOM 节点(如果是多个元素,它会返回第一个);
let h1 = document.querySelector("#title");
h1.innerHTML = "你好世界";
  • document.querySelectorAll() :参数是 css 选择器 (通常是类选择器),作用是获取多个节点,返回值一个 DOM 节点的集合,需要遍历;
let btns = document.querySelectorAll(".btn");
for (let i in btns) {
  btns[i].innerHTML = "test";
}
  • element.innerHTML : DOM 节点的属性,获取和设置元素内所有的内容。
let a = document.querySelector("#a");
a.innerHTML = "<span>链接</span>";

4. 事件类型

  • click : onclick 点击事件;
let btn = document.querySelector("button");

// 事件监听函数
btn.onclick = function () {
  console.log("hello btn");
};
  • mouseenter : onmouseenter 鼠标移入元素;
let box = document.querySelector(".box");

// 鼠标移入元素
box.onmouseenter = function () {
  console.log("hello");
};
  • mouseleave :onmouseleave 鼠标移出元素。
let box = document.querySelector(".box");

// 鼠标移出元素
box.onmouseleave = function () {
  console.log("bye");
};

5. 设置样式

语法:
element.style.color = “red”
element.style.backgroundColor = “pink”

通过 click、mouseenter、mouseleave 事件控制样式。

示例1:设置 box 的样式。

let box = document.querySelector("#box");

// 鼠标移入元素
box.onmouseenter = function () {
  this.style.backgroundColor = "blue";
};
// 鼠标移出元素
box.onmouseleave = function () {
  this.style.backgroundColor = "red";
};

示例2:通过点击 button 设置 box 的样式:

let btn = document.querySelector("#btn");
let box = document.querySelector("#box");

// 点击事件监听函数
btn.onclick = function () {
  box.style.backgroundColor = "yellow";
};

6. 设置属性

语法:

element.src = "images/1.png";
element.id = "wrapper";

示例:点击数字列表切换图片。

// 图片列表
let imgList = ["images/1.png", "images/2.png", "images/3.png"];
let btnList = document.querySelectorAll("btn");
let imgBox = document.querySelector("#box");

for(let i in btnList){
	btnList[i].onclick = function(){
		imgBox.src = imgList[i];
	}
}

页面效果:
在这里插入图片描述

6.1 通过 class 属性设置属性

语法:

element.className = "red";

示例:点击 li ,设置激活的背景色

.active {
	background-color: red;
}
let liList = document.querySelectorAll("li");
for (let i in liList) {
  liList[i].onclick = function () {
  	if(this.className === 'active'){
		this.className = ''
		return;
	}
    this.className = 'active';
  };
}

页面效果:
在这里插入图片描述

五、DOM 操作

1. 节点操作

首先,我们来回顾一下 innerHTML 的用法:
示例:在 ul 中添加 li 标签。

<ul class="list"></ul>

let list = document.querySelector('.list');
list.innerHTML =`
	<li>香蕉</li>
	<li>苹果</li>
	<li>鸭梨</li>
`

页面效果:
在这里插入图片描述
注意:innerHTML 是大刀阔斧的操作,平时在开发过程中并不推荐。

下面介绍4中常用的节点操作方法:

  • createElement:创建元素节点;
  • createTextNode:创建文本节点;
  • appendChild:添加节点;
  • removeChild:删除节点;

示例1:通过 input 文本框添加 li 元素。

	<div>
    	<input type="text" class="text">
    	<button>添加</button>
	</div>
	<ul class="list"></ul>
	
	...
	
    let input = document.querySelector('.text');
    let btn = document.querySelector('button');
    let list = document.querySelector('.list');

    btn.onclick = () => {
    	// 创建新元素
        let li = document.createElement('li');
        // 创建文本元素
        let txt = document.createTextNode(input.value);
        // 插入元素
        li.appendChild(txt);
        list.appendChild(li);
    }

页面效果:
在这里插入图片描述
示例2:点击删除已有 li 元素。

<ul>
	<li>香蕉</li>
	<li>橘子</li>
	<li>水蜜桃</li>
</ul>
...
let ul = document.querySelector('ul');
let lis = document.querySelectorAll('li');

for (let i in lis) {
	lis[i].onclick = function () {
		// 移除当前点击的li
		ul.removeChild(this)
	}
}

页面效果:
在这里插入图片描述
删除后:
在这里插入图片描述

2. 事件对象

事件监听函数的形参可以获取事件对象。

<div class="box"></div>

let box = document.querySelector('.box');
box.onclick = function (e) {
	// 事件对象
	console.log(e);
}

在这里插入图片描述
其中,可以通过事件对象可以获取鼠标坐标:

  • 获取 x 坐标:e.clientX;
  • 获取 y 坐标:e.clientY;

示例:随鼠标移入显示图片的大图,鼠标移出则大图消失。

.imgList img {
	width: 200px;
	height: 200px;
    }

.bigImg {
	position: absolute;
}

...

<div class="imgList">
    <img src="images/1.jpg" alt="">
    <img src="images/2.jpg" alt="">
    <img src="images/3.jpg" alt="">
</div>

<img class="bigImg" src="">

...
// 获取元素
let imgList = document.querySelector('.imgList');
let img = document.querySelectorAll('.imgList img');
let bigImg = document.querySelector('.bigImg');
// 给每个 img 元素添加移入移出事件
for (let i in img) {
	// 移入展示大图
	img[i].onmouseenter = function () {
		bigImg.src = this.src;
	}
	// 移入隐藏大图
	img[i].onmouseleave = function () {
		bigImg.src = '';
	}
}
// 当鼠标在 imgList 滑动时,根据鼠标的 x,y值 设置大图的 left,top 值
imgList.onmousemove = function (e) {
	// 添加20px,避免大图闪烁
	// 如果直接设置鼠标 x 和 y 的值,鼠标会与大图左上角完全重合
	// 会导致重合的时候大图覆盖到了 img ,此时 触发 img 的 mouseleave 事件,大图消失
	// 大图消失后,鼠标在 img 上方又触发了 img 的 mouseenter 事件,导致大图闪烁
	bigImg.style.left = e.clientX + 20 + 'px';
	bigImg.style.top = e.clientY + 20 + 'px';
}

页面效果:
在这里插入图片描述