js高级第二天
1.原型链继承
利用代码的能力实现 面向对象的特性 封装 和 继承
1.初体验
-
子类strudent 继承了父类 Person的属性
// 父类 function Person(name, height) { this.name = name; this.height = height; } Person.prototype.say = function () { console.log(this.name); console.log(this.height); } // 子类 function Student(grade, name, height) { // 借用了父类的构造函数,完成对自己的赋值 Person.call(this, name, height) this.grade = grade; } // 赋值了父类原型上的所有的 属性和方法 Student.prototype = Person.prototype; // 修改之类的指向 Student.prototype.constructor = Student; // 创建子类的实例 const stu = new Student("一年", "周星星", 170); stu.say();需求
- 有一个负责创建元素的构造函数 A
- 有一个负责创建图片的构造函数 B
- 构造函数 B 可以使用 构造函数 A 的原型上的所有的功能 实现继承
效果
代码
// 1 负责创建元素 function Element(nodeName, text) { const node = document.createElement(nodeName); node.classList.add("element") node.innerText = text; this.node = node; } // 2 原型上挂载 共用的方法 Element.prototype.appendTo = function (selector) { const parent = document.querySelector(selector); parent.appendChild(this.node); } // 3 创建一个实例 const div = new Element("div", "做人开心就好"); // 4 追加到父元素上 div.appendTo("section"); // 5 一个新的构造函数 用来创建图片 function ElementImg(src) { // 6 借用了 1 中的构造函数,并且把参数传递了进去 Element.call(this, "img", ""); // 7 图片设置路径 this.node.src = src; } // 8 继承了 父亲的构造函数上的原型上的所有 函数 ElementImg.prototype=Element.prototype; // 修改来原型,也就修改了构造函数 // 9 重新将 constructor 的指向改回来 ElementImg.prototype.constructor=ElementImg; // 10 创建一个图片实例:注意,实例化代码10不能在8和9之前 const img = new ElementImg("images/01.png"); // 11 创建到父元素上 img.appendTo("section");2.es6 class(重点)
es6的class 的出现 基本上可以替代了es5的构造函数和原型,使之代码结构上更加简洁。
关键字
-
class
-
属性
-
方法
-
继承 extends
-
构造函数 constructor
-
方法重写 override:子类方法覆盖父类,super.父类方法()
-
父类的构造函数 super :子类有构造方法且使用this前,必须使用super()
完整代码体验
class Person { // 构造方法 constructor(name) { // 属性 this.name = name; } // 方法 say() { console.log(this.name); } } // 继承 class Student extends Person{ constructor(name,height){ // console.log(this); // 语法错误:必须先调用super()才能使用this super(name); this.height=height; } } const s1=new Student("八神",180); s1.say(); // 八神 class Saler extends Person{ constructor(name,age){ super(name); this.age = age; } // 覆盖(重写) say(){ // 访问父类方法 super.say(); // 马云 console.log(this.age); } } const s2 = new Saler('马云',50); s2.say(); // 50Tab栏切换
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>tab栏切换</title> <style> * { margin: 0; padding: 0; } ul { list-style: none; } .box { width: 400px; height: 300px; border: 1px solid #ccc; margin: 100px auto; } .hd { height: 45px; } .hd span { display: inline-block; /*将行内元素转换成行内块元素,宽高才起作用*/ width: 90px; background-color: pink; line-height: 45px; text-align: center; cursor: pointer; } .hd span.current { /*交集选择器,标签指定式选择器*/ background-color: purple; /*紫色*/ } .bd li { height: 255px; background-color: purple; display: none; /*设置隐藏*/ } .bd li.current { display: block; /*显示*/ } </style> <script src="js/TabExchange.js"></script> </head> <body> <div class="box" id="box"> <div class="hd"> <span class="current">体育</span> <span>娱乐</span> <span>新闻</span> <span>综合</span> </div> <div class="bd"> <ul id="list"> <li class="current">我是体育模块</li> <li>我的娱乐模块</li> <li id="li3">我是新闻模块</li> <li>我是综合模块</li> </ul> </div> </div> <script> let tab = new TabExchange('box', 'current'); console.dir(tab); </script> </body> </html>class TabExchange { // 构造函数 constructor(id, className) { // 获取id对应的元素:规范,属于最外层 this.id = document.querySelector(`#${id}`) // 规定内部有两层解构:div>span this.hd = this.id.querySelectorAll('div>span') // 规范内部有两层解构:div>ul>li this.bd = this.id.querySelectorAll('div>ul>li') // 保存当前要处理的样式类 this.style = className; // 调用自己的tabClick 方法处理点击事件 this.tabClick(); } // 绑定点击事件 tabClick() { // 因为接下来要绑定事件,事件里面也会出现this,所以先定义变量that来保存当前TabExchange对象的this let that = this // 循环遍历,绑定事件,并进行事件处理 this.hd.forEach(function (item, key) { // item就是span,key就是span的下标 item.addEventListener('click', function () { // 排他:清空hd全部样式 that.hd.forEach(function (item1) { item1.classList.remove(that.style) }) // 清空bd的所有样式 that.bd.forEach(function (item2) { item2.classList.remove(that.style) }) // 给当前事件元素添加样式 that.hd[key].classList.add(that.style) that.bd[key].classList.add(that.style) }) }) } }
2.元素创建继承
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.element {
width: 200px;
height: 200px;
border-radius: 50%;
border: 10px solid #00ffff;
text-align: center;
font-size: 30px;
line-height: 200px;
overflow: hidden;
float: left;
}
</style>
<body>
<script>
// 创建一个类:可以创建普通元素,并且可以实现将元素挂到指定父元素下
class CreateElement {
// 构造函数,传入标签名,文本内容,和元素默认类名
constructor(element, text, className = 'element') {
// 1.创建元素
const node = document.createElement(element)
// 2.添加类名
node.classList.add(className)
// 3.添加文本内容(双标签有效,单标签无效)
node.innerText = text
// 4.将创建的元素节点存入到属性node
this.node = node
}
// 将当前创建的元素 添加到指定父元素下
appendTo(selector) {
// 获取父元素
const parent = document.querySelector(selector)
// 添加子元素
parent.appendChild(this.node)
}
}
// 直接创建一个普通元素
const div = new CreateElement('div', '做人开心就好')
div.appendTo('body')
// 图片标签不适用于普通元素创建(有属性src),但是想使用默认的样式和创建过程
// 创建子类 继承 父类
class CreateImg extends CreateElement {
// 自己的构造函数:只需要传入图片
constructor(src, className = 'element') {
// 父类构造函数
super('img', '', className)
// 给创建的元素增加src属性
this.node.src = src
}
}
// 改变了创建的内容,但是可以直接应用父类继承的方法
const img = new CreateImg('images/02.jpg')
img.appendTo('body')
</script>
</body>
</html>
2.函数参数默认值
定义函数的同时,可以给形参一个默认值
// 定义函数的同时,可以给形参一个默认值
function show(msg = "大家一起快活呀") {
console.log(msg);
}
show();// 打印 大家一起快活呀
show("搞笑不");// 打印 搞笑不
3.对象简写
在定义对象的时候,如果属性名和变量名一直,那么可以实现简写
const name = "悟空";
const skill = "72变";
const say = function () { }
const obj = {
name, skill, say
}
console.log(obj);// {name:"悟空",skill:"72变",say:function(){}}
对象的方法也可以简写
const obj = {
say() {
console.log(this);
}
}
4.Set
1.永远不会有重复元素的对象
2.可以理解为不重复的数组
const set = new Set([1, 5, 3, 4]);
set.add(5);
set.add(5);
console.log(set);
Set对象转为数组
const set = new Set([1, 5, 3, 4]);
set.add(5);
set.add(5);
console.log(set);
const arr = [...set];// 将set对象转数组
console.log(arr);
5.函数的四种调用模式
根据函数内部this的指向不同,可以将函数的调用模式分成4种
- 函数调用模式
- 方法调用模式
- 构造函数调用模式
- 上下文调用模式(借用方法模式)
1.函数调用模式
如果一个函数不是一个对象的属性时,就是被当做一个函数来进行调用的。此时this指向了window
function fn(){
console.log(this);// 指向window
}
fn();
2.方法调用模式
当一个函数被保存为对象的一个属性时,我们称之为一个方法。当一个方法被调用时,this被绑定到当前对象
const obj = {
sayHi:function(){
console.log(this);//在方法调用模式中,this指向调用当前方法的对象。
}
}
obj.sayHi();
3.构造函数调用模式
如果函数是通过new关键字进行调用的,此时this被绑定到创建出来的新对象上。
function Person(){
console.log(this);
}
Person();//this指向什么?
var p = new Person();//this指向什么?
4.方法借用模式
也叫上下文模式,分为 apply 与 call
1.call
call方法可以调用一个函数,并且可以指定这个函数的this指向
const RichWumon = {
name: "富婆",
say: function () {
console.log(this.name, " 我要重金求子");
}
}
const obj = {
name: "屌丝"
}
RichWumon.say(); // 富婆
RichWumon.say.call(obj); // 屌丝
2.all应用
- 将伪数组转成数组
let divs = document.querySelectorAll('div');
// let divs = document.body.children;
console.log(divs);
function change(nodelist) {
console.log(Object.prototype.toString.call(nodelist));
return Array.prototype.slice.call(nodelist);
}
3.apply
就是apply()方法接受的是一个包含多个参数的数组。而call()方法接受的是若干个参数的列表
可以利用apply 将 刚才的call 的代码修改一下
const RichWumon = {
name: "富婆",
say: function () {
console.log(this.name, " 我要重金求子");
}
}
const obj = {
name: "屌丝"
}
RichWumon.say(); // 富婆
RichWumon.say.apply(obj); // 屌丝
4.apply应用
1.简化log方法
// 简化log方法
function log() {
// 不需要改变this
console.log.apply(console, arguments);
}
5.bind方法
**bind()**方法创建一个新的函数, 可以绑定新的函数的this指向
var name = '张三';
function Fn(){
this.age = 1;
console.log(this.name + this.age);
}
Fn(); // 张三 1
// 返回值:新的函数
// 参数:新函数的this指向,当绑定了新函数的this指向后,无论使用何种调用模式,this都不会改变。
let obj = {
name:'小强',
}
const newFn = Fn.bind(obj);
newFn(); // 小强 1