了解ES5中对象概念的小伙伴应该知道,对象中有两个存储器属性,分别为 getter
和 setter
,它们是对象中某个属性的特性,并且可以通过 Object.getOwnPropertyDescriptor()
方法获得对象中某个属性的属性描述符
//查询Person.prototype中属性number的属性描述符
Object.getOwnPropertyDescriptor(Person.prototype, ‘number’)
/*
{
get: [Function: get number],
set: [Function: set number],
enumerable: false,
configurable: true
}
*/
因此,我们在 class
类中写的 get
和 set
函数只是设置了某个属性的属性特性,而不是该类的方法。
在 class
类中的方法都是写在原型上的,因此生成的实例对象可以直接调用。现在有一个关键字 static
,若写在方法的前面,则表示此方法不会被写在原型上,而只作为该类的一个方法,这样的方法叫做静态方法;相反,若没加关键字 static
的方法就叫做非静态方法
我们来看一下具体的例子
class Person {
show() {
console.log(‘我是非静态方法show’)
}
static show() {
console.log(‘我是静态方法show’)
}
static hide() {
console.log(‘我是静态方法hide’)
}
}
Person.show() // 我是静态方法show
var person = new Person()
person.show() // 我是非静态方法show
person.hide() /* person.hide()
^
TypeError: person.hide is not a function
*/
我们分析一下这个例子:
首先我们直接调用 Person
类的 show
方法,实际调用的就是有关键字 static
的 show
方法;
然后我们生成了一个实例对象 person
,然后调用 person
实例对象上的 show
方法,实际调用的就是没有关键字 static
的 show
方法,从这我们可以看出,静态方法和非静态方法可以重名;
最后我们调用了 person
实例对象上的 hide
方法,但报错了,因为在 class
类中,我们定义的是静态方法,即有关键字 static
的 hide
方法,也就是此方法没有被写进类的原型中,因而实例对象 person
无法调用此方法。
我们都知道,类中定义的方法内的 this
指向的是实例对象,但在静态方法中的 this
指向的是类对象
我们来看一个例子
class Person {
constructor() {
this.name = ‘Lpyexplore’
}
show() {
console.log(this.name)
}
static cite() {
this.show()
}
static show() {
console.log(‘我是静态方法show’)
}
}
Person.cite() // 我是静态方法show
var person = new Person()
person.show() // Lpyexplore
我们来分析一下这段代码:
首先我们直接调用 Person
类的静态方法 cite
,执行代码 this.show()
,因为静态方法中的 this
指向 Person
类,所以其实调用的就是静态方法 show
,所以打印了 我是非静态方法show
然后我们生成了一个实例对象 person
,调用 person
的 show
方法,因为在非静态方法 show
中,this
指向的是实例对象 person
,因此打印了 Lpyexplore
原先我们为实例对象定义的属性都是写在 constructor
函数中的,例如
class Person {
constructor() {
this.name = ‘Lpyexplore’
this.age = 18
}
show() {
console.log(‘hello world’)
}
}
var person = new Person()
console.log(person.name) // Lpyexplore
console.log(person.age) // 18
现在我们用实例对象的属性新写法来改写以上代码
class Person {
name = ‘Lpyexplore’
age = 18
show() {
console.log(‘hello world’)
}
}
var person = new Person()
console.log(person.name) // Lpyexplore
console.log(person.age) // 18
这种写法就是将 constructor
函数中的属性定义放到了外部,同时不需要写 this
,因为此时的属性定义与其他方法也处于同一个层级。因此这样的写法看上去就会比较一目了然,一眼就能看到实例对象有几个属性有几个方法。
虽然这样的写法比较简便,但也有一定的缺点,那就是用这种写法定义的属性是写死的。
我们都知道在生成实例对象时,可以传入参数,传入的参数会作为 constructor
函数的参数,所以我们在 constructor
函数中定义的属性的值就可以动态地根据参数改变而改变。
而实例属性的简易写法就无法根据参数的改变而改变,所以用这种写法的时候需要稍微注意一下。
既然有静态方法,那怎么能少了静态属性呢?其实,原本的 class
类中是没有静态属性这个概念的,后来才加上的。静态属性就只属于 class
类的属性,而不会被实例对象访问到的属性。
同样的,静态属性的申明就是在属性的前面加关键字 static
。上面我们刚讲到,实例对象的属性的定义可以不写在 constructor
函数中,而是直接写在外部,此时我们可以暂且称之为非静态属性
class Person {
name = ‘我是实例对象的name属性’
static name = ‘我是Person类的name属性’
static age = 18
}
console.log(Person.name) // 我是Person类的name属性
var person = new Person()
console.log(person.name) // 我是实例对象的name属性
console.log(person.age) // undefined
这段代码中,定义了非静态属性 name
、静态属性 name
和 静态属性 age
。
因此我们在访问 Person
类的 name
属性时,访问的是静态属性 name
,即加了关键字 static
的 name
属性;
生成实例对象 person
,访问其 name
属性,实际访问的就是非静态属性 name
,即没有加关键字 static
的 name
属性;
最后我们访问实例对象 person
的 age
属性,返回了 undefined
。因为 age
是静态属性,是属于 Person
类的,而不会被实例对象 person
访问到。
===================================================================
继承是面向对象编程中一个非常重要的概念,那什么是继承呢?
继承就是使一个类获得另一个类的属性和方法。就好比一个手艺精湛的师傅传授给你他所有的毕生绝学,那么就相当于你继承了他,此时你既学会了你师傅教你的技能,同时你也一定有属于自己的技能,这不是从你师傅那学来的。
其实在ES5中是通过修改原型链实现继承的,我们可以来看一下简单的例子
// 创建构造函数 Parent
function Parent() {
// 定义了实例对象属性 name1
this.name1 = ‘parent’
}
// 为 Parent原型定义方法 show1
Parent.prototype.show1 = function() {
console.log(‘我是Parent的show1方法’)
}
// 创建构造函数 Child
function Child() {
this.name2 = ‘child’
}
// 将构造函数 Child的原型设置成 Parent的实例对象
Child.prototype = new Parent()
// 为Child原型定义方法 show2
Child.prototype.show2 = function() {
console.log(‘我是Child的show2方法’)
}
// 生成实例对象 child
var child = new Child()
console.log(child.name1) // parent
console.log(child.name2) // child
child.show1() // 我是Parent的show1方法
child.show2() // 我是Child的show2方法
我们可以看到,我们通过改变构造函数 Child
的原型 prototype
为构造函数 Parent
生成的实例对象,实现了继承,即通过构造函数 Child
生成的实例对象具有 Parent
中定义的属性name1
和方法show1
,同时也具有属于自己的属性name2
和方法show2
ES5中实现继承的写法显然有些麻烦,所以在 class
类中,我们可以通过关键字 extends
来实现继承
我们来改写一下ES5中的继承实现
class Parent{
constructor() {
this.name1 = ‘parent’
}
show1() {
console.log(‘我是Parent的show1方法’)
}
}
// Child类 继承 Parent类
class Child extends Parent{
constructor() {
super();
this.name2 = ‘child’
}
show2() {
console.log(‘我是Child的show2方法’)
}
}
var child = new Child()
console.log(child.name1) // parent
console.log(child.name2) // child
child.show1() // 我是Parent的show1方法
child.show2() // 我是Child的show2方法
继承得实现整体上看上去非常得简洁
在上述代码中,我们看到了,我们在定义 Child
类时用到了关键字 extends
,申明了 Child
类继承Parent
类,同时在 Child
类得 constructor
函数中调用了 super
函数。仅仅用两个关键字就实现了继承,这里我们要对 super
进行详细得讲解
在ES6中规定了,在子类继承了父类以后,必须先在子类的 constructor
函数中调用 super
函数,其表示的就是父级的 constructor
函数,作用就是为子类生成 this
对象,将父类的属性和方法赋值到子类的 this
上。因此,若没有调用 super
函数,则子类无法获取到 this
对象,紧接着就会报错
class A{
constructor() {
this.name1 = ‘A’
}
}
class B extends A{
constructor() {
this.name2 = ‘B’
}
}
var b = new B()
/*
this.name2 = ‘B’
^
ReferenceError: Must call super constructor in derived class before accessing ‘this’ or returning from derived constructor
*/
上述代码中,B
类继承 A
类,但 B
类的 constructor
函数中没有调用 super
函数,因此没有生成 this
对象,所以 this.name2 = 'B'
就报错了
若子类省略了 constructor
函数,则默认会帮你调用 super
函数的
class A{
constructor() {
this.name1 = ‘A’
}
}
class B extends A{
}
var b = new B()
// 没有报错
super()
代表的是父类的构造函数,其实 super
还可以作为对象使用,即不作为函数调用。当 super
在子类的普通方法内时,指向的是父类的原型对象;在子类的静态方法内时,指向的时父类
class A{
show1() {
console.log(‘我是A类的show1方法’)
}
}
class B extends A{
constructor() {
super()
}
show2() {
super.show1()
}
}
var b = new B()
b.show2() // 我是A类的show1方法
上述代码,B
类继承 A
类,其中 A
类有一个 show1
方法,是写在其原型 A.prototype
上的,而在 B
类的 show2
方法中调用了 super.show1()
,我们说过 super
在普通的方法中指向的是父类的原型对象,所以 super.show1()
相当于 A.prototype.show1()
我们再来看一个 super
在子类的静态方法中的例子
class A{
static hide1() {
console.log(‘我是A类的hide1方法’)
}
}
class B extends A{
constructor() {
super()
}
static hide2() {
super.hide1()
}
}
B.hide2() // 我是A类的hide1方法
上述代码,B
类继承 A
类,B
类在其静态方法 hide2
中调用了 super.hide1()
,因为 super
在静态方法中指向的是父类,所以 super.hide1()
就相当于 A.hide1()
说到静态方法,其实类的继承,也是可以继承静态方法的
class A{
static show() {
console.log(‘我是A类的show方法’)
}
}
class B extends A{
}
B.show() // 我是A类的show方法
还需要注意的是,当我们在子类的普通方法中通过 super
调用父类的方法时,方法中的 this
指向的是当前子类的实例对象
class A {
constructor() {
this.name = ‘Jack’
}
show1() {
console.log(this.name)
}
}
class B extends A{
constructor() {
super();
this.name = ‘Lpyexplore’
}
show2() {
super.show1()
}
}
var b = new B()
b.show2() // Lpyexplore
那么,当我们在子类的静态方法中通过 super
调用父类的方法时,方法中的 this
指向的是子类,而不是子类的实例对象
class A {
constructor() {
this.x = 1
}
static show1() {
console.log(this.x)
}
}
class B extends A{
constructor() {
super();
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
前端资料汇总
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
我一直觉得技术面试不是考试,考前背背题,发给你一张考卷,答完交卷等通知。
首先,技术面试是一个 认识自己 的过程,知道自己和外面世界的差距。
更重要的是,技术面试是一个双向了解的过程,要让对方发现你的闪光点,同时也要 试图去找到对方的闪光点,因为他以后可能就是你的同事或者领导,所以,面试官问你有什么问题的时候,不要说没有了,要去试图了解他的工作内容、了解这个团队的氛围。
找工作无非就是看三点:和什么人、做什么事、给多少钱,要给这三者在自己的心里划分一个比例。
最后,祝愿大家在这并不友好的环境下都能找到自己心仪的归宿。
,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-WWBKrZLh-1710594501351)]
[外链图片转存中…(img-MuiFNCZP-1710594501351)]
[外链图片转存中…(img-kXFBzP9f-1710594501352)]
[外链图片转存中…(img-KOjS2YER-1710594501352)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
[外链图片转存中…(img-Usgd775a-1710594501353)]
前端资料汇总
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
我一直觉得技术面试不是考试,考前背背题,发给你一张考卷,答完交卷等通知。
首先,技术面试是一个 认识自己 的过程,知道自己和外面世界的差距。
更重要的是,技术面试是一个双向了解的过程,要让对方发现你的闪光点,同时也要 试图去找到对方的闪光点,因为他以后可能就是你的同事或者领导,所以,面试官问你有什么问题的时候,不要说没有了,要去试图了解他的工作内容、了解这个团队的氛围。
找工作无非就是看三点:和什么人、做什么事、给多少钱,要给这三者在自己的心里划分一个比例。
最后,祝愿大家在这并不友好的环境下都能找到自己心仪的归宿。