js语言中,生成实例对象的传统方法就是通过构造函数:
function Student(name, number) {
this.name = name
this.number = number
}
Student.prototype.sayHi = function() {
console.log('姓名 ' + this.name + ', 学号' + this.number)
}
var xialuo = new Student('夏洛', 100)
console.log(xialuo.name) // 夏洛
console.log(xialuo.number) // 100
xialuo.sayHi() // 姓名 夏洛 , 学号 100
ES6提供了class(类)
这个概念,作为对象的模板。通过class关键字,可以定义类
;
基本上,ES6的class
可以看做知识一个语法糖
,它的绝大部分功能,ES5都可以看到,新的class写法让对象原型的写法更加清晰,上面的代码用class改写:
class Student {
constructor(name, number) {
this.name = name
this.number = number
}
sayHi() {
console.log(`姓名 ${this.name} , 学号 ${this.number}`)
}
}
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name) // 夏洛
console.log(xialuo.number) // 100
xialuo.sayHi() // 姓名 夏洛 , 学号 100
class
定义了一个Student
类,他里面有constructor方法
,这就是构造方法
;而this
关键字则代表实例对象
,
也就是说,ES5的构造函数Student,对应ES6的Student类的构造方法; Student类除了构造方法,还定义了一个sayHi方法,定义类的方法的时候,方法之间不需要逗号分隔
;
构造函数的prototype属性,在ES6的类上继续存在,实际上,类的所有方法
都定义在类的prototype属性
上面;
constructor方法是类的默认方法,通过new 命令生成对象实例时,自动调用该方法,一个类必须有constructor方法
,如果没有显示定义,一个空的constructor方法会被默认添加
constructor方法默认返回实例对象(即this)
class Student{}
// 等同于 定义了一个空的类Student,JavaScript 引擎会自动为它添加一个空的constructor方法。
class Student{
constructor() {}
}
生成的实例对象的写法,与ES一样都是使用new命令,实例的属性除非显示定义在其本身(即定义在this对象上),否则都是定义在原型上
class Student {
constructor(name, number) {
this.name = name
this.number = number
}
sayHi() {
console.log(`姓名 ${this.name} , 学号 ${this.number}`)
}
}
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name) // 夏洛
console.log(xialuo.number) // 100
xialuo.sayHi() // 姓名 夏洛 , 学号 100
xialuo.hasOwnProperty('name') // true
xialuo.hasOwnProperty('number') // true
xialuo.hasOwnProperty('sayHi') // true
name和number都是实例对象Student 自身的属性(因为定义在this变量上),所以hasOwnProperty方法返回true,而sayHi是原型对象的属性(因为定义在Student 类上),所以hasOwnProperty()方法返回false,这些都是ES5的行为保持一致;
这个和ES5完全不一样
new Student() // Cannot access 'Student' before initialization
class Student{}
Foo类使用在前,定义在后,这样会报错,因为 ES6 不会把类的声明提升到代码头部。这种规定的原因与下文要提到的继承有关,必须保证子类在父类之后定义。
与 ES5 一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
class Student {
constructor(name, number) {
this.name = name;
this.number = number;
}
get like() {
return "like";
}
set like(value) {
console.log(`set ${value}`);
}
sayHi() {
console.log(`姓名 ${this.name} , 学号 ${this.number}`);
}
}
const xialuo = new Student("夏洛", 100);
xialuo.like; // 'like'
xialuo.like = "math"; // 'set math' 'math'
like属性有对应的存值函数和取值函数,因此赋值和读取行为都被自定义了。
类相当于实例的原型
,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字
,就表示该方法不会被实例继承
,而是直接通过类来调用,这就称为“静态方法
”
ES6 明确规定,Class 内部只有静态方法,没有静态属性。
class Student{
static sayHi(){
console.log('hi')
}
}
Student.sayHi() // 'hi'
const xialuo = new Studeng()
xialuo.sayHi() // xialuo.sayHi is not a function
Student类的sayHi方法前有static关键字,表明该方法是一个静态方法,可以直接在Foo类上调用(Student.sayHi()),而不是在Student类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。
注意,如果静态方法包含this关键字
,这个this指的是类
,而不是实例。
父类的静态方法,可以被子类继承。
class People{
static sayHi() {
return 'hi';
}
}
class Student extends People{
}
Student .sayHi() // 'hi'
Class可以通过extends关键字
实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多;
通过extends关键字,子类继承了父类的所有属性和方法
// 父类
class People {
constructor(name) {
this.name = name
}
eat() {
console.log(`${this.name} eat something`)
}
}
// 子类
class Student extends People {
constructor(name, number) {
super(name) // 调用父类People的构造函数constructor(name)
this.number = number
}
sayHi() {
console.log(`姓名 ${this.name} , 学号 ${this.number}`)
}
}
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name) // 夏洛
console.log(xialuo.number) // 100
xialuo.sayHi() // 姓名 夏洛 , 学号 100
super这个关键字,既可以当作函数使用,也可以当作对象使用,
第一情况是:super当作函数调用时,代表父类的构造函数,ES6要求,子类的构造函数必须执行一个super函数;
第二种情况,super作为对象时,在普通方法中,指向父类的原型对象,在静态方法中,指向父类;ES6 规定,通过super调用父类的方法时,super会绑定子类的this
子类必须在constructor方法中调用super方法
,否则新建实例时会报错,这是因为子类没有自己的this对象
,而是继承父类的this对象
,然后对其进行加工
,如果不调用super方法,子类就得不到this对象;
super
虽然代表了父类Father的构造函数
,但是返回的是子类Student的实例
,即super内部的this指向的是People ,因此super()在这里相当于People .constructor.call(this);
而且作为函数时
,super()只能用在子类的构造函数中,在其他地方会报错;
// 父类
class People {
constructor(name) {
this.name = name
}
eat() {
console.log(`${this.name} eat something`)
}
}
// 子类
class Student extends People {
constructor(name, number) {
super(name) // 调用父类People的构造函数constructor(name)
this.number = number
}
sayHi() {
console.log(`姓名 ${this.name} , 学号 ${this.number}`)
}
}
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name) // 夏洛
console.log(xialuo.number) // 100
xialuo.sayHi() // 姓名 夏洛 , 学号 100
// 子类
class Teacher extends People {
constructor(name, subject) {
super(name) // 调用父类People的构造函数
this.subject = subject
}
teach () {
console.log(`${this.name} 教授 ${this.subject}`)
}
}
const wanglaoshi = new Teacher('王老师', '语文')
console.log(wanglaoshi.name) // 王老师
console.log(wanglaoshi.subject) // 语文
wanglaoshi.teach() // 王老师 教授 语文
基于原型的执行规则
class的原型本质
// 父类
class People {
constructor(name) {
this.name = name
}
eat() {
console.log(`${this.name} eat something`)
}
}
// 子类
class Student extends People {
constructor(name, number) {
super(name) // 调用父类People的构造函数constructor(name)
this.number = number
}
sayHi() {
console.log(`姓名 ${this.name} , 学号 ${this.number}`)
}
}
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name) // 夏洛
console.log(xialuo.number) // 100
xialuo.sayHi() // 姓名 夏洛 , 学号 100
xialuo.__proto__ === Student.prototype // true
Student.prototype.__proto__ === People.prototype// true
People.prototype.__proto__ === Object.prototype // true
xialuo instanceof Student // true
xialuo instanceof People // true
xialuo instanceof Object // true
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>jQuery-testtitle>
head>
<body>
<p>这是个p标签1p>
<p>这是个p标签2p>
<p>这是个p标签3p>
<script>
class jQuery {
constructor(selector) {
const result = document.querySelectorAll(selector);
const length = result.length;
for (let i = 0; i < length; i++) {
this[i] = result[i];
}
this.length = length;
this.selector = selector;
}
get(index) {
return this[index];
}
each(fn) {
for (let i = 0; i < this.length; i++) {
const elem = this[i];
fn(elem);
}
}
on(type, fn) {
return this.each((elem) => {
elem.addEventListener(type, fn, false);
});
}
}
// const $p = new jQuery('p')
// $p.get(1)
// $p.each((elem) => console.log(elem.nodeName))
// $p.on('click', () => alert('clicked'))
// 插件
jQuery.prototype.dialog = function (info) {
alert(info);
};
// const $p = new jQuery('p')
// $p.dialog('dialog')
// "造轮子"
class myJQuery extends jQuery {
constructor(selector) {
super(selector);
}
// 扩展自己的方法
addClass(className) {
this.each((elem) => elem.classList.add(className));
}
style(data) {
// 相关操作
}
}
// const $p = new myJQuery('p')
// $p.addClass('test')
script>
body>
html>
谢谢你阅读到了最后
期待你,点赞、评论、交流