1. ES6 Class 類(lèi)
ES6 提供了更接近傳統(tǒng)語(yǔ)言的寫(xiě)法,引入了 Class(類(lèi))這個(gè)概念,作為對(duì)象的模板。通過(guò)class關(guān)鍵字,可以定義類(lèi)。
ES6 的class可以看作只是一個(gè)語(yǔ)法糖,它的絕大部分功能,ES5 都可以做到,新的class寫(xiě)法只是讓對(duì)象原型的寫(xiě)法更加清晰、更像面向?qū)ο缶幊痰恼Z(yǔ)法而已。
1.1 . 復(fù)習(xí)構(gòu)造函數(shù)
類(lèi)的功能是創(chuàng)建對(duì)象的,js之前實(shí)現(xiàn)類(lèi)的功能需要利用構(gòu)造函數(shù)模擬類(lèi)的功能,通過(guò)new操作符實(shí)例化一個(gè)對(duì)象,對(duì)象上的原型[[Prototype]]上有個(gè)constructor方法指向構(gòu)造函數(shù)
function person(name,age) { //構(gòu)造函數(shù)
this.name=name;
this.age=age
}
person.prototype.sayName = function(){ //原型方法
console.log(`我的名字叫${this.name}。`)
}
person.prototype.sayAge = function(){ //原型方法
console.log(`我今年${this.age}歲了`)
}
person.run=function(){ //靜態(tài)方法
console.log('靜態(tài)方法')
}
let student =new person('xiaoming',18)
console.log(student);
console.log(student.sayAge());
console.log(student.sayName());
console.log(person.run()); //靜態(tài)方法需要使用構(gòu)造函數(shù)調(diào)用
但是之前講了一個(gè)合并對(duì)象的,那么我們可以將新增的方法放在一個(gè)對(duì)象里合并到原型上
<script type='module'>
let Person = class { //類(lèi)名為Person的ES6的類(lèi)的聲明
//之前es5寫(xiě)的類(lèi)功能分為構(gòu)造函數(shù),原型方法,靜態(tài)方法,最終實(shí)例化,都是分開(kāi)的?,F(xiàn)在es6實(shí)現(xiàn)了整體的概念,將他們羅列到一個(gè)大括號(hào)容器里,其不是代碼塊,也不是對(duì)象,里面不加逗號(hào)。
constructor(name, age) { //構(gòu)造函數(shù)
this.name = name;
this.age = age;
this.aa = function () { //實(shí)例對(duì)象上的方法,生成到實(shí)例對(duì)象上的方法
console.log(這是實(shí)例方法);
}
};
//除了constructor方法、使用關(guān)鍵字static的方法,其他方法都是原型上的方法
sayName() { //原型方法
console.log(`我的名字叫${this.name}。`)
};
sayAge() { //原型方法
console.log(`我今年${this.age}歲了`)
};
static run() { //關(guān)鍵字static 寫(xiě)的方法是靜態(tài)方法
console.log(`這是靜態(tài)方法`);
}
}
let student = new Person('李四', 20); //通過(guò)類(lèi)實(shí)例化對(duì)象時(shí)本質(zhì)上執(zhí)行類(lèi)內(nèi)部的constructor函數(shù)
console.log(student);
student.sayAge()
console.dir(Person) //對(duì)象方式打印這個(gè)類(lèi),可以看到靜態(tài)方法
Person.run() //在這種類(lèi)里靜態(tài)方法只能通過(guò)類(lèi)名調(diào)用
console.log(typeof Person); //通過(guò)typeof檢測(cè)類(lèi)名結(jié)果是function,因此class聲明類(lèi)實(shí)際上是創(chuàng)建了一個(gè)具有構(gòu)造函數(shù)方法行為的函數(shù)
console.log(Person.prototype);
</script>
<script type='module'>
let Person = class { //匿名類(lèi)的表達(dá)式賦值給變量
}
</script>
1.2 ES6 變形的類(lèi)Class
// 這是類(lèi) 不是json 所以里面不需要加逗號(hào)
class Person{
// 這是一個(gè)構(gòu)造方法(函數(shù)),只要new Person() 這個(gè)構(gòu)造函數(shù)會(huì)自動(dòng)執(zhí)行
constructor(name,age){
console.log(`構(gòu)造函數(shù)執(zhí)行了,name:${name},age:${age}`)
}
//以下是構(gòu)造函數(shù)原型上的方法
showName(){
return `名字:${this.name}`;
} // 不需要加逗號(hào),加逗號(hào)就報(bào)錯(cuò)
showAge(){
return `年齡:${this.age}`;
}
}
// 這個(gè)Person也是要給函數(shù),typeof Person 打印function
let p1 = new Person("wuwei",18);
console.log(p1.showName());
console.log(p1.showAge());
表達(dá)式的類(lèi)(不推薦)
var Person = class {
constructor(){
this.name = "wuwei";
}
showName(){
return `名字:${this.name}`
}
}
2. constructor
constructor方法是類(lèi)的默認(rèn)方法,通過(guò)new命令生成對(duì)象實(shí)例時(shí),自動(dòng)調(diào)用該方法。一個(gè)類(lèi)必須有constructor方法,如果沒(méi)有顯式定義,一個(gè)空的constructor方法會(huì)被默認(rèn)添加。
class Fn {
}
// 等同于
class Fn {
constructor() {}
}
constructor方法默認(rèn)返回實(shí)例對(duì)象(即this),完全可以指定返回另外一個(gè)對(duì)象。
class Foo {
constructor() {
return Object.create(null);
}
}
new Foo() instanceof Foo
// false
//constructor函數(shù)返回一個(gè)全新的對(duì)象,結(jié)果導(dǎo)致實(shí)例對(duì)象不是Foo類(lèi)的實(shí)例。
3. 類(lèi)必須使用new調(diào)用
類(lèi)必須使用new調(diào)用,否則會(huì)報(bào)錯(cuò)。這是它跟普通構(gòu)造函數(shù)的一個(gè)主要區(qū)別,后者不用new也可以執(zhí)行。
class Foo {
constructor() {
return Object.create(null);
}
}
Foo()
// TypeError: Class constructor Foo cannot be invoked without 'new'
4. 方法名字可以是變量
var aaa = 'wu';
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
[aaa]() {
return `此時(shí)屬性名是一個(gè)變量,就是wu`
}
}
// console.log(typeof Person)
let p1 = new Person("wuwei", 18);
console.log(p1.wu()); // 此時(shí)不能通過(guò)aaa調(diào)用函數(shù),aaa是變量,通過(guò)wu調(diào)用,可以通過(guò)[aaa]調(diào)用 p1[aaa]();
console.log(p1[aaa]());
這里就可以發(fā)現(xiàn)屬性名可以為表達(dá)式
var aa = 'wu';
var bb = 'wei';
var obj = {
[aa + bb]: "無(wú)為"
}
5. ES6 class類(lèi)沒(méi)有提升和預(yù)解析的功能
也就是說(shuō)如果寫(xiě)法會(huì)報(bào)錯(cuò)
var p1 = new Person()
console.log(p1);
class Person{
constructor(){
this.name = "aaa"
}
}
但是以前通過(guò)ES5 構(gòu)造函數(shù)模擬類(lèi)是可以變量提升的,因?yàn)楸旧砭褪呛瘮?shù)
// 這種寫(xiě)法構(gòu)造函數(shù)會(huì)提升,正常打印一個(gè)對(duì)象
var p1 = new Person()
console.log(p1);
function Person(){
this.name = "aaa"
}
6. get set
取值函數(shù) get 存值函數(shù) set
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
get aa(){
console.log(`成功獲取a的值`)
return this.age;
}
set aa(val){
console.log(`成功設(shè)置a的值為${val}`);
this.age = val;
}
}
var p1 = new Person('wuwei',18)
7.靜態(tài)方法
類(lèi)相當(dāng)于實(shí)例的原型,所有在類(lèi)中定義的方法,都會(huì)被實(shí)例繼承。
如果在一個(gè)方法前,加上static關(guān)鍵字,就表示該方法不會(huì)被實(shí)例繼承,而是直接通過(guò)類(lèi)來(lái)調(diào)用,這就稱(chēng)為“靜態(tài)方法”。
ES6 明確規(guī)定,Class 內(nèi)部只有靜態(tài)方法,沒(méi)有靜態(tài)屬性。
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
showName(){
return `這是通過(guò)構(gòu)造對(duì)象調(diào)用方法`;
}
static aaa(){
return `這是靜態(tài)方法,是通過(guò)類(lèi)來(lái)調(diào)用的方法`;
}
}
var p1 = new Person('wuwei',18)
console.log(p1.showName);
console.log(Person.aaa());
8. 繼承
子類(lèi)繼承父類(lèi)
8.1 之前的子類(lèi)繼承父類(lèi)
// 父類(lèi)
function Person(name){ //構(gòu)造函數(shù)
this.name = name;
}
Person.prototype.showName = function(){ //構(gòu)造函數(shù)原型對(duì)象上的方法
return `名字是:${this.name}`;
}
// 子類(lèi)
function Student(name,skill){
Person.call(this,name); // 繼承父類(lèi)的屬性 處理構(gòu)造函數(shù)的繼承,調(diào)用構(gòu)造函數(shù)的方法,同時(shí)改變this指向
this.skill = skill;
}
Student.prototype = new Person; // 繼承父類(lèi)的方法,繼承原型鏈
var stu = new Student("wuwei","逃課");
console.log(stu);
console.log(stu.name);
console.log(stu.showName())
8.2 ES6 類(lèi)的繼承 extends
class Person{ //基類(lèi)
constructor(name){
this.name = name;
}
}
class Student extends Person{ //派生類(lèi) es6的繼承通過(guò)關(guān)鍵字extends
// constructor(name){ //這種繼承 沒(méi)法添加自己擴(kuò)展的屬性和方法,添加就報(bào)錯(cuò)
// this.name = name;
// }
}
let stu = new Student('張三')
console.log(stu);
以上這種繼承 沒(méi)法添加自己擴(kuò)展的屬性和方法,添加就報(bào)錯(cuò)
通過(guò)super()擴(kuò)展自己的屬性和方法,super就是調(diào)用基類(lèi)中的構(gòu)造函數(shù)
super注意點(diǎn):
1、super只可在派生類(lèi)的構(gòu)造函數(shù)中使用,如果嘗試在非派生類(lèi)(未使用extends的類(lèi))或函數(shù)中使用則會(huì)導(dǎo)致程序報(bào)錯(cuò)
2、在構(gòu)造函數(shù)中訪(fǎng)問(wèn)this之前一定要調(diào)用super(),因?yàn)閟uper()負(fù)責(zé)初始化this,如果在super之前訪(fǎng)問(wèn)this會(huì)導(dǎo)致報(bào)錯(cuò)
3、如果不想調(diào)用super(),則唯一的方法就是讓類(lèi)的構(gòu)造函數(shù)顯示返回一個(gè)對(duì)象
// 父類(lèi)
class Person{
constructor(name){
this.name = name;
}
showName(){
console.log('父類(lèi)的showName方法');
return `名字:${this.name}`;
}
}
// 子類(lèi)
class Student extends Person{
constructor(name,skill){
super(name); // 先執(zhí)行父類(lèi)的屬性,繼承后在設(shè)置自己的屬性
this.skill = skill;
}
// 繼承父類(lèi)的方法同時(shí)擴(kuò)展內(nèi)容
showName(){
super.showName(); // 先執(zhí)行父類(lèi)的showName方法
// 在設(shè)置自己的showName方法
console.log('執(zhí)行了子類(lèi)里面的showName');
}
// 設(shè)置自己的方法
showSkill(){
return `技能:${this.skill}`
}
}
var stu = new Student("wuwei","逃課");
console.log(stu.name);
console.log(stu.showName())
<script type='module'>
class Person {
constructor(name) {
this.name = name;
}
}
class Student extends Person {
constructor(name, age) {
super(name) ; //super繼承基類(lèi)中的中的構(gòu)造函數(shù),修正初始化this,不能在super之前使用this
// this.name = name; //這個(gè)this.name不是繼承了父類(lèi)的this.name,不寫(xiě)是undefined。應(yīng)該把那么放到super中,相當(dāng)于擴(kuò)展,執(zhí)行super相當(dāng)于執(zhí)行基類(lèi)的構(gòu)造函數(shù)
this.age = age;
// return { name, age }
}
}
let stu = new Student('張三', 18)
console.log(stu);
</script>
類(lèi)方法的遮蔽,派生類(lèi)中的方法總會(huì)覆蓋基類(lèi)中的同名方法
//遮蔽
let a =10;
function aa(){
let a =20;
function bb(){
let a =30;
console.log(a);
}
bb() //如果aa() bb()不執(zhí)行a是沒(méi)有值的
}
aa()
//派生類(lèi)遮蔽基類(lèi)中的方法
class Person {
constructor(name) {
this.name = name;
}
sayName() {
return (`'基類(lèi)'中的${this.name}`); //基類(lèi) 原型對(duì)象的原型上的方法
}
}
class Student extends Person {
constructor(name, age) {
super(name);
this.age = age;
}
sayName() {
return (`'派生類(lèi)'中的${this.name}`); //派生類(lèi) 原型對(duì)象上的方法
}
}
let stu = new Student('張三', 18)
console.log(stu); //控制臺(tái)能看到原型上的方法 原型的原型sayName()方法
console.log(stu.sayName());
如果想調(diào)用基類(lèi)中的方法,則可以通過(guò)super在派生類(lèi)同名方法中手動(dòng)調(diào)用基類(lèi)方法
//派生類(lèi)擴(kuò)展基類(lèi)中的方法(調(diào)用基類(lèi)的方法)
class Person {
constructor(name) {
this.name = name;
}
sayName() {
console.log(`'基類(lèi)'中的${this.name}`);
}
}
class Student extends Person {
constructor(name, age) {
super(name);
this.age = age;
}
sayName() {
console.log(super.sayName()); //super超級(jí)的意思,代表的就是基類(lèi)。通過(guò)super取到基類(lèi)中的sayName(),先執(zhí)行基類(lèi)中的方法,再執(zhí)行派生類(lèi)中的同名方法
console.log(`'派生類(lèi)'中的${this.name}`);
}
}
let stu = new Student('張三', 18)
console.log(stu.sayName());
拖拽例子:
<style>
* {
margin: 0;
padding: 0;
}
#box {
position: fixed;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<!-- es5的拖拽 -->
<div id="box"></div>
<script>
let dixTop = 0, dixLeft = 0;//定義兩個(gè)信號(hào)量,記住top和left的值
box.onmousedown = function (e) {// 有了值就可以綁定鼠標(biāo)點(diǎn)擊事件
console.log(e);
let dixL = e.clientX - dixLeft, dixT = e.clientY - dixTop; //鼠標(biāo)到元素左上角的距離= 鼠標(biāo)距離頁(yè)面的距離 - 元素距離頁(yè)面左上角的距離
console.log(dixL, dixT);
box.onmousemove = function (e) { //鼠標(biāo)移動(dòng)事件
dixLeft = e.clientX - dixL;
dixTop = e.clientY - dixT;
console.log(dixTop, dixLeft);
this.style = `top:${dixTop}px;left:${dixLeft}px`
}
box.onmouseup = function () { //鼠標(biāo)松開(kāi)時(shí)還是跟著拖動(dòng),需要解綁自己和移動(dòng)事件
box.onmouseup = null;
box.onmousemove = null
}
}
</script>
<style>
* {
margin: 0;
padding: 0;
}
#box {
position: fixed;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<!-- es5的拖拽 -->
<div id="box"></div>
<script>
//拖動(dòng)速度過(guò)快時(shí),瀏覽器來(lái)不及繪制,鼠標(biāo)離開(kāi)了元素,無(wú)法觸發(fā)拖動(dòng)事件。把拖動(dòng)事件綁到document 樣式style的this為box,就算鼠標(biāo)脫落了元素,元素也會(huì)一直跟著鼠標(biāo)
let dixTop = 0, dixLeft = 0;
box.onmousedown = function (e) {
console.log(e);
let dixL = e.clientX - dixLeft, dixT = e.clientY - dixTop;
console.log(dixL, dixT);
document.onmousemove = function (e) { //事件綁給document
dixLeft = e.clientX - dixL;
dixTop = e.clientY - dixT;
console.log(dixTop, dixLeft);
box.style = `top:${dixTop}px;left:${dixLeft}px` //修改元素box的樣式
}
document.onmouseup = function () { //解綁也要給document 里面為this
this.onmouseup = null;
this.onmousemove = null
}
}
</script>
<style>
* {
margin: 0;
padding: 0;
}
#box {
position: fixed;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
//將box修改為this的寫(xiě)法,更優(yōu)化了
let dixTop = 0, dixLeft = 0;
box.onmousedown = function (e) {
let dixL = e.clientX - dixLeft, dixT = e.clientY - dixTop;
console.log(dixL, dixT);
document.onmousemove = function (e) {
dixLeft = e.clientX - dixL;
dixTop = e.clientY - dixT;
console.log(dixTop, dixLeft);
this.style = `top:${dixTop}px;left:${dixLeft}px` //這里也修改為this,要讓它的函數(shù)強(qiáng)制修改this
}.bind(this)
document.onmouseup = function () {
this.onmouseup = null;
this.onmousemove = null
}
}
</script>
function Drag(className) {
this.dom = document.createElement('div');
this.dom.className = className;
document.body.appendChild(this.dom)
this.disX = 0;
this.disY = 0;
// 初始化
this.init()
}
Object.assign(Drag.prototype, {
// 初始化函數(shù)
init() {
this.dom.onmousedown = function (e) {
this.down(e)
document.onmousemove = this.move.bind(this)
document.onmouseup = this.up
}.bind(this)
},
down(e) {
// console.log(e.clientX, e.clientY)
this.disX = e.clientX - this.dom.offsetLeft;
this.disY = e.clientY - this.dom.offsetTop;
// console.log(this.disX, this.disY)
},
move(e) {
// console.log(this)
this.dom.style.left = e.clientX - this.disX + 'px';
this.dom.style.top = e.clientY - this.disY + 'px';
},
up(e) {
// console.log(this)
document.onmousemove = null;
document.onmouseup = null;
}
})
let left = new Drag('left')
ES6 的拖拽(ES5拖拽不太適合給多個(gè)元素綁定拖拽,會(huì)很亂)
<style>
* {
margin: 0;
padding: 0;
}
.abox,
.bbox {
position: fixed;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: pink;
}
.bbox {
background-color: skyblue;
}
</style>
</head>
<body>
<script>
class Drag { //類(lèi)
constructor(className) { //構(gòu)造函數(shù)
this.dom = document.createElement('div'); //創(chuàng)建div節(jié)點(diǎn)
this.dom.className = className; //節(jié)點(diǎn)上添加屬性lassName,也就是參數(shù)
document.body.appendChild(this.dom) //放到頁(yè)面中body元素中
this.disX = 0; //信號(hào)量用的是屬性,變量不適合多個(gè)元素的拖拽,會(huì)發(fā)生覆蓋
this.disY = 0;
// 初始化代碼
this.init()
}
// 初始化函數(shù)
init() {
this.dom.onmousedown = function (e) {
this.down(e) //差值
document.onmousemove = this.move.bind(this) //鼠標(biāo)移動(dòng)事件。不強(qiáng)制綁定this就是document
document.onmouseup = this.up //鼠標(biāo)抬起事件
}.bind(this) //強(qiáng)制綁定使內(nèi)部的this不再指向內(nèi)部的document,需要指向外部的this
}
down(e) { //計(jì)算差值
// console.log(e.clientX, e.clientY)
this.disX = e.clientX - this.dom.offsetLeft;
this.disY = e.clientY - this.dom.offsetTop;
// console.log(this.disX, this.disY)
}
move(e) {
// console.log(this)
this.dom.style.left = e.clientX - this.disX + 'px'; //元素屬性值+px
this.dom.style.top = e.clientY - this.disY + 'px';
}
up(e) {
// console.log(this)
document.onmousemove = null;
document.onmouseup = null;
}
}
let abox = new Drag('abox') //對(duì)象,所有的屬性都是對(duì)象自己控制
let bbox = new Drag('bbox')
// bbox{
// dom:<div class="bbox"></div>,
// disX:0,
// dixY:0
// }
</script>
通過(guò)繼承創(chuàng)建有邊界的拖拽,拖拽控制在邊界內(nèi)部
<style>
* {
margin: 0;
padding: 0;
}
.abox,
.bbox,
.right {
position: fixed;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: pink;
}
.bbox {
background-color: skyblue;
}
.right {
background-color: tomato;
}
</style>
</head>
<body>
<script>
class Drag {
constructor(className) {
this.dom = document.createElement('div');
this.dom.className = className;
document.body.appendChild(this.dom)
this.disX = 0;
this.disY = 0;
this.init()
}
init() {
this.dom.onmousedown = function (e) {
this.down(e)
document.onmousemove = this.move.bind(this)
document.onmouseup = this.up
}.bind(this)
}
down(e) {
this.disX = e.clientX - this.dom.offsetLeft;
this.disY = e.clientY - this.dom.offsetTop;
}
move(e) {
this.dom.style.left = e.clientX - this.disX + 'px';
this.dom.style.top = e.clientY - this.disY + 'px';
}
up(e) {
document.onmousemove = null;
document.onmouseup = null;
}
}
let abox = new Drag('abox')
let bbox = new Drag('bbox')
class LimitDrag extends Drag { //擴(kuò)展父類(lèi)的拖拽
move(e) {
super.move(e); //繼承父類(lèi)
this.dom.style.left = Math.max(0, this.dom.offsetLeft) + 'px'; //擴(kuò)展自己的邊界限定,不能超出邊界
this.dom.style.left = Math.min(window.innerWidth - this.dom.offsetWidth, this.dom.offsetLeft) + 'px';
this.dom.style.top = Math.max(0, this.dom.offsetTop) + 'px';
this.dom.style.top = Math.min(window.innerHeight - this.dom.offsetHeight, this.dom.offsetTop) + 'px';
}
}
let right = new LimitDrag('right') //擴(kuò)展的拖拽