文章目录
前言
这部分学习继承和进阶函数,深化js学习。
一、继承
1.对象拷贝
通过for in的方式将父级的内容拷贝给子级。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var laoli = {
name: "laoli",
money: 1000000,
house: ["商铺","住宅"],
tech: function () {
console.log("厨艺");
}
};
var xiaoli = {
name: "xiaoli"
};
//封装一个继承的函数
function extend(parent,child) {
for (var k in parent){
//判断子级是否含有父级的元素
if (child[k]) {
continue;
}
child[k] = parent[k];
}
}
//调用函数 实现继承
extend(laoli,xiaoli);
console.log(xiaoli);
</script>
</body>
</html>
2.原型继承
通过原型来继承。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//人类类型
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//学生类型
function Student(score) {
this.score = score;
}
//老师类型
function Teacher(salary) {
this.salary = salary;
}
//通过原型让学生继承人类
Student.prototype = new Person("zs",18,"男");
Student.prototype.constructor = Student;
var s1 = new Student(88);
var s2 = new Student(100);
console.log(s2);
</script>
</body>
</html>
3.构造函数属性继承
(1)call方法
功能:既能执行函数,又能将this的指向改变
参数:第一个参数是this指向的内容,第二个及后续参数是执行函数的参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//call方法有两个功能
//1.能够将内容指向this
//2.执行函数
function fn(a,b) {
console.log(this);
console.log(a+b);
}
var o = {
name: "lm"
}
//call有两个参数,第一个参数是指向this,第二个及以后的参数是正常的需要执行函数的参数
fn.call(o,3,4);
</script>
</body>
</html>
(2)构造函数属性继承(通过使用call方法)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//人类类型
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//学生类型
function Student(name,age,sex,score) {
Person.call(this,name,age,sex);
this.score = score;
}
//老师类型
function Teacher(name,age,sex,salary) {
Person.call(this,name,age,sex);
this.salary = salary;
}
var s1 = new Student("zs",17,"男",88);
var s2 = new Student("ls",19,"男",90);
console.dir(s1);
console.dir(s2);
</script>
</body>
</html>
4.构造函数方法继承
(1)第一种方式:拷贝继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//人类类型
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.sayHi = function () {
console.log("你好");
};
//学生类型
function Student(name,age,sex,score) {
Person.call(this,name,age,sex);
this.score = score;
}
//第一种方式:拷贝继承
for (var k in Person.prototype) {
//将constructor重新指向Student
if (k === "constructor") {
Student.prototype.constructor = Student;
}
Student.prototype[k] = Person.prototype[k];
}
//第二种方式:原型继承
// Student.prototype = new Person();
// Student.prototype.constructor = Student;
//老师类型
function Teacher(name,age,sex,salary) {
Person.call(this,name,age,sex);
this.salary = salary;
}
var s1 = new Student("zs",17,"男",88);
var s2 = new Student("ls",19,"男",90);
console.dir(s1);
console.dir(s2);
s1.sayHi();
</script>
</body>
</html>
(2)第二种方式:原型继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//人类类型
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.sayHi = function () {
console.log("你好");
};
//学生类型
function Student(name,age,sex,score) {
Person.call(this,name,age,sex);
this.score = score;
}
//第一种方式:拷贝继承
// for (var k in Person.prototype) {
// //将constructor重新指向Student
// if (k === "constructor") {
// Student.prototype.constructor = Student;
// }
// Student.prototype[k] = Person.prototype[k];
// }
//第二种方式:原型继承
Student.prototype = new Person();
Student.prototype.constructor = Student;
//老师类型
function Teacher(name,age,sex,salary) {
Person.call(this,name,age,sex);
this.salary = salary;
}
var s1 = new Student("zs",17,"男",88);
var s2 = new Student("ls",19,"男",90);
console.dir(s1);
console.dir(s2);
s1.sayHi();
</script>
</body>
</html>
5.组合继承
组合继承:属性继承在构造函数内部继承,方法继承通过原型对象继承。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//组合继承:属性继承在构造函数内部继承,方法继承通过原型对象继承
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function () {
console.log("你好");
};
//一个子级继承父级的属性和方法
function Student(name,age,score) {
//继承父级的属性
Person.call(this,name,age);
this.score = score;
}
//继承父级方法
Student.prototype = new Person();
Student.prototype.constructor = Student;
//创建实例
var s1 = new Student("gg",22,100);
console.log(s1.name);
s1.sayHi();
</script>
</body>
</html>
二、函数进阶
1.函数的调用和this
函数调用根据目前的学习,在五种情况下,有五种不同的调用方法,同时this的指向也不同,但this的具体指向要根据实际调用的情况来决定。
接下来通过代码来体验不同方式调用函数的方式以及this的指向。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//五种不同的函数调用方式
//1.普通函数调用通过变量名加()来调用
//默认this指向window
function fun() {
console.log("普通函数调用");
}
fun();
//2.构造函数通过new方式调用
//this指向实例对象
function Person(name) {
this.name = name;
}
var fn = new Person("gg");
//3.对象函数通过对象打点调用
//this指向该方法所使用的对象
var funs = {
sayHi: function () {
console.log("对象函数");
}
};
funs.sayHi();
//4.事件函数,当事件执行时就自动进行调用
//this指向事件函数
document.onclick = function () {
console.log("事件函数");
}
//5.定时器和延时器,在规定时间内自动进行调用
//this指向window
setInterval(function () {
console.log("定时器");
},1000)
</script>
</body>
</html>
2.改变this指向的三种方法
(1)call
(2)apply
(3)bind
下面通过代码来看各种方法的功能、参数及返回值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function fn(a,b) {
console.log(this);
console.log(a+b);
}
var o = {
name: "hg"
};
//call方法
//1.功能:更改this指向的对象,执行函数并传参数
//2.参数:第一个参数是this指向的对象,第二个以后的参数指向的是参数列表
//3.返回值:返回的是函数执行的结果
fn.call(o,1,2);
//apply方法
//1.功能:更改this指向的对象,执行函数并传参数
//2.参数:第一个参数是this指向的对象,第二个以后的参数是参数数组
//3.返回值:返回的是函数执行的结果
fn.apply(o,[2,3]);
//bind方法
//1.功能:更改this指向的对象,不能执行函数,但可以传参数
//2.参数:第一个参数是this指向的对象,第二个以后的参数指向的是参数列表,参数可以接收存储
//3.返回值:this的指向
var fn = fn.bind(o,2,5);
console.log(fn);
fn(6,8);
</script>
</body>
</html>
3.call的应用
使用call方法改变this的指向,可以将this指向数组,从而给对象添加一些数组可以使用的方法,那么对象就也可以使用数组的方法了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var o = {
0: 10,
1: 20,
2: 30,
length: 3
}
//将this指向数组,借用数组的push方法,给对象添加最后一项数据
Array.prototype.push.call(o,40);
console.log(o);
</script>
</body>
</html>
4.apply的应用
同样的,可以利用apply方法将this指向Math,从而给数组添加一些数组没有的方法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var arr = [1,2,6,9,3];
//数组本身是不具备给数组中的数找最大值的,但是Math中的max方法可以找最大值
//那么我们就可以将this指向max,从而让数组也可以找最大值
console.log(Math.max.apply(null,arr));
//可以将数组输出为零散的数,而不是一个数组
console.log(arr);
console.log.apply(null,arr);
</script>
</body>
</html>
5.bind的应用
在只指定this并且不需要执行函数的时候使用,那么就可以应用于事件函数更改this和定时器内部函数更改this,接下来用代码进行进一步的理解。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//更改定时内部函数的this
var o = {
name: "zs",
age: 18,
timer: function () {
setInterval(function () {
console.log(this.age);
}.bind(this),1000);
}
}
o.timer();
//更改事件函数内部的this
document.onclick = function () {
console.log(this);
}.bind(o);
</script>
</body>
</html>
6.高阶函数
两种情况下是高阶函数:
(1)参数作为另一个函数的参数
(2)函数的返回值是一个函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//高阶函数
//函数作为另一个函数的参数
function eat(fn) {
console.log("吃饭");
fn();
}
eat(function () {
console.log("看书");
});
//函数的返回值是一个函数
function outer(n) {
return function inner(m) {
console.log(m+n);
}
}
var fun = outer(100);
fun(3);
fun(20);
var fun1 = outer(1000);
fun1(2);
fun1(30);
</script>
</body>
</html>
三、闭包
1.闭包的理解和应用
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
闭包的用途:可以在函数外部读取内部成员,让函数内的成员始终存活在内存中。
函数定义时天生就能记住自己生成的作用域环境和函数自己,将它们形成一个密团的环境,这就是闭包。不论函数以任何方式在任何地方进行调用,都会回到自己定义时的密闭环境进行执行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function outer() {
var a = 10;
function inner() {
console.log(a++);
}
return inner;
}
var fun = outer();
fun();
fun();
</script>
</body>
</html>
2.闭包的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var arr = [];
for (var i = 0 ; i <= 10 ; i++ ) {
(function (i) {
arr[i] = function () {
console.log(i);
}
})(i);
}
arr[0]();
arr[1]();
arr[2]();
</script>
</body>
</html>
总结
这部分学习了继承和函数的进阶,能够更好地掌握函数,能更加熟练的书写js代码,使代码更加简洁。每天坚持学习前端~