1. 箭头函数
1.1 箭头函数的应该场景分析
给原生的回调函数/匿名函数提供了一个更加简洁的书写方式
解决回调中this关键字的指向问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>箭头函数应用场景</title>
</head>
<body>
<script>
// 1. 简化原生回调函数的书写方式
let demo1 = function (name) {
return name;
};
console.log(demo1("jack")); // jack
let demo2 = (name) => name;
console.log(demo2("tom")); // tom
// 用于遍历
let arr = ["a", "b", "c", "d"];
let ul = document.createElement("ul");
let str = "";
arr.forEach((item) => {
str += `<li>我是${item}</li>`;
});
ul.innerHTML = str;
document.body.appendChild(ul);
// 2. 解决回调中this指向的问题
let name = "outName";
const p1 = {
name: "p1的name",
getName: function () {
// 使用原生的回调方式 访问到的是全局name
setTimeout(function () {
console.log(this.name);
}, 500);
},
};
p1.getName(); // outName
const p2 = {
name: "p2的name",
getName: function () {
// 使用箭头函数,访问到的是当前对象的name
setTimeout(() => {
console.log(this.name);
}, 500);
},
};
p2.getName(); // p2的name
// 通过上面示例理解如下两个问题:
// 1. 普通回调函数中的this是指向全局的
// 2. 箭头函数中实际上是没有this的,而箭头函数支持词法作用域(块级作用域),所以箭头函数中的
// this指向 是根据它的作用域而判断到底是指向当前对象还是全局的
// 3. 千万不能理解成箭头函数中的 this 就一定是指向当前对象,如下:
const p3 = {
name: "p3的name",
getName: () => {
console.log(this.name);
},
};
p3.getName(); // outName
const p4 = {
name: "p4的name",
getName: function () {
console.log(this.name);
},
};
p4.getName(); // p4的name
// 从上面可以看出,p3的getName()方法使用箭头函数方式书写,但是其中的this指向了全局
// 箭头函数理解这块困了我很久,最终我总结如下:
// 如果箭头函数中存在this,那么这个this会和定义这个箭头函数的this绑定,简单的说,就是箭头函数的父函数的this是谁,那么箭头函数的this就是谁
const p5 = {
name: "p5的name",
getName: () => {
console.log(this); // Window
setTimeout(() => {
console.log(this.name);
}, 500);
},
};
p5.getName(); // outName
// 1. setTimeout中的this会和getName中的this绑定,而getName中的this是Window,所以最终这个出现的this是全局
const p6 = {
name: "p6的name",
getName: function () {
console.log(this); // 当前对象
setTimeout(() => {
console.log(this.name);
}, 500);
},
};
p6.getName(); // p6的name
// 2. 此时,getName的this和当前对象绑定了,所以setTimeout箭头函数中的this也绑定到了当前对象
// 3. 最后总结:其实这个箭头函数也并不是哪里都适合用
// 当我们不需要使用this对象的时候,可以放心大胆的用箭头函数来简化语法
// 当我们需要this对象的时候,你得明确知道你需要的this是全局还是当前对象,然后再酌情使用,就像p5和p6的例子中,
// 如果你需要当前对象,那么在父函数getName()中,你就不能使用箭头函数的语法来书写getName()
</script>
</body>
</html>
1.2 箭头函数示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>箭头函数示例</title>
<style>
p {
background-color: black;
color: white;
line-height: 50px;
width: 500px;
text-align: center;
}
</style>
</head>
<body>
<form action="" method="post">
<label for="idNumber">请输入用户id号:</label>
<input type="text" name="idNumber" id="idNumber" />
<button>查询用户信息</button>
</form>
<p></p>
<script>
// 示例:根据id获取对应的用户信息
// 模拟用户数据
const users = [
["6001", "jack", 20, "male"],
["8002", "alice", 21, "female"],
["9003", "lisa", 22, "secret"],
];
// 获取 输入框,查询按钮,以及存放用于信息的P标签
let idNumber = document.querySelector("#idNumber");
let btn = document.querySelector("button");
let p = document.querySelector("p");
btn.addEventListener(
"click",
(ev) => {
// 禁用默认行为
ev.preventDefault();
// console.log(idNumber.value);
show(idNumber.value);
// 执行完 显示用户信息 这个函数以后,清空输入框并再次获得输入焦点
idNumber.value = "";
idNumber.focus();
},
false
);
// 显示用户信息的函数
function show(idNum) {
// 获取所有用户信息的id号
let ids = [];
users.forEach((user) => {
ids.push(user[0]);
});
// console.log(ids);
// 下面一定要用循环,千万不能用foreach,因为遍历不能中途停止循环,而这里要用到break,continue
// 1. 找到对应的用户信息时,要break,终止循环
// 2. 如果没找到对应的用户信息,就continue,继续执行
for (let i = 0; i < ids.length; i++) {
if (ids[i] == idNum) {
users.forEach((user) => {
if (user[0] == idNum) {
// 解构当前找到的这个用户信息这个数组
let [id, name, age, sex] = user;
p.innerHTML = `用户姓名:${name},用户年龄:${age},用户性别:${sex}`;
}
});
break;
} else {
p.innerHTML = "信息不存在";
continue;
}
}
}
</script>
</body>
</html>
2. 类的原理与实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ES6中的类的原理与实现</title>
</head>
<body>
<script>
// 先看看ES6中的类
class A {
// 构造方法
constructor(name) {
this.name = name;
}
// 原型方法
getName() {
return this.name;
}
}
let obj = new A("xixi");
console.log(obj.getName()); // xixi
// 下面了解一下类的原理与实现
// 在原生js中是没有类的概念的,都是通过“构造函数”来模拟类;
console.dir(function () {});
console.log({});
// 通过上面两行代码,在控制器中看到:
// 1. 一个函数有2个属性:prototype(原型属性) __proto__(原型链属性)
// 2. 一个对象有1个属性:__proto__
// 下面使用构造函数来模拟 类的实现
// 1. 声明一个构造函数
function Demo(name) {
// 属性
this.name = name;
// 方法
this.getName = function () {
return this.name;
};
}
// 2. 实例化类
let a = new Demo("jack");
console.log(a.getName()); // jack
console.log(a); // 在控制台中可以看到,a这个对象从Demo类中继承到了name属性和getName()方法
// 但是为了防止代码的冗余,像一些公共方法通常都会写到它的原型属性上,这样的话,生成的对象也能自动继承方法,如下:
function Demo1(name) {
this.name = name;
}
// 公共方法
Demo1.prototype.getName = function () {
return this.name;
};
let b = new Demo1("alice");
console.log(b.getName()); // alice
console.log(b); // 此时,getName()方法就跑到对象b的__proto__属性中去了,而且以后实例化生成的对象,它的__proto__属性中都会继承这个方法
// 因此,通过函数的原型属性prototype,将它的指针对应一个方法的时候,new出来的对象,也都能继承这个方法,并保存在__proto__原型属性链中
// 基于这一点,构造函数便模拟了类的实现(继承父类中的方法)
// 总结:函数的两大属性
// 1. prototype:当该函数做为构造函数使用时(new 函数名),并且使用此属性声明的方法能对应上新生成的对象的__proto__属性,才有意义
// 2. __proto__:此属性指向对象的公共方法
// 分析总结:
// 1. 函数中为什么也有__proto__属性:因为函数也是属于对象
// 2. 为什么使用prototype声明方法后,生成的对象就在__proto__属性中继承了方法?
// a.在控制台中打开函数的prototype属性,可以看到在prototype属性中也有__proto__属性
// b.__proto__属性:其实是原型链属性,类似生物链把,它应该是在对象的最顶层,就简单理解成父类把
// c.当使用prototype声明方法时,这个方法就会保存在__proto__这个原型链属性中
// d.当实例化这个构造函数生成一个新对象时,对象的__proto__属性必然会继承构造函数的__proto__属性,从而也就继承了方法
</script>
</body>
</html>