🍊ES6中的类和对象
🍊创建类
语法
// 1.创建类
class Star{
constructor(uname,age){
this.uname = uname;
this.age = age;
}
}
// 2.利用类创建对象 new
var ldh = new Star('刘德华',18)
var zxy = new Star('张学友',20)
console.log(ldh.uname) // 打印一个对象:刘德华,18
console.log(zxy.uname) // 打印一个对象:张学友,20
🍊类中添加方法
- 类里面所有的函数不需要写function
- 多个函数方法之间不需要添加逗号分隔
// 1.创建类
class Star{
constructor(uname,age){
this.uname = uname;
this.age = age;
}
sing(song){
console.log(this.uname+song)
}
//
}
// 2.利用类创建对象 new
var ldh = new Star('刘德华',18)
var zxy = new Star('张学友',20)
console.log(ldh.uname) // 打印一个对象:刘德华,18
console.log(zxy.uname) // 打印一个对象:张学友,20
// 调用方法
ldh.sing('冰雨')
zxy.sing('李香兰')
🍊类的继承
- 用
extends
关键字 - 子类 继承 父类里面的属性和方法
class Father{
constructor(){
}
money(){
console.log(100)
}
}
class Son extends Father{
}
var Son = new Son();
son.money(); // 打印 100
🍊super关键字
🍊调用构造函数
super
关键字用于访问和调用父类对象的函数,可以调用父类构造函数,也可以调用父类的普通函数
错误用法-----sum函数中的 this 是指向父类的,父类中的 this 未定义
class Father{
constructor(x,y){
this.x = x;
this.y = y;
}
sum(){
console.log(this.x+this.y);
}
}
class Son extends Father{
constructor(x,y){
this.x = x;
this.y = y;
}
}
var Son = new Son(1,3);
son.sum(); // 打印 100
正确用法
class Father{
constructor(x,y){
this.x = x;
this.y = y;
}
sum(){
console.log(this.x+this.y);
}
}
class Son extends Father{
constructor(x,y){
super(x,y) //调用了父类的构造函数
}
}
var Son = new Son(1,3);
son.sum(); // 打印 100
🍊调用普通函数
- 若子类中有调用的方法就输出子类的方法 ---------------重写
- 若子类没有该方法 就去父类查找 调用父类的方法------继承
class Father{
say(){
return '我是爸爸'
}
}
class Son extends Father{
say(){
console.log('我是儿子')
}
}
var son = new Son();
son.say();
- 用 super 关键字直接调用父类的方法
class Father{
say(){
return '我是爸爸';
}
}
class Son extends Father{
say(){
// console.log('我是儿子')
console.log(super.say()+'的儿子') // super 可以看做Fathe类
}
}
var son = new Son();
son.say();
🍊既要调用子类的方法,也要掉用父类的方法
class Father{
constructor(x,y){
this.x = x;
this.y = y;
}
sum(){
console.log(this.x+this.y);
}
}
class Son extends Father{
constructor(x,y){
super(x,y) //调用了父类的构造函数
// super 必须要在this之前调用
this.x = x;
this.y = y;
}
constructor(){
console.log(this.x-this.y)
}
}
var Son = new Son(4,3);
son.constructor(); // 输出 1
son.sum(); // 输出 7
注意:
-
在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
-
类里面的共有的属性和方法一定要加 this 使用
-
类里面 this 的指向问题
constructor 里面的 this 指向的是创建的实例对象
方法里面的 this 指向的是调用者
🍊Tab 栏切换案例
抽象对象:Tab 对象
- 该对象具有切换功能
- 该对象具有添加功能
- 该对象具有删除功能
- 该对象具有修改功能
class Tab{
var that;
constructor(id){
that = this;
// 获取元素
this.main = document.querySelector(id);
this.lis = this.main.querySelectorAll('li');
this.sections = this.main.querySelectorAll('section');
this.add = this.main.querySelectorAll('section');
// li 的父元素
this.ul = this.main.querySelector('.fisrstnav ul:first-child');
// section 的父元素
this.fsection = this.main.querySelector('.tabscon');
// 方法可以直接放在构造器中,因为在外面 new 了Tab 这个实例对象会调用
// 构造器里面的方法
this.init();
}
// 初始化
init(){
this.updateNode();
// init 初始化操作让相关的元素绑定事件
this.add.onclick = this.addTab;
for (var i = 0;i<this.lis.length;i++){
this.lis[i].index = i;
// 这里 toggleTab 后面不需要加小括号,否则页面加载后就直接
// 调用了;不加小括号就是点击之后才调用
this.lis[i].onclick = this.toggleTab;
this.remove[i].onclick = this,removeTab;
this.spans[i].ondbclick= this.editTab;
this.sections[i].ondbclick= this.editTab;
}
}
// 获取所有的小 li 和 section
updateNode(){
this.lis = this.main.querySelectorAll('li');
this.sections = this.main.querySelectorAll('section');
this.remove = this.main.querySelectorAll('.icon-guanbi');
this.spans = this.main.querySelectorAll('.firstnav li span:first-child');
}
// 1.切换功能
toggleTab(){
this.clearClass();
this.className = 'liactive';
that.sections[this.index].className = 'concative'
}
// 清空样式
clearClass(){
for (var i = 0;i<this.lis.length;i++){
this.lis[i].className = '';
this.sections[i].className = '';
}
}
// 2.添加功能
addTab(){
that.clearClass();
// 创建 li 元素 和 section 元素
let li = `<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi"></span></li>`;
let section = `<section class="conactive">测试1</section>`;
// 把这两个元素追加到对应的父元素中
// insertAdjacentHTML 方法可以将 字符串添加到父元素中
// beforenend 表示在父元素的最后面追加
that.ul.insertAdjacentHTML('beforeend',li);
that.fsection.insertAdjacentHTML('beforeend',section);
that.init();
}
// 3.删除功能
removeTab(e){
// 防止冒泡,阻止 tab 切换效果
// 使用stopPropagation()函数可以阻止当前事件向祖辈元素的冒泡传递,也就是说该事件不会触 // 发执行当前元素的任何祖辈元素的任何事件处理函数。
e.stopPropagation();
// remove 没有索引号,可以用remove 父亲的索引号
let index = this.parentNode.index;
// remove 方法可以直接删除指定的元素
that.lis[index].remove();
that.sections[index].remove();
that.init();
// 当我们删除的不是选中状态的 li 则只需要删除 li 不改变选中状态
if(document.querySelector('.liactive')) return;
// 当我们删除了选中状态的li 的时候,让它前一个li 处于选中状态
index--;
// click() 这个方法手动调用我们的点击事件,不需要用鼠标触发
// that.lis[index] && that.lis[index].click(); 前面为真则执行点击事件
// 否则不执行
that.lis[index] && that.lis[index].click();
}
// 4.修改功能
editTab(){
// 获取原本的内容
var str = this.innerHTML;
// 双击机制选定文字
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
this,innerHTML = '<input type="text"/>'
var input = this.children[0];
input.value = str;
input.select(); // 让文本框的文字处于选中状态
// 当离开文本框(失去焦点)就把文本框里面的值给 span
input.onblur = function(){
this.parentNode.innerHTML = this.value
}
// 按下回车也可以吧文本框里面的值给 span
input.onkeyup = function(e){
if(e.keyCode === 13){
// 手动调用表单失去焦点事件 不需要鼠标离开操作
this.blur();
}
}
}
}
new Tab('#tab')
🍊构造函数和原型
🍊概述
创建对象的三种方式:
-
对象字面量
var obj2 = {}
-
new Object()
var obj2 = new Object();
-
自定义构造函数
function Star(uname,age){ this.uname = uname; this.age = age; this.sing = function(){ console.log('我会唱歌') } } var ldh = new Star('刘德华',18); console.log(ldh)
new 在执行时会做四件事情
- 在内存中创建一个新的空对象
- 让 this 指向这个新对象
- 执行构造函数里面的代码,给这个新对象添加属性和方法
- 返回这个新对象(所以构造函数里面不需要 return)
🍊构造函数
- 实例成员- 就是构造函数内部通过 this 添加的成员
- 实例成员只能通过实例化的对象来访问
console.log(ldh.uname)
- 不可以通过构造函数来访问实例成员
console.log(Star.uname)
- 实例成员只能通过实例化的对象来访问
- 静态成员- 在构造函数本身上添加成员
Star.sex = '男';
这就是一个静态成员- 只能通过构造函数来访问
console.log(Star.sex)
- 不能通过对象来访问
console.log(ldh.sex) “不行”
🍊构造函数的问题
- 同一个函数放在不同构造函数,会浪费内存空间
🍊构造函数原型 prototype
-
构造函数通过原型分配的函数时所有对象所共享的
-
javaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。注意这个 prototype 就是一个对象。这个对象的所有方法和属性,都会被构造函数所拥有
-
可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法了
Star.prototype.sing = function(){
console.log('我会唱歌')
}
ldh.sing(); // 输出: 我会唱歌
zxy.sing(); // 输出: 我会唱歌
问答:
1. 原型是什么?
是一个对象,我们也称为 prototype 为原型对象
2. 原型的作用是什么?
共享方法
🍊对象原型 __proto__
- 对象都会有一个属性
__proto__
,指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数的属性和方法,就是因为对象有__proto__
原型的存在 __proto__
对象原型和原型对象prototype
是等价的- 方法的查找规则:首先看看 ldh 对象上是否有 sing 方法,如果有就执行这个对象上的 sing 方法;如果没有 sing 这个方法,因为有
__proto__
的存在,就去构造函数原型对象 prototype 身上去查找 sing 这个方法 - 注意:
__proto__
对象的原型的意义在于为对象的查找机制提供一个方向,或者说一条线路,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
Star.prototype === ldh.__proto__ // 返回 TRUE
🍊constructor 构造函数
- 对象原型(
__proto__
)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性,constructor 我们称为构造函数,因为他指向构造函数本身 - constructor 主要用于记录对象引用哪个构造函数,它可以让原型对象重新指向原来的构造函数
Star.prototype = {
// 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利 用constructor指回原来的构造函数
construtor:Star,
sing:function(){
console.log('我会唱歌')
},
movie:function(){
console.log('我会演电影')
}
}
var ldh = new Star('刘德华',18);
var zxy = new Star('张学友',19);
console.log(Star.prototype.construtor);
console.log(ldh.__proto__.construtor);
🍊原型链
🍊js 中成员查找机制(规则)
- 从 ldh 对象实例中 层层往上找,如下图所示
- 优先级 (最下层优先),若在找到了该对象 则不会继续往上找
🍊原型对象的this指向
- 原型对象里面的 this 指向的是实例对象
var that;
Star.prototype.sing = function(){
console.log('我会唱歌');
that = this
}
var ldh = new Star('刘德华',18)
ldh.sing();
console.log(that === ldh) // 打印: true
🍊扩展内置对象
Array.prototype.sum = function(){
var sum = 0;
for(var i = 0;i<this.length;i++){
sum += this[i]
}
return sum;
}
var arr = [1,2,3]
console.log(arr.sum()); // 6
var arr1 = new Array(11,22,33);
console.log(arr1.sum());
🍊继承
🍊 call()
调用这个函数,并修改函数运行时的 this 指向
fun.call(thisArg,arg1,arg2,...);
// 含有几个参数
// 1. thisArg: 当前调用函数 this 指向对象
// 2.arg1,arg2: 函数需要传递的参数
几个作用:
-
调用函数
例:
fn.call();
-
修改函数里的this指向
// 例如 function fn(){ console.log('我想喝手磨咖啡'); console.log(this) // this 指 向 window } var o = { name:'andy'; } fn.call() // 原本 fn 的this是指向调用者也是就是window this=>window fn.call(o)// this 指向o 此时fn函数里面的console.log(this) 会打印 o 这个对象 // ----------------------------- fn.call(0,1,2) // 后面两个参数是需要传进fn里面的参数,fn 需要接受这个参数
🍊继承
🍊继承属性
// 借用福构造函数继承属性
// 1. 父构造函数
function Father(uname,age){
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
// 2.子构造函数
function Son(uname,age){
// this 指向子构造函数的对象实例
// call 把父构造函数的 this 改成 指向子构造函数的 this
Father.call(this,uname,age);
}
var son = new Son('刘德华',18)
console.log(son)
🍊继承方法
// 借用福构造函数继承属性
// 1. 父构造函数
function Father(uname,age){
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
// 父构造函数的原型对象
Father.prototype.money = function(){
console,log(10000)
}
// 2.子构造函数
function Son(uname,age){
// this 指向子构造函数的对象实例
// call 把父构造函数的 this 改成 指向子构造函数的 this
Father.call(this,uname,age);
}
// Son.prototype = Father.prototype 这样直接会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
// 一般这么采用,实例化一个 Father 对象 跟原来的构造函数分格开来
Son.prototype = new Father();
// 如果利用对象的形式修改了原型对象,别忘了利用 constructor 指回原来的原型对象
Son.prototype.constructor = Son;
var son = new Son('刘德华',18)
console.log(son)
🍊es5 中新增的方法
🍊数组方法
-
foreach 方法
-
遍历这个数组
-
var arr = [1,2,3] arr.forEach(function(value,index,array){ console.log('每个数组元素'+value); console.log('每个数组元素的索引号'+index); console.log('数组本身'+array) })
-
-
filter 筛选数组
-
筛选数组
-
var arr = [12,66,4,88]; var newArr = arr.filter(function(value,index){ return value >= 20; }) console.log(newArr) // 打印 [66,88]
-
-
**some **
-
检测数组中的元素是否满足条件,返回布尔值
-
var arr = [10,30,4] arr.some(function(value){ return value >= 20; }) console.log(flag); // 返回 TRUE
-
some 和 forEach 的区别
- 在 forEach 里面 return 不会终止迭代
- 在 some 里面 遇到了return true 就是终止遍历
- filter 也不会终止迭代
🍊字符串方法
str.trim()
- 该方法会从一个字符串的两端删除空白字符
- 中间的空格不会去除
- trim() 方法不影响字符串本身,它返回的是一个新的字符串
🍊函数的进阶
🍊函数的定义和调用
🍊函数的定义
-
// 自定义函数 (命名函数) function fn(){};
-
// 函数表达式(匿名函数) var fun = function(){};
-
// 利用 new Function('参数1','参数2','函数体') var f = new Function();
🍊函数的调用
-
// 普通函数 function fn(){ console.log('人生的巅峰') }
-
// 对象的方法 var o = { sayHi:function(){ console.log('人生的额巅峰') } } 0.sayHi();
-
// 构造函数 function Star(); new Star();
-
// 绑定事件函数 btn.onclick = function(){};
-
// 计时器函数 setInterval(function(){},1000); // 这个函数时定时器自动1秒钟调用1次
-
// 立即执行函数 (function(){ console.log('人生的巅峰') })();
🍊this
-
// 普通函数 this指向 window function fn(){ console.log('人生的巅峰') }
-
// 对象的方法 this 指向该方法的所属对象 var o = { sayHi:function(){ console.log('人生的额巅峰') } } o.sayHi();
-
// 构造函数 this 指向实例对象,原型对象里面的方法也指向实例对象 function Star(); new Star();
-
// 绑定事件函数 this 指向 绑定事件对象 btn.onclick = function(){};
-
// 计时器函数 this 指向 window setInterval(function(){},1000); // 这个函数时定时器自动1秒钟调用1次
-
// 立即执行函数 this指向 window (function(){ console.log('人生的巅峰') })();
改变函数内部的this指向
- call 方法
- apply 方法
- 调用函数
- 可以改变函数内部的 this 指向
- 参数必须是数组(伪数组)
🍊严格模式
🍊什么是严格模式
-
为脚本开启严格模式
为整个脚本开启严格模式,需要在所有语句之间放一个特定语句“use strict”;(或 ‘use strict’;)。 -
为某个函数开启严格模式
🍊严格模式的变化
-
严格模式下必须先声明再使用
-
严禁删除已经声明过的变量
-
严格模式下的this指向
严格模式下全局作用域中的函数的
this
是undefined
-
严格模式下,如果构造函数不加
new
调用,this
会报错 -
new
实例化构造函数指向创建的对象实例 -
定时器
this
指向window
-
事件、对象还是指向调用者
🍊函数的变化
- 不能有重名的参数
🍊高阶函数
- 高阶函数是对其他函数进行操作的函数,它接受函数作为参数或将函数作为返回值输出----- 函数可以作为参数传递
<script>
// 高阶函数- 函数可以作为参数传递
function fn(a,b,callback){
console.log(a+b);
callback && callback();
}
fn(1,2,function(){
console.log('我是最后调用的')
})
</script>
🍊闭包
什么是闭包
- 闭包是指有权访问另一个函数作用域中变量的函数
- 简单理解就是,一个作用域可以访问另外一个函数内部的局部变量
// 闭包:我们fun 这个函数作用域 访问了另一个函数 fn 里面的局部变量 num
function fn(){
var num = 10;
function fun(){
console.log(num);
}
fun();
}
fn(); // 打印 10
闭包的作用
- 延伸了变量的作用范围
// 作用: 我们 fn 外面的作用域可以访问 fn 内部的局部变量
// 延伸了变量的作用范围
function fn(){
var num = 10;
function fun(){
console.log(num);
}
return fun;
}
var f = fn();
f(); // 打印 10
🍊浅拷贝和深拷贝
- 浅拷贝知识拷贝一层,更深层次对象级别的只拷贝引用
- 深拷贝拷贝多层,每一级别的数据都会拷贝
🍊浅拷贝
- 方法:for in 循环
// 只拷贝一层
var obj = {
id:1,
name:"andy",
}
var o = {};
for(var k in obj){
o[k] = obj[k];
}
console.log(o);
// 若有多层数据进行浅拷贝,修改拷贝后的数据 只是把obj的地址给了 o 如果修改了 o 里面的值, obj的值也会改变
// 例如:
var obj = {
id:1,
name:'andy',
msg:{
age:18
}
}
var o = {};
for(var k in obj){
o[k] = obj[k];
}
// 修改多层结构里面的数据
o.msg.age = 20;
console.log(o);
console.log(obj);
// 上面这两个对象相等
// ES6 中浅拷贝的方法 Object.assgin(o,obj)
🍊深拷贝
- 方法 for in 循环 + 递归
// 拷贝多层
var obj = {
id:1,
name:"andy",
msg:{
age:18
},
color:['pink','red']
}
var o = {};
// 封装函数
function deepCopy(newobj,oldobj){
for(var k in oldobj){
// 判断属性值数据哪种数据类型
// 1.获取属性值
var item = oldobj[k];
// 2.判断这个值是否是数组
if (item instanceof Array){
newobj[k] = [];
deepCopy(newobj,oldobj)
}else if(item instanceof Object){
// 3.判断这个值是否是对象
newobj[k] = {};
deepCopy(newobj,oldobj)
}else{
// 4.属于简单数据类型
newobj[k] = item;
}
}
}
deepCopy(o,obj)
console.log(o);